In addtion to the standard widget config, the staking widget requires a stakeConfig.
Working example
Adding the Staking transactions
Basically the stakeConfig is here to tell the widget what happens after the cross chain swap.
Here's an example flow:
User wants to stake USDC on Moonbeam from BNB on Binance
Normal cross chain swap happens with Squid (swap to axlUSDC, bridge, swap to destination token, configured by the developer)
Now it's time to execute customContractCalls:
Give approval to SquidMultiCall to send dest token (let's say GLMR received earlier)
Effectively stake this amount on Moonbeam
Send staked token to user
The main attribute to update is customContractCalls.
You can read more about the configuration of customContractCalls here: Contract calls
stakeConfig structure
exportinterfaceStakeConfig {// TokenData will have all informations about the stakedToken,// such as symbol, image, name, chainId, etc) stakedToken:TokenData;// This method will be used to compute the exchange rate between the staked token and the token to stake// Basically a multiplier from 0 to x // Then if the amount that the route gets is 100, and the exchange rate is 0.5, we'll show 50 as the amount to be received for stakedToken
// If nothing is specified, the exchange rate will be 1stakedTokenExchangeRateGetter?: () =>Promise<number>;// At the moment we cannot unstake directly through the widget, // If you want to tell the user that he can unstake, we'll display a link for this unstakeLink?:string;// Intro page is optional, it will display a page before proposing to swap,// This can be useful for the user to understand what will be possible introPage?: { title:string; logoUrl:string; stakingContract?: { address:string; explorerLink?:string; }; };// This is mandatory, and different from stakedToken// This token won't be displayed but will be used to stake// Route will be like this: // - User selects BNB on Binance// - We swap this to axlUSDC using Axelar// - axlUSDC is bridged to the chainId of token below// - axlUSDC is swapped to token below tokenToStake: { chainId:number|string; address:string; };// After swap/bridge is done, custom contract call can be executed// For example staking the "tokenToStake" into "stakedToken" customContractCalls: (Omit<ContractCall,"callData"> & {callData?: (routeVariables:CallDataGetter) =>string; })[];}
import { AppConfig } from"@0xsquid/widget/widget/core/types/config";import { SquidCallType } from"@0xsquid/sdk";import { SquidStakingWidget } from"@0xsquid/staking-widget";import { StakeConfig } from"@0xsquid/staking-widget/widget/core/types/config";import { ethers } from"ethers";import erc20Abi from"../abi/erc20Abi";import pinjamStakingPoolAbi from"../abi/pinjamStakingPoolAbi";import styles from"../styles/Home.module.css";/** * Here you'll find an example of how to use the SquidStakingWidget. * @returns */exportdefaultfunctionHome() {// Define the staking contract configconstkavaId=2222;constpinjamAxlUsdcPoolAddress="0x11c3d91259b1c2bd804344355c6a255001f7ba1e";constaxlUsdcKavaAddress="0xeb466342c4d449bc9f53a865d5cb90586f405215";constpAxlUsdcAddress="0x5c91f5d2b7046a138c7d1775bffea68d5e95d68d";constpinjamStakingPoolInterface=newethers.utils.Interface( pinjamStakingPoolAbi );consterc20Interface=newethers.utils.Interface(erc20Abi);conststakeConfig:StakeConfig= {// This method will be used to compute the exchange rate between the staked token and the token to stake// Basically a multiplier from 0 to x // Then if the amount that the route gets is 100, and the exchange rate is 0.5, we'll show 50 as the amount to be received for stakedToken
// If nothing is specified, the exchange rate will be 1stakedTokenExchangeRateGetter:async () => {awaitnewPromise((res) =>setTimeout(res,5000));return1; },// This link will be used to redirect the user to the unstake page, because it's not possible yet with the widget unstakeLink:"https://app.pinjamlabs.com/manage",// Here are the calls that will be called by Squid MultiCall contract after the cross chain swap is done customContractCalls: [ { callType: SquidCallType.FULL_TOKEN_BALANCE, // Full token balance means that the MultiCall will stake all token balance it received from the swap
target: axlUsdcKavaAddress,// Contract address value:"0",// CallData is a method instead of a static value because it could depend on the routecallData: () => {returnerc20Interface.encodeFunctionData("approve", [ pinjamAxlUsdcPoolAddress,"0", ]); }, payload: { tokenAddress: axlUsdcKavaAddress, inputPos:1, }, estimatedGas:"50000", }, { callType:SquidCallType.FULL_TOKEN_BALANCE, target: pinjamAxlUsdcPoolAddress, value:"0",// CallData is a method instead of a static value because it could depend on the route// Here we can see that we use the user address as a parameter (destinationAddress)// It's needed as a callback because you can't know upfront what the user address will be// route object also offers the possibility to get other parameters like swapRoute:// - fromChainId?: number | string;// - toChainId?: number | string;// - fromTokenAddress?: string;// - toTokenAddress?: string;callData: (route) => {if (route.destinationAddress) {returnpinjamStakingPoolInterface.encodeFunctionData("deposit", [ axlUsdcKavaAddress,"0",route.destinationAddress,true, ]); }// If the user address is not available, we return a dummy value // The funds won't be sent there, but it's needed to avoid having errors when fetching data when the user is not connected
// For example getting a quote for a staking mechanismreturn"0x0000000000000000000000000000000000000000"; }, payload: { tokenAddress: axlUsdcKavaAddress, inputPos:1, }, estimatedGas:"250000", }, ],// This will be the token that the user will receive after the swap + staking// The coingeckoId is used to fetch the price of the token (USD price) stakedToken: { chainId: kavaId, address: pAxlUsdcAddress, name:"paxlUSDC", symbol:"paxlUSDC", decimals:6, logoURI:"https://assets.coingecko.com/coins/images/6319/thumb/USD_Coin_icon.png?1547042389", coingeckoId:"usd-coin", },// Won't be seen by the user, but is needed for the config// This is the token that will be swapped TO tokenToStake: { chainId: kavaId, address: axlUsdcKavaAddress, },// The intro page is optional, but it's a good way to explain to the user what he's going to do// It will be displayed as the first page of the widget if it's defined introPage: { title:"Stake paxlUSDC on Kava", stakingContract: { address:"0xe5274e38e91b615d8822e8512a29a16ff1b9c4af", explorerLink:"https://kava.mintscan.io/account/", }, logoUrl:"https://app.pinjamlabs.com/icons/pinkav.svg", }, };constconfig= { companyName:"Test Widget", integratorId:"example-swap-widget", slippage:3, instantExec:true, infiniteApproval:false, apiUrl:"https://dev.api.0xsquid.com", stakeConfig, } asAppConfig;return ( <divclassName={styles.container}> <SquidStakingWidgetconfig={config} /> </div> );}