跳转至

Sera.sol

用于订单撮合、存款和提款的核心结算合约。

主网: 0xB5C50C5D5f038404F85970b7f5B7259C4AC0E198 Sepolia: 0x83475A1bD98a8DC2DCd507A747e4DC85da241D6e 源代码: src/Sera.sol

调用者与暂停要求

操作 调用者与条件
depositFund 发送者为 _owner;未暂停、代币在白名单中
depositFundWithPermit 允许第三方发送;未暂停、代币在白名单中;满足下述 allowance/permit 与金额检查
matchOrders 调用者持有 Sera EXECUTOR_ROLE;未暂停;用户未被加入黑名单
executeInstantWithdrawDualSig 调用者可转交两个有效签名;签名执行者持有 Sera EXECUTOR_ROLE;未暂停;代币/金额数组非空且等长、金额为正
emergencyWithdraw 交易发送者提取自身余额;该提款路径不检查暂停或黑名单;仍须满足下述请求规则

只读状态查询不授予任何上述写入权限。

常量

string public constant NAME = "Sera";
string public constant VERSION = "1";
uint32 public constant WITHDRAW_DELAY_BLOCKS = 7200;       // ~24 hours
uint32 public constant WITHDRAW_EXPIRATION_BLOCKS = 14400;  // ~48 hours
uint256 public constant MAX_EXPIRATION = 365 days;
uint256 constant BPS_DENOMINATOR = 100_000_000_000_000;

feeBps 使用 BPS_DENOMINATOR = 10^14(100%),不是通常的 10,000;按此分母缩放费用。

数据结构

Order

struct Order {
    address user;                 // Order originator
    uint48 expiration;            // Expiration timestamp
    uint48 feeBps;                // Fee in basis points
    address recipient;            // External recipient (address(0) = vault swap)
    address fromToken;            // Input token
    address toToken;              // Output token
    uint256 fromAmount;           // Input amount
    uint256 toAmount;             // Minimum output amount (price protection)
    uint256 initialDepositAmount; // Wallet funding for SOR legs
    uint256 uuid;                 // Unique identifier for replay protection
}

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
}

存款函数

depositFund

将代币从您的钱包存入 Vault。

function depositFund(address _token, address _owner, uint256 _value) public
参数 类型 说明
_token address 要存入的 ERC-20 代币
_owner address 必须为 msg.sender
_value uint256 存入金额

要求:

  • 调用者必须为 _owner
  • 代币必须在白名单中
  • 合约不能处于暂停状态

depositFundWithPermit

使用 ERC-2612 permit 存款(在一笔交易中完成授权 + 存款)。

function depositFundWithPermit(
    address _token,
    address _owner,
    uint256 _permitAmount,
    uint256 _depositAmount,
    uint256 _deadline,
    bytes calldata _sig
) public
参数 类型 说明
_token address ERC-20 代币
_owner address 代币所有者及 Vault 受益人;调用者可以是第三方
_permitAmount uint256 permit 签名中的金额
_depositAmount uint256 实际存款金额(必须 ≤ _permitAmount)
_deadline uint256 Permit 截止时间
_sig bytes ERC-2612 permit 签名(64 或 65 字节)

如果对 Vault 的 allowance 已覆盖 _depositAmount,合约会跳过 permit 校验;否则先尝试 permit,再由 Vault 从 _owner 拉取代币。公共 API 辅助端点另有 owner/签名检查,不能据此放宽。

提款函数

emergencyWithdraw

当 API 不可用时的两步延迟提款。

function emergencyWithdraw(address token, uint256 amount) public

流程与要求:

  1. 使用非零代币地址和正数金额申请,申请时 Vault 余额必须足够。
  2. 等待至少 7,200 个区块(按每区块 12 秒估算约 24 小时)。
  3. 在申请区块 + 7,200 至 + 14,400(含边界)期间,对同一代币调用,金额满足 0 < amount <= 申请金额,执行时余额也必须足够。
  4. 成功后整个申请被删除,即使少提也一样;剩余金额需要重新申请。
  5. 超过申请区块 + 14,400 后,再次调用会创建新申请并重新等待,不会执行过期申请,也不会抛 WithdrawExpired。

