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
| Field | Type | Description |
|---|---|---|
from | address | The address of the payment sender whose funds are being transferred and whose signature/nonce are validated. |
to_address | address | Direct recipient address. Used when to_identity is empty. |
to_identity | string | Username/identity of the recipient. If provided, the contract resolves it to an address via the NameService. |
token | address | The token contract address for the transfer. |
amount | uint256 | The quantity of tokens to transfer from from to the recipient. |
priorityFee | uint256 | Additional fee for transaction priority. If the executor is a staker, they receive this fee as a reward. |
senderExecutor | address | Address 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. |
originExecutor | address | Address restriction for transaction origin (tx.origin). Use address(0) to allow any origin. Provides additional security layer for multi-service transaction flows. |
nonce | uint256 | Transaction 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. |
isAsyncExec | bool | Execution type flag: false = synchronous nonce (sequential), true = asynchronous nonce (parallel). |
signature | bytes | Cryptographic signature (EIP-191) from the from address authorizing this payment. Validated by Core.sol's centralized signature system. |
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
- A user signs the payment details and sends the request (parameters + signature) to a fishing spot.
- A fisher (preferably a staker for rewards) captures the transaction and validates the request.
- The fisher submits the transaction to the function for processing and receives rewards if they are a staker.
Direct Execution
- The user or any authorized service directly calls the
payfunction. - If a
senderExecutoraddress is specified, only that address can submit the transaction. - If
senderExecutoris set toaddress(0), any service can execute the transaction with a valid signature - enabling flexible multi-service transaction flows.
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 marketssenderExecutor = specific address: Only that specific service can execute - provides deterministic execution guaranteesoriginExecutor: Additionally restrictstx.originfor 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
-
Signature Verification: Validates the
signatureusing Core.sol's centralized signature verification system:- Constructs signature payload:
buildSignaturePayload(evvmId, senderExecutor, hashPayload, originExecutor, nonce, isAsyncExec) hashPayloadis generated viaCoreHashUtils.hashDataForPay(to_address, to_identity, token, amount, priorityFee)- Recovers signer and compares with
fromaddress. Reverts withInvalidSignatureon failure.
- Constructs signature payload:
-
Sender Executor Validation: If
senderExecutoris notaddress(0), validates thatmsg.sendermatchessenderExecutor. Reverts withSenderMismatchif they don't match. Whenaddress(0), any service can execute. -
Origin Executor Validation: If
originExecutoris notaddress(0), validates thattx.originmatchesoriginExecutor. Reverts withOriginMismatchif they don't match. Provides additional security for multi-service transactions. -
User Validation: Checks if the user is allowed to execute transactions using
canExecuteUserTransaction(from). Reverts withUserCannotExecuteTransactionif not allowed. -
Nonce Management: Core.sol handles nonce verification and updates based on
isAsyncExec:- Async (isAsyncExec = true): Checks nonce status via
asyncNonceStatus(from, nonce). IfsenderExecutorwasaddress(0)in signature, any service can consume it; otherwise only the specified service. Reverts withAsyncNonceAlreadyUsedif already used, orAsyncNonceIsReservedByAnotherServiceif reserved by another service. - Sync (isAsyncExec = false): Verifies the nonce matches
nextSyncNonce[from], then increments it. Reverts withSyncNonceMismatchon mismatch.
- Async (isAsyncExec = true): Checks nonce status via
-
Resolve Recipient Address: Determines the final recipient address:
- If
to_identityis provided (not empty), resolves the identity to an owner address usingverifyStrictAndGetOwnerOfIdentityfrom the NameService contract. - If
to_identityis empty, uses the providedto_address.
- If
-
Balance Update: Executes the payment transfer using the
_updateBalancefunction, sendingamountoftokenfrom thefromaddress to the resolved recipient address. -
Staker Benefits Distribution: If the executor (
msg.sender) is a registered staker:- Priority Fee Transfer: If
priorityFee > 0, transfers thepriorityFeeamount oftokenfrom thefromaddress to themsg.sender(executor) as a staker reward. - Principal Token Reward: Grants 1x reward amount in principal tokens to the
msg.sender(executor) using the_giveRewardfunction.
- Priority Fee Transfer: If
For more information about the signature structure, refer to the Payment Signature Structure section.
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.