Skip to main content

dispersePay Function

Function Type: external
Function Signature: dispersePay(address,(uint256,address,string)[],address,uint256,uint256,address,uint256,bool,bytes)

Distributes tokens from a single sender to multiple recipients with efficient single-source multi-recipient payment distribution. This function uses a single signature to authorize distribution to multiple recipients, supports both direct addresses and identity-based recipients, and includes integrated priority fee and staker reward systems.

The signature structure for these payments is detailed in the Disperse Payment Signature Structure section.

Parameters

ParameterTypeDescription
fromaddressThe address of the payment sender whose funds will be distributed.
toDataDispersePayMetadata[]An array detailing each recipient's address/identity and the amount they should receive. See struct below.
tokenaddressThe token address to be distributed.
amountuint256The total amount of tokens to distribute across all recipients. Must equal the sum of individual amounts in toData.
priorityFeeuint256Fee amount for the transaction executor (distributed to stakers as reward).
senderExecutoraddressAddress authorized to execute this transaction. Use address(0) to allow any address to execute.
nonceuint256Transaction nonce for replay protection managed by Core.sol. Usage depends on isAsyncExec.
isAsyncExecboolDetermines nonce type: true for asynchronous (parallel), false for synchronous (sequential).
signaturebytesCryptographic signature (EIP-191) from the from address authorizing the distribution.
info

If you want to know more about the signature structure, refer to the Disperse Payment Signature Structure section.

DispersePayMetadata Struct

Defines the payment details for a single recipient within the toData array.

struct DispersePayMetadata {
uint256 amount;
address to_address;
string to_identity;
}
FieldTypeDescription
amountuint256The amount of tokens to be sent to this recipient.
to_addressaddressDirect recipient address. Used when to_identity is an empty string ("").
to_identitystringUsername/identity of the recipient. If provided, the contract resolves it to an address via the NameService.
note

If to_identity is an empty string (""), the to_address field will be used as the recipient's destination address. Otherwise, the contract attempts to resolve the to_identity to its owner address using the NameService.

Execution Methods

This function can be executed by any address, with different behavior depending on whether the executor is a staker:

Fisher Execution

  • A fisher collects multiple disperse payment requests with valid signatures from users through fishing spots.
  • The fisher submits the transaction and receives priority fees and principal token rewards if they are a staker.

Direct Execution

  • A user or service directly calls dispersePay with appropriate authorization.
  • Staker executors receive priority fees and principal token rewards for processing.
tip

When using a service as the executor, we recommend specifying the service's address in the senderExecutor parameter for additional security.

Workflow

  1. Signature Verification: Validates the signature using Core.sol's centralized signature verification:

    • Constructs signature payload: buildSignaturePayload(evvmId, address(this), hashPayload, senderExecutor, nonce, isAsyncExec)
    • hashPayload is generated via CoreHashUtils.hashDataForDispersePay(toData, token, amount, priorityFee)
    • Recovers signer and compares with from address. Reverts with InvalidSignature on failure.
  2. User Validation: Checks if the user is allowed to execute transactions using canExecuteUserTransaction(from). Reverts with UserCannotExecuteTransaction if not allowed.

  3. Nonce Management: Core.sol handles nonce verification and updates based on isAsyncExec:

    • Async (isAsyncExec = true): Checks if the nonce hasn't been used via asyncNonceStatus(from, nonce), then marks it as used. Reverts with AsyncNonceAlreadyUsed if already used, or AsyncNonceIsReservedByAnotherService if reserved by another service.
    • Sync (isAsyncExec = false): Verifies the nonce matches nextSyncNonce[from], then increments it. Reverts with SyncNonceMismatch on mismatch.
  4. Executor Validation: If senderExecutor is not address(0), checks that msg.sender matches the senderExecutor address. Reverts with SenderIsNotTheSenderExecutor if they don't match.

  5. Staker Check: Determines if the executor (msg.sender) is a registered staker using isAddressStaker.

  6. Balance Verification: Checks that the from address has sufficient balance. The required balance depends on staker status:

    • If executor is a staker: amount + priorityFee
    • If executor is not a staker: amount only (priorityFee is not collected)

    Reverts with InsufficientBalance if insufficient.

  7. Balance Deduction: Subtracts the required amount from the sender's balance upfront:

    • If executor is a staker: deducts amount + priorityFee
    • If executor is not a staker: deducts amount only
  8. Distribution Loop: Iterates through each recipient in the toData array:

    • Amount Tracking: Maintains a running total (accumulatedAmount) of distributed amounts
    • Recipient Resolution:
      • If to_identity is provided, verifies the identity exists using strictVerifyIfIdentityExist and resolves it to an owner address using getOwnerOfIdentity
      • If to_identity is empty, uses to_address
    • Token Distribution: Adds the specified amount to the recipient's balance
  9. Amount Validation: Verifies that the total distributed amount (accumulatedAmount) exactly matches the specified amount parameter. Reverts with InvalidAmount if mismatch.

  10. Staker Benefits: If the executor is a staker (isAddressStaker(msg.sender)):

    • Grants 1 principal token reward using _giveReward
    • Transfers the priorityFee to the executor's balance
  11. Nonce Update: Marks the nonce as used to prevent replay attacks:

    • Async (priorityFlag = true): Marks the custom nonce as used in asyncUsedNonce
    • Sync (priorityFlag = false): Increments the sequential nonce in nextSyncUsedNonce