Step 4: Initiate transfer transaction

Initiate transaction given the prior quote.

The full API reference for our /send endpoint can be found in the API reference section.

Similar to our quoting system, to initiate a transaction it requires all the following required information:

  • Bridge
  • Source chain
  • Destination chain
  • Source Token on the Source chain
  • Destination Token on the Destination chain
  • The amount you want to send
  • Source Wallet Address to pull funds from
  • Contract Call Information

Making a Request

POST: https://swap.prod.swing.xyz/v0/transfer/send

Body Parameters:

KeyExampleDescription
fromChainethereumThe blockchain where the transaction originates.
fromTokenAddress0x0000000000000000000000000000000000000000Source Token Address
fromUserAddress0x018c15DA1239B84b08283799B89045CD476BBbBbSender's wallet address
tokenSymbolETHSource token slug
toTokenAddress0x0000000000000000000000000000000000000000Destination token address.
toChainpolygonDestination chain
toTokenAmount4376081Amount of the destination token to be received.
toTokenSymbolMATICDestination Chain slug
toUserAddress0x018c15DA1239B84b08283799B89045CD476BBbBbReceiver's wallet address
tokenAmount1000000000000000000Amount of the source token being sent (in wei for ETH).
projectIdreplugYour project's ID
routesee Get QouteSelected route
contractCallInfosee Generating Contract Calldata sectionTarget contract info for deposits

Sample Request

const result = await axios.post(
  'https://swap.prod.swing.xyz/v0/transfer/send',
  {
    fromChain: 'polygon',
    tokenSymbol: 'USDC',
    fromTokenAddress: '0xcbe56b00d173a26a5978ce90db2e33622fd95a28',
    fromUserAddress: '0x018c15DA1239B84b08283799B89045CD476BBbBb',
    toChain: 'ethereum',
    toTokenSymbol: 'ETH',
    toTokenAddress: '0x0000000000000000000000000000000000000000',
    toUserAddress: '0x018c15DA1239B84b08283799B89045CD476BBbBb',
    tokenAmount: '1000000000',
    projectId: 'replug',
    route: [
      {
        bridge: 'stargate',
        bridgeTokenAddress: '0xc2132d05d31c914a87c6611c10748aeb04b58e8f',
        steps: ['allowance', 'approve', 'send'],
        name: 'USDT',
        part: 100,
      },
    ],
    contractCallInfo: [
      {
        toContractAddress: '0x9ee91F9f426fA633d227f7a9b000E28b9dfd8599',
        toContractCallData:
          '0xa9059cbb0000000000000000000000001234567890abcdef1234567890abcdef12345678000000000000000000000000',
        outputTokenAddress: '0x018c15DA1239B84b08283799B89045CD476BBbBb',
      },
    ],
  },
);

Generating Contract Calldata

For Contract Calls, Swing's /send endpoint gives us the option to instruct the system on what to do with our funds post swap.

For example, we can instruct Swing to deposit our funds into a smart contract of our choosing. A more practical instruction would be to stake our funds into a staking contract, and then send us that staking contract's LP token. We can achieve this using the contractCallInfo array parameter in the /send endpoint.

To execute a contract call via Swing, we'll need three (3) parameters to be passed our contractCallInfo Array:

PropertyExampleDescription
toContractAddress0x9ee91F9f426fA633d227f7a9b000E28b9dfd8599requiredSmart Contract address we'll deposit your funds into
toContractCallData0xa9059cbb0000000000000000000000abcdef........requiredCalldata to be passed to Swing
outputTokenAddress0x018c15DA1239B84b08283799B89045CD476BBbBboptionalAddress of Liquidity Provider Token to be received by sender

When performing a contract call transaction, you are required to provide the contract calldata for the contract to which you wish to send your funds.

