Cancel Order Signature Structure
P2PSwap signatures are verified by Core.sol using validateAndConsumeNonce(). Uses P2PSwapHashUtils.hashDataForCancelOrder() for hash generation.
To authorize order cancellation operations, users must generate a cryptographic signature compliant with the EIP-191 standard using the Ethereum Signed Message format.
Signature Format
{evvmId},{senderExecutor},{hashPayload},{originExecutor},{nonce},{isAsyncExec}
Components:
- evvmId: Network identifier (uint256, typically
1) - senderExecutor: Address that can call the function via msg.sender (
0x0...0for anyone) - hashPayload: Hash of cancel order parameters (bytes32, from P2PSwapHashUtils)
- originExecutor: EOA that can initiate the transaction via tx.origin (
0x0...0for anyone) - nonce: User's centralized nonce from Core.sol (uint256)
- isAsyncExec: Always
truefor P2PSwap (async execution)
Hash Payload Generation
The hashPayload is generated using P2PSwapHashUtils.hashDataForCancelOrder():
import {P2PSwapHashUtils} from "@evvm/testnet-contracts/library/signature/P2PSwapHashUtils.sol";
bytes32 hashPayload = P2PSwapHashUtils.hashDataForCancelOrder(
tokenA, // Token A in market pair (from original order)
tokenB, // Token B in market pair (from original order)
orderId // ID of order to cancel
);
// Internal implementation
// keccak256(abi.encode("cancelOrder", tokenA, tokenB, orderId))
Centralized Verification
Core.sol verifies the signature using validateAndConsumeNonce():
// Called internally by P2PSwap.sol.cancelOrder()
Core(coreAddress).validateAndConsumeNonce(
user, // Signer's address (order owner)
senderExecutor, // Who can call via msg.sender
hashPayload, // From P2PSwapHashUtils
originExecutor, // Who can initiate via tx.origin
nonce, // User's nonce
true, // Always async for P2PSwap
signature // EIP-191 signature
);
Message Construction
The signature message is constructed using AdvancedStrings.buildSignaturePayload():
import {AdvancedStrings} from "@evvm/testnet-contracts/library/utils/AdvancedStrings.sol";
import {P2PSwapHashUtils} from "@evvm/testnet-contracts/library/signature/P2PSwapHashUtils.sol";
// Step 1: Generate hash payload
bytes32 hashPayload = P2PSwapHashUtils.hashDataForCancelOrder(
tokenA,
tokenB,
orderId
);
// Step 2: Build signature message
string memory message = AdvancedStrings.buildSignaturePayload(
1, // evvmId
address(0), // senderExecutor (anyone can call)
hashPayload, // Hash from step 1
address(0), // originExecutor (anyone can initiate)
nonce, // User's current nonce
true // isAsyncExec (always true for P2PSwap)
);
// Result: "1,0x0000000000000000000000000000000000000000,0x[hashPayload],0x0000000000000000000000000000000000000000,[nonce],true"
Example
Scenario: User wants to cancel their order #3 in the USDC/ETH market
Step 1: Generate Hash Payload
import {P2PSwapHashUtils} from "@evvm/testnet-contracts/library/signature/P2PSwapHashUtils.sol";
address tokenA = 0xA0b86a33E6441e6e80D0c4C6C7527d72E1d00000; // USDC
address tokenB = address(0); // ETH
uint256 orderId = 3;
bytes32 hashPayload = P2PSwapHashUtils.hashDataForCancelOrder(
tokenA,
tokenB,
orderId
);
// Result: 0x7f8e9d0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8
Step 2: Construct Signature Message
1,0x0000000000000000000000000000000000000000,0x7f8e9d0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8,0x0000000000000000000000000000000000000000,25,true
Components:
evvmId:1senderExecutor:0x0000...(anyone can call)hashPayload:0x7f8e...(from Step 1)originExecutor:0x0000...(anyone can initiate)nonce:25isAsyncExec:true
Step 3: Sign with Wallet
const message = "1,0x0000000000000000000000000000000000000000,0x7f8e...7d8,0x0000000000000000000000000000000000000000,25,true";
const signature = await signer.signMessage(message);
Function Call
The complete cancelOrder() function call:
P2PSwap(p2pSwapAddress).cancelOrder(
user, // Order owner's address
tokenA, // Token A in market pair
tokenB, // Token B in market pair
orderId, // Order ID to cancel
senderExecutor, // Address(0) for anyone
originExecutor, // Address(0) for anyone
nonce, // User's nonce from Core.sol
signature, // EIP-191 signature of the message
priorityFeePay, // Optional priority fee
noncePay, // Nonce for pay operation (if needed)
signaturePay // Signature for pay operation (if needed)
);
Security Considerations
The signature alone does not prove order ownership. The contract performs additional validation:
- Signature Verification: Confirms the user signed the cancellation request
- Order Existence: Verifies the order exists in the specified market
- Ownership Check: Confirms the signer is the original order creator
- Nonce Validation: Ensures the nonce hasn't been used before
- Hash Independence: The hash payload does NOT include executors (only tokenA, tokenB, orderId)
- Operation Name: "cancelOrder" is included in hash via P2PSwapHashUtils
- Async Execution: Always uses async nonces (
isAsyncExec: true) - Order Ownership: Only the original order creator can cancel their order
- Market Identification: Token pair (tokenA, tokenB) identifies the correct market
- Refund: Cancelled orders release locked tokens back to the order creator
AdvancedStrings.addressToStringconverts addresses to lowercase hex with "0x" prefixStrings.toStringconverts numbers to decimal strings- Order Identification: Requires both token pair and order ID to uniquely identify the order
- Nonce Independence: P2P Swap nonces are separate from EVVM payment nonces
:::