Destination Zaps
Introduction​
- Destination Actions enable developers to perform arbitrary transactions on the destination chain in addition to bridging tokens.
- Developers can pass the
calldata
on the source chain that will be executed post-bridging on the destination chain. You’ll also need to estimate and pass thegasLimit
of this destination transaction. - Accordingly, users would pay additional gas fees on the source chain which will cover the destination fees of the relayer.
- This
calldata
will be executed on the destination chain in the same transaction in which the bridged funds are transferred. - Destination Actions has limited bridge support to begin with and will support more bridges/tokens in upcoming updates
Chain Support​
The destination action integration flow mostly follows Single Transaction Bridging & Multi Transaction Bridging flows. The only changes are in the Quote Step and Build-Tx Step. In case you haven’t checked out either guides, please check it first.
- Bungee has implemented a receiver contract on supported destination chains, which receives the token and executes the calldata.
Chain Receiver Contract Ethereum 0x362c116779D2d27F822a497E4650B6e2616d3859 Polygon 0x8DfeB2e0B392f0033C8685E35FB4763d88a70d12 Optimism 0xddC3A2bc1D6252D09A82814269d602D84Ca3E7ae Arbitrum 0x88616cB9499F32Ff6A784B66B60aABF0bCf0df39 BNB Chain 0x71cF3E64E42bcAEC7485AF71571d7033E5b7dF93 Avalanche 0x83b2cda6A33128324ee9cb2f0360bA8a42Cec2C6 Base 0xf510A87f6E28B4AE71c87123026617aBB6CE1420
Passing calldata to API​
1. Generating calldata
​
-
You need to build the
bytes calldata
to be executed on the destination chain & estimate thegasLimit
of executing this transaction before-hand -
Considerations for the targetContract
- Currently, destination actions only supports ERC20 tokens as the receiving token. Support for native assets will be added in upcoming releases.
- The receiver contract increases the token allowance of bridged ERC20 by
amountLD
(bridged output amount) for targetContract - The function being executed on targetContract must have a
transferFrom
function which transfers the bridged ERC20 token from the receiverContract to targetContract
Click to see an example
Depositing sUSD into Lyra on Optimism
calldata
for callinginitiateDeposit()
function on Lyra contracts post-bridging
import { ethers } from "ethers";
// Calldata for depositing 1000 sUSD with benificiary 0x58Daefe2A4224966535dfbBca1f3c90D09919c2D
const lyraContractAddress = "0x5Db73886c4730dBF3C562ebf8044E19E8C93843e";
async function generateLyraData() {
const provider = new ethers.providers.JsonRpcProvider(
"https://mainnet.optimism.io"
);
const contract = new ethers.Contract(lyraContractAddress, lyraAbi, provider);
const beneficiary = "0x58Daefe2A4224966535dfbBca1f3c90D09919c2D";
const amount = "1000000000000000000000";
const txData = await contract.populateTransaction.initiateDeposit(
beneficiary,
amount
);
return txData;
}
// Output
// 0xec55234600000000000000000000000058daefe2a4224966535dfbbca1f3c90d09919c2d00000000000000000000000000000000000000000000003635c9adc5dea00000
// Estimated gasLimit
// ~ 285K
2. Fetching Bridging Quote with destination calldata
​
-
You can request a bridging quote as usual with selected fromChainId, toChainId, fromTokenAddress, toTokenAddress. More on fetching quotes here.
-
When calling the Quote endpoint, you need to pass 4 additional params for executing destination transactions
Param Description recipient Address of targetContract on destination chain where calldata will be executed destinationPayload calldata to be executed on destination chain destinationGasLimit gas limit required to post-bridging tx + 30k extra gas singleTxOnly Must be set to true -
IMPORTANT
Please add
30_000
gas units to the estimateddestinationGasLimit
. This covers the cost of the receiver contract on the destination chain. For e.g if the estimatedgasLimit
to execute your destination tx is570_000
, adddestinationGasLimit
as600_000
Example Request
Example Quote Request for bridging 1000 sUSD from Ethereum to Optimism & depositing into Lyra
https://api.socket.tech/v2/quote?fromChainId=1&fromTokenAddress=0x57Ab1ec28D129707052df4dF418D58a2D46d5f51&toChainId=10&toTokenAddress=0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9&fromAmount=1000000000000000000000&userAddress=0x58Daefe2A4224966535dfbBca1f3c90D09919c2D&recipient=0x5Db73886c4730dBF3C562ebf8044E19E8C93843e&uniqueRoutesPerBridge=true&includeBridges=&sort=output&singleTxOnly=true&destinationPayload=0xec55234600000000000000000000000058daefe2a4224966535dfbbca1f3c90d09919c2d00000000000000000000000000000000000000000000003635c9adc5dea00000&destinationGasLimit=310000
Example Response
{
"success": true,
"result": {
"routes": [
{
// Route Object to be used
}
],
"destinationCallData": {
"destinationPayload": "0xec55234600000000000000000000000058daefe2a4224966535dfbbca1f3c90d09919c2d00000000000000000000000000000000000000000000003635c9adc5dea00000",
"destinationGasLimit": "310000"
}, // DestinationCallData to be used
...
}
}
3. Building transaction for source chain​
(This guide uses the POST /build-tx endpoint)
- The selected
route
’s object anddestinationCallData
returned in the quote response can be used to build the transaction to be executed on the source chain
REQUEST BODY
{
"route": {
// Insert selected route object here
},
"destinationCallData": {
// Insert selected destinationCallData from the Quote response here
}
}
Example Request
- Using the route returned in the Quote Example above
REQUEST BODY
{
"route": {
"routeId": "49de2227-346f-4234-96c8-f892e4f9a46f",
"isOnlySwapRoute": false,
"fromAmount": "1000000000000000000000",
"toAmount": "999780563000000000000",
"usedBridgeNames": ["stargate"],
"minimumGasBalances": {
"1": "30000000000000000",
"10": "1800000000000000"
},
"chainGasBalances": {
"1": {
"minGasBalance": "30000000000000000",
"hasGasBalance": false
},
"10": {
"minGasBalance": "1800000000000000",
"hasGasBalance": false
}
},
"totalUserTx": 1,
"sender": "0x58Daefe2A4224966535dfbBca1f3c90D09919c2D",
"recipient": "0x5Db73886c4730dBF3C562ebf8044E19E8C93843e",
"totalGasFeesInUsd": 21.0896694041751,
"receivedValueInUsd": 979.680670595825,
"userTxs": [
{
"userTxType": "fund-movr",
"txType": "eth_sendTransaction",
"chainId": 1,
"toAmount": "999780563000000000000",
"toAsset": {
"chainId": 10,
"address": "0x8c6f28f2f1a3c87f0f938b96d27520d9751ec8d9",
"symbol": "SUSD",
"name": "Synthetic sUSD",
"decimals": 18,
"icon": "https://assets.coingecko.com/coins/images/5013/small/sUSD.png?1616150765",
"logoURI": "https://assets.coingecko.com/coins/images/5013/small/sUSD.png?1616150765",
"chainAgnosticId": null
},
"stepCount": 1,
"routePath": "0-27",
"sender": "0x58Daefe2A4224966535dfbBca1f3c90D09919c2D",
"approvalData": {
"minimumApprovalAmount": "1000000000000000000000",
"approvalTokenAddress": "0x57ab1ec28d129707052df4df418d58a2d46d5f51",
"allowanceTarget": "0x6cf8d2bf45fe99e369db145faf6fb606a50b27f3",
"owner": "0x58Daefe2A4224966535dfbBca1f3c90D09919c2D"
},
"steps": [
{
"type": "bridge",
"protocol": {
"name": "stargate",
"displayName": "Stargate",
"icon": "https://s2.coinmarketcap.com/static/img/coins/128x128/18934.png",
"securityScore": 3,
"robustnessScore": 3
},
"bridgeSlippage": 0.5,
"fromChainId": 1,
"fromAsset": {
"chainId": 1,
"address": "0x57ab1ec28d129707052df4df418d58a2d46d5f51",
"symbol": "SUSD",
"name": "Synth sUSD",
"decimals": 18,
"icon": "https://assets.coingecko.com/coins/images/5013/small/sUSD.png?1616150765",
"logoURI": "https://assets.coingecko.com/coins/images/5013/small/sUSD.png?1616150765",
"chainAgnosticId": null
},
"fromAmount": "1000000000000000000000",
"toChainId": 10,
"toAsset": {
"chainId": 10,
"address": "0x8c6f28f2f1a3c87f0f938b96d27520d9751ec8d9",
"symbol": "SUSD",
"name": "Synthetic sUSD",
"decimals": 18,
"icon": "https://assets.coingecko.com/coins/images/5013/small/sUSD.png?1616150765",
"logoURI": "https://assets.coingecko.com/coins/images/5013/small/sUSD.png?1616150765",
"chainAgnosticId": null
},
"minAmountOut": "994781660000000000000",
"toAmount": "999780563000000000000",
"protocolFees": {
"asset": {
"chainId": 1,
"address": "0x57ab1ec28d129707052df4df418d58a2d46d5f51",
"symbol": "SUSD",
"name": "Synth sUSD",
"decimals": 18,
"icon": "https://assets.coingecko.com/coins/images/5013/small/sUSD.png?1616150765",
"logoURI": "https://assets.coingecko.com/coins/images/5013/small/sUSD.png?1616150765",
"chainAgnosticId": null
},
"feesInUsd": 0,
"amount": "219437000000000000"
},
"gasFees": {
"gasAmount": "12607331020364000",
"asset": {
"chainId": 1,
"address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"symbol": "ETH",
"name": "Ethereum",
"decimals": 18,
"icon": "https://maticnetwork.github.io/polygon-token-assets/assets/eth.svg",
"logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/eth.svg",
"chainAgnosticId": null
},
"gasLimit": 420000,
"feesInUsd": 21.0896694041751
},
"serviceTime": 60,
"maxServiceTime": 7200
}
],
"gasFees": {
"gasAmount": "12607331020364000",
"feesInUsd": 21.0896694041751,
"asset": {
"chainId": 1,
"address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"symbol": "ETH",
"name": "Ethereum",
"decimals": 18,
"icon": "https://maticnetwork.github.io/polygon-token-assets/assets/eth.svg",
"logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/eth.svg",
"chainAgnosticId": null
},
"gasLimit": 420000
},
"serviceTime": 60,
"recipient": "0x5Db73886c4730dBF3C562ebf8044E19E8C93843e",
"maxServiceTime": 7200,
"bridgeSlippage": 0.5,
"userTxIndex": 0
}
],
"serviceTime": 60,
"maxServiceTime": 7200,
"integratorFee": {
"amount": "0",
"asset": {
"chainId": 1,
"address": "0x57ab1ec28d129707052df4df418d58a2d46d5f51",
"symbol": "SUSD",
"name": "Synth sUSD",
"decimals": 18,
"icon": "https://assets.coingecko.com/coins/images/5013/small/sUSD.png?1616150765",
"logoURI": "https://assets.coingecko.com/coins/images/5013/small/sUSD.png?1616150765",
"chainAgnosticId": null
}
}
},
"destinationCallData": {
"destinationPayload": "0xec55234600000000000000000000000058daefe2a4224966535dfbbca1f3c90d09919c2d00000000000000000000000000000000000000000000003635c9adc5dea00000",
"destinationGasLimit": "310000"
}
}
Example Response
{
"success": true,
"result": {
"userTxType": "fund-movr",
"txType": "eth_sendTransaction",
"txData": "0xa44bbb150000000000000000000000000000000000000000000000000000000000000020000000000000000000000000ddc3a2bc1d6252d09a82814269d602d84ca3e7ae000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000003635c9adc5dea0000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000057ab1ec28d129707052df4df418d58a2d46d5f5100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000025381a1c4f9c100000000000000000000000057ab1ec28d129707052df4df418d58a2d46d5f51000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000003b4b29dc000000000000000000000000000000000000000000000000000000000000006f00000000000000000000000058daefe2a4224966535dfbbca1f3c90d09919c2d000000000000000000000000000000000000000000000000000000000004baf000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000005db73886c4730dbf3c562ebf8044e19e8c93843e00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000044ec55234600000000000000000000000058daefe2a4224966535dfbbca1f3c90d09919c2d00000000000000000000000000000000000000000000003635c9adc5dea0000000000000000000000000000000000000000000000000000000000000",
"txTarget": "0xc30141B657f4216252dc59Af2e7CdB9D8792e1B0",
"chainId": 1,
"userTxIndex": 0,
"value": "0x025381a1c4f9c1",
"approvalData": {
"minimumApprovalAmount": "1000000000000000000000",
"approvalTokenAddress": "0x57ab1ec28d129707052df4df418d58a2d46d5f51",
"allowanceTarget": "0x6cf8d2bf45fe99e369db145faf6fb606a50b27f3",
"owner": "0x58Daefe2A4224966535dfbBca1f3c90D09919c2D"
}
}
}
If you’re using the GET /build-tx instead.
If you’re following the Single Transaction Bridging guide & using the GET Build-Tx endpoint
-
Check out the GET build-tx for more details
-
In addition to passing all the request params, you’ll also need to pass the
recipient
,destinationPayload
anddestinationGasLimit
returned in the Quote Response in thedestinationCallData
object. -
Example Request
https://api.socket.tech/v2/build-tx?sender=0x58Daefe2A4224966535dfbBca1f3c90D09919c2D&recipient=0x5Db73886c4730dBF3C562ebf8044E19E8C93843e&routePath=0-27&fromChainId=1&toChainId=10&fromTokenAddress=0x57ab1ec28d129707052df4df418d58a2d46d5f51&toTokenAddress=0x8c6f28f2f1a3c87f0f938b96d27520d9751ec8d9&fromAmount=1000000000000000000000&toAmount=999893238000000000000&bridgeInputTokenAddress=0x57ab1ec28d129707052df4df418d58a2d46d5f51&destinationPayload=0xec55234600000000000000000000000058daefe2a4224966535dfbbca1f3c90d09919c2d00000000000000000000000000000000000000000000003635c9adc5dea00000&destinationGasLimit=310000
If you’re using the POST /route/start instead.
If you’re following the Multi Transaction Bridging guide & using the /Route endpoints
- When starting the route, you need to add the
desintinationCallData
object, same as the POST /build-tx endpoint - More on the /route/start endpoint
{
"route": {
// Insert selected route object here
},
"destinationCallData": {
// Insert selected destinationCallData from the Quote response here
}
}
4. In-flight & Post Bridging​
-
Once the transaction is initiated, its status can be queried using the Bridge Status endpoint. More on this here.
-
Here’s some examples of transactions being executed on the destination chain
Glossary​
Term | Description |
---|---|
Payload | Calldata to be executed on the destination chain. |
Receiver Contract | Bungee's contract on the destination chain that receives tokens and calls the target contract. |
Target Contract | The contract on the destination chain which is being called in the calldata. |
Debugging failed transactions​
In case the tokens are bridged and sent to the Receiver Contract but the destination calldata
isn’t executed, check if there’s a CachedSwapSaved
event emitted. This generally means the calldata was faulty and couldn’t be executed or the gasLimit was insufficient to cover the cost of the transaction. Please reach out to us if your transaction fails and we can help debug.