diff --git a/docs/docs/03-sdk/04-methods-reference/transfer/sweep.md b/docs/docs/03-sdk/04-methods-reference/transfer/sweep.md
new file mode 100644
index 0000000..0dc75cf
--- /dev/null
+++ b/docs/docs/03-sdk/04-methods-reference/transfer/sweep.md
@@ -0,0 +1,141 @@
+---
+id: sweep
+title: sweep
+sidebar_position: 3
+---
+
+# `sweep`
+
+The `sweep` method generates a quote to transfer the full token balances of source chains to a destination chain.
+
+## Usage
+
+```typescript
+import { Sprinter, Environment } from "@chainsafe/sprinter-sdk";
+
+const sprinter = new Sprinter({ baseUrl: Environment.TESTNET });
+
+const settings = {
+ account: "0x3E101Ec02e7A48D16DADE204C96bFF842E7E2519",
+ destinationChain: 11155111,
+ token: "USDC",
+};
+
+sprinter.sweep(settings).then((solution) => {
+ console.log(solution);
+});
+```
+
+## Parameters
+
+- `settings`: _(Required)_ An object containing the following fields:
+
+ - `account`: The user’s address.
+ - `destinationChain`: The ID of the destination chain.
+ - `token`: The symbol of the token to be transferred (e.g., `USDC`, `ETH`).
+ - `recipient?`: _(Optional)_ The address of the recipient of the tokens on the destination chain.
+ - `sourceChains?`: _(Optional)_ An array of source chain IDs to be considered for the sweep. If omitted, Sprinter will use all available chains for the solution. To limit the solution to a specific chain, provide an array containing only that chain's ID.
+
+- `fetchOptions?`: _(Optional)_ An object containing `baseUrl` to override the default API endpoint for this request.
+
+### Example: Using `sourceChains` for sweeping from specific chains
+
+To get a sweep solution from specific chains, you can set `sourceChains` to an array with chain IDs.
+
+```typescript
+const settings = {
+ account: "0xYourAddressHere",
+ destinationChain: 11155111, // Sepolia testnet
+ token: "USDC",
+ sourceChains: [84532, 137],
+};
+
+sprinter.sweep(settings).then((solution) => {
+ console.log(solution);
+});
+```
+
+### Example: Using `fetchOptions`
+
+```typescript
+sprinter.sweep(settings, { baseUrl: "https://custom.api.url" }).then((solution) => {
+ console.log(solution);
+});
+```
+
+## Response
+
+Returns a promise that resolves to a `SolutionResponse`.
+
+```typescript
+type SolutionResponse = Array | FailedSolution;
+
+interface Solution {
+ destinationChain: number;
+ destinationTokenAddress: string;
+ duration: number; // Time estimate in seconds
+ fee: Amount;
+ gasCost: Amount;
+ senderAddress: string;
+ sourceChain: number;
+ sourceTokenAddress: string;
+ tool: Tool;
+ transaction: Transaction;
+ approvals?: Array;
+ amount: Amount;
+}
+
+interface FailedSolution {
+ error: string;
+}
+```
+
+### Example Response
+
+import GasTip from "../\_gas-tip.md"
+
+
+
+```json
+[
+ {
+ "sourceChain": 84532,
+ "destinationChain": 11155111,
+ "sourceTokenAddress": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
+ "destinationTokenAddress": "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
+ "senderAddress": "0x3E101Ec02e7A48D16DADE204C96bFF842E7E2519",
+ "tool": {
+ "name": "Across",
+ "logoURI": "https://raw.githubusercontent.com/lifinance/types/main/src/assets/icons/bridges/acrossv2.png"
+ },
+ "gasCost": {
+ "amount": "130680140710000",
+ "amountUSD": 0
+ },
+ "fee": {
+ "amount": "6239846",
+ "amountUSD": 0
+ },
+ "amount": "1178950000",
+ "duration": 120000000000,
+ "transaction": {
+ "data": "0x7b9392320000000000000000000000003e101ec02e7a48d16dade204c96bff842e7e25190000000000000000000000003e101ec02e7a48d16dade204c96bff842e7e2519000000000000000000000000036cbd53842c5426634e7929541ec2318f3dcf7e0000000000000000000000001c7d4b196cb0c7b01d743fbc6116a902379c723800000000000000000000000000000000000000000000000000000000464559700000000000000000000000000000000000000000000000000000000045e6230a0000000000000000000000000000000000000000000000000000000000aa36a70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006780030c000000000000000000000000000000000000000000000000000000006780576c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000001dc0de",
+ "to": "0x82B564983aE7274c86695917BBf8C99ECb6F0F8F",
+ "from": "0x3E101Ec02e7A48D16DADE204C96bFF842E7E2519",
+ "value": "0x0",
+ "gasLimit": "0x16378",
+ "chainId": 84532
+ },
+ "approvals": [
+ {
+ "data": "0x095ea7b300000000000000000000000082b564983ae7274c86695917bbf8c99ecb6f0f8f0000000000000000000000000000000000000000000000000000000046455970",
+ "to": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
+ "from": "0x3E101Ec02e7A48D16DADE204C96bFF842E7E2519",
+ "value": "0x0",
+ "gasLimit": "0xe484",
+ "chainId": 84532
+ }
+ ]
+ }
+]
+```
diff --git a/docs/docs/04-react-sdk/03-using-hooks.md b/docs/docs/04-react-sdk/03-using-hooks.md
index 58c9aa3..a3f41ba 100644
--- a/docs/docs/04-react-sdk/03-using-hooks.md
+++ b/docs/docs/04-react-sdk/03-using-hooks.md
@@ -28,6 +28,7 @@ function YourComponent() {
getUserBalances,
getAvailableTokens,
getAvailableChains,
+ getSweep,
getTransfer,
getTransferWithHook,
getPoolAssetOnDestination,
diff --git a/docs/docs/04-react-sdk/04-methods-reference/useSprinterTransfers.md b/docs/docs/04-react-sdk/04-methods-reference/useSprinterTransfers.md
index d80437c..3c97c56 100644
--- a/docs/docs/04-react-sdk/04-methods-reference/useSprinterTransfers.md
+++ b/docs/docs/04-react-sdk/04-methods-reference/useSprinterTransfers.md
@@ -12,6 +12,7 @@ The `useSprinterTransfers` hook provides a simplified interface for generating c
- `getTransferWithHook`: Single-hop token transfers combined with a contract call on the destination chain.
- `getPoolAssetOnDestination`: Aggregates token balances across multiple chains into a single destination.
- `getPoolAssetOnDestinationWithHook`: Aggregates token balances and performs a contract call on the destination chain.
+- `getSweep`: A function that generates a quote to transfer the full token balances of source chains to a destination chain
You can trigger any of these methods via the hook to fetch a cross-chain solution.
@@ -59,6 +60,7 @@ The `useSprinterTransfers` hook returns an object with the following properties:
- **`getTransferWithHook`**: A function that generates a single-hop token transfer combined with a contract call.
- **`getPoolAssetOnDestination`**: A function that generates a solution to aggregate balances from multiple chains into a single destination.
- **`getPoolAssetOnDestinationWithHook`**: A function that generates a solution to aggregate balances and execute a contract call on the destination chain.
+- **`getSweep`**: A function that generates a quote to transfer the full token balances of source chains to a destination chain
## Example Response
@@ -99,5 +101,6 @@ Each method accepts a `settings` parameter, which varies depending on the operat
- **`getTransferWithHook`**: See [SDK Transfer with Contract Call Method Reference](../../sdk/methods-reference/transfer/transfer-with-hook).
- **`getPoolAssetOnDestination`**: See [SDK Pool Asset Method Reference](../../sdk/methods-reference/pool-asset-on-destination/pool-asset-on-destination).
- **`getPoolAssetOnDestinationWithHook`**: See [SDK Pool Asset with Contract Call Method Reference](../../sdk/methods-reference/pool-asset-on-destination/pool-asset-on-destination-with-hook).
+- **`getSweep`**: See [Sweep Method Reference](../../sdk/methods-reference/transfer/sweep).
Each method calls the Sprinter SDK's corresponding function and returns the intent-based solution for cross-chain transfers or contract calls.
diff --git a/packages/react/lib/context.tsx b/packages/react/lib/context.tsx
index cc3a345..0e1eeef 100644
--- a/packages/react/lib/context.tsx
+++ b/packages/react/lib/context.tsx
@@ -63,6 +63,7 @@ export function SprinterContext({ children, baseUrl }: SprinterContextProps) {
getTransferWithHook,
getPoolAssetOnDestination,
getPoolAssetOnDestinationWithHook,
+ getSweep,
} = useTransfers(sprinter);
/** Initialization */
@@ -85,6 +86,7 @@ export function SprinterContext({ children, baseUrl }: SprinterContextProps) {
getTransferWithHook,
getPoolAssetOnDestination,
getPoolAssetOnDestinationWithHook,
+ getSweep,
}}
>
{children}
diff --git a/packages/react/lib/hooks.ts b/packages/react/lib/hooks.ts
index 36c0f1f..6839de5 100644
--- a/packages/react/lib/hooks.ts
+++ b/packages/react/lib/hooks.ts
@@ -83,7 +83,7 @@ export function useSprinterBalances(account: Address) {
const balances: BalancesEntry = _balances[account] || balancesEmptyState;
const getUserBalances = useCallback(
() => _getUserBalances(account),
- [_getUserBalances, account]
+ [_getUserBalances, account],
);
return { balances, getUserBalances };
@@ -227,6 +227,7 @@ export function useSprinterChains() {
* - `getTransferWithHook`: A function to get a transfer solution that includes an additional contract call on the destination chain.
* - `getPoolAssetOnDestination`: A function to get a solution for pooling assets from multiple chains and transferring them to the destination chain.
* - `getPoolAssetOnDestinationWithHook`: A function to get a solution for pooling assets and performing a contract call on the destination chain.
+ * - `getSweep`: A function to get a solution for sweeping assets from source chains to a destination chain.
*
* @example
* ```ts
@@ -269,6 +270,7 @@ export function useSprinterTransfers() {
getTransferWithHook,
getPoolAssetOnDestination,
getPoolAssetOnDestinationWithHook,
+ getSweep,
} = useSprinter();
return {
solution,
@@ -276,5 +278,6 @@ export function useSprinterTransfers() {
getTransferWithHook,
getPoolAssetOnDestination,
getPoolAssetOnDestinationWithHook,
+ getSweep,
};
}
diff --git a/packages/react/lib/internal/useTransfers.ts b/packages/react/lib/internal/useTransfers.ts
index 9781d4b..d380080 100644
--- a/packages/react/lib/internal/useTransfers.ts
+++ b/packages/react/lib/internal/useTransfers.ts
@@ -1,34 +1,59 @@
-import {
- type SolutionResponse,
- Sprinter,
-} from "@chainsafe/sprinter-sdk";
-import {useCallback} from "react";
-import {useAsyncRequest} from "./useAsyncRequest";
-import type {Infer} from "superstruct";
+import { type SolutionResponse, Sprinter } from "@chainsafe/sprinter-sdk";
+import { useCallback } from "react";
+import { useAsyncRequest } from "./useAsyncRequest";
+import type { Infer } from "superstruct";
import {
MultiHopSchema,
MultiHopWithContractSchema,
- SingleHopSchema, SingleHopWithContractSchema
+ SingleHopSchema,
+ SingleHopWithContractSchema,
+ SweepSchema,
} from "@chainsafe/sprinter-sdk/dist/internal/validators";
export function useTransfers(sprinter: Sprinter) {
const { state: solution, makeRequest } = useAsyncRequest();
- const getPoolAssetOnDestination = useCallback((settings: Infer) => {
- makeRequest(sprinter.poolAssetOnDestination(settings));
- }, [sprinter, makeRequest]);
+ const getPoolAssetOnDestination = useCallback(
+ (settings: Infer) => {
+ makeRequest(sprinter.poolAssetOnDestination(settings));
+ },
+ [sprinter, makeRequest],
+ );
+
+ const getPoolAssetOnDestinationWithHook = useCallback(
+ (settings: Infer) => {
+ makeRequest(sprinter.poolAssetOnDestinationWithHook(settings));
+ },
+ [sprinter, makeRequest],
+ );
- const getPoolAssetOnDestinationWithHook = useCallback((settings: Infer) => {
- makeRequest(sprinter.poolAssetOnDestinationWithHook(settings));
- }, [sprinter, makeRequest]);
+ const getTransfer = useCallback(
+ (settings: Infer) => {
+ makeRequest(sprinter.transfer(settings));
+ },
+ [sprinter, makeRequest],
+ );
- const getTransfer = useCallback((settings: Infer) => {
- makeRequest(sprinter.transfer(settings));
- }, [sprinter, makeRequest]);
+ const getTransferWithHook = useCallback(
+ (settings: Infer) => {
+ makeRequest(sprinter.transferWithHook(settings));
+ },
+ [sprinter, makeRequest],
+ );
- const getTransferWithHook = useCallback((settings: Infer) => {
- makeRequest(sprinter.transferWithHook(settings));
- }, [sprinter, makeRequest]);
+ const getSweep = useCallback(
+ (settings: Infer) => {
+ makeRequest(sprinter.sweep(settings));
+ },
+ [sprinter, makeRequest],
+ );
- return { solution, getTransfer, getTransferWithHook, getPoolAssetOnDestination, getPoolAssetOnDestinationWithHook };
+ return {
+ solution,
+ getTransfer,
+ getTransferWithHook,
+ getPoolAssetOnDestination,
+ getPoolAssetOnDestinationWithHook,
+ getSweep,
+ };
}
diff --git a/packages/react/src/App.tsx b/packages/react/src/App.tsx
index c5c4fd4..5907cc4 100644
--- a/packages/react/src/App.tsx
+++ b/packages/react/src/App.tsx
@@ -1,14 +1,15 @@
-import { useState } from 'react'
-import './App.css'
-import {SprinterContext} from "../lib/main.ts";
-import {Component} from "./Component.tsx";
-import {Action} from "./Action.tsx";
+import { useState } from "react";
+import "./App.css";
+import { SprinterContext } from "../lib/main.ts";
+import { Component } from "./Component.tsx";
+import { Action } from "./Action.tsx";
+import { Environment } from "@chainsafe/sprinter-sdk";
function App() {
- const [count, setCount] = useState(0)
+ const [count, setCount] = useState(0);
return (
-
+
@@ -26,7 +27,7 @@ function App() {
Click on the Vite and React logos to learn more
- )
+ );
}
-export default App
+export default App;
diff --git a/packages/react/src/Component.tsx b/packages/react/src/Component.tsx
index e738773..62bd8ac 100644
--- a/packages/react/src/Component.tsx
+++ b/packages/react/src/Component.tsx
@@ -1,12 +1,21 @@
import { useEffect } from "react";
-import { useSprinterTokens } from "../lib/hooks.ts";
+import { useSprinterTokens, useSprinterTransfers } from "../lib/hooks.ts";
export function Component() {
- const hook = useSprinterTokens();
+ const { getAvailableTokens } = useSprinterTokens();
+ const { getSweep, solution } = useSprinterTransfers();
useEffect(() => {
- hook.getAvailableTokens();
- }, [hook]);
+ getAvailableTokens();
+ }, [getAvailableTokens]);
- return A component
;
+ useEffect(() => {
+ getSweep({
+ account: "0x3E101Ec02e7A48D16DADE204C96bFF842E7E2519",
+ destinationChain: 11155111,
+ token: "USDC",
+ });
+ }, [getSweep]);
+
+ return {JSON.stringify(solution, null, 2)};
}
diff --git a/packages/sdk/src/api.ts b/packages/sdk/src/api.ts
index ad4ab41..1c07a8a 100644
--- a/packages/sdk/src/api.ts
+++ b/packages/sdk/src/api.ts
@@ -12,6 +12,7 @@ import type {
Solution,
SolutionOptions,
SolutionResponse,
+ SweepSolutionOptions,
TokenSymbol,
} from "./types";
import { getEnv } from "./utils";
@@ -123,8 +124,8 @@ export async function getSolution(
url.searchParams.set("token", token);
url.searchParams.set("amount", String(amount));
//
- if (threshold) url.searchParams.set("threshold", String(threshold));
- if (whitelistedSourceChains?.length)
+ threshold && url.searchParams.set("threshold", String(threshold));
+ whitelistedSourceChains?.length &&
url.searchParams.set(
"whitelistedSourceChains",
whitelistedSourceChains.join(","),
@@ -214,3 +215,31 @@ export async function getContractCallSolution(
if ("error" in response) return response;
return response.data;
}
+
+export async function getSweepSolution(
+ {
+ account,
+ destinationChain,
+ recipient,
+ token,
+ sourceChains,
+ }: SweepSolutionOptions,
+ { baseUrl, signal }: FetchOptions = {},
+): Promise {
+ const url = new URL("/solutions/balance-sweep", baseUrl || BASE_URL);
+
+ url.searchParams.set("account", account);
+ url.searchParams.set("destination", String(destinationChain));
+ url.searchParams.set("token", token);
+ recipient && url.searchParams.set("recipient", recipient);
+ sourceChains?.length &&
+ url.searchParams.set("whitelistedSourceChains", sourceChains.join(","));
+
+ const response = await fetch(url, { signal }).then(
+ (response) =>
+ response.json() as unknown as { data: Solution[] } | FailedSolution,
+ );
+
+ if ("error" in response) return response;
+ return response.data;
+}
diff --git a/packages/sdk/src/internal/validators.ts b/packages/sdk/src/internal/validators.ts
index cf11ec2..5d53d4d 100644
--- a/packages/sdk/src/internal/validators.ts
+++ b/packages/sdk/src/internal/validators.ts
@@ -79,3 +79,11 @@ export const MultiHopWithContractSchema = assign(
BridgeCoreSchema,
ContractCallSchema,
);
+
+export const SweepSchema = object({
+ account: hexString(),
+ destinationChain: number(),
+ recipient: optional(hexString()),
+ token: string(),
+ sourceChains: optional(array(number())),
+});
diff --git a/packages/sdk/src/sprinter.ts b/packages/sdk/src/sprinter.ts
index fedb7e4..6e7cf50 100644
--- a/packages/sdk/src/sprinter.ts
+++ b/packages/sdk/src/sprinter.ts
@@ -7,6 +7,7 @@ import {
getFungibleTokens,
getSolution,
getSupportedChains,
+ getSweepSolution,
} from "./api";
import { formatBalances, getUserBalances } from "./internal/userBalances";
import {
@@ -14,6 +15,7 @@ import {
MultiHopWithContractSchema,
SingleHopSchema,
SingleHopWithContractSchema,
+ SweepSchema,
} from "./internal/validators";
import type {
Address,
@@ -24,6 +26,7 @@ import type {
FungibleToken,
SolutionOptions,
SolutionResponse,
+ SweepSolutionOptions,
} from "./types";
export class Sprinter {
@@ -476,6 +479,53 @@ export class Sprinter {
);
}
+ /**
+ * Fetches and returns a quote to transfer full token balances of source chains to a destination chain.
+ *
+ * @param {Infer} settings - The settings object for defining the sweep parameters:
+ * - `account` {string}: The user's wallet address for the transaction.
+ * - `destinationChain` {number}: The ID of the destination blockchain.
+ * - `token` {string}: The token symbol (e.g., "ETH", "USDC") to be transferred.
+ * - `sourceChains` {Array} (optional): An array of source chain IDs to be considered for the transfer.
+ * - `recipient` {string} (optional): The address of the recipient of the tokens on the destination chain.
+ *
+ * @param {FetchOptions} [options] - Optional configuration for the fetch request, such as custom headers or query parameters.
+ *
+ * @returns {Promise} A promise that resolves to the solution object containing the optimal sweep strategy and contract call, or a `FailedSolution` in case of an error.
+ *
+ * @example
+ * ```ts
+ * import { Sprinter } from '@chainsafe/sprinter-sdk';
+ *
+ * const sprinter = new Sprinter();
+ *
+ * const settings = {
+ * account: "0x3e101ec02e7a48d16dade204c96bff842e7e2519",
+ * destinationChain: 11155111,
+ * sourceChains: [84532, 137],
+ * token: "USDC",
+ * recipient: "0xRecipientAddress",
+ * };
+ *
+ * sprinter.sweep(settings).then(solution => {
+ * console.log(solution);
+ * }).catch(error => {
+ * console.error(error);
+ * });
+ * ```
+ */
+ public async sweep(
+ settings: Infer,
+ options?: FetchOptions,
+ ): Promise {
+ assert(settings, SweepSchema);
+
+ return await getSweepSolution(
+ settings as SweepSolutionOptions,
+ this.makeFetchOptions(options),
+ );
+ }
+
private deferredRequest(
name: string,
request: () => Promise,
diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts
index a44e149..36961c9 100644
--- a/packages/sdk/src/types.ts
+++ b/packages/sdk/src/types.ts
@@ -66,6 +66,14 @@ export interface ContractSolutionOptions extends SolutionOptions {
contractCall: ContractCallSolutionOptions;
}
+export interface SweepSolutionOptions {
+ account: Address;
+ destinationChain: ChainID;
+ token: TokenSymbol;
+ sourceChains?: ChainID[];
+ recipient?: Address;
+}
+
interface Amount {
amount: string;
amountUSD: number;