In the Quickstart guide, we showed a complete example using a fairly simple two-step transaction. In this section, we will go over a few additional important questions that will help you build Peaze transactions with confidence.

1. Who is the caller and msg.sender?

The Peaze smart contract address. Let’s clarify further with an example.

Suppose you want to enable a two-part operation: a USDC to ETH swap on Uniswap, followed by staking that ETH on Lido. What should the parameters of the Uniswap trade be in the transaction submitted to Peaze?

The Uniswap trade is performed - we’ll use V2 for simplicity - by calling the swapExactTokensForETH function of the Uniswap V2 contract:

function swapExactTokensForETH(
    uint amountIn,
    uint amountOutMin,
    address[] calldata path,
    address to,
    uint deadline
) external virtual override ensure(deadline) 
returns (uint[] memory amounts)

The to parameter here should be specified as the Peaze smart contract. You may be tempted to think that it should be the user’s address, but that would be wrong. If you do that, then during the execution the user will receive the ETH, but the Peaze contract will not be able to execute the second step of the transaction - it won’t have the ETH to deposit into Lido!

In the second part of the overall operation, staking into Lido, the minted stETH tokens will be sent to the caller, which - let’s emphasize again - is the Peaze contract. How will the user get them? Since stETH is an ERC-20 token, all you have to do is to specify it in the expectedERC20Tokens array, and the Peaze contract will forward it to the user.

2. What is expectedERC20Tokens?

This is an array of ERC-20 tokens that the user should receive as a result of the transaction. To clarify this further, let’s dive into how Peaze works.

When the Peaze Server receives an execute request, it instructs the Peaze Relayer EOA to call the appropriate function of the Peaze contract. The execution then proceeds as follows:

  1. The authorized amount of the user’s source token (e.g. USDC) is transferred to the Peaze contract.
  2. If the transaction is cross-chain, these funds are bridged to the Peaze contract on the destination chain.
  3. The Peaze contract that now has the user’s funds executes the requested sequence of transactions.
  4. At the end, if the Peaze contract ends up with any ERC-20, ERC-721, or ERC-1155 tokens, it transfers them to the user, along with any remaining native tokens.

For the last step to work, the Peaze contract needs to know which tokens it ended up with. For ERC-721 and ERC-1155 tokens, it implements functions onERC721Received, onERC1155Received, and onERC1155BatchReceived, but there’s no equivalent of these functions for ERC-20 tokens. Therefore, the Peaze contract needs to be told which ERC-20 tokens it should expect to receive, so that it can forward them to the user. This is what expectedERC20Tokens is for.

3. What about receiving NFTs?

Automatic forwarding to the user is only supported for NFTs received via safe transfers.

The methods that Peaze leans on to forward NFTs (onERC721Received, onERC1155Received, onERC1155BatchReceived) are only triggered during a safe transfer.

Most protocols utilize safe transfers. If this is the case for your transaction, then you don’t need to do anything. Any received NFTs will be forwarded to the user automatically.

What if my transaction involves receiving NFTs via unsafe transfers? Peaze allows you to encode any sequence of contract calls into the overall transaction, so you don’t have to rely on the auto-forwarding mechanism. You can instead encode the transfer of the NFTs to the user as the last step of the transaction. Please be cautious and remember: Peaze is non-custodial, so we can’t help you recover tokens.

4. What is tokenAmount?

This is the amount of the destination token that is needed for the overall transaction (on the destination chain) excluding fees. By “destination token”, we mean:

  1. For a single-chain transaction - just the user’s source token (source == destination)
  2. For a cross-chain transaction - the token on the destination chain bridged from the source chain

In both cases, this is the token that will be used to directly fund the user’s desired transaction on the destination chain. As such, the amount must be expressed in destination chain decimals. On all currently supported chains, USDC and USDT have 6 decimals.

Consider this example where the overall transaction includes:

  1. Swapping 1000 USDC for ETH on Uniswap.
  2. Minting an NFT for 0.1 ETH.
  3. Depositing 500 USDC into Aave.

tokenAmount should be set to “1500000000” (1500 USDC), because that’s the amount of USDC that the Peaze contract will need in order to execute the transaction.

5. What happens if the transaction fails?

During the estimation step, we screen for transactions that would fail if executed. However, it’s always possible that the state of the blockchain changes between estimation and execution in a way that makes the transaction fail. In that case:

  1. For single-chain transactions, the whole operation will revert, and the user will keep their funds.
  2. For cross-chain transactions, since the execution attempt will only be made after the funds have been bridged, the user will receive the bridged tokens on the destination chain.