Squid Dev Documentation
  • Getting Started
    • Overview
    • Get an Integrator ID
    • Integrator Quickstart
  • Widget Integration
    • Add A Widget
      • Swap widget
        • Getting Started
        • Installing the Widget
        • React Installation
        • NextJS Installation
        • Vite Installation
        • Iframe Installation
        • Customization Guide
          • Default User Settings
          • Configuring Content
          • Default Chains and Tokens
          • Configuration Types
          • Collect Fees
        • Information for Integrators
        • Set default chains and tokens via URL
        • Errors
    • Upgrade Your Widget
  • API & SDK Integration
    • API
      • Swap & Bridge Example
      • Staking Example
      • Cross-chain NFT Purchase Example
      • Get Route Status
    • SDK
      • Cross-chain Swap Example
      • Staking Example
      • Cross-chain NFT Purchase Example
      • Get Route Status
    • Key Concepts
      • Route Request Parameters
      • Get a route
      • Execute the route
      • 🪝Hooks
        • How do hooks work
        • Build a preHook
        • Build a postHook
        • Using the full ERC20 or Native balance in a call
        • ❗Transfer the remainder to the user
        • Get a route and execute as normal
      • Express
      • Track status
      • Types
      • Collect Fees
      • Get supported tokens and chains
    • Coral: Intent Swaps
      • Integrating Coral
    • NEW: Bitcoin and Solana
  • Adding Tokens
    • Whitelist a Token
    • Interchain Token(ITS) Listings
      • How To List
    • Circle's EURC Integration Guide
  • Migration
    • Squid v2 vs Squid v1
    • v1 -> v2 Migration
  • Additional Resources
    • Contracts
    • Squid v1 Docs
      • SDK
        • Installing our SDK
        • Get supported tokens and chains
          • Example chains response
          • Example tokens response
        • Set your transfer parameters
        • Get a route
          • Understanding the route response
        • Execute your swap or call
        • Get Route Status
        • Contract calls
          • Code example
          • Creating a ContractCall object
          • Contract call types
          • Approving ERC20s in our multicall
          • Transferring the remainder to a user
        • Collect Fees
          • Fee collector contract addresses
        • Working example!
      • API
        • integratorId
        • API playground
        • Base URLs
        • Get supported tokens and chains
        • Get a route
        • Checking the status of a transaction
        • Error codes
          • V2 API
      • Cosmos
        • Installing our SDK
        • Get supported tokens and chains
          • Example chains response
          • Example tokens response
        • Set your transfer parameters
        • Get a route
          • Cosmos route response
        • Execute your swap or call on Cosmos
        • Get Route Status
        • Cosmos custom calls
        • Working cosmos example!
        • Fallback Addresses
    • Additional Dev Resources
      • Choose How To Integrate Squid
      • Squid x Tenderly
      • Testnet or Mainnet?
      • Understanding Gas Payments
      • Supported Chains and Tokens
      • Request a new supported token
      • Liquidity Sources
        • axlASSET Liquidity
      • Integrator type specific docs
        • DEXs
        • Aggregators
        • NFTs
      • Setting up an EVM signer
      • Setting up Cosmos signer
      • Contract addresses
      • SDK easter eggs
        • Setting toAmount
        • getAllBalances
        • Advanced set up
        • Manually approve a route
        • Manually approve an ERC20
      • Whitelist a token
      • Security & Audits
        • Security
        • Audits
        • Axelar
      • Get an integrator-id
    • Architecture
      • FAQ
      • Liquidity model
      • Architecture
      • General message passing (GMP)
      • Transaction times and fees
      • Fallback behaviour on failed transactions
      • Axelar
    • Brand Assets
      • "Powered by Squid" Policy
    • Audits & Security
    • Contact
  • Changelog
    • SDK
      • v1.3
      • v1.2
    • API
      • v1.9
      • v1.8
      • v1.7
      • v1.6
  • Old v2 Documentation (Deprecated)
    • Whitelist a Token
    • Interchain Token(ITS) Listings
      • How To List
    • Add A Widget
      • Swap widget
        • Getting Started
        • Installing the Widget
        • React Installation
        • NextJS Installation
        • Iframe Installation
        • Customization Guide
          • Default User Settings
          • Theme Customization
          • Configuring Content
          • Default Chains and Tokens
          • Configuration Types
          • Collect fees
        • Information for Integrators
        • Set default chains and tokens via URL
        • Errors
      • Stake widget
        • Installing the Widget
        • Configuration
        • Importing The widget
    • Quick Start
    • API
      • Swap & Bridge Example
      • Staking Example
      • Cross-chain NFT Purchase Example
    • SDK
      • Cross-chain Swap Example
      • Staking Example
      • Cross-chain NFT Purchase Example
    • Key Concepts
      • Route Request Parameters
      • Get a route
      • Execute the route
      • 🪝Hooks
        • How do hooks work
        • Build a preHook
        • Build a postHook
        • Using the full ERC20 or Native balance in a call
        • ❗Transfer the remainder to the user
        • Add your hooks to a route request, or widget
        • Get a route and execute as normal
      • Boost
      • Track status
      • Types
      • Collect Fees
      • Get supported tokens and chains