Let's say we've got some USDC on the Polygon chain and want to stake some ETH into LIDO's Ethereum staking contracts via Swing. The following will take place to make this transaction to be possible:

  • Swing will start the transaction by deducting the necessary funds from the fromUserAddress.
  • Swing will then bridge our USDC (tokenSymbol) from the Polygon chain (fromChain) to the Ethereum Chain (toChain).
  • Swing will then swap that USDC (tokenSymbol) to ETH (toTokenSymbol) on the destination chain (toChain) and send it to wallet address supplied to the toUserAddress.
  • Using the contractCallInfo parameter, Swing will then execute a function on a target Smart Contract and deposit your funds.
  • Optionally, If the target smart contract emits any token to the sender's wallet like an LP token, Swing can then send those token to the address supplied to the toUserAddress.

If you omit the toUserAddress parameter, Swing will default it's value to whatever value you supply to the fromUserAddress.

To get started, we'll first need to generate the necessary callData by using LIDO's staking contract ABI:

// Using partial ABI for simplicity
const LIDO_ABI = [
  {
    constant: false,
    inputs: [
      { name: '_amount', type: 'uint256' },
      { name: '_referral', type: 'address' },
    ],
    name: 'submit',
    outputs: [{ name: '', type: 'uint256' }],
    payable: true,
    stateMutability: 'payable',
    type: 'function',
  },
];

It's very important to remember what we're trying to achieve here. We want Swing to interact with LIDO's contract immediately after swapping our funds. When Swing is done swapping our assets, we want Swing to call the submit function on LIDO's contract.

For this example, we'll be using ethers.js to generate the callData required for our contract call example.

import { ethers } from 'ethers';

Using ethers to generate our callData is fairly straight forward. First we'll load up LIDO's staking smart contract using it's ABI and contract address:

const LIDO_ADDRESS = '0x9ee91F9f426fA633d227f7a9b000E28b9dfd8599';
const MY_REWARDS_ADDRESS = '0x184AbEfBdCa24Ce0Dd964a74f6d5E69CE44D9579';
const LIDO_OUTPUT_TOKEN_ADDRESS = '0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84'; // sETH token address
const toAmount = '2578306944516966';
 
const contract = new ethers.Contract(
  LIDO_ADDRESS,
  LIDO_ABI,
  ethers.getDefaultProvider(),
);

Next, we'll encode the function data by using the encodeFunctionData() method to generate the final callData for the submit() function in LIDO's staking contract.

LIDO's submit() function takes two parameters, namely:

  • _amount — the amount of tokens to be staked
  • _referral — an address for incentivisation reward
const interface = new ethers.utils.Interface(LIDO_ABI);
const callData = interface.encodeFunctionData('submit', [
  toAmount,
  MY_REWARDS_ADDRESS,
]);

Putting it all together into a function called generateCallData():

const LIDO_ADDRESS = '0x9ee91F9f426fA633d227f7a9b000E28b9dfd8599';
const MY_REWARDS_ADDRESS = '0x184AbEfBdCa24Ce0Dd964a74f6d5E69CE44D9579';
const LIDO_OUTPUT_TOKEN_ADDRESS = '0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84'; // sETH token address
const toAmount = '1000000000';
 
const LIDO_ABI = [
  {
    constant: false,
    inputs: [
      { name: '_amount', type: 'uint256' },
      { name: '_referral', type: 'address' },
    ],
    name: 'submit',
    outputs: [{ name: '', type: 'uint256' }],
    payable: true,
    stateMutability: 'payable',
    type: 'function',
  },
];
 
async function generateCallData() {
  const contract = new ethers.Contract(
    LIDO_ADDRESS,
    LIDO_ABI,
    ethers.getDefaultProvider(),
  );
 
  const interface = new ethers.utils.Interface(LIDO_ABI);
  const callData = await interface.encodeFunctionData('submit', [
    toAmount,
    MY_REWARDS_ADDRESS,
  ]);
 
  return callData;
}

Next, let's generate our callData and make a request to Swing to get our transaction ready to be sent over to the network:

