diff --git a/package-lock.json b/package-lock.json index 8c1aee5..0119e53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cirlces-sdk/root", - "version": "0.12.2", + "version": "0.14.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@cirlces-sdk/root", - "version": "0.12.2", + "version": "0.14.0", "license": "MIT", "workspaces": [ "packages/abi-v1", @@ -6531,7 +6531,7 @@ }, "packages/abi-v1": { "name": "@circles-sdk/abi-v1", - "version": "0.12.2", + "version": "0.14.0", "license": "MIT", "dependencies": { "ethers": "^6.13.2" @@ -6542,7 +6542,7 @@ }, "packages/abi-v2": { "name": "@circles-sdk/abi-v2", - "version": "0.12.2", + "version": "0.14.0", "license": "MIT", "dependencies": { "ethers": "^6.13.2" @@ -6553,7 +6553,7 @@ }, "packages/adapter": { "name": "@circles-sdk/adapter", - "version": "0.12.2", + "version": "0.14.0", "license": "MIT", "devDependencies": { "typescript": "^5.3.3" @@ -6561,10 +6561,10 @@ }, "packages/adapter-cometh": { "name": "@circles-sdk/adapter-cometh", - "version": "0.12.2", + "version": "0.14.0", "license": "MIT", "dependencies": { - "@circles-sdk/adapter": "0.12.2", + "@circles-sdk/adapter": "0.14.0", "@cometh/connect-sdk": "1.2.29" }, "devDependencies": { @@ -6573,11 +6573,11 @@ }, "packages/adapter-ethers": { "name": "@circles-sdk/adapter-ethers", - "version": "0.12.2", + "version": "0.14.0", "license": "MIT", "dependencies": { - "@circles-sdk/adapter": "0.12.2", - "@circles-sdk/utils": "0.12.2", + "@circles-sdk/adapter": "0.14.0", + "@circles-sdk/utils": "0.14.0", "ethers": "^6.13.2" }, "devDependencies": { @@ -6586,7 +6586,7 @@ }, "packages/adapter-safe": { "name": "@circles-sdk/adapter-safe", - "version": "0.12.2", + "version": "0.14.0", "license": "MIT", "dependencies": { "@circles-sdk/adapter": "^0.10.0", @@ -6599,10 +6599,10 @@ }, "packages/adapter-safe-app": { "name": "@circles-sdk/adapter-safe-app", - "version": "0.12.2", + "version": "0.14.0", "license": "MIT", "dependencies": { - "@circles-sdk/adapter": "0.12.2", + "@circles-sdk/adapter": "0.14.0", "@safe-global/safe-apps-sdk": "^9.1.0" }, "devDependencies": { @@ -6615,10 +6615,10 @@ }, "packages/data": { "name": "@circles-sdk/data", - "version": "0.12.2", + "version": "0.14.0", "license": "MIT", "dependencies": { - "@circles-sdk/utils": "0.12.2" + "@circles-sdk/utils": "0.14.0" }, "devDependencies": { "typescript": "^5.3.3" @@ -6626,10 +6626,10 @@ }, "packages/profiles": { "name": "@circles-sdk/profiles", - "version": "0.12.2", + "version": "0.14.0", "license": "ISC", "dependencies": { - "@circles-sdk/utils": "0.12.2" + "@circles-sdk/utils": "0.14.0" }, "devDependencies": { "typescript": "^5.3.3" @@ -6637,14 +6637,14 @@ }, "packages/sdk": { "name": "@circles-sdk/sdk", - "version": "0.12.2", + "version": "0.14.0", "license": "MIT", "dependencies": { - "@circles-sdk/abi-v1": "0.12.2", - "@circles-sdk/abi-v2": "0.12.2", - "@circles-sdk/adapter-ethers": "0.12.2", - "@circles-sdk/data": "0.12.2", - "@circles-sdk/profiles": "0.12.2", + "@circles-sdk/abi-v1": "0.14.0", + "@circles-sdk/abi-v2": "0.14.0", + "@circles-sdk/adapter-ethers": "0.14.0", + "@circles-sdk/data": "0.14.0", + "@circles-sdk/profiles": "0.14.0", "ethers": "^6.13.2", "multihashes": "^4.0.3" }, @@ -6655,7 +6655,7 @@ }, "packages/utils": { "name": "@circles-sdk/utils", - "version": "0.12.2", + "version": "0.14.0", "license": "MIT", "dependencies": { "bignumber.js": "^9.1.2", diff --git a/package.json b/package.json index fb7b93f..0042b10 100644 --- a/package.json +++ b/package.json @@ -36,5 +36,5 @@ }, "name": "@cirlces-sdk/root", "license": "MIT", - "version": "0.13.4" + "version": "0.14.0" } diff --git a/packages/abi-v1/package.json b/packages/abi-v1/package.json index 4c59b83..6084b9b 100644 --- a/packages/abi-v1/package.json +++ b/packages/abi-v1/package.json @@ -1,6 +1,6 @@ { "name": "@circles-sdk/abi-v1", - "version": "0.13.4", + "version": "0.14.0", "description": "", "type": "module", "main": "./dist/index.js", diff --git a/packages/abi-v2/package.json b/packages/abi-v2/package.json index cf69eac..40db207 100644 --- a/packages/abi-v2/package.json +++ b/packages/abi-v2/package.json @@ -1,6 +1,6 @@ { "name": "@circles-sdk/abi-v2", - "version": "0.13.4", + "version": "0.14.0", "description": "", "type": "module", "main": "./dist/index.js", diff --git a/packages/adapter-cometh/package.json b/packages/adapter-cometh/package.json index abc94a3..f2de349 100644 --- a/packages/adapter-cometh/package.json +++ b/packages/adapter-cometh/package.json @@ -1,6 +1,6 @@ { "name": "@circles-sdk/adapter-cometh", - "version": "0.13.4", + "version": "0.14.0", "description": "", "type": "module", "main": "./dist/index.js", @@ -17,7 +17,7 @@ "build": "rollup -c" }, "dependencies": { - "@circles-sdk/adapter": "0.13.4", + "@circles-sdk/adapter": "0.14.0", "@cometh/connect-sdk": "1.2.29" }, "keywords": [], diff --git a/packages/adapter-ethers/package.json b/packages/adapter-ethers/package.json index ccdf384..115460b 100644 --- a/packages/adapter-ethers/package.json +++ b/packages/adapter-ethers/package.json @@ -1,6 +1,6 @@ { "name": "@circles-sdk/adapter-ethers", - "version": "0.13.4", + "version": "0.14.0", "description": "", "type": "module", "main": "./dist/index.js", @@ -18,8 +18,8 @@ }, "dependencies": { "ethers": "^6.13.2", - "@circles-sdk/adapter": "0.13.4", - "@circles-sdk/utils": "0.13.4" + "@circles-sdk/adapter": "0.14.0", + "@circles-sdk/utils": "0.14.0" }, "keywords": [], "author": "", diff --git a/packages/adapter-safe-app/package.json b/packages/adapter-safe-app/package.json index bef0f0e..514bdc2 100644 --- a/packages/adapter-safe-app/package.json +++ b/packages/adapter-safe-app/package.json @@ -1,6 +1,6 @@ { "name": "@circles-sdk/adapter-safe-app", - "version": "0.13.4", + "version": "0.14.0", "description": "", "type": "module", "main": "./dist/index.js", @@ -17,7 +17,7 @@ "build": "rollup -c" }, "dependencies": { - "@circles-sdk/adapter": "0.13.4", + "@circles-sdk/adapter": "0.14.0", "@safe-global/safe-apps-sdk": "^9.1.0" }, "keywords": [], diff --git a/packages/adapter-safe/package.json b/packages/adapter-safe/package.json index c0de02d..1447823 100644 --- a/packages/adapter-safe/package.json +++ b/packages/adapter-safe/package.json @@ -1,6 +1,6 @@ { "name": "@circles-sdk/adapter-safe", - "version": "0.13.4", + "version": "0.14.0", "description": "", "type": "module", "main": "./dist/index.js", diff --git a/packages/adapter/package.json b/packages/adapter/package.json index b79f004..3406968 100644 --- a/packages/adapter/package.json +++ b/packages/adapter/package.json @@ -1,6 +1,6 @@ { "name": "@circles-sdk/adapter", - "version": "0.13.4", + "version": "0.14.0", "description": "", "type": "module", "main": "./dist/index.js", diff --git a/packages/data/package.json b/packages/data/package.json index fdf3d89..e384767 100644 --- a/packages/data/package.json +++ b/packages/data/package.json @@ -1,6 +1,6 @@ { "name": "@circles-sdk/data", - "version": "0.13.4", + "version": "0.14.0", "description": "", "type": "module", "main": "./dist/index.js", @@ -17,7 +17,7 @@ "build": "rollup -c" }, "dependencies": { - "@circles-sdk/utils": "0.13.4" + "@circles-sdk/utils": "0.14.0" }, "keywords": [], "author": "", diff --git a/packages/profiles/package.json b/packages/profiles/package.json index 7fb57c7..1881f45 100644 --- a/packages/profiles/package.json +++ b/packages/profiles/package.json @@ -1,6 +1,6 @@ { "name": "@circles-sdk/profiles", - "version": "0.13.4", + "version": "0.14.0", "description": "", "type": "module", "main": "./dist/index.js", @@ -17,7 +17,7 @@ "build": "rollup -c" }, "dependencies": { - "@circles-sdk/utils": "0.13.4" + "@circles-sdk/utils": "0.14.0" }, "keywords": [], "author": "", diff --git a/packages/profiles/src/index.ts b/packages/profiles/src/index.ts index be3b35d..e8412dd 100644 --- a/packages/profiles/src/index.ts +++ b/packages/profiles/src/index.ts @@ -1,71 +1,72 @@ export interface Profile { - name: string; - description?: string; - previewImageUrl?: string; - imageUrl?: string; + name: string; + description?: string; + previewImageUrl?: string; + imageUrl?: string; + extensions?: Record; } export interface GroupProfile extends Profile { - symbol: string; + symbol: string; } export class Profiles { - constructor(private readonly profileServiceUrl: string) { - } - - private getProfileServiceUrl(): string { - return this.profileServiceUrl.endsWith('/') ? this.profileServiceUrl : `${this.profileServiceUrl}/`; - } + constructor(private readonly profileServiceUrl: string) { + } - async create(profile: Profile): Promise { - const response = await fetch(`${this.getProfileServiceUrl()}pin`, { - method: 'POST', - headers: {'Content-Type': 'application/json'}, - body: JSON.stringify(profile) - }); + private getProfileServiceUrl(): string { + return this.profileServiceUrl.endsWith('/') ? this.profileServiceUrl : `${this.profileServiceUrl}/`; + } - if (!response.ok) { - throw new Error(`Failed to create profile. Status: ${response.status} ${response.statusText}. Body: ${await response.text()}`); - } + async create(profile: Profile): Promise { + const response = await fetch(`${this.getProfileServiceUrl()}pin`, { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify(profile) + }); - const data = await response.json(); - return data.cid; + if (!response.ok) { + throw new Error(`Failed to create profile. Status: ${response.status} ${response.statusText}. Body: ${await response.text()}`); } - /** - * Retrieves a profile by its CID. If the profile is not found, an error is thrown. - * @param cid The CID of the profile to retrieve. - */ - async get(cid: string): Promise { - const response = await fetch(`${this.getProfileServiceUrl()}get?cid=${cid}`); - if (!response.ok) { - console.warn(`Failed to retrieve profile ${cid}. Status: ${response.status} ${response.statusText}. Body: ${await response.text()}`); - return undefined; - } + const data = await response.json(); + return data.cid; + } - return await response.json(); + /** + * Retrieves a profile by its CID. If the profile is not found, an error is thrown. + * @param cid The CID of the profile to retrieve. + */ + async get(cid: string): Promise { + const response = await fetch(`${this.getProfileServiceUrl()}get?cid=${cid}`); + if (!response.ok) { + console.warn(`Failed to retrieve profile ${cid}. Status: ${response.status} ${response.statusText}. Body: ${await response.text()}`); + return undefined; } - /** - * Retrieves multiple profiles by their CIDs. If a profile is not found, it will not be included in the result. - * @param cids The CIDs of the profiles to retrieve. - * @returns A map of CIDs to profiles. If a profile is not found, it will not be included in the map. - */ - async getMany(cids: string[]): Promise> { - const response = await fetch(`${this.getProfileServiceUrl()}getBatch?cids=${cids.join(',')}`); - if (!response.ok) { - throw new Error(`Failed to retrieve profiles ${cids.join(',')}. Status: ${response.status} ${response.statusText}. Body: ${await response.text()}`); - } + return await response.json(); + } - const profilesArray = await response.json(); - const profiles: Record = {}; + /** + * Retrieves multiple profiles by their CIDs. If a profile is not found, it will not be included in the result. + * @param cids The CIDs of the profiles to retrieve. + * @returns A map of CIDs to profiles. If a profile is not found, it will not be included in the map. + */ + async getMany(cids: string[]): Promise> { + const response = await fetch(`${this.getProfileServiceUrl()}getBatch?cids=${cids.join(',')}`); + if (!response.ok) { + throw new Error(`Failed to retrieve profiles ${cids.join(',')}. Status: ${response.status} ${response.statusText}. Body: ${await response.text()}`); + } - for (let i = 0; i < cids.length; i++) { - if (profilesArray[i]) { - profiles[cids[i]] = profilesArray[i]; - } - } + const profilesArray = await response.json(); + const profiles: Record = {}; - return profiles; + for (let i = 0; i < cids.length; i++) { + if (profilesArray[i]) { + profiles[cids[i]] = profilesArray[i]; + } } + + return profiles; + } } \ No newline at end of file diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 7863e89..c6fb4b4 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@circles-sdk/sdk", - "version": "0.13.4", + "version": "0.14.0", "description": "", "type": "module", "main": "./dist/index.js", @@ -17,11 +17,11 @@ "author": "", "license": "MIT", "dependencies": { - "@circles-sdk/abi-v1": "0.13.4", - "@circles-sdk/abi-v2": "0.13.4", - "@circles-sdk/data": "0.13.4", - "@circles-sdk/profiles": "0.13.4", - "@circles-sdk/adapter-ethers": "0.13.4", + "@circles-sdk/abi-v1": "0.14.0", + "@circles-sdk/abi-v2": "0.14.0", + "@circles-sdk/data": "0.14.0", + "@circles-sdk/profiles": "0.14.0", + "@circles-sdk/adapter-ethers": "0.14.0", "ethers": "^6.13.2", "multihashes": "^4.0.3" }, diff --git a/packages/sdk/src/circlesConfig.ts b/packages/sdk/src/circlesConfig.ts index 962b8a8..6f3ab77 100644 --- a/packages/sdk/src/circlesConfig.ts +++ b/packages/sdk/src/circlesConfig.ts @@ -1,5 +1,4 @@ export interface CirclesConfig { - readonly v2PathfinderUrl?: string; readonly pathfinderUrl?: string; readonly circlesRpcUrl: string; readonly profileServiceUrl?: string; diff --git a/packages/sdk/src/config.ts b/packages/sdk/src/config.ts index 413c127..bb0f7ec 100644 --- a/packages/sdk/src/config.ts +++ b/packages/sdk/src/config.ts @@ -5,7 +5,6 @@ export const circlesConfig: { [chainId: number]: CirclesConfig } = { circlesRpcUrl: "https://rpc.aboutcircles.com/", pathfinderUrl: "https://pathfinder.aboutcircles.com", profileServiceUrl: "https://rpc.aboutcircles.com/profiles/", - v2PathfinderUrl: "https://rpc.aboutcircles.com/pathfinder/", v1HubAddress: "0x29b9a7fbb8995b2423a71cc17cf9810798f6c543", v2HubAddress: "0xc12C1E50ABB450d6205Ea2C3Fa861b3B834d13e8", nameRegistryAddress: "0xA27566fD89162cC3D40Cb59c87AAaA49B85F3474", diff --git a/packages/sdk/src/sdk.ts b/packages/sdk/src/sdk.ts index 781c6ad..66c86b6 100644 --- a/packages/sdk/src/sdk.ts +++ b/packages/sdk/src/sdk.ts @@ -21,6 +21,7 @@ import {GroupProfile, Profile, Profiles} from '@circles-sdk/profiles'; import {ContractRunner, ContractTransactionReceipt, ZeroAddress} from "ethers"; import {SdkContractRunner, TransactionRequest} from "@circles-sdk/adapter"; import {circlesConfig} from "./config"; +import {V2Pathfinder} from "./v2/pathfinderV2"; /** * The SDK interface. @@ -51,6 +52,10 @@ interface SdkInterface { * An instance of the v1 Pathfinder client (necessary for transfers; only available on gnosis chain with v1 Circles at the moment). */ v1Pathfinder?: Pathfinder; + /** + * An instance of the v2 Pathfinder client. + */ + v2Pathfinder?: V2Pathfinder; /** * Stores and retrieves profiles from the Circles profile service. */ @@ -143,7 +148,7 @@ export class Sdk implements SdkInterface { /** * The pathfinder client (v2). */ - readonly v2Pathfinder?: Pathfinder; + readonly v2Pathfinder: V2Pathfinder; /** * The profiles service client. */ @@ -163,6 +168,7 @@ export class Sdk implements SdkInterface { } this.circlesRpc = new CirclesRpc(this.circlesConfig.circlesRpcUrl); + this.v2Pathfinder = new V2Pathfinder(this.circlesConfig.circlesRpcUrl); this.data = new CirclesData(this.circlesRpc); this.v1Hub = HubV1Factory.connect(this.circlesConfig.v1HubAddress ?? '0x29b9a7fBb8995b2423a71cC17cf9810798F6C543', this.contractRunner); if (this.circlesConfig.v2HubAddress) { @@ -171,9 +177,6 @@ export class Sdk implements SdkInterface { if (this.circlesConfig.pathfinderUrl) { this.v1Pathfinder = new Pathfinder(this.circlesConfig.pathfinderUrl); } - if (this.circlesConfig.v2PathfinderUrl) { - this.v2Pathfinder = new Pathfinder(this.circlesConfig.v2PathfinderUrl); - } if (this.circlesConfig.nameRegistryAddress) { this.nameRegistry = NameRegistry__factory.connect(this.circlesConfig.nameRegistryAddress, this.contractRunner); } diff --git a/packages/sdk/src/v2/pathfinderV2.ts b/packages/sdk/src/v2/pathfinderV2.ts index 796d0a7..c99a9c0 100644 --- a/packages/sdk/src/v2/pathfinderV2.ts +++ b/packages/sdk/src/v2/pathfinderV2.ts @@ -1,3 +1,5 @@ +import {CirclesRpc} from "@circles-sdk/data"; + export interface TransferPathStep { readonly from: string; readonly to: string; @@ -5,36 +7,17 @@ export interface TransferPathStep { readonly value: string; } -type ApiTransferStep = { - from: string; - to: string; - token_owner: string; - value: string; -}; - -type directPathResponse = { - data?: { - directPath?: { - requestedAmount: string; - flow: unknown; - transfers: TransferPathStep[]; - isValid?: boolean; - }; - }; -}; - type FlowEdge = { streamSinkId: number; amount: bigint; }; type Stream = { - sourceCoordinate: bigint, - flowEdgeIds: number[], - data: Uint8Array -} + sourceCoordinate: number; + flowEdgeIds: number[]; + data: Uint8Array; +}; -// Define FlowMatrix type type FlowMatrix = { flowVertices: string[]; flowEdges: FlowEdge[]; @@ -43,68 +26,63 @@ type FlowMatrix = { sourceCoordinate: number; }; +export type MaxFlowResponse = { + maxFlow: string; + transfers: TransferPathStep[]; +}; -export class Pathfinder { - pathfinderURL: string; +export interface TransferPathStep { + readonly from: string; + readonly to: string; + readonly tokenOwner: string; + readonly value: string; +} + +export class V2Pathfinder { + private rpc: CirclesRpc; + + constructor(circlesRpcUrl: string) { + this.rpc = new CirclesRpc(circlesRpcUrl); // Using CirclesRpc class + } - constructor(pathfinderURL: string) { - this.pathfinderURL = pathfinderURL; + async getMaxFlow(from: string, to: string): Promise { + const requestBody = { + Source: from, + Sink: to, + TargetFlow: "99999999999999999999999999999999999999", // A large target flow + }; + + const response = await this.rpc.call('circlesV2_findPath', [requestBody]); + return BigInt(response.result.maxFlow); + } + + async getPath(from: string, to: string, value: string): Promise { + const requestBody = { + Source: from, + Sink: to, + TargetFlow: value.toString(), + }; + + const response = await this.rpc.call('circlesV2_findPath', [requestBody]); + return response.result; } async getArgsForPath(from: string, to: string, value: string): Promise { - const query = { - method: 'compute_transfer', - params: { from, to, value: value.toString() } + const requestBody = { + Source: from, + Sink: to, + TargetFlow: value.toString(), }; - try { - const response = await fetch(this.pathfinderURL, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(query) - }); - - if (!response.ok) { - throw new Error(`Error calling API: ${response.status}`); - } - - const parsed = await response.json(); - - const transformedResponse: directPathResponse = { - data: { - directPath: { - requestedAmount: value, - flow: parsed.result.maxFlowValue, - transfers: parsed.result.transferSteps.map((step: ApiTransferStep) => ({ - from: step.from, - to: step.to, - tokenOwner: step.token_owner, - value: step.value - })), - isValid: parsed.result.final - } - } - }; - - // todo: this is not great, I mangle the PathFinder response; should split these functions - if (transformedResponse.data?.directPath) { - const flowMatrix = createFlowMatrix(from, to, value, transformedResponse.data.directPath.transfers); - - return flowMatrix; - } else { - throw new Error('Invalid response from pathfinder'); - } - - } catch (error) { - if (error instanceof Error) { - throw error; - } else { - throw new Error('An unknown error occurred'); - } + const response = await this.rpc.call('circlesV2_findPath', [requestBody]); + const transfers = response.result.transfers; + + if (transfers.length > 0) { + return createFlowMatrix(from, to, value, transfers); + } else { + throw new Error('No transfers found in response from pathfinder'); } - }; + } } function transformToFlowVertices(transfers: TransferPathStep[], from: string, to: string) { @@ -118,7 +96,7 @@ function transformToFlowVertices(transfers: TransferPathStep[], from: string, to addressSet.add(transfer.tokenOwner.toLowerCase()); } - // Convert addresses to uint160 and sort + // Convert addresses to BigInt and sort const sortedAddresses = Array.from(addressSet).sort((a, b) => { const uint160A = BigInt(a); const uint160B = BigInt(b); @@ -133,7 +111,7 @@ function transformToFlowVertices(transfers: TransferPathStep[], from: string, to return { sortedAddresses: sortedAddresses, - lookUpMap: lookUpMap + lookUpMap: lookUpMap, }; } @@ -141,44 +119,56 @@ function packCoordinates(coordinates: number[]): Uint8Array { const packedCoordinates = new Uint8Array(coordinates.length * 2); for (let i = 0; i < coordinates.length; i++) { packedCoordinates[2 * i] = coordinates[i] >> 8; // High byte - packedCoordinates[2 * i + 1] = coordinates[i] & 0xFF; // Low byte + packedCoordinates[2 * i + 1] = coordinates[i] & 0xff; // Low byte } return packedCoordinates; } function createFlowMatrix(from: string, to: string, value: string, transfers: TransferPathStep[]): FlowMatrix { + const expectedValue = BigInt(value); + // Transform transfers to flow matrix structure with normalized addresses - const { sortedAddresses, lookUpMap } = transformToFlowVertices(transfers, from.toLowerCase(), to.toLowerCase()); + const { + sortedAddresses, + lookUpMap + } = transformToFlowVertices(transfers, from.toLowerCase(), to.toLowerCase()); // Initialize flow edges - const flowEdges: FlowEdge[] = transfers.map((transfer, index) => ({ + const flowEdges: FlowEdge[] = transfers.map((transfer) => ({ streamSinkId: transfer.to.toLowerCase() === to.toLowerCase() ? 1 : 0, // Set streamSinkId to 1 if transfer.to matches the given 'to' address - amount: BigInt(transfer.value) // Convert string value to bigint + amount: BigInt(transfer.value), // Convert string value to bigint })); - // If no terminal edge was found, set the last edge as terminal by default - if (!flowEdges.some(edge => edge.streamSinkId === 1)) { - flowEdges[flowEdges.length - 1].streamSinkId = 1; + // Ensure at least one terminal edge is marked + if (!flowEdges.some((edge) => edge.streamSinkId === 1)) { + // Find the last edge where transfer.to matches the sink address + const lastIndex = transfers.map((t) => t.to.toLowerCase()).lastIndexOf(to.toLowerCase()); + if (lastIndex !== -1) { + flowEdges[lastIndex].streamSinkId = 1; + } else { + // If not found, set the last edge as terminal by default + flowEdges[flowEdges.length - 1].streamSinkId = 1; + } } // Check if the sum of terminal amounts matches the provided value const totalTerminalAmount = flowEdges - .filter(edge => edge.streamSinkId === 1) + .filter((edge) => edge.streamSinkId === 1) .reduce((sum, edge) => sum + edge.amount, BigInt(0)); - if (totalTerminalAmount !== BigInt(value)) { - throw new Error(`The total terminal amount (${totalTerminalAmount}) does not match the provided value (${value}).`); + if (totalTerminalAmount !== expectedValue) { + throw new Error(`The total terminal amount (${totalTerminalAmount}) does not match the provided value (${expectedValue}).`); } // Initialize stream object const flowEdgeIds: number[] = flowEdges .map((edge, index) => (edge.streamSinkId === 1 ? index : -1)) - .filter(index => index !== -1); + .filter((index) => index !== -1); const stream: Stream = { - sourceCoordinate: BigInt(lookUpMap[from.toLowerCase()]), + sourceCoordinate: lookUpMap[from.toLowerCase()], flowEdgeIds: flowEdgeIds, - data: new Uint8Array() // Empty bytes for now + data: new Uint8Array(), // Empty bytes for now }; // Get coordinates for each triple (tokenOwner, sender, receiver) and pack them @@ -195,6 +185,6 @@ function createFlowMatrix(from: string, to: string, value: string, transfers: Tr flowEdges: flowEdges, streams: [stream], packedCoordinates: packedCoordinates, - sourceCoordinate: lookUpMap[from.toLowerCase()] // Add sourceCoordinate using the lookup map + sourceCoordinate: lookUpMap[from.toLowerCase()], // Add sourceCoordinate using the lookup map }; } diff --git a/packages/sdk/src/v2/v2Avatar.ts b/packages/sdk/src/v2/v2Avatar.ts index c326480..c599963 100644 --- a/packages/sdk/src/v2/v2Avatar.ts +++ b/packages/sdk/src/v2/v2Avatar.ts @@ -14,8 +14,8 @@ import { TransactionHistoryRow, TrustRelationRow } from '@circles-sdk/data'; -import { addressToUInt256, attoCirclesToCircles, cidV0ToUint8Array } from '@circles-sdk/utils'; -import {Pathfinder} from './pathfinderV2'; +import {addressToUInt256, attoCirclesToCircles, cidV0ToUint8Array} from '@circles-sdk/utils'; +import {V2Pathfinder} from './pathfinderV2'; import {Profile} from "@circles-sdk/profiles"; import {TokenType} from "@circles-sdk/data/dist/rows/tokenInfoRow"; import {BatchRun, TransactionRequest, TransactionResponse} from "@circles-sdk/adapter"; @@ -89,21 +89,8 @@ export class V2Avatar implements AvatarInterfaceV2 { return tokenBalance?.circles ?? 0; } - const largeAmount = BigInt('79228162514264337593543950335'); - const transferPath = await this.sdk.v2Pathfinder!.getTransferPath( - this.address, - to, - largeAmount); - - if (transferPath.transferSteps.length == 0) { - return 0; - } - - if (!transferPath.isValid) { - return 0; - } - - return attoCirclesToCircles(transferPath.maxFlow); + const result = await this.sdk.v2Pathfinder.getMaxFlow(this.address, to); + return attoCirclesToCircles(result); } async getMintableAmount(): Promise { @@ -162,8 +149,7 @@ export class V2Avatar implements AvatarInterfaceV2 { private async transitiveTransfer(to: string, amount: bigint, batch: BatchRun) { this.throwIfV2IsNotAvailable(); - const pathfinder = new Pathfinder(this.sdk.circlesConfig.v2PathfinderUrl!); - const flowMatrix = await pathfinder.getArgsForPath(this.address, to, amount.toString()); + const flowMatrix = await this.sdk.v2Pathfinder.getArgsForPath(this.address, to, amount.toString()); if (!this.sdk.v2Hub) { throw new Error('V2Hub not available'); diff --git a/packages/utils/package.json b/packages/utils/package.json index b7a1000..829a4f1 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@circles-sdk/utils", - "version": "0.13.4", + "version": "0.14.0", "description": "", "type": "module", "main": "./dist/index.js",