5.2 Multi-Call & Multi-Output Scenarios
5.2.1 One Input, One Output (Multi-Call)
In this example, we demonstrate how to use a single input transaction that results in one output, with multiple smart contract calls. This type of configuration allows for executing several actions in sequence while retaining privacy through the SurferMonkey SDK.
AMOUNT_TOTAL
must equal the sum of allamountOutput
values inoutputTxArr
.- Each
amountOutput
must match the total value used in thesmartContractCalls
payload array. - For Native Assets, each
amountOutput
must match the sum ofpayloadAmountNative
values.
Steps:
- Configure Input Transaction: Specify the total amount to be locked in the Universal Plugin.
- Set Up Output Transaction: Define a single output transaction that makes multiple smart contract calls, providing flexibility for complex operations.
- Create Deposit and Generate ZKP: Use
createDeposit()
to generate the deposit object, followed bycreateWithdraw()
to ensure privacy compliance. - Submit Transactions: Submit the transactions to the blockchain through your institution's relayer or the SurferMonkey relayer.
All actions in the smartContractCalls
array are executed sequentially within the same atomic output transaction, allowing you to orchestrate complex blockchain interactions seamlessly.
Example Code Snippet:
const inputTx = {
AMOUNT_TOTAL: "10000000000000", // Amount to lock (in uint256 format)
ASSET_ID: "0x0000000000000000000000000000000000000000" // Native asset ID
};
const outputTxArr = [
{
amountOutput: "10000000000000",
smartContractCalls: [
{
payloadAmountNative: "5000000000000",
targetSC: "0xTargetSmartContractAddress1",
payloadObject: {
functionHeader: "function performAction1(address target, uint256 amount)",
functionName: "performAction1",
payloadParmsArr: ["0xRecipientAddress1", "5000000000000"]
}
},
{
payloadAmountNative: "5000000000000",
targetSC: "0xTargetSmartContractAddress2",
payloadObject: {
functionHeader: "function performAction2(address target, uint256 amount)",
functionName: "performAction2",
payloadParmsArr: ["0xRecipientAddress2", "5000000000000"]
}
}
]
}
];
const userMessage = {
userEOA: "0xYourEvmAddressHere",
inputTx: inputTx,
outputTxArr: outputTxArr,
numberTxOut: 1,
pubKey: ["PubKeyString[0]", "PubKeyString[1]"],
chainID: "ChainIdentifier",
userMerkleProof: userMerkleProof
};
In this example, the input amount is distributed across two different concatenated smart contract calls, allowing you to perform two distinct actions with a single output transacction.
5.2.2 One Input, Multiple Outputs (Single-Call)
This example explains how to use one input transaction and split it into multiple outputs, each with different amounts and potentially different smart contract calls. This scenario is useful when you want to fractionalize an input into separate independent output transactions, allowing for more complex and diverse transactions.
- The
outputTxArr
length must matchnumberTxOut
to ensure consistency between defined outputs and expected outputs.
The key difference with Multiple Outputs is that targetLeafChild
can be greater than 0:
- Outputs are withdrawn asynchronously, allowing you to withdraw one today and another tomorrow.
- You can withdraw outputs in any order, like index 3 before index 2.
Steps:
- Configure Input Transaction: Specify the total amount to lock into the Universal Plugin.
- Set Up Multiple Outputs: Define multiple output transactions with different amounts. Each output can call different smart contracts or perform multiple calls within the same transaction.
- Create Deposit and Generate ZKP: Use
createDeposit()
to generate the deposit object, followed bycreateWithdraw()
to ensure privacy compliance. - Submit Transactions: Submit the transactions to the blockchain to finalize the withdrawal.
Example Code Snippet:
const inputTx = {
AMOUNT_TOTAL: "20000000000000", // Amount to lock (in uint256 format)
ASSET_ID: "0x0000000000000000000000000000000000000000" // Native asset ID
};
const outputTxArr = [
{
amountOutput: "10000000000000",
smartContractCalls: [
{
payloadAmountNative: "10000000000000",
// When transfering ETH, the target smart contract address is the SurferMonkey Proxy
targetSC: "0xProxySmartContractAddress",
payloadObject: { // Proxy transferEth function header
functionHeader: "function transferEth(address targetAddress, uint256 amount)",
functionName: "transferEth",
payloadParmsArr: ["0xRecipientAddress1", "10000000000000"]
}
}
]
},
{
amountOutput: "10000000000000",
smartContractCalls: [
{
payloadAmountNative: "10000000000000",
targetSC: "0xTargetSmartContractAddress2",
payloadObject: {
functionHeader: "function performAnotherAction(address target, uint256 amount, string anotherParameter)",
functionName: "performAnotherAction",
payloadParmsArr: ["0xRecipientAddress2", "10000000000000", "Pizza Please"]
}
}
]
}
];
const userMessage = {
userEOA: "0xYourEvmAddressHere",
inputTx: inputTx,
outputTxArr: outputTxArr,
numberTxOut: 2,
pubKey: ["PubKeyString[0]", "PubKeyString[1]"],
chainID: "ChainIdentifier",
userMerkleProof: userMerkleProof
};