const sendTransaction = async () => {
  const callData = await generateCallData(); //sample output: 0xa9059cbb0000000000000000000000001234567890abcdef1234567890abcdef12345678000000000000000000000000...
 
  const result = await axios.post(
    'https://swap.prod.swing.xyz/v0/transfer/send',
    {
      fromChain: 'polygon',
      tokenSymbol: 'USDC',
      fromTokenAddress: '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359',
      fromUserAddress: '0x018c15DA1239B84b08283799B89045CD476BBbBb',
      toChain: 'ethereum',
      toTokenSymbol: 'ETH',
      toTokenAddress: '0x0000000000000000000000000000000000000000',
      tokenAmount: '1000000000',
      projectId: 'replug', // create your project here: https://platform.swing.xyz/
      route: [
        {
          bridge: 'stargate',
          bridgeTokenAddress: '0xc2132d05d31c914a87c6611c10748aeb04b58e8f',
          steps: ['allowance', 'approve', 'send'],
          name: 'USDT',
          part: 100,
        },
      ],
      contractCallInfo: [
        {
          toContractAddress: LIDO_ADDRESS,
          toContractCallData: callData,
          outputTokenAddress: LIDO_OUTPUT_TOKEN_ADDRESS, //Address of Liquidity Provider Token to be received by sender
        },
      ],
    },
  );
  return result.data;
};

Since performing a contract call will change the state of a user's wallet, the next step of this process requires invoking a contract-level function on the wallet address you supply to the fromUserAddress parameter. Swing only returns the necessary call-data that will need to be signed and executed by the local wallet (i.e. Metamask, WalletConnect, Coinbase Wallet).

Sending our transaction over the network

It's important to note that the /send endpoint only builds an unsigned transaction. To actually send the transaction, you'll have to give Swing the permission to do so by signing the transaction using a non-custodial wallet like MetaMask or Coinbase.

Now that we've got our txData, we next need to invoke a function in one of Swing's contracts to be able to take funds from a user's wallet and begin the transaction.

We'll be using ethers.js with MetaMask Wallet to demonstrate how you can execute our txData, but feel free to use whatever wallet provider you're comfortable using.

The txData from the /send will look something like this:

{
   ....
    "tx": {
        "from": "0x018c15DA1239B84b08283799B89045CD476BBbBb",
        "to": "0x39E3e49C99834C9573c9FC7Ff5A4B226cD7B0E63",
        "data": "0x301a3720000000000000000000000000eeeeeeeeeeee........",
        "value": "0x0e35fa931a0000",
        "gas": "0x06a02f"
    }
   ....
}

First, we'll call the /send endpoint via the sendTransaction() function and retrieve our txData from it's response:

const transaction = await sendTransaction();
 
let txData = {
  data: transaction.tx.data,
  from: transaction.tx.from,
  to: transaction.tx.to,
  value: transaction.tx.value,
  gasLimit: transaction.tx.gas,
};

Next, we'll create a function called getSigner() to retrieve our Signer Object using MetaMask's Injected Provider:

async function getSigner() {
  const provider = await new ethers.providers.Web3Provider(window.ethereum);
  return provider.getSigner();
}

Now that we've got our Signer Object, we can go ahead and execute the txData by calling the sendTransaction() function present in our signer object.

async function executeTxData(txData) {
  const signer = await getSigner();
 
  const txResponse = await signer.sendTransaction(txData);
 
  const receipt = await txResponse.wait();
  console.log('Transaction receipt:', receipt);
 
  return txResponse.txHash; // We'll need this later for checking the transaction's status
}

Finally, immediately after we get our txData from the /send Response, we'll prompt a user's MetaMask wallet to execute our txData by calling executeTxData():

const transaction = await sendTransaction();
 
let txData = {
  data: transaction.tx.data,
  from: transaction.tx.from,
  to: transaction.tx.to,
  value: transaction.tx.value,
  gasLimit: transaction.tx.gas,
};
 
const txHash = await executeTxData(txData); // This will open MetaMask and ask the user to confirm the transaction
 
console.log(txHash);