Once the user has been authenticated, you can now utilize common web3 functions to interact with the blockchain - completely seamlessly.

  • Get Wallet Address: Access the wallet linked to the user’s authenticated account. This wallet serves as the gateway to all account functions.
  • Sign Messages: Signing a message using the embedded wallet.
  • Send Transactions: After a user has been authenticated, use the WaaS SDK to send transactions using crafted EVM calldata.

Get Wallet Address

Each user’s wallet address is unique, cannot be changed, and is 42 characters long, starting with 0x:

import { SequenceWaaS } from "@0xsequence/waas";



const sequence = new SequenceWaaS(

  {

    projectAccessKey: `${process.env.VITE_PROJECT_ACCESS_KEY}`,

    waasConfigKey: `${process.env.VITE_WAAS_CONFIG_KEY}`,

    network: "arbitrum-nova",

  }

);



const { wallet } = await sequence.signIn({ idToken }, "Session name")

const address = await sequence.getAddress();



console.log(address == wallet) // true



console.log(address)
0xE4b10c53aa75E19E088cfDD0cff7D46a0E4206F0

Sign Messages

Wallets can sign messages, which can be validated both onchain and offchain:

import { SequenceWaaS } from "@0xsequence/waas";



const sequence = new SequenceWaaS(

  {

    projectAccessKey: `${process.env.PROJECT_ACCESS_KEY}`,

    waasConfigKey: `${process.env.WAAS_CONFIG_KEY}`,

    network: "arbitrum-nova",

  }

);



await sequence.signIn({ idToken }, "Session name");



const signature = await sequence.signMessage({

  chainId: 42170,

  message: "Hello world",

});



console.log(signature);
{

  "code": "signedMessage",

  "data": {

    "message": "0x48656c6c6f20776f726c64",

    "signature": "0x0100010000000002012128ff2dd168dc250dc3da93db3131f737e6961a0000fe0100030000000006010001000074000197013331090a763fc7ef2216502cfbff5d855530f977a0ee6db3615722ed9bad498781d8ed72d52b5c9717708ac757f7789c9567e5468566179bd03f72d1fc7b1c010400002c01011111b16c6268897233eddea98a041b326b0faef2010122229ce37ccfee1cbab2b743b22c314b5667cf1a06020001000074000100deb9091f5beb1ebd8d91a1b81e562a70cdb3a1cdafc5e61087b18d1c221c570754ecbe056bdef5f82c388a9bf53f074521aeaf5afdeed3a2ba70adb89362631b010400002c0101444444444444444444444444444444444444444401015555555555555555555555555555555555555555030100a5a91b133336e5ef1c7e23c13974535018fab1c0"

  }

}

Send Transactions

All wallets can send transactions right after creation. No extra steps are required to create the wallet, as all users have a wallet by default.

At any point when sending a transaction that requires a fee on a network, you can follow the Fee Options walkthrough for how to send a fee object alongside your transaction or cover gas fees for your users found here.

Raw transaction

  • Immediate Transactions: Wallets are ready to send transactions immediately upon creation.
  • Raw Transactions: Specify transaction parameters such as recipient, value, and data. Gas limits and nonce are managed automatically.
  • Network Requirements: Transactions require specifying a chainId for the intended network., for example 1 for Ethereum mainnet, 42161 for Arbitrum, etc.

Error Handling: Use isSentTransactionResponse to verify transactions before execution to prevent failures. WaaS checks that a transaction won’t fail before it runs. If it does fail, you’ll get an error message instead of a transaction receipt. See more in transaction receipts.

import { Sequence, isSentTransactionResponse } from "@0xsequence/waas";



const sequence = new SequenceWaaS(

  {

    projectAccessKey: `${process.env.PROJECT_ACCESS_KEY}`,

    waasConfigKey: `${process.env.WAAS_CONFIG_KEY}`,

    network: "arbitrum-nova",

  }

);



await sequence.signIn({ idToken }, "Session name");



const tx = await sequence.sendTransaction({

  chainId: 42161,

  transactions: [

    {

      to: "0x27CabC9700EE6Db2797b6AC1e1eCe81C72A2cD8D",

      value: "200000000000000000000", // 200 ETH

      data: "0x9fa2b3c4",

    },

  ],

});



if (isSentTransactionResponse(tx)) {

  console.log(tx);

}
{

  "code": "transactionReceipt",

  "data": {

    "txHash": "0xf2e9f728abd65089f25efda5852e605ced377f4e2c89dbf143b124623ed09b2c",

    "metaTxHash": "acc36ed4ef40db74137266e48d863083a5c7e85e2735d69adafcb5b362b6cfc0",

    "nativeReceipt": { ... },

    "receipt": { ... },

    "request": { ... },

    "simulations": [ ... ],

  }

}

Send ERC20 Tokens

Helper methods are available for common operations, such as sending ERC20 tokens. This automatically handles the data field of the transaction:

import { Sequence, isSentTransactionResponse } from "@0xsequence/waas";



