Transfer (disperse) native or ERC-20 tokens to multiple addresses in a single RPC call.

The workflow of the omnia_disperse method is based on a two-stage process in which:

  1. The first call is used to create and return an unsinged disperse transaction. This transaction will contain the addresses of the recipients, the amounts (values) to be sent to each recipient and the address of the token contract (ERC-20), in case of dispersing ERC-20 tokens, not native ones (Ether, Avax etc.);

  2. The signing and starting the disperse. The caller should sign the unsigned disperse transaction returned by call in stage 1 and then make another start disperse call to launch the disperse aciton on-chain.

In case of dispersing ERC-20 tokens: before sending the signed disperse transaction, make sure to give the approve allowance from the spending wallet to the ERC-20 token contract for the total amount (sum) spent. This is a requirement of the ERC-20 standard. Follow step-by-step explanation here.

When dispersing the same amount (value) to all recipients, there's no need to send an array of values with the length of the recipients array. It is enough to send only one amount in the values array.


  1. DATA, ["native" || "efficient" || "tracking"] - disperseType. The type of disperse operation wanted. REQUIRED

    • native - used for dispersing native tokens of the specific network. (Ether for Ethereum/Arbitrum/Optimism, BNB for Binance Smart Chain, Avax for Avalanche C-Chain etc.);

    • efficient - used when dispersing ERC-20 tokens in a more gas-efficient manner. This will generate less gas cost, but will be harder to track/audit for many recipients from the Disperse smart contract;

    • tracking - this allows tracking every transfer directly from the Disperse smart contract but increases the gas consumed.

  2. DATA, ["createUnsignedDisperse" || "startDisperse"] - disperseStage. As the disperse workflow uses two stages, the createUnsignedDisperse stage is used for creating the initial unsigned disperse transaction, while startDisperse stage is used for sending the signed disperse transaction. REQUIRED

  3. DATA, 20 Bytes - spendingAddress. Wallet addres from which the funds will be dispersed to recipients. REQUIRED

  4. DATA, Bytes - gasLimit. Hex value used overwrite the default gas limit when creating the unsigned disperse transaction. Default value: 0xC350 (50,000).

  5. Array of DATA, 20 Bytes - recipients. The addresses of the wallets receiving the dispersed funds. REQUIRED

  6. Array of DATA, Bytes - values. The amounts (values) of dispersed funds received by each recipient. SHOULD be provided in Hex format. In case of same value for multiple recipients, see the note above. REQUIRED

  7. DATA, Bytes - signedTransaction. The signed disperse transaction RLP-encoded with 0x prefix. Required only in the startDisperse stage. REQUIRED


  1. Unsigned disperse transaction serialised into JSON and ready to be signed.

  2. Hash of disperse transaction.


Driver code (Node.js)

const ethers = require('ethers');
const axios = require('axios');

async function main() {
  const omniaRpcUrl = '<INSERT YOUR OMNIA RPC ENDPOINT>';

  // #region Disperse createUnsignedDisperse
  const generateDisperseReq = {
    id: 1,
    jsonrpc: 2.0,
    method: "omnia_disperse",
    params: [{
        recipients: ["0xb3Be198329E658547381A2a823732122522D3566"],
        values: ["0x16345785d8a0000"],
        tokenAddress: "0x21F8246ECdE66A4F193f4a288371718484256d0a",
        spendingAddress: "0x6D8A32Fb9210dC8B903347204E105f4De08f0c34",
        disperseStage: "createUnsignedDisperse",
        disperseType: "tracking",
        gasLimit: "0x186A0"
  const disperseRes = await, generateDisperseReq);
  const unsignedTransaction = JSON.parse(;
  // #endregion Disperse createUnsignedDisperse

  // #region Disperse signDisperse
  const privateKey = '<INSERT YOUR WALLET PRIVATE KEY>';
  const rpcProvider = new ethers.providers.JsonRpcProvider(omniaRpcUrl);
  const wallet = new ethers.Wallet(privateKey, rpcProvider);
  const signedTransaction = await wallet.signTransaction(unsignedTransaction);
  console.log('signedTransaction: ', signedTransaction);

  const sendSignedDisperseReq = {
    id: 1,
    jsonrpc: 2.0,
    method: "omnia_disperse",
    params: [{
        disperseStage: "startDisperse",

  const sendSignedTxRes = await, sendSignedDisperseReq, { headers })
  // #endregion Disperse signDisperse

main().catch((error) => {
  process.exitCode = 1;


  id: 1,
  jsonrpc: "2.0",
  result: '0xf335db1f9534a753180a53f1f82710c2703abfde4a97cc1208cb2b85340dc12b'

Last updated