Skip to main content

pay Function

Function Type: external
Function Signature: pay(address,address,string,address,uint256,uint256,address,address,uint256,bool,bytes)

The pay function executes a payment from one address to a single recipient address or identity. This is EVVM Core's primary single payment function with intelligent staker detection and centralized signature verification.

Key features:

  • Single Payment: Transfers tokens from one sender to one recipient (address or username)
  • Staker Detection: Automatically detects if the executor is a staker and distributes rewards accordingly
  • Centralized Nonce Management: Uses Core.sol's unified nonce system for enhanced security
  • Identity Resolution: Can send payments to usernames which are resolved to addresses via NameService
  • Signature Verification: Validated through Core.sol's centralized signature system

The function supports both synchronous and asynchronous nonce management through the isAsyncExec parameter, making it flexible for various execution patterns and use cases. For details on nonce types, see Nonce Types in EVVM. For signature details, see Payment Signature Structure.

Parameters

FieldTypeDescription
fromaddressThe address of the payment sender whose funds are being transferred and whose signature/nonce are validated.
to_addressaddressDirect recipient address. Used when to_identity is empty.
to_identitystringUsername/identity of the recipient. If provided, the contract resolves it to an address via the NameService.
tokenaddressThe token contract address for the transfer.
amountuint256The quantity of tokens to transfer from from to the recipient.
priorityFeeuint256Additional fee for transaction priority. If the executor is a staker, they receive this fee as a reward.
senderExecutoraddressAddress authorized to execute this transaction (msg.sender). Use address(0) to allow any service with the correct signature to execute. When set to a specific address, only that executor can consume the nonce.
originExecutoraddressAddress restriction for transaction origin (tx.origin). Use address(0) to allow any origin. Provides additional security layer for multi-service transaction flows.
nonceuint256Transaction nonce value managed by Core.sol. Usage depends on isAsyncExec: if false (sync), must equal the expected synchronous nonce; if true (async), can be any unused nonce.
isAsyncExecboolExecution type flag: false = synchronous nonce (sequential), true = asynchronous nonce (parallel).
signaturebytesCryptographic signature (EIP-191) from the from address authorizing this payment. Validated by Core.sol's centralized signature system.
Centralized Nonce Management

The nonce parameter is managed centrally by Core.sol. When isAsyncExec is false (synchronous), the provided nonce must equal Core.getNextCurrentSyncNonce(from). When true (asynchronous), the nonce can be any value not yet used, checked via Core.getIfUsedAsyncNonce(from, nonce).

Execution Methods

The function can be executed in multiple ways:

Fisher Execution

  1. A user signs the payment details and sends the request (parameters + signature) to a fishing spot.
  2. A fisher (preferably a staker for rewards) captures the transaction and validates the request.
  3. The fisher submits the transaction to the function for processing and receives rewards if they are a staker.

Direct Execution

  1. The user or any authorized service directly calls the pay function.
  2. If a senderExecutor address is specified, only that address can submit the transaction.
  3. If senderExecutor is set to address(0), any service can execute the transaction with a valid signature - enabling flexible multi-service transaction flows.
Executor Security Model

The dual-executor model provides flexible security:

  • senderExecutor = address(0): Any service with the correct signature can execute and consume the nonce - useful for competitive fisher markets
  • senderExecutor = specific address: Only that specific service can execute - provides deterministic execution guarantees
  • originExecutor: Additionally restricts tx.origin for extra security in multi-service flows

When building services that dispatch payments on behalf of users, both senderExecutor and originExecutor should be set to the service address for clear accountability.

Workflow

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

    • Constructs signature payload: buildSignaturePayload(evvmId, senderExecutor, hashPayload, originExecutor, nonce, isAsyncExec)
    • hashPayload is generated via CoreHashUtils.hashDataForPay(to_address, to_identity, token, amount, priorityFee)
    • Recovers signer and compares with from address. Reverts with InvalidSignature on failure.
  2. Sender Executor Validation: If senderExecutor is not address(0), validates that msg.sender matches senderExecutor. Reverts with SenderMismatch if they don't match. When address(0), any service can execute.

  3. Origin Executor Validation: If originExecutor is not address(0), validates that tx.origin matches originExecutor. Reverts with OriginMismatch if they don't match. Provides additional security for multi-service transactions.

  4. User Validation: Checks if the user is allowed to execute transactions using canExecuteUserTransaction(from). Reverts with UserCannotExecuteTransaction if not allowed.

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

    • Async (isAsyncExec = true): Checks nonce status via asyncNonceStatus(from, nonce). If senderExecutor was address(0) in signature, any service can consume it; otherwise only the specified service. 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.
  6. Resolve Recipient Address: Determines the final recipient address:

    • If to_identity is provided (not empty), resolves the identity to an owner address using verifyStrictAndGetOwnerOfIdentity from the NameService contract.
    • If to_identity is empty, uses the provided to_address.
  7. Balance Update: Executes the payment transfer using the _updateBalance function, sending amount of token from the from address to the resolved recipient address.

  8. Staker Benefits Distribution: If the executor (msg.sender) is a registered staker:

    • Priority Fee Transfer: If priorityFee > 0, transfers the priorityFee amount of token from the from address to the msg.sender (executor) as a staker reward.
    • Principal Token Reward: Grants 1x reward amount in principal tokens to the msg.sender (executor) using the _giveReward function.
info

For more information about the signature structure, refer to the Payment Signature Structure section.

tip

Need to send from one user to multiple recipients?
Use dispersePay to send tokens from a single sender to multiple different addresses or identities in one transaction.

Need to execute multiple separate payments?
Use batchPay to process several individual pay operations within a single transaction, each with their own sender, recipient, and parameters.