5.1 Single Call Scenarios
5.1.1 Native Asset Transfer
In this example, we demonstrate how to execute a privacy-preserving transfer of native blockchain assets using the SurferMonkey SDK. This type of transfer is ideal when you want to maintain user privacy while moving assets like Ether or other native tokens.
For Native Asset the field ASSET_ID
is address(0) = 0x0000000000000000000000000000000000000000000000000000000000000000
For Native Transfers, specify the targetSC
as the SurferMonkey Proxy Smart Contract address. And use the following function details:
- Function Header:
"function transferEth(address targetAddress, uint256 amount)"
- Function Name:
"transferEth"
Steps:
- Configure Input Transaction: Define the amount of the native asset you want to lock into the Universal Plugin. Use
address(0)
for native assets. - Set Up Output Transaction: Specify the amount to be sent and the recipient's address. Ensure the smart contract call parameters include details such as
transferEth
for native transfers. - Create Deposit and Generate ZKP: Call the
createDeposit()
function to initiate the lock-in and generate the required Zero-Knowledge Proof (ZKP) usingcreateWithdraw()
. - Submit the Transactions: Use your institution’s relayer or SurferMonkey's relayer to submit the transactions to the blockchain.
Example Code Snippet:
const userMessage = {
userEOA: "0xYourEvmAddressHere",
inputTx: {
AMOUNT_TOTAL: "10000000000000",
ASSET_ID: "0x0000000000000000000000000000000000000000"
},
outputTxArr: [
{
amountOutput: "10000000000000",
smartContractCalls: [
{
payloadAmountNative: "10000000000000",
targetSC: "0xSurferMonkeyProxy",
payloadObject: {
functionHeader: "function transferEth(address targetAddress, uint256 amount)",
functionName: "transferEth",
payloadParmsArr: ["0xRecipientAddress", "10000000000000"]
}
}
]
}
],
numberTxOut: 1,
pubKey: ["PubKeyString[0]", "PubKeyString[1]"],
chainID: "ChainIdentifier",
userMerkleProof: userMerkleProof
};
5.1.2 ERC20 Token Transfer
This example explains how to transfer ERC20 tokens while maintaining privacy. This scenario is relevant when interacting with fungible tokens such as stablecoins or other ERC20 assets.
For ERC20 the field ASSET_ID
is the ERC20 address.
For ERC20 transactions, you must approve the Universal Plugin Address as the spender before locking funds. This approval step is necessary to allow the Universal Plugin to handle your ERC20 tokens.
For ERC20 the field payloadAmountNative
is "0". And is ignored but required.
Steps:
- Configure Input Transaction: Define the ERC20 token contract address and the amount to be locked in.
- Set Up Output Transaction: Specify the amount to transfer and the recipient. Unlike native asset transfers,
payloadAmountNative
is set to[0, 0, 0, ...]
for ERC20, and is ignored but required. - Create Deposit and Generate ZKP: Use
createDeposit()
to create the deposit object, followed bycreateWithdraw()
to generate the ZKP. - Submit Transactions: Submit the ZKP and complete the withdrawal process using the mixer contract.
Example Code Snippet:
const inputTx = {
AMOUNT_TOTAL: "5000000", // Amount of ERC20 to lock (in smallest unit, e.g., wei)
ASSET_ID: "0xERC20TokenContractAddress"
};
const outputTxArr = [
{
amountOutput: "5000000",
smartContractCalls: [
{
payloadAmountNative: "0",
targetSC: "0xERC20TokenContractAddress",
payloadObject: {
functionHeader: "function transfer(address recipient, uint256 amount)",
functionName: "transfer",
payloadParmsArr: ["0xRecipientAddress", "5000000"]
}
}
]
}
];
const userMessage = {
userEOA: "0xYourEvmAddressHere",
inputTx: inputTx,
outputTxArr: outputTxArr,
numberTxOut: 1,
pubKey: ["PubKeyString[0]", "PubKeyString[1]"],
chainID: "ChainIdentifier",
userMerkleProof: userMerkleProof
};
Transfer msg.value in the Deposit Call shall be 0 For ERC20 deposits, msg.value must be set to 0; otherwise, the transaction will revert.
const BACKEND_RPC = "https://your-surfermonkey-backend-rpc-url.com";
const UP_ABI = require('UniversalPlugin.json');
const deposit = await SurferMonkey.createDeposit(
{
BACKEND_RPC,
userMessage,
verbose: true
});
const ERC20_SC = new ethers.Contract("ERC20 Address", "ERC20 ABI", USER_SIGNER);
const approveTx = await ERC20_SC.approve("0xUniversalPluginAddress", inputTx.AMOUNT_TOTAL, {
maxPriorityFeePerGas: Number(25000000000),
maxFeePerGas: Number(27000000000)
});
await approveTx.wait(); // Wait for transaction to get minted
// Proceed with submitting the deposit to the blockchain
const USER_SIGNER = new ethers.Wallet(PRIV_KEY, PROVIDER);
const UniversalPlugin_SC = new ethers.Contract("0xUniversalPluginAddress", UP_ABI, USER_SIGNER);
const submitDeposit = await UniversalPlugin_SC.Deposit(
deposit.SOLIDITY_DATA.a,
deposit.SOLIDITY_DATA.b,
deposit.SOLIDITY_DATA.c,
deposit.SOLIDITY_DATA.Input,
{
value: 0, // For ERC20 asset shall be zero
maxPriorityFeePerGas: 25000000000,
maxFeePerGas: 27000000000,
gasLimit: 5142880
}
);