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 ensures funds are always recoverable.
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 |