Robinhood Chain股票代币化实践:代币合约与协议层代码解析

Robinhood Chain主网上线,基于Arbitrum专有链构建,为股票、ETF等RWA资产提供机构级基础设施。目前已代币化96种股票和ETF,实现路径分为合约层和协议层。

  • 代币合约层:采用可升级ERC-20模板(Stock.sol),集成权限控制、预言机暂停、UI数量缩放(ERC-8056)等模块。缩放机制可处理股票拆分和分红调整,确保链上链下数据一致。权限角色细分13类,但高度依赖多签治理安全。

  • 协议层:利用ArbOS 61升级引入排序器级合规筛查,通过ArbFilteredTransactions预编译合约(0x74地址)在交易入块前过滤受限地址,实现链上反洗钱和受制裁实体拦截。

Robinhood Chain为传统金融资产迁移至区块链提供了标准化合约与合规框架的实践参考。

总结

7月1日,Robinhood Chain主网正式上线。作为券商巨头Robinhood基于Arbitrum Dedicated Blockchains构建的EVM区块链,主要为股票、ETF、私募资产等RWA、链上交易及借贷等金融活动提供机构级基础设施支持。目前,Robinhood Chain已将96种股票和ETF代币化并支持链上交易,本文将从代币合约层与协议层分析其实现路径。

代币合约层分析

Robinhood Chain称其代币化股票为Stock Tokens,具体命名规则非常直接,以特斯拉为例,代币名为“Tesla • Robinhood Token”(TSLA)。

TSLA代币合约地址为0x322F0929c4625eD5bAd873c95208D54E1c003b2d,通过它的合约代码,我们可以看到Robinhood Chain上Stock Tokens的通用合约代码Stock.sol

link: https://robinhoodchain.blockscout.com/token/0x322F0929c4625eD5bAd873c95208D54E1c003b2d?tab=contract

这是一个可升级、带权限控制、可暂停、带 UI 数量缩放机制的 ERC-20 代币合约。它继承了四个模块:

- IStock:定义股票代币接口,包含初始化、铸造、销毁、元数据、暂停状态等外部可见能力。- AccessControlled:项目自定义权限层。构造函数接收 registry 地址- OraclePausable:用于在价格源异常、合规限制等情况下阻止关键操作。- ERC20ScaledUIUpgradeable:通过 multiplier 或 scaling 机制转换代币内部余额和用户界面显示余额的数量关系。对于 tokenized stock,这类设计用于处理拆股、反向拆股、单位换算或显示精度调整。

在 StockStorage 里能看到两个字段:- uid:股票资产的唯一标识- paused:单个股票代币自身的暂停状态。注意它与 OraclePausable 是两层暂停:一个是单个合约的暂停,另一个是预言机的暂停。

数量缩放(ERC-8056)

这一设计主要是用于处理RWA资产因现实世界ETF分红导致的净值侵蚀和股票拆分导致的链上链下数据不一致的问题,作为ERC-20代币的拓展功能,需实现以下接口:

