Skip to main content

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.

warning
  • AMOUNT_TOTAL must equal the sum of all amountOutput values in outputTxArr.
  • Each amountOutput must match the total value used in the smartContractCalls payload array.
  • For Native Assets, each amountOutput must match the sum of payloadAmountNative values.

Steps:

  1. Configure Input Transaction: Specify the total amount to be locked in the Universal Plugin.
  2. Set Up Output Transaction: Define a single output transaction that makes multiple smart contract calls, providing flexibility for complex operations.
  3. Create Deposit and Generate ZKP: Use createDeposit() to generate the deposit object, followed by createWithdraw() to ensure privacy compliance.
  4. Submit Transactions: Submit the transactions to the blockchain through your institution's relayer or the SurferMonkey relayer.
tip

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.

warning
  • The outputTxArr length must match numberTxOut to ensure consistency between defined outputs and expected outputs.
tip

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:

  1. Configure Input Transaction: Specify the total amount to lock into the Universal Plugin.
  2. 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.
  3. Create Deposit and Generate ZKP: Use createDeposit() to generate the deposit object, followed by createWithdraw() to ensure privacy compliance.
  4. 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
};