区块数对应的时间仅为估算。此函数通过交易发送者识别提款账户,不要求用户 EIP-712 消息签名。

事件:

event WithdrawRequested(address indexed user, address indexed token, uint256 amount, uint256 indexed requestBlock);
event Withdraw(address indexed token, address indexed to, uint256 amount);

Note

黑名单用户可以使用紧急提款,但仍需满足余额、等待期及代币转账条件。

executeInstantWithdrawDualSig

使用用户和执行者双重签名的即时提款。

function executeInstantWithdrawDualSig(
    WithdrawIntent calldata intent,
    bytes calldata userSignature,
    address executor,
    bytes calldata executorSignature
) external
参数 类型 说明
intent WithdrawIntent 提款参数
userSignature bytes 用户的 EIP-712 WithdrawIntent 签名
executor address 协同签名的执行者地址;必须持有 EXECUTOR_ROLE
executorSignature bytes 执行者的 EIP-712 WithdrawIntent 签名

要求:

  • 截止时间未过期
  • UUID 未被使用过
  • 每次提款 1-20 种代币
  • executor 必须持有 EXECUTOR_ROLE
  • 两个签名都必须有效(EOA 或 ERC-1271)

支持在单笔交易中进行多代币提款。

事件:

event InstantWithdraw(address indexed user, uint256 indexed uuid, address indexed token, uint256 amount, address recipient);

订单撮合

matchOrders

结算已匹配的订单对。由授权的执行者调用。

function matchOrders(MatchData calldata _match, uint256 deadline) external
参数 类型 说明
_match MatchData 两个订单及其签名和成交金额
deadline uint256 Unix 时间戳;过期则交易回退

验证:

  • 代币对称性:order0.fromToken == order1.toToken,反之亦然
  • 不能自我撮合(不同的订单哈希)
  • 不能同币种撮合
  • 两个订单都未过期
  • 两个签名都有效
  • Vault 余额充足
  • 成交金额在订单限额范围内

事件:

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);

状态变量

mapping(bytes32 => uint256) public filledAmount;  // Partial fill tracking per order hash
mapping(address => mapping(uint256 => bool)) public isIntentUuidUsed; // Shared SOR replay protection
mapping(address => mapping(uint256 => bool)) public isUuidExecuted;  // Replay protection
mapping(address => mapping(address => WithdrawRequest)) public withdrawRequests;  // Pending withdrawals

EIP-712 类型哈希

// 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 辅助函数

以下公开只读函数无需执行者角色,也不会提交交易:

function getIntentDigest(
    address taker,
    address inputToken, address outputToken,
    uint256 maxInputAmount, uint256 minOutputAmount,
    address recipient, uint256 initialDepositAmount,
    uint256 uuid, uint48 deadline
) external view returns (bytes32);

function DOMAIN_SEPARATOR() public view returns (bytes32);

getIntentDigest 返回完整 EIP-712 Intent 摘要,包括 taker 和 Sera 域;它不验证签名,也不消费 UUID。DOMAIN_SEPARATOR() 返回当前部署的域分隔符。构造签名时使用目标链及 GET /config 返回的 Sera 地址。

管理员函数

函数 角色 说明
setTreasury(address) Admin 设置金库地址
setSlippageShares(...) Admin 配置链上滑点分配参数
batchModifyWhitelistedTokens(...) Admin 白名单/取消白名单代币
setTrustedRouter(address) Admin 设置 SeraSOR 地址
rescueToken(address, address) Admin 救回误发的代币
pause() / unpause() Pauser 紧急暂停/恢复

错误

错误 原因
BlacklistedUser Vault 黑名单阻止该交易操作
AmountBelowMinimum 订单低于代币最低金额
WithdrawNotReady 提款延迟期未满
IntentExpired 签名截止时间已过
UuidAlreadyUsed 检测到 UUID 重放
TokenMismatch 订单代币对不匹配
SelfMatch 尝试将订单与自身撮合
OrderExpired 订单过期时间戳已过
OrderFilledAmountExceeded 成交将超过订单剩余金额
InvalidSignature 签名验证失败
InsufficientVaultBalance 用户存款不足