Pathfinder
Circles pathfinder calculates “how much and through which tokens can flow from A to B” based on trust and balances (wrapped and unwrapped). You can use it to:
Preview a route and achievable amount.
Feed
TransferBuilderor customoperateFlowMatrixcalls.
Entry points
High-level SDK:
const rpc = (sdk as any).rpc; rpc.pathfinder.findPath / findMaxFlowStandalone:
const rpc = new CirclesRpc(config.circlesRpcUrl);
Methods
findPath(params: FindPathParams): Promise<PathfindingResult>findMaxFlow(params: Omit<FindPathParams, 'targetFlow'>): Promise<bigint>
Parameters (FindPathParams)
from(Address, required): Source avatar.to(Address, required): Destination avatar.targetFlow(bigint, required forfindPath): Desired amount.useWrappedBalances(boolean, optional): Allow ERC20 wrappers in the route. Set true if you hold wrapped CRC.fromTokens/toTokens(Address[], optional): Only allow these token owners from sender/recipient sides.excludeFromTokens/excludeToTokens(Address[], optional): Ban specific token owners.simulatedBalances(array, optional): Inject hypothetical balances{ holder, token, amount, isWrapped, isStatic }.maxTransfers(number, optional): Hop cap; lower = faster, but may reduce flow.
findPath (targeted route)
findPath (targeted route)Computes a path for a requested amount; returns what is actually achievable plus hop-by-hop legs.
import { CirclesRpc } from '@aboutcircles/sdk-rpc';
const rpc = new CirclesRpc('https://rpc.circlesubi.network/');
const path = await rpc.pathfinder.findPath({
from: '0xSender',
to: '0xRecipient',
targetFlow: BigInt('10000000000000000000'), // 10 CRC
useWrappedBalances: true,
fromTokens: ['0xPreferredToken'], // optional pin
excludeToTokens: ['0xAvoidToken'], // optional ban
maxTransfers: 4
});
console.log(path.maxFlow); // bigint achievable
console.log(path.transfers); // [{ from, to, tokenOwner, value }]PathfindingResult:
maxFlow– achievable flow for this request.transfers– ordered legs;tokenOwneris the ERC1155 owner or wrapper used in that hop.
If maxFlow < targetFlow, decide to scale down or show an error.
findMaxFlow (ceiling)
findMaxFlow (ceiling)Returns the absolute maximum possible flow between two avatars with the same options.
const max = await rpc.pathfinder.findMaxFlow({
from: '0xSender',
to: '0xRecipient',
useWrappedBalances: true,
simulatedBalances: [
{ holder: '0xSender', token: '0xToken', amount: 5n * 10n ** 18n, isWrapped: false, isStatic: false }
],
});Use for sliders, validation, or deciding if you must unwrap/wrap first.
Advanced usage
Wrapped tokens:
useWrappedBalances: trueenables consuming inflationary/demurraged ERC20 wrappers the sender holds.Token pinning/bans: Use
fromTokens/toTokensto force specific personal or group tokens; use theexclude*variants to forbid costly or undesired wrappers.Simulations:
simulatedBalanceslet you preview flows after an expected mint/wrap without waiting for indexer updates.Hop limit:
maxTransferscurbs route length; helpful for UX responsiveness but may lowermaxFlow.
From path to execution
Easiest: call
TransferBuilder.constructAdvancedTransfer(from, to, amount, options)—it runsfindPath, adds unwrap/rewrap, approvals, and returns an ordered tx array.Manual: convert
PathfindingResulttooperateFlowMatrix:
import { createFlowMatrix } from '@aboutcircles/sdk-pathfinder';
import { Core } from '@aboutcircles/sdk-core';
const core = new Core();
const rpc = new CirclesRpc(core.config.circlesRpcUrl);
const path = await rpc.pathfinder.findPath({ from, to, targetFlow, useWrappedBalances: true });
const flow = createFlowMatrix(from, to, path.maxFlow, path.transfers);
const tx = core.hubV2.operateFlowMatrix(
flow.flowVertices,
flow.flowEdges,
flow.streams,
flow.packedCoordinates
);
// send tx via your runnerPathfinder helper functions (when you build flows yourself)
createFlowMatrix(from, to, maxFlow, transfers)– turns aPathfindingResultintoflowVertices,flowEdges,streams,packedCoordinatesforhubV2.operateFlowMatrix.packCoordinates(flowVertices)/transformToFlowVertices(transfers)– lower-level helpers used bycreateFlowMatrixfor packing vertex coordinates.getTokenInfoMapFromPath(from, rpcUrl, path)– fetches token metadata for all tokens/wrappers in a path; feed into other helpers.getWrappedTokensFromPath(path, tokenInfoMap)– list wrapped tokens present in the path.getExpectedUnwrappedTokenTotals(path, tokenInfoMap)– estimate ERC1155 amounts recovered after unwrapping wrappers used in the path.replaceWrappedTokensWithAvatars(path, tokenInfoMap)– rewrite path legs to underlying avatar addresses (for ERC1155 semantics).replaceWrappedTokens(path, unwrappedMap)– replace wrapped token addresses using a precomputed mapping.shrinkPathValues(path, sink, retainBps)– scale down flows proportionally (e.g., execute only 70% of a path).assertNoNettedFlowMismatch(path)– throw if transfers do not net to zero (consistency check).computeNettedFlow(path)– derive net inflow/outflow per address for diagnostics.
Error handling & tips
maxFlow === 0: check mutual trust or unwrap static/demurraged balances back to ERC1155.Paths needing wrapped balances will fail unless
useWrappedBalancesis true.
Last updated
Was this helpful?