......contract Stock is IStock, AccessControlled, OraclePausable, ERC20ScaledUIUpgradeable {    event MetaDataUpdated(string name, string symbol);    error IsPaused();    /// @custom:oz-upgrades-unsafe-allow constructor    constructor(address registry) AccessControlled(registry) {        _disableInitializers();    }    /// @custom:storage-location erc7201:robinhood.storage.Stock    struct StockStorage {        bytes32 uid;        bool paused;    }    bytes32 private constant StockStorageLocation = 0x8d25ea8ee309999a79f0af498fbab0e424669497170669bd9e93b81a62babc00;    modifier onlyNotPaused() {        if (paused()) {            revert IsPaused();        }        _;    }    function initialize(bytes32 uid_, string calldata name_, string calldata symbol_) external override initializer {        ......    }    function _getStockStorage() private pure returns (StockStorage storage $) {        assembly {            $.slot := StockStorageLocation        }    }    /// @dev Storage from the base ERC20 contract    function _getERC20StorageFromBase() internal pure returns (ERC20Storage storage $) {        assembly {            // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC20")) - 1)) & ~bytes32(uint256(0xff))            $.slot := 0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00        }    }    function transfer(address to, uint256 value){......}    function transferFrom(address from, address to, uint256 value){......}    function approve(address spender, uint256 value){......}    function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s){......}    function mint(address to, uint256 amount){......}    function burn(address from, uint256 amount){......}    function adminBurn(address from, uint256 amount) public override onlyRole(ADMIN_BURNER_ROLE){......}    function uid() public view returns (bytes32){......}    ......    function paused() public view returns (bool) {        StockStorage storage $ = _getStockStorage();        return $.paused || IAccessControlsRegistry(ACCESS_CONTROLLED_REGISTRY).paused();    }    function tokenPaused() public view returns (bool) {        StockStorage storage $ = _getStockStorage();        return $.paused;    }    function pause() public onlyRole(TOKEN_PAUSER_ROLE) {        StockStorage storage $ = _getStockStorage();        $.paused = true;        emit Paused();    }    function unpause() public onlyRole(TOKEN_PAUSER_ROLE) {        StockStorage storage $ = _getStockStorage();        $.paused = false;        emit Unpaused();    }    function updateMultiplier(uint256 newMultiplier) public onlyNotPaused onlyRole(MULTIPLIER_UPDATER_ROLE) {        _updateUIMultiplier(newMultiplier);    }    function updateMultiplier(uint256 newMultiplier, uint256 effectiveAt_)        public        onlyNotPaused        onlyRole(MULTIPLIER_UPDATER_ROLE)    {        _updateUIMultiplier(newMultiplier, effectiveAt_);    }    function pauseOracle() external onlyRole(ORACLE_PAUSER_ROLE) {        _pauseOracle();    }    function unpauseOracle() external onlyRole(ORACLE_PAUSER_ROLE) {        _unpauseOracle();    }    function setMetadata(string calldata _name, string calldata _symbol) public onlyRole(METADATA_UPDATER_ROLE) {        _getERC20StorageFromBase()._name = _name;        _getERC20StorageFromBase()._symbol = _symbol;        emit MetaDataUpdated(_name, _symbol);    }    ......}

interface IScaledUIAmountinterface IScaledUIAmountBalancesinterface IScaledUIAmountNewUIMultiplier

此前,Solana网络的Token-2022 标准原生提供了缩放 UI 金额扩展。发行方只需更新一个乘数,该乘数即可调整用户界面显示的余额,而无需改变原始代币数量。随后Ethereum网络推出了ERC-8056作为对应的标准,其核心思想是:合约内部维护一个基础余额,但前端、钱包或查询接口展示给用户的余额,会乘以或除以某个UI乘数。

权限控制

Stock Tokens采用角色化权限控制,合约的角色分为13类,实现精细化控制,但这也表明合约安全性高度依赖各角色私钥/多签的治理安全:

从合约层看,Robinhood Chain构建了用于股票/ETF 类代币的标准化合约,把 ERC-20、权限控制、暂停机制、可升级存储和股票数量缩放这几个模块组合成一套可落地的资产发行模板。

协议层分析

Robinhood Chain利用ArbOS 61升级提供的合规筛查功能对交易进行排序器级别的筛查。合规引擎运行于排序器内部,并在每笔交易被排序之前对其进行模拟。如果交易违反任何合规性规则,则会在排序器这一级别被拒绝。

在评估交易时,合规引擎会对所有相关地址进行哈希处理,并将其与配置的受限列表进行比对,受限地址的哈希列表应以 JSON 文件形式提供,格式如下

{  salt: string, # UUID  hashes: [string], # list of 32-byte hashes as hex encoded strings  issued_at: string, # timestamp  hashing_scheme: string # sha256-stringinput or sha256-rawbytesinput.}

Robinhood Chain的ArbFilteredTransactionsManage合约地址为0x0000000000000000000000000000000000000074,负责合规筛查的地址为0xebDc18A1F5C42fC25552eA233fAcf4054DF224b7。

0x74地址并未进行验证,但我们可以通过Arbitrum Nitro公开的预编译合约获得一些关键信息:

pragma solidity >=0.4.21 <0.9.0;

/** * @title Enables ability to filter transactions by authorized callers. * @notice Authorized callers are added/removed through ArbOwner precompile. *         Available in ArbOS version 60 and above */interface ArbFilteredTransactionsManager {    /// @notice Emitted when a transaction hash is added to the filtered transactions list.    event FilteredTransactionAdded(bytes32 indexed txHash);

    /// @notice Emitted when a transaction hash is removed from the filtered transactions list.    event FilteredTransactionDeleted(bytes32 indexed txHash);

    function addFilteredTransaction(        bytes32 txHash    ) external;

    function deleteFilteredTransaction(        bytes32 txHash    ) external;

    function isTransactionFiltered(        bytes32 txHash    ) external view returns (bool);}

(1)AddFilteredTransaction(txHash):只有授权调用者可以把某个交易哈希加入过滤列表。(2)DeleteFilteredTransaction(txHash):只有授权调用者可以把某个交易哈希从过滤列表删除。(3)IsTransactionFiltered(txHash):任意调用者都可以查询某个交易哈希是否在过滤列表中。

其筛查过程为:

用户发起交易 -> Robinhood Chain sequencer/RPC -> Nitro transaction filtering/precheck -> 命中过滤规则 -> transaction-filterer 服务调用 0x74 预编译 -> AddFilteredTransaction(txHash) -> txHash 写入 ArbOS 状态 -> 后续执行/出块时检查 IsTransactionFiltered(txHash) -> 拒绝或按配置的规则进行处理

通过在排序器层级筛查交易,Robinhood Chain实现了对包括股票代币在内的所有链上交易进行合规控制,限制高风险或受制裁实体参与链上活动,以满足链上反洗钱的要求。

结语

RWA能够降低资产交易和清算门槛,拓展全球流动性,但相关智能合约安全、治理规范、反洗钱等核心挑战依然存在。Robinhood Chain是一次传统金融资产与区块链技术的融合尝试,展示了它从代币合约层的标准化设计到协议层合规筛查机制的工程化实践路径,为后续RWA赛道的发展与监管提供了可参考的案例。

分享至:

作者:Beosin

本文为PANews入驻专栏作者的观点,不代表PANews立场,不承担法律责任。

文章及观点也不构成投资意见

图片来源:Beosin如有侵权,请联系作者删除。

关注PANews官方账号,一起穿越牛熊
PANews APP
Arcus上线基于 Robinhood Chain 的美股代币与永续合约交易
PANews 快讯