Skip to main content
So far you have been able to create a Portal client and create their MPC wallets. Now, lets sign a transaction and send someone some MON tokens over Monad Testnet.

Steps

  1. In order to send your first transaction, you’ll first need to fund your wallet. You can get some test MON tokens by using the Client API Fund Wallet endpoint.
  1. Next, we’ll need to format a transaction object.
Here is the example transaction:
  1. Now that we some testnet ETH, let’s send a transaction! We will send our test funds to a Portal test wallet using a single API request to https://mpc-client.portalhq.io/v1/sign .
    1. You can learn more about Ethereum transactions here, but for this example you only need to know about a few fields:
      1. method: this defines which Ethereum RPC signer method we want to use.
      2. params : these are the parameters of your transaction.
        • value : this is the amount of MON measured in wei and hex encoded.
        • from: this is the address of the sender - your wallet in this case.
        • to: this is the address you’re sending to - our test wallet in this case.
        • data: this is used for interacting with smart contracts - we can ignore it.
In the example below we are using the eth_sendTransaction Ethereum RPC method to instruct the Portal API to both sign and submit a transaction to the blockchain.
If your wallet is not funded, you will receive a RPC error on this step.
Congrats! 🎉 You have successfully created a wallet and signed a transaction!

Sign a user operation without submitting it

If your client uses Account Abstraction, eth_sendTransaction builds, signs and submits an ERC-4337 user operation for you in one call. Use eth_signUserOperation when you want Portal to build and sign the user operation but submit it to a bundler yourself. You do not build the user operation. You send a transaction, and Portal returns the signed user operation made from it. To batch several calls into one user operation, or to have Portal submit it for you, use Build, sign and broadcast a user operation instead.

Steps

  1. Call /v1/sign with method set to eth_signUserOperation. params is the same transaction you would pass to eth_sendTransaction, as a JSON object or a JSON-encoded string:
  1. The response data is the signed user operation, encoded as hex of its JSON:
  1. Decode it back to JSON:
The decoded user operation looks like this:
  1. Submit the decoded user operation, unchanged, to an ERC-4337 bundler (Portal’s RPC endpoint does not accept eth_sendUserOperation for these operations yet) with eth_sendUserOperation and the EntryPoint v0.6 address 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789. For example, with Pimlico:
The bundler returns the user operation hash:
  1. Poll the bundler with eth_getUserOperationReceipt and that hash until it returns a result. success tells you whether the call inside the user operation succeeded, and receipt.transactionHash is the on-chain transaction:

Things to know

  • Every field is covered by the signature, so any change, including gas fees, invalidates it. Call eth_signUserOperation again instead of editing the result. A bundler whose minimum fees are higher than the ones Portal priced will reject the user operation.
  • signature already carries the 4 leading zero bytes the smart account expects.
  • initCode is set until the smart account is deployed. The first user operation you submit deploys it.
  • Gas is sponsored by default when your environment has gas sponsorship configured for the chain. To opt out, pass "sponsorGas": false at the top level of the request body (next to method, not inside params); paymasterAndData then comes back empty and the smart account pays for gas.
  • When the smart account pays its own gas, EntryPoint v0.6 takes the maximum possible gas cost up front and refunds the unused part to the account’s EntryPoint deposit (balanceOf(sender) on the EntryPoint), not its native balance. The deposit is used toward the next user operation’s gas.
  • If sponsorship is not available for an operation, the call still succeeds and paymasterAndData comes back empty. Check it before submitting: with no paymaster, the smart account must hold enough native token to pay for gas, or the bundler rejects the operation with AA21 didn't pay prefund.
  • Paymaster data on a sponsored user operation is only valid for a short time (about 10 minutes), so submit it soon after signing.
  • eth_signUserOperation only works for Account Abstraction clients. Other clients get a 400 with METHOD_UNSUPPORTED.

Build, sign and broadcast a user operation

Use this flow when you want to batch several calls into one ERC-4337 user operation, or to see the user operation and its gas estimate before signing it. To control every field of the user operation yourself, see Sign a user operation you built. Portal builds the user operation, you sign its hash with the Enclave MPC API, and Portal submits it to the bundler. You do not need a bundler of your own. It works for Account Abstraction clients only.

Steps

  1. Build the user operation with the Client API build user operation endpoint. Each entry in calls is one call the smart account makes, in order:
The response has the user operation as a JSON string, the hash to sign, and a gas estimate. estimatedGasCostWei is the most the user operation can cost when the smart account pays for its own gas:
  1. Sign userOpHash with the raw signing endpoint, without the 0x prefix:
The response data is the signature.
  1. Broadcast the user operation with the Client API broadcast user operation endpoint. Pass userOperation exactly as the build step returned it, and the signature exactly as the raw signing endpoint returned it. Portal formats the signature for the smart account and submits the user operation to the bundler:
  1. Look up the user operation hash with get transaction (GET /api/v3/clients/me/chains/{chain}/transactions/{hash}). The on-chain transaction hash appears once a bundler includes the user operation, usually within a few seconds; until then the lookup returns 404.

Things to know

  • Sign and broadcast soon after building. The user operation carries the gas prices and nonce from the moment it was built, and a sponsored one carries paymaster data that expires after a few minutes.
  • The build step runs your environment’s transaction policies on every call, and returns 403 with policyViolation when one blocks it.
  • Gas is sponsored when your environment has gas sponsorship configured for the chain. If paymasterAndData in the built user operation is 0x, the smart account pays for gas and needs enough native token to cover estimatedGasCostWei.
  • Clients without Account Abstraction get a 400: Account Abstraction is not enabled, contact support to enable it.

Sign a user operation you built

Use this flow when you build the user operation yourself, for example to use your own paymaster, gas settings or calldata encoding. Portal only signs its hash. You can then submit it through Portal or to your own bundler. The user operation has to match the smart account Portal created for the client:

Steps

  1. Compute the EntryPoint v0.6 user operation hash, for example with viem:
Portal smart accounts use EntryPoint v0.6 at 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789, the same address on every chain where Portal supports Account Abstraction.
  1. Sign userOpHash with the raw signing endpoint, without the 0x prefix:
The response data is a 65-byte signature: r, s, then a recovery id of 00 or 01.
  1. Submit the user operation one of two ways:
    • Through Portal: call broadcast user operation with your user operation as userOperation and the signature exactly as the raw signing endpoint returned it. Portal formats the signature for the smart account and submits it.
    • To your own bundler: format the signature for the Kernel smart account first, then call eth_sendUserOperation:
The 0x00000000 prefix selects the Kernel smart account’s default validator. A signature without it, or with a v of 00/01, fails validation at the bundler.
  1. Confirm inclusion with get transaction for the user operation hash, or with your bundler’s eth_getUserOperationReceipt.