const sequence = new SequenceWaaS(

  {

    projectAccessKey: `${process.env.PROJECT_ACCESS_KEY}`,

    waasConfigKey: `${process.env.WAAS_CONFIG_KEY}`,

    network: "mumbai",

  },

  defaults.TEST

);



await sequence.signIn({ idToken }, "Session name");



const tx = await sequence.sendERC20({

  chainId: 42161,

  token: "0x6b175474e89094c44da98b954eedeac495271d0f", // DAI

  to: "0x27CabC9700EE6Db2797b6AC1e1eCe81C72A2cD8D", // Recipient

  value: "200000000000000000000", // 200 DAI

});



if (isSentTransactionResponse(tx)) {

  console.log(tx);

}
{

  "code": "transactionReceipt",

  "data": {

    "txHash": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3",

    "metaTxHash": "01a087979dccbbc49a45b72d987e5651d65bd97349ccbfdd601b0b7beee9ddc4",

    "nativeReceipt": { ... },

    "receipt": { ... },

    "request": { ... },

    "simulations": [ ... ],

  }

}

Send ERC721 Tokens

Sending ERC721 tokens has a helper method. This automatically handles the data field of the transaction:

import { Sequence, isSentTransactionResponse } from "@0xsequence/waas";



const sequence = new SequenceWaaS(

  {

    projectAccessKey: `${process.env.PROJECT_ACCESS_KEY}`,

    waasConfigKey: `${process.env.WAAS_CONFIG_KEY}`,

    network: "arbitrum-nova",

  }

);



await sequence.signIn({ idToken });



const tx = await sequence.sendERC721({

  chainId: 42161,

  token: "0xF87E31492Faf9A91B02Ee0dEAAd50d51d56D5d4d", // Decentraland LAND

  to: "0x27CabC9700EE6Db2797b6AC1e1eCe81C72A2cD8D", // Recipient

  id: "33347671958251969419410711528313284722562", // Asset ID

});



if (isSentTransactionResponse(tx)) {

  console.log(tx);

}
{

  "code": "transactionReceipt",

  "data": {

    "txHash": "0x4936962d9972a70bffc27f376f55d9c60c12e762819fa6384fdb466664122b6e",

    "metaTxHash": "e6513a60b63359a365f0d3f05744d89823278ec829fc5cb4d275bb815d0f5887",

    "nativeReceipt": { ... },

    "receipt": { ... },

    "request": { ... },

    "simulations": [ ... ],

  }

}

Send ERC1155 Tokens

Sending ERC1155 tokens is also supported:

import { Sequence, isSentTransactionResponse } from "@0xsequence/waas";



const sequence = new SequenceWaaS(

  {

    projectAccessKey: `${process.env.PROJECT_ACCESS_KEY}`,

    waasConfigKey: `${process.env.WAAS_CONFIG_KEY}`,

    network: "arbitrum-nova",

  }

);



await sequence.signIn({ idToken });



const tx = await sequence.sendERC1155({

  chainId: 137,

  token: "0x631998e91476da5b870d741192fc5cbc55f5a52e", // Skyweaver assets

  values: [

    {

      id: "66547", // Asset ID

      value: "200", // Amount for this asset

    },

    {

      id: "68572",

      value: "1000",

    },

  ],

});

Call any Contract

Use callContract to interact with any contract method, either through a function signature or ABI, supporting both named and positional arguments.

Function Signature

Providing a function signature is the easiest way to call a contract method, as it doesn’t require an ABI. The function signature can be provided with named parameters or positional parameters.

Named Arguments
const tx = await sequence.callContract({

  to: "0x503388C73Ca663eA34e103c11C9F47C9433af471", // Contract address

  abi: "mint(address to, uint256 tokenId)", // Function signature

  func: "mint", // Function name

  args: {

    to: "0xf439e432d54c2Bf5518A1901D3791070d4192986",

    tokenId: "1",

  },

  value: 0, // Value to send

});
Positional Arguments

Notice that passing a named function signature with positional arguments is allowed.

const tx = await sequence.callContract({

  to: "0x503388C73Ca663eA34e103c11C9F47C9433af471", // Contract address

  abi: "mint(address,uint256)", // Function signature

  func: "mint", // Function name

  args: ["0xf439e432d54c2Bf5518A1901D3791070d4192986", "1"],

  value: 0, // Value to send

});

ABI

Providing an ABI is more verbose, but allows for more flexibility, as a single ABI can be used to call multiple methods. ABIs support named arguments and positional arguments.

const abi = `[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}]`;



const tx = await sequence.callContract({

  to: "0x6b175474e89094c44da98b954eedeac495271d0f", // Contract address

  abi: abi, // ABI

  func: "transfer", // Function name

  args: {

    _to: "0xf439e432d54c2Bf5518A1901D3791070d4192986",

    _value: "1",

  },

  value: 0, // Value to send

});

Transaction Permissions:

  • Email Accounts: Transactions are enabled after the current session is confirmed via a link sent to the email or phone.
  • Social Login Accounts: Accounts like Google and Facebook can transact immediately after sign-in.
  • For more details, see session management validation.