尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Sway 钱包智能合约实战:ABI 声明与合约实现的双项目架构

Sway 钱包智能合约实战:ABI 声明与合约实现的双项目架构 Sway 钱包智能合约实战ABI 声明与合约实现的双项目架构【免费下载链接】sway Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway本文基于 Sway 官方文档 Wallet Smart Contract 示例讲解如何构建一个典型的 Sway 钱包合约将 ABI 接口声明抽离为独立库项目由合约项目以路径依赖方式引用并实现。读完本文你可以掌握abi声明、#[payable]/#[storage]属性、std库消息上下文函数与transfer转账机制的配合方式以及 Checks-Effects-Interactions 防重入模式的落地细节。一、总体架构ABI 声明与实现分离Sway 中 ABI 接口声明与 ABI 实现分属两个独立的 Forc 项目。wallet_abi项目是一个library只声明接口契约wallet_smart_contract项目是contract负责具体实现。官方文档给出的目录结构如下对应仓库中的 examples/wallet_abi 与 examples/wallet_smart_contract. ├── wallet_abi │ ├── Forc.toml │ └── src │ └── main.sw └── wallet_smart_contract ├── Forc.toml └── src └── main.sw其中wallet_abi被当作外部库使用。使用外部库时必须在项目自身的Forc.toml中声明依赖来源。wallet_smart_contract项目中的声明为[dependencies] wallet_abi { path ../wallet_abi/ }仓库中实际的 Forc.toml 完整内容如下可以看到除了wallet_abi路径依赖外还显式声明了std标准库依赖指向本仓库的 sway-lib-std 源码目录[project] authors [Fuel Labs contactfuel.sh] entry main.sw license Apache-2.0 name wallet_smart_contract [dependencies] std { path ../../sway-lib-std } wallet_abi { path ../wallet_abi }而 ABI 库项目自身的 Forc.toml 仅声明std依赖因为纯接口声明本身不依赖合约逻辑。二、ABI 声明定义合约对外契约ABI 声明位于 examples/wallet_abi/src/main.sw完整代码如下library; abi Wallet { #[storage(read, write), payable] fn receive_funds(); #[storage(read, write)] fn send_funds(amount_to_send: u64, recipient_address: Address); }关键要素说明library;声明该项目为库而非合约/脚本其产物只被其他项目以依赖方式引用abi Wallet { ... }定义名为Wallet的接口契约其中的函数只有签名没有函数体。任何合约只要impl Wallet for Contract就必须按签名提供实现#[storage(read, write), payable]receive_funds允许读写 storage入账需要累计余额且标记为payable即调用方可以随调用发送资产#[storage(read, write)]send_funds同样需要读写 storage扣减余额但不是payable——转账本身不依赖随调用发送的资产。三、ABI 实现合约完整源码合约实现位于 examples/wallet_smart_contract/src/main.sw完整代码如下contract; use std::{asset::transfer, call_frames::msg_asset_id, context::msg_amount}; use wallet_abi::Wallet; const OWNER_ADDRESS Address::from(0x8900c5bec4ca97d4febf9ceb4754a60d782abbf3cd815836c1872116f203f861); storage { balance: u64 0, } impl Wallet for Contract { #[storage(read, write), payable] fn receive_funds() { if msg_asset_id() AssetId::base() { // If we received the base asset then keep track of the balance. // Otherwise, were receiving other native assets and dont care // about our balance of coins. storage.balance.write(storage.balance.read() msg_amount()); } } #[storage(read, write)] fn send_funds(amount_to_send: u64, recipient_address: Address) { let sender msg_sender().unwrap(); match sender { Identity::Address(addr) assert(addr OWNER_ADDRESS), _ revert(0), }; let current_balance storage.balance.read(); assert(current_balance amount_to_send); storage.balance.write(current_balance - amount_to_send); // Note: transfer() is not a call and thus not an // interaction. Regardless, this code conforms to // checks-effects-interactions to avoid re-entrancy. transfer( Identity::Address(recipient_address), AssetId::base(), amount_to_send, ); } }3.1 存储与常量storage { balance: u64 0 }定义一个持久化存储字段balance初始值为 0。在合约中通过storage.balance.read()/storage.balance.write(...)访问这正是 ABI 声明中#[storage(read, write)]属性的实现依据——编译器会校验实现侧对 storage 的访问不超出声明范围OWNER_ADDRESS以 32 字节十六进制字面量构造Address作为唯一有权限提取资金的地址。3.2 receive_funds入账记账receive_funds逻辑msg_asset_id()来自 sway-lib-std/src/call_frames.sw返回当前调用随附的资产类型仅当资产是AssetId::base()基础资产即燃料代币时才记账其他原生资产直接忽略注释明确说明were receiving other native assets and dont care about our balance of coins;msg_amount()来自 sway-lib-std/src/context.sw其实现是读取当前调用上下文中的balance寄存器返回本次调用发送过来的资产数量。记账方式为读取—累加—写回三步storage.balance.write(storage.balance.read() msg_amount())。3.3 send_funds权限校验、余额检查与转账send_funds按严格的 Checks-Effects-Interactions 顺序执行身份检查Checkmsg_sender()来自 sway-lib-std/src/auth.sw返回ResultIdentity, AuthError封装了当前调用的发送者身份。实现侧先unwrap()然后match解构Identity只有Identity::Address(addr)且addr OWNER_ADDRESS时通过assert校验其他任何身份合约、predicate 等直接revert(0)中止执行效果Effect读取当前storage.balanceassert(current_balance amount_to_send)防止超额提取随后立即将扣减后的余额写回 storage。先改状态、后转账是防重入的关键交互Interaction最后调用std::asset::transfer完成实际转账。3.4 std 库中 transfer 的底层实现transfer定义在 sway-lib-std/src/asset.sw。从源码结构看它是一个分发函数按收款方身份选择不同路径pub fn transfer(to: Identity, asset_id: AssetId, amount: u64) { match to { Identity::Address(addr) transfer_to_address(addr, asset_id, amount), Identity::ContractId(id) force_transfer_to_contract(id, asset_id, amount), }; }转给合约时走force_transfer_to_contract最终执行内联汇编的tr指令转给地址时走transfer_to_address见 asset.sw由于tro指令需要占用一个空的变量输出槽位实现会遍历交易输出output_count/output_type/output_amount找到amount为 0 的Output::Variable后执行tro指令找不到空闲槽位则以revert(FAILED_TRANSFER_TO_ADDRESS_SIGNAL)失败。文档中特别强调transfer()is not a call and thus not an interaction——transfer只是向交易输出写资金不会调用其他合约的代码因此不构成可被重入的交互。即便如此示例仍严格遵循 Checks-Effects-Interactions 顺序把余额扣减放在transfer之前形成纵深防御。此外asset.sw的文档注释明确列出了transfer的三种 revert 条件金额超过合约该资产余额、金额为 0、转给地址时没有空闲变量输出。示例合约中的assert(current_balance amount_to_send)正是对第一种条件的业务侧提前拦截。四、外部调用方视角如何驱动这个钱包仓库中配套的 examples/wallet_contract_caller_script/src/main.sw 演示了脚本如何持有同一份wallet_abi并发起跨项目调用script; use wallet_abi::Wallet; fn main() { let contract_address 0x9299da6c73e6dc03eeabcce242bb347de3f5f56cd1c70926d76526d7ed199b8b; let caller abi(Wallet, contract_address); let amount_to_send 200; let recipient_address Address::from(0x9299da6c73e6dc03eeabcce242bb347de3f5f56cd1c70926d76526d7ed199b8b); caller .send_funds { gas: 10000, coins: 0, asset_id: b256::zero(), }(amount_to_send, recipient_address); }这段代码印证了ABI 声明独立成项目的价值调用方不需要知道合约的内部实现只需依赖wallet_abi通过abi(Wallet, contract_address)得到带类型的客户端。调用参数块中的gas、coins、asset_id分别是本次跨合约调用预留的 gas 上限、随调用发送的币数和资产 IDsend_funds非payable这里coins传 0 也符合接口约定。五、安全设计要点小结机制实现位置作用Owner 白名单main.sw 中match senderrevert(0)仅OWNER_ADDRESS可提取资金非地址身份直接回滚余额下限断言assert(current_balance amount_to_send)防止超额提取与无资产转账先记账后转账扣减 storage 在transfer之前符合 Checks-Effects-Interactions防重入仅记基础资产receive_funds中msg_asset_id() AssetId::base()判断明确记账范围避免混币歧义综上这个钱包示例以最小的代码量覆盖了 Sway 合约开发的核心链路独立 ABI 库项目 →Forc.toml路径依赖 →contract实现 storage声明 → std 库消息上下文与资产转账 → 脚本侧abi客户端调用是理解 Sway 合约接口设计与安全编码模式的标准起点。【免费下载链接】sway Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表