Powered by GitBook
On this page
  1. API & SDK Integration
  2. SDK

Staking Example

PreviousCross-chain Swap ExampleNextCross-chain NFT Purchase Example

Last updated 1 month ago

This example walks you through how to implement the SDK to allow a wallet to swap from Binance to Arbitrum and lend on Radiant.

Download the full example .

// Import necessary libraries
import { ethers } from "ethers";
import { Squid } from "@0xsquid/sdk";
import { ChainType, EvmContractCall } from "@0xsquid/squid-types"; 

// Load environment variables from the .env file
import * as dotenv from "dotenv";
dotenv.config();

// Load environment variables from .env file
const privateKey: string = process.env.PRIVATE_KEY!;
const integratorId: string = process.env.INTEGRATOR_ID!;
const FROM_CHAIN_RPC: string = process.env.RPC_ENDPOINT!;
const aaveArbitrumPoolAddress: string = "0x794a61358D6845594F94dc1DB02A252b5b4814aD"; // Aave v3 pool on Arbitrum
const usdcArbitrumAddress: string = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831";

// Define chain and token addresses
const fromChainId = "56"; // Binance
const toChainId = "42161"; // Arbitrum
const fromToken = "0x55d398326f99059fF775485246999027B3197955"; // Define departing token

// Define amount to be sent
const amount = "100000000000000000";

// Set up JSON RPC provider and signer using the private key and RPC URL
const provider = new ethers.JsonRpcProvider(FROM_CHAIN_RPC);
const signer = new ethers.Wallet(privateKey, provider);

// Import erc20 contract ABI
import erc20Abi from "../abi/erc20Abi";

// Define Aave pool ABI (simplified for this example)
const aavePoolAbi = [
  "function supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external"
];

// Function to get Squid SDK instance
const getSDK = (): Squid => {
  const squid = new Squid({
    baseUrl: "https://v2.api.squidrouter.com",
    integratorId: integratorId,
  });
  return squid;
};

// Function to approve the transactionRequest.target to spend fromAmount of fromToken
const approveSpending = async (transactionRequestTarget: string, fromToken: string, fromAmount: string) => {
  const erc20Abi = [
    "function approve(address spender, uint256 amount) public returns (bool)"
  ];
  const tokenContract = new ethers.Contract(fromToken, erc20Abi, signer);
  try {
    const tx = await tokenContract.approve(transactionRequestTarget, fromAmount);
    await tx.wait();
    console.log(`Approved ${fromAmount} tokens for ${transactionRequestTarget}`);
  } catch (error) {
    console.error('Approval failed:', error);
    throw error;
  }
};


