This section will give you a walkthrough of the configurations & process for creating and executing Zenlink aggregation swap v2.
- Moonbeam
- Astar (Waiting for upgrade from v1)
- Arbitrum One (Waiting for upgrade from v1)
| Network/Chain | Contract Address |
|---|---|
| Moonbeam | 0x3494764d3bE100BA489c8BC5C3438E7629c5e5E5 |
| Network/Chain | Contract Address |
|---|---|
| Moonbeam | 0x832B21FA3AA074Ee5328f653D9DB147Bcb155C7a |
First things first, we need to configure some required user-specific things (including initialize viem client. Viem is a typeScript interface for Ethereum):
- The chainConfig of an Ethereum node to connect to
- The private key of the account to trade
import { createPublicClient, createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { moonbeam } from 'viem/chains'
import type { Address } from 'viem'
const account = privateKeyToAccount(process.env.PRIVATE_KEY as Address)
const chainConfig = {
chain: moonbeam,
transport: http(moonbeam.rpcUrls.default.http[0]),
}
const publicClient = createPublicClient(chainConfig)
const walletClient = createWalletClient({
account,
...chainConfig,
})Next, define the parameters for the swap you want to perform.
| Parameter name | Type | Description |
|---|---|---|
| chainId | number (Required) | chainId of the network(parachainId or ethereumId) |
| fromTokenId | string (Required) | contract address of a token to sell (or Native) |
| toTokenId | string (Required) | contract address of a token to buy (or Native) |
| amount | string (Required) | amount of a token to sell, set in minimal divisible units (eg: 51.03 USDC set as 51030000) |
| gasPrice | number (Required) | zenlink takes in account gas expenses to determine exchange route |
| priceImpact | number (Optional) | limit of price slippage you are willing to accept in percentage (eg: 0.01 set as 1%) |
| to | number (Optional) | receiver address (note that the routeParams will not be returned if it is not provided) |
const chainId = 2004 // can also use moonbeam ethereum chainId 1284
const fromTokenId = 'Native' // can also use '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' as Native address
const toTokenId = '0x931715FEE2d06333043d11F658C8CE934aC61D0c' // USDC.wh address
const amount = BigInt(10000e18).toString() // 10000 GLMR
const gasPrice = await publicClient.getGasPrice()
const priceImpact = 0.01 // 1%Now, define the API URLs and fetch route result including simple description, contract address and contract call parameters.
const {
routeHumanString,
routeParams,
routerAddress,
executorAddress,
} = await fetch(
`https://path-finder.zenlink.pro/v2?chainId=${
chainId
}&fromTokenId=${
fromTokenId
}&toTokenId=${
toTokenId
}&amount=${
amount
}&gasPrice=${
Number(gasPrice)
}&priceImpact=${
priceImpact
}&to=${account.address}`,
).then(res => res.json())
console.log(`Route Description: \n${routeHumanString}`)
const { tokenIn, amountIn, tokenOut, amountOutMin, to, routeCode, value } = routeParams
if (!routerAddress || !executorAddress)
console.error('Contract address not found')| Parameter name | Type | Description |
|---|---|---|
| routeHumanString | string | describe the entire route in human-readable form |
| routerAddress | string | contract address of router |
| executorAddress | String | contract address of executor |
| routeParams | object | tokenIn (string) parameters of a token to selltokenOut (string) parameters of a token to buyamountIn (string) input amount of tokenIn in minimal divisible unitsamountOutMin (string) minimumal amount of a token to buyto (string) receiver that transaction will be sent torouteCode (string) bytes code required for the contract |
If srcToken(fromToken) is not Native currency of network(like GLMR), we need to check the allowance and create the token allowance (approval) transaction.
// check erc20 allowance
if (tokenIn !== '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE') {
const allowance = await publicClient.readContract({
address: tokenIn,
abi: erc20ABI,
functionName: 'allowance',
args: [account.address, routerAddress],
})
if (allowance < BigInt(amountIn)) {
const { request } = await publicClient.simulateContract({
account,
address: tokenIn,
abi: erc20ABI,
functionName: 'approve',
args: [routerAddress, BigInt(amountIn)],
})
const hash = await walletClient.writeContract(request)
console.log('Approving Aggregation Router: ', hash)
await publicClient.waitForTransactionReceipt({ hash })
console.log('Approved!')
}
}Before proceeding, please confirm that your approval transaction has a status of Success!
AggregationRouterV2ABI
const aggregationRouterV2ABI = [
{
inputs: [
{
internalType: 'contract IAggregationExecutor',
name: 'executor',
type: 'address',
},
{
components: [
{
internalType: 'Currency',
name: 'srcToken',
type: 'address',
},
{
internalType: 'Currency',
name: 'dstToken',
type: 'address',
},
{
internalType: 'address',
name: 'dstReceiver',
type: 'address',
},
{
internalType: 'uint256',
name: 'amount',
type: 'uint256',
},
{
internalType: 'uint256',
name: 'minReturnAmount',
type: 'uint256',
},
],
internalType: 'struct AggregationRouter.SwapDescription',
name: 'desc',
type: 'tuple',
},
{
internalType: 'bytes',
name: 'route',
type: 'bytes',
},
],
name: 'swap',
outputs: [
{
internalType: 'uint256',
name: 'returnAmount',
type: 'uint256',
},
{
internalType: 'uint256',
name: 'spentAmount',
type: 'uint256',
},
],
stateMutability: 'payable',
type: 'function',
},
] as constconst {
result: [returnAmount, spentAmount],
request,
} = await publicClient.simulateContract({
address: routerAddress,
abi: aggregationRouterV2ABI,
functionName: 'swap',
account,
args: [
executorAddress,
{
srcToken: tokenIn,
dstToken: tokenOut,
amount: BigInt(amountIn),
dstReceiver: to,
minReturnAmount: BigInt(amountOutMin),
},
routeCode,
],
value: BigInt(value || '0'),
})
// check simulate result
if (returnAmount >= BigInt(amountOutMin) && spentAmount === BigInt(amount)) {
console.log('Simulate Completed!')
const hash = await walletClient.writeContract(request)
console.log('Transaction Signed and Sent: ', hash)
await publicClient.waitForTransactionReceipt({ hash })
console.log('Transaction Completed, Swap tx hash: ', hash)
}
else {
console.log('Simulate failed, please try again!')
}After running this code in the console, you should see something like this
Transaction Completed, Swap tx hash: 0xe90955ddec325e26f274f6467e92bbd43753173a5bac877b30e0d835055f9d02
Let's check the result of the transaction on the explorer: https://moonbeam.moonscan.io/tx/0xe90955ddec325e26f274f6467e92bbd43753173a5bac877b30e0d835055f9d02