Skip to main content

Get Cross-chain Quote

There are three ways to sign the data returned by the Get Cross-chain Quote API:

  1. Using our TypeScript SDK
    The TypeScript SDK provides built-in methods to handle the signature process easily and securely.

    const { data: feeData, fetchData } = useCrossChainFee();
    const { transfer } = useCrossChainTransfer();

    const calculateFee = async () => {
    const result = await fetchData({
    sourceChain: sourceChainId,
    destChain: destChainId,
    sourceToken,
    destToken,
    amount: String(parseUnits(amount, decimals)),
    to: recipient,
    });
    };

    const handleTransfer = async () => {
    await transfer(feeData.signData);
    alert('Transfer successful!');
    };
  2. Using our Go SDK
    The Go SDK also offers convenient functions to sign the returned data and submit the transaction.

    func main() {
    var rpcUrl = "" // Blockchain RPC node URL
    var privKey = "" // Private key for data signing
    var appKey = "" // App key

    privateKey, _ := crypto.HexToECDSA(privKey)
    fromAddress := crypto.PubkeyToAddress(privateKey.PublicKey)

    // Get signature data
    routerParams := types.RouterParams{
    SourceChain: "1",
    DestChain: "56",
    SourceToken: "0xF3F2b4815A58152c9BE53250275e8211163268BA",
    DestToken: "0xe37Bdc6F09DAB6ce6E4eBC4d2E72792994Ef3765",
    Amount: "10000000",
    To: fromAddress.Hex(),
    }
    routerUrl := "https://www.fintechain.org/proxy/api/oracle/router"
    routerResp, err := api.Router(appKey, routerUrl, &routerParams) // Pass appKey for Authorization header
    if err != nil {
    fmt.Println("Router failed:", err)
    }

    fmt.Println("Bridge data:", routerResp)

    // Sign the returned data and send it to the blockchain
    b := bridge.NewBridge(types.Config{RPCUrl: rpcUrl})
    txHash, err := b.Cross(privateKey, &routerResp.Data.SignData)
    if err != nil {
    fmt.Println("Bridge failed:", err)
    return
    }
    fmt.Println("Bridge txHash:", txHash)
    }
  3. Without SDK
    If you prefer not to use our SDKs, you can manually sign the required fields using your own implementation. Please refer to the API response and signature specification for details.

    // Call Get Cross-chain Quote API using fetch (no SDK required, async/await version, returns data)
    const url = "https://www.fintechain.org/proxy/api/oracle/router";

    const params = {
    sourceChain: "42161",
    sourceToken: "0x0000000000000000000000000000000000000000",
    destChain: "1",
    destToken: "0x0000000000000000000000000000000000000000",
    amount: "100000000000000",
    to: "0xYourRecipientAddress"
    };

    const appKey = "YOUR_APP_KEY"; // Your app key for Authorization header

    async function getCrossChainQuote() {
    try {
    const response = await fetch(url, {
    method: "POST",
    headers: {
    "Content-Type": "application/json",
    "Authorization": appKey
    },
    body: JSON.stringify(params)
    });
    const data = await response.json();
    console.log("Cross-chain quote response:", data);
    // You can now use data.data.signData for signing
    return data;
    } catch (error) {
    console.error("Error fetching cross-chain quote:", error);
    throw error;
    }
    }

    const routerData = await getCrossChainQuote();

    // Check if approval is needed
    async function approve(params, routerData) {
    const token = getContract({
    address: params.sourceToken,
    abi: erc20Abi,
    client,
    });

    if (params.amount > (await token.allowance([wallet.address, routerData.signData.bridge])))
    await token.approve([routerData.signData.bridge, maxUint256]);
    }
    }

    await approve(params, routerData);

    // For routerData.bridgeType == 0
    async function bridge(params, routerData) {
    const gateBridge = getContract({
    address: routerData.signData.bridge,
    abi: bridge0Abi,
    client,
    });

    const signParams = routerData.signData.params;

    let fees = fee;
    if (sourceToken === address(0)) {
    fees += amount;
    }
    await gateBridge.bridge(
    signParams.destChainId,
    signParams.sourceToken,
    signParams.destToken,
    signParams.amount,
    signParams.fee,
    signParams.to,
    signParams.sourceSwap,
    signParams.sourceSwapFee,
    signParams.sourceBridgeToken,
    signParams.sourceMinAmount,
    signParams.destSwap,
    signParams.destSwapFee,
    signParams.destMinAmount,
    {
    value: signParams.fees
    }
    );
    }

    // For routerData.bridgeType == 1
    async function bridge(params, routerData) {
    const gateBridge = getContract({
    address: routerData.signData.bridge,
    abi: bridge1Abi,
    client,
    });

    const signParams = routerData.signData.params;

    let fees = fee;
    if (sourceToken === address(0)) {
    fees += amount;
    }
    await gateBridge.bridge(
    signParams.destChainId,
    signParams.sourceToken,
    signParams.destToken,
    signParams.amount,
    signParams.fee,
    signParams.to,
    signParams.sourceSwap,
    signParams.sourceSwapFee,
    signParams.sourceMinAmount,
    signParams.destSwap,
    signParams.destSwapFee,
    signParams.destMinAmount,
    {
    value: signParams.fees
    }
    );
    }

    // For routerData.bridgeType == 2
    async function bridge(params, routerData) {
    const gateBridge = getContract({
    address: routerData.signData.bridge,
    abi: bridge1Abi,
    client,
    });

    const signParams = routerData.signData.params;

    let fees = fee;
    if (sourceToken === address(0)) {
    fees += amount;
    }
    await gateBridge.bridge(
    signParams.destChainId,
    signParams.sourceToken,
    signParams.destToken,
    signParams.amount,
    signParams.fee,
    signParams.to,
    signParams.sourceSwap,
    signParams.sourceSwapFee,
    signParams.sourceMinAmount,
    signParams.destSwap,
    signParams.destSwapFee,
    signParams.destMinAmount,
    {
    value: signParams.fees
    }
    );
    }

    // For routerData.chainName == Solana
    import * as anchor from "@coral-xyz/anchor";
    import { Program } from "@coral-xyz/anchor";
    import { Keypair, PublicKey } from "@solana/web3.js";
    import bs58 from "bs58";
    import "dotenv/config";
    import { getBytes } from "ethers";
    import { GateBridgeCctp } from "../target/types/gate_bridge_cctp";


    const evmAddressToBytes32 = (address: string): string => `0x000000000000000000000000${address.replace("0x", "")}`;

    interface FindProgramAddressResponse {
    publicKey: anchor.web3.PublicKey;
    bump: number;
    }

    export const findProgramAddress = (
    label: string,
    programId: PublicKey,
    extraSeeds: (string | number[] | Buffer | PublicKey)[] = null
    ): FindProgramAddressResponse => {
    const seeds = [Buffer.from(anchor.utils.bytes.utf8.encode(label))];
    if (extraSeeds) {
    for (const extraSeed of extraSeeds) {
    if (typeof extraSeed === "string") {
    seeds.push(Buffer.from(anchor.utils.bytes.utf8.encode(extraSeed)));
    } else if (Array.isArray(extraSeed)) {
    seeds.push(Buffer.from(extraSeed as number[]));
    } else if (Buffer.isBuffer(extraSeed)) {
    seeds.push(extraSeed);
    } else {
    seeds.push(extraSeed.toBuffer());
    }
    }
    }
    const res = PublicKey.findProgramAddressSync(seeds, programId);
    return { publicKey: res[0], bump: res[1] };
    };

    async function main() {
    const provider = anchor.AnchorProvider.env();
    anchor.setProvider(provider);
    const program = anchor.workspace.GateBridgeCctp as Program<GateBridgeCctp>;


    const privateKey = "";
    const wallet = Keypair.fromSecretKey(bs58.decode(privateKey));

    const walletAddress = wallet.publicKey;
    const usdcAddress = new PublicKey(process.env.USDC_ADDRESS);
    const messageTransmitterProgramId = new PublicKey(process.env.MESSAGE_TRANSMITTER);
    const tokenMessengerMinterProgramId = new PublicKey(process.env.TOKEN_MESSENGER_MINTER);

    const userTokenAccount = await anchor.utils.token.associatedAddress({
    mint: new PublicKey(usdcAddress),
    owner: wallet.publicKey
    });

    const destChainId = 0;
    const destToken = new PublicKey(getBytes(evmAddressToBytes32("0x0000000000000000000000000000000000000000")));
    const amount = new anchor.BN(100000);
    const to = new PublicKey(getBytes(evmAddressToBytes32("0x0000000000000000000000000000000000000000")));
    const destMinAmount = new anchor.BN(1000000);

    const [destChainReceiver] = anchor.web3.PublicKey.findProgramAddressSync(
    [
    Buffer.from("dest_chain_receiver"),
    Buffer.from(new Uint32Array([destChainId]).buffer)
    ],
    program.programId
    );

    const sender_authority_pda = findProgramAddress("sender_authority", tokenMessengerMinterProgramId);
    const messageTransmitterAccount = findProgramAddress("message_transmitter", messageTransmitterProgramId);
    const tokenMessengerAccount = findProgramAddress("token_messenger", tokenMessengerMinterProgramId);
    const tokenMinterAccount = findProgramAddress("token_minter", tokenMessengerMinterProgramId);
    const localToken = findProgramAddress("local_token", tokenMessengerMinterProgramId, [usdcAddress]);
    const remoteTokenMessengerKey = findProgramAddress("remote_token_messenger", tokenMessengerMinterProgramId, [
    destChainId.toString(),
    ]);
    const eventAuthority = findProgramAddress("__event_authority", tokenMessengerMinterProgramId);

    // Generate a new keypair for the MessageSent event account.
    const messageSentEventData = Keypair.generate();

    const tx = await program.methods
    .bridge(
    {
    destChainId,
    destToken,
    amount,
    to,
    destMinAmount,
    }
    )
    .accounts({
    user: wallet.publicKey,
    destChainReceiver: destChainReceiver,
    burnTokenAccount: userTokenAccount,
    senderAuthorityPda: sender_authority_pda.publicKey,
    messageTransmitter: messageTransmitterAccount.publicKey,
    tokenMessenger: tokenMessengerAccount.publicKey,
    remoteTokenMessenger: remoteTokenMessengerKey.publicKey,
    tokenMinter: tokenMinterAccount.publicKey,
    localToken: localToken.publicKey,
    burnTokenMint: new PublicKey(usdcAddress),
    messageSentEventData: messageSentEventData.publicKey,
    messageTransmitterProgram: messageTransmitterProgramId,
    tokenMessengerMinterProgram: tokenMessengerMinterProgramId,
    eventAuthority: eventAuthority.publicKey
    })
    .signers([wallet,messageSentEventData])
    .rpc();

    }

    main().then(
    () => process.exit(0),
    (err) => {
    console.error(err);
    process.exit(1);
    }
    );