// Main function
(async () => {
  // Initialize Squid SDK
  const squid = getSDK();
  await squid.init();
  console.log("Initialized Squid SDK");

  // Creating Contract interfaces
  // Approve the Aave pool contract to spend the USDC
  const erc20Interface = new ethers.Interface(erc20Abi);
  const approvalData = erc20Interface.encodeFunctionData("approve", [
    aaveArbitrumPoolAddress,
    ethers.MaxUint256, // ethers v6 uses MaxUint256 directly on ethers
  ]);

  // Create contract interface and encode supply function for Aave lending pool
  const aavePoolInterface = new ethers.Interface(aavePoolAbi);
  
  const userAddress = await signer.getAddress();
  
  const supplyData = aavePoolInterface.encodeFunctionData(
    "supply",
    [
      usdcArbitrumAddress,
      "0", // Amount will be replaced with the full token balance
      userAddress,
      0, // referralCode
    ]
  );

  
  
  // Set up parameters for swapping tokens and depositing into Aave lending pool
  const params = {
    fromAddress: userAddress,
    fromChain: fromChainId,
    fromToken: fromToken,
    fromAmount: amount,
    toChain: toChainId,
    toToken: usdcArbitrumAddress,
    toAddress: userAddress,
    quoteOnly: false,
    postHook: {
      chainType: ChainType.EVM,
      calls: [
        {
          chainType: ChainType.EVM, 
          callType: 1, // SquidCallType.FULL_TOKEN_BALANCE
          target: usdcArbitrumAddress,
          value: "0", // this will be replaced by the full native balance of the multicall after the swap
          callData: approvalData,
          payload: {
            tokenAddress: usdcArbitrumAddress,
            inputPos: 1,
          },
          estimatedGas: "50000",
        } as EvmContractCall,
        {
          chainType: ChainType.EVM,
          callType: 1, // SquidCallType.FULL_TOKEN_BALANCE
          target: aaveArbitrumPoolAddress,
          value: "0",
          callData: supplyData,
          payload: {
            tokenAddress: usdcArbitrumAddress,
            inputPos: 1,
          },
          estimatedGas: "200000",
        } as EvmContractCall,
      ],
      provider: "Aave",
      description: "Deposit to Aave on Arbitrum",
      logoURI: "https://app.aave.com/favicon.ico",
    },
  };
  

  console.log("Parameters:", params); // Printing the parameters for QA

  // Get the swap route using Squid SDK
  const { route, requestId } = await squid.getRoute(params);
  console.log("Calculated route:", route.estimate.toAmount);

  // Get the transaction request from route
  if (!route.transactionRequest) {
    console.error("No transaction request in route");
    process.exit(1);
  }

  // For SquidData objects, we need to check what type it is and extract the target
  let target: string;
  if ('target' in route.transactionRequest) {
    target = route.transactionRequest.target;
  } else {
    console.error("Cannot determine target address from transaction request");
    console.log("Transaction request:", route.transactionRequest);
    process.exit(1);
  }

  // Approve the target to spend fromAmount of fromToken
  await approveSpending(target, fromToken, amount);

  // Execute the swap transaction
  const txResponse = await squid.executeRoute({
    signer: signer as any, // Cast to any to bypass type checking issues
    route,
  });

  // Handle the transaction response - could be an ethers v6 TransactionResponse or something else
  let txHash: string = 'unknown';
  
  if (txResponse && typeof txResponse === 'object') {
    if ('hash' in txResponse) {
      // This is an ethers TransactionResponse
      txHash = txResponse.hash as string;
      await (txResponse as any).wait?.(); // Wait for the transaction to be mined if possible
    } else if ('transactionHash' in txResponse) {
      // This might be a v5 style response or custom Squid format
      txHash = (txResponse as any).transactionHash as string;
    } else {
      // Fallback - try to find a hash property
      txHash = (txResponse as any).hash as string || 'unknown';
    }
  }

  // Show the transaction receipt with Axelarscan link
  const axelarScanLink = "https://axelarscan.io/gmp/" + txHash;
  console.log(`Finished! Check Axelarscan for details: ${axelarScanLink}`);

  // Wait a few seconds before checking the status
  await new Promise((resolve) => setTimeout(resolve, 5000));

  // Parameters for checking the status of the transaction
  const getStatusParams = {
    transactionId: txHash,
    requestId: requestId,
    integratorId: integratorId,
    fromChainId: fromChainId,
    toChainId: toChainId,
  };

  const completedStatuses = ["success", "partial_success", "needs_gas", "not_found"];
  const maxRetries = 10; // Maximum number of retries for status check
  let retryCount = 0;
  
  // Get the initial status
  let status = await squid.getStatus(getStatusParams);
  console.log(`Initial route status: ${status.squidTransactionStatus}`);

  // Loop to check the transaction status until it is completed or max retries are reached
  do {
    try {
      // Wait a few seconds before checking the status
      await new Promise((resolve) => setTimeout(resolve, 5000));

      // Retrieve the transaction's route status
      status = await squid.getStatus(getStatusParams);

      // Display the route status
      console.log(`Route status: ${status.squidTransactionStatus}`);

    } catch (error: unknown) {
      // Handle error if the transaction status is not found
      if (error instanceof Error && (error as any).response && (error as any).response.status === 404) {
        retryCount++;
        if (retryCount >= maxRetries) {
          console.error("Max retries reached. Transaction not found.");
          break;
        }
        console.log("Transaction not found. Retrying...");
        continue;
      } else {
        throw error;
      }
    }

  } while (status && !completedStatuses.includes(status.squidTransactionStatus));

  // Wait for the transaction to be executed
  console.log("Swap transaction executed:", txHash);
})();
here