Destination payload lets you execute arbitrary contract calls on the destination chain after a crosschain transfer completes. This is useful for flows such as depositing to a vault, repaying a loan, or interacting with a protocol immediately after bridging or swapping.
Destination payload execution is available on Socket Swap V3 through /v3/swap/quote.
How it works
When building a Socket request, you can provide calldata that will be executed on the destination chain after assets are delivered. It works for both same-chain swaps and cross-chain swaps.
Provide these parameters in your quote request:
destinationPayload: ABI-encoded calldata for the target contract on the destination chain
receiverAddress: The execution target that will receive the calldata and perform the call
destinationGasLimit: Gas budget on destination to execute the payload
interface IBungeeExecutor {
function executeData (
bytes32 requestHash ,
uint256 [] calldata amounts ,
address [] calldata tokens ,
bytes memory callData
) external payable ;
}
Implementation example
const SOCKET_API_BASE_URL = "https://dedicated-backend.socket.tech" ;
const SOCKET_AFFILIATE_ID = "YOUR_AFFILIATE_ID" ;
const CONTRACT_ADDRESS = "0x1111111111111111111111111111111111111111" ;
async function getQuoteWithDestinationPayload () {
const quoteParams = {
userOps: "tx" ,
userAddress: "0xYourUsersAddress" ,
originChainId: "8453" , // Base
destinationChainId: "8453" , // Base
inputToken: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" , // USDC on Base
outputToken: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2" , // USDT on Base
inputAmount: "10000000" , // 10 USDC (6 decimals)
receiverAddress: CONTRACT_ADDRESS , // contract implementing IBungeeExecutor.executeData
destinationPayload: "0x1234acbd" , // calldata for the receiver contract
destinationGasLimit: "100000" , // gas ceiling for destination execution
};
const url = ` ${ SOCKET_API_BASE_URL } /v3/swap/quote` ;
const queryParams = new URLSearchParams ( quoteParams );
const response = await fetch ( ` ${ url } ? ${ queryParams } ` , {
headers: {
affiliate: SOCKET_AFFILIATE_ID ,
},
});
const data = await response . json ();
const serverReqId = response . headers . get ( "server-req-id" );
if ( ! data . success ) {
throw new Error (
`Quote error: ${ data . statusCode } : ${ data . message } . server-req-id: ${ serverReqId } `
);
}
}
Important Notes
The destinationPayload is the callData parameter that will be passed to the executeData function
The receiverAddress needs to be a contract that implements the IBungeeExecutor interface
Ensure the receiver contract trusts/can handle calls from the Socket executor
Complex receivers or multi-token logic require higher gas limits
Execution may fail due to out of gas on destination — increase destinationGasLimit
Re-check encodeAbiParameters types match the receiver decode logic to avoid encoding errors
Complete Examples
Executing a Custom Payload on Destination Using Viem
import {
createWalletClient ,
createPublicClient ,
http ,
encodeAbiParameters ,
} from 'viem' ;
import { privateKeyToAccount } from 'viem/accounts' ;
import { base } from 'viem/chains' ;
const PRIVATE_KEY = process . env . PRIVATE_KEY ;
// Example receiver contract on Base that implements executeData(...)
// https://basescan.org/address/0xC0b43F2B38CA47CC9e1b9697296716ebCF3D8177#code
const CONTRACT_ADDRESS = '0xC0b43F2B38CA47CC9e1b9697296716ebCF3D8177' ;
const account = privateKeyToAccount ( PRIVATE_KEY );
const publicClient = createPublicClient ({ chain: base , transport: http () });
const walletClient = createWalletClient ({ account , chain: base , transport: http () });
const SOCKET_API_BASE_URL = "https://dedicated-backend.socket.tech" ;
const SOCKET_AFFILIATE_ID = "YOUR_AFFILIATE_ID" ;
const quoteParams = {
userOps: "tx" ,
userAddress: account . address ,
originChainId: 8453 ,
inputToken: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" , // USDC on Base
destinationChainId: 8453 ,
outputToken: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2" , // USDT on Base
inputAmount: "3000000" , // 3 USDC
};
async function main () {
quoteParams . destinationPayload = encodeAbiParameters (
[{ type: 'address' }],
[ account . address ] // payload: forward funds to this address
);
quoteParams . destinationGasLimit = "100000" ;
quoteParams . receiverAddress = CONTRACT_ADDRESS ;
const url = ` ${ SOCKET_API_BASE_URL } /v3/swap/quote? ${ new URLSearchParams ( quoteParams ) } ` ;
const response = await fetch ( url , {
headers: {
affiliate: SOCKET_AFFILIATE_ID ,
},
});
const data = await response . json ();
if ( ! data . success || ! data . result ?. routes ?. length ) {
throw new Error ( `Quote failed: ${ data . message } ` );
}
const route = data . result . routes [ 0 ];
console . log ( "Quote ID:" , route . quoteId );
// Approve route.approval if present, submit route.txData.object, then poll /v3/swap/status with route.quoteId.
}
main ();
Example Smart Contract Implementation
For Example Purposes Only The smart contract below is for example purposes only. Please audit your contracts for production usage.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.28 ;
import { Ownable } from "solady/auth/Ownable.sol" ;
import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol" ;
interface IBungeeExecutor {
function executeData (
bytes32 requestHash ,
uint256 [] calldata amounts ,
address [] calldata tokens ,
bytes memory callData
) external payable ;
}
contract ExecuteDestinationPayload is IBungeeExecutor , Ownable {
address public constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE ;
event DestinationPayload (
bytes32 indexed requestHash ,
uint256 amount ,
address indexed token ,
address indexed recipient
);
error InvalidArrayLength ();
error ZeroAmount ();
error ZeroAddress ();
constructor () {
_initializeOwner ( msg.sender );
}
function executeData (
bytes32 requestHash ,
uint256 [] calldata amounts ,
address [] calldata tokens ,
bytes memory callData
) external payable override {
if (amounts.length != 1 || tokens.length != 1 ) {
revert InvalidArrayLength ();
}
address recipient = abi . decode (callData, ( address ));
address token = tokens[ 0 ];
uint256 amount = amounts[ 0 ];
if (token == NATIVE_TOKEN) {
SafeTransferLib. safeTransferETH (recipient, amount);
} else {
SafeTransferLib. safeTransfer (token, recipient, amount);
}
emit DestinationPayload (requestHash, amount, token, recipient);
}
receive () external payable {}
}