Sera.sol¶
Sera is the core settlement contract for deposits, matching, replay protection, and withdrawals.
Mainnet: 0xB5C50C5D5f038404F85970b7f5B7259C4AC0E198 Source: src/Sera.sol
Constants¶
string public constant NAME = "Sera";
string public constant VERSION = "1";
uint32 public constant WITHDRAW_DELAY_BLOCKS = 7200;
uint32 public constant WITHDRAW_EXPIRATION_BLOCKS = 14400;
uint256 public constant MAX_EXPIRATION = 365 days;
uint256 constant BPS_DENOMINATOR = 100_000_000_000_000;
Note that BPS_DENOMINATOR is 100_000_000_000_000, not 10_000 — the contract uses a higher-precision denominator inside uint48, so any percentage value stored against it must be scaled accordingly.
Data Structures¶
Order¶
struct Order {
address user;
uint48 expiration;
uint48 feeBps;
address recipient;
address fromToken;
address toToken;
uint256 fromAmount;
uint256 toAmount;
uint256 initialDepositAmount;
uint256 uuid;
}
recipient = address(0)means proceeds stay in the user's vault ledger.initialDepositAmountis used by routed swaps and can be zero for normal resting orders.uuidis the on-chain composite uint256, not the human-readable UUID string used by clients.
MatchData¶
struct MatchData {
Order order0; // First order
bytes signature0; // EIP-712 signature for order0
uint256 matchAmount0; // Amount of order0.fromToken to fill
Order order1; // Second order
bytes signature1; // EIP-712 signature for order1
uint256 matchAmount1; // Amount of order1.fromToken to fill
}
WithdrawIntent¶
struct WithdrawIntent {
address user; // Withdrawing user
address[] tokens; // Tokens to withdraw (1-20)
uint256[] amounts; // Amounts per token
address recipient; // Withdrawal destination
uint256 deadline; // Signature deadline
uint256 uuid; // Replay protection
}
Deposit Functions¶
depositFund¶
Deposit tokens from your wallet into the vault.
| Parameter | Type | Description |
|---|---|---|
_token | address | ERC-20 token to deposit |
_owner | address | Must be msg.sender |
_value | uint256 | Amount to deposit |
Requirements:
- Caller must be
_owner - Token must be whitelisted
- Contract must not be paused
depositFundWithPermit¶
Deposit with ERC-2612 permit (approve + deposit in one transaction).
function depositFundWithPermit(
address _token,
address _owner,
uint256 _permitAmount,
uint256 _depositAmount,
uint256 _deadline,
bytes calldata _sig
) public
| Parameter | Type | Description |
|---|---|---|
_token | address | ERC-20 token |
_owner | address | Depositor (must be msg.sender) |
_permitAmount | uint256 | Amount in the permit signature |
_depositAmount | uint256 | Actual deposit amount (must be ≤ _permitAmount) |
_deadline | uint256 | Permit deadline |
_sig | bytes | ERC-2612 permit signature (64 or 65 bytes) |
Withdrawal Functions¶
emergencyWithdraw¶
Two-step delayed withdrawal for when the API is unavailable.
Flow:
- First call — Initiates a withdrawal request, recording the block number
- Wait — At least 7,200 blocks (~24 hours) must pass
- Second call — Executes the withdrawal with the same parameters
Requirements:
- Amount must be > 0
- Execution must happen within 14,400 blocks (~48 hours) of the request
- Execution amount must match the request amount
Events:
event WithdrawRequested(address indexed user, address indexed token, uint256 amount, uint256 indexed requestBlock);
event Withdraw(address indexed token, address indexed to, uint256 amount);
Note
Frozen users can use emergency withdrawal. This keeps an on-chain withdrawal path available.
executeInstantWithdrawDualSig¶
Instant withdrawal with both user and executor signatures.
function executeInstantWithdrawDualSig(
WithdrawIntent calldata intent,
bytes calldata userSignature,
address executor,
bytes calldata executorSignature
) external
| Parameter | Type | Description |
|---|---|---|
intent | WithdrawIntent | Withdrawal parameters |
userSignature | bytes | User's EIP-712 WithdrawIntent signature |
executor | address | Co-signing executor; must hold EXECUTOR_ROLE |
executorSignature | bytes | Executor's EIP-712 WithdrawIntent signature |
Requirements:
- Deadline not expired
- UUID not previously used
- 1-20 tokens per withdrawal
executormust haveEXECUTOR_ROLE- Both signatures must be valid (EOA or ERC-1271)
Supports multi-token withdrawal in a single transaction. Replay protection is enforced per user via the signed uuid.
Events:
event InstantWithdraw(address indexed user, uint256 indexed uuid, address indexed token, uint256 amount, address recipient);
Order Matching¶
matchOrders¶
Settle a matched order pair. Called by authorized executors.
| Parameter | Type | Description |
|---|---|---|
_match | MatchData | Both orders with signatures and fill amounts |
deadline | uint256 | Unix timestamp; transaction reverts if expired |
Validation:
- Token symmetry:
order0.fromToken == order1.toTokenand vice versa - No self-matching (different order hashes)
- No same-token matching
- Both orders not expired
- Both signatures valid
- Sufficient vault balances
- Fill amounts within order limits
feeBps <= BPS_DENOMINATOR- Token must be whitelisted and meet its minimum order amount on first fill
Events:
event OrderMatched(
bytes32 indexed orderHash0, address indexed user0, address token0, uint256 amount0, uint256 protocolTake0,
bytes32 indexed orderHash1, address user1, address token1, uint256 amount1, uint256 protocolTake1
);
event OrderFullyFilled(bytes32 indexed orderHash, address indexed user);
State Variables¶
mapping(bytes32 => uint256) public filledAmount;
mapping(address => mapping(uint256 => bool)) public isUuidExecuted;
mapping(address => mapping(uint256 => bool)) public isIntentUuidUsed;
mapping(address => mapping(address => WithdrawRequest)) public withdrawRequests;
isIntentUuidUsed is the shared replay registry that SeraSOR consumes through consumeIntentUuid(...).
EIP-712 Type Hashes¶
// Order signing
bytes32 constant ORDER_TYPEHASH = keccak256(
"Order(address user,uint48 expiration,uint48 feeBps,address recipient,address fromToken,address toToken,uint256 fromAmount,uint256 toAmount,uint256 initialDepositAmount,uint256 uuid)"
);
// Withdrawal signing
bytes32 constant WITHDRAW_INTENT_TYPEHASH = keccak256(
"WithdrawIntent(address user,address[] tokens,uint256[] amounts,address recipient,uint256 deadline,uint256 uuid)"
);
SOR Digest Helper¶
Sera also exposes getIntentDigest(...), which returns the EIP-712 digest used by SeraSOR for routed swap intents.
Admin Functions¶
| Function | Role | Description |
|---|---|---|
setTreasury(address) | Admin | Set the treasury address |
setSlippageShares(...) | Admin | Configure on-chain slippage-share parameters |
batchModifyWhitelistedTokens(...) | Admin | Whitelist/unwhitelist tokens |
setTrustedRouter(address) | Admin | Set SeraSOR address |
rescueToken(address, address) | Admin | Rescue accidentally sent tokens |
pause() / unpause() | Pauser | Emergency pause/unpause |
Errors¶
| Error | Cause |
|---|---|
UserFrozen | User account is frozen |
AmountBelowMinimum | Order below token's minimum amount |
WithdrawNotReady | Withdrawal delay period not elapsed |
WithdrawExpired | Withdrawal request expired |
IntentExpired | Signature deadline passed |
UuidAlreadyUsed | UUID replay detected |
TokenMismatch | Order token pair doesn't match |
SelfMatch | Attempting to match order against itself |
OrderExpired | Order expiration timestamp passed |
OrderFilledAmountExceeded | Fill would exceed remaining order amount |
InvalidSignature | Signature verification failed |
InsufficientVaultBalance | User doesn't have enough deposited |