完全指南:msg_sender、msg_asset_id 与 msg_amount 实战解析)
Sway 合约调用元数据Call Data完全指南msg_sender、msg_asset_id 与 msg_amount 实战解析【免费下载链接】sway Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/swaycall-data调用数据是 Fuel 区块链上 Sway 智能合约在一次调用中可获取的元数据集合它告诉合约谁在调用我、带了什么资产、带了多少。本文基于 Sway 官方参考文档 call-data 章节 展开结合 sway-lib-std 标准库源码与 examples 目录下的真实合约示例系统讲解msg_sender()、msg_asset_id()、msg_amount()三个核心 API 的语义、底层实现与实战用法。读完本文你将能够为合约实现基于调用者的权限控制、按资产类型分流业务逻辑以及对转入资产数量做精确校验。Call Data 是什么在 Sway 官方参考文档中call-data被定义为调用接收方recipient of a call可用的元数据见 call-data/index.md。当一笔调用call从外部脚本或另一个合约进入当前合约时Fuel 虚拟机会在调用帧call frame中携带一组与本次调用强相关的上下文信息合约代码可以通过标准库函数读取它们而无需调用者显式传递。官方文档将call-data归纳为三类元数据标准库函数返回类型语义Message Sendermsg_sender()ResultIdentity, AuthError谁发起了本次调用Asset Sentmsg_asset_id()AssetId调用转入了哪种资产Amount of Asset Sentmsg_amount()u64该资产转入了多少数量这三个函数构成了 Sway 合约感知调用现场的基础能力几乎所有涉及资产接收、权限校验的合约都会用到。谁在调用msg_sender 与访问控制函数来源与返回类型msg_sender()位于标准库auth模块sway-lib-std/src/auth.sw并已被自动导入标准库 preludesway-lib-std/src/prelude.sw 第 37 行pub use ::auth::msg_sender;默认将其暴露给所有程序因此你无需手动use即可直接调用。它返回ResultIdentity, AuthError。Identity是定义于 sway-lib-std/src/identity.sw 的一个枚举用于统一表示地址和合约两类调用来源pub enum Identity { Address: Address, ContractId: ContractId, }AuthError同样定义在 auth.sw 中包含两种错误变体InputsNotAllOwnedBySameAddress调用者为外部脚本时交易的输入并不全部由同一地址拥有CallerIsInternal内部上下文下调用了caller_address之类的外部判定函数。底层实现原理从源码看msg_sender()的实现逻辑auth.sw 第 115-125 行是通过gm r1 i1GM_GET_CALLER元数据指令由caller_is_external()封装判断调用者是否为外部脚本若为外部调用则遍历交易的全部输入Input::Coin/Input::Message比对它们的所有者地址是否一致返回第一个所有者作为Identity::Address若为内部调用合约调用合约则通过gm r1 i2GM_GET_VERIFYING...类元数据指令由caller_contract_id()封装读取调用方合约 ID返回Identity::ContractId。因此msg_sender()天然同时覆盖钱包地址发起的交易与合约之间互调两种场景返回的Identity不区分调用者究竟是个人账户还是智能合约。实战仅所有者可调用的函数文档 msg-sender.md 给出了访问控制的标准写法将msg_sender()的结果与预定义的所有者常量比较不匹配则 revert。完整的可运行代码在 docs/reference/src/code/operations/call_data/src/lib.sw 的access_control代码段const OWNER Identity::Address(Address::from(0x0000000000000000000000000000000000000000000000000000000000000000)); fn update() { require(msg_sender().unwrap() OWNER, Owner Only); // code }要点拆解这里将OWNER声明为Identity::Address因此与msg_sender()的返回值类型直接对齐可以执行比较.unwrap()用于提取Result中的Identity——在生产合约中应结合match分支处理错误情况避免未预期调用场景下 panicrequire(condition, msg)由 prelude 从 sway-lib-std/src/revert.sw 导入条件不满足时合约回滚并附带错误信息。仓库中 examples/msg_sender/src/main.sw 提供了一个更完整的变体用if let匹配Identity变体并分别处理Address与ContractId情况const OWNER Address::from(0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c); impl MyOwnedContract for Contract { fn receive(field_1: u64) - bool { let sender msg_sender().unwrap(); if let Identity::Address(addr) sender { assert(addr OWNER); } else { revert(0); } true } }同样的模式还出现在 examples/cei_analysis/src/main.sw 与 examples/identity/src/main.sw 中可见这是 Sway 合约访问控制的通用范式。转入的资产是什么msg_asset_id函数来源与返回类型msg_asset_id()定义于标准库call_frames模块sway-lib-std/src/call_frames.sw需要显式导入use std::call_frames::msg_asset_id;它返回AssetId用于标识本次调用转入的资产。AssetId是 sway-lib-std/src/asset_id.sw 中定义的类型其base()关联函数通过gm r1 i6指令读取链上基础资产Fuel 原生币的 ID是判断是否收到原生资产的常用基准pub fn base() - Self { Self { bits: asm(r1) { gm r1 i6; r1: b256 }, } }底层实现原理从 call_frames.sw 第 40-45 行可见msg_asset_id()直接读取当前调用帧call frame中固定偏移量处的 32 字节数据pub fn msg_asset_id() - AssetId { AssetId::from(asm(asset_id) { addi asset_id fp i32; asset_id: b256 }) }即从帧指针fp偏移i32第 32 个字处加载b256并包装为AssetId。这意味着资产 ID 是虚拟机在跨合约调用、脚本调用时自动写入调用帧的元数据而非合约逻辑可以伪造的值。实战按资产类型分流业务文档 msg-asset.md 展示的经典场景是判断转入资产是否为原生资产走不同分支代码见 docs/reference/src/code/operations/call_data/src/lib.sw 的import_asset与deposit代码段use std::call_frames::msg_asset_id; fn deposit() { if msg_asset_id() AssetId::base() { // code } else { // code } }仓库中的 examples/liquidity_pool/src/main.sw 是这一用法的完整落地它声明了一个常量BASE_ASSET一个硬编码的AssetId并在deposit中先断言转入资产必须等于该基础资产否则整个交易回滚const BASE_ASSET: AssetId AssetId::from(0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c); fn deposit(recipient: Address) { assert(msg_asset_id() BASE_ASSET); assert(msg_amount() 0); // Mint two times the amount. let amount_to_mint msg_amount() * 2; mint_to(Identity::Address(recipient), DEFAULT_SUB_ID, amount_to_mint); }再如 examples/wallet_smart_contract/src/main.sw 的receive_funds只有收到基础资产时才累加storage.balance收到其他原生资产则忽略从而区分余额记账与其他资产收存两类语义。转入了多少msg_amount函数来源与返回类型msg_amount()定义于标准库context模块sway-lib-std/src/context.sw同样需要显式导入use std::context::msg_amount;它返回u64表示本次调用转入的资产数量。文档特别强调msg_amount()只关心数量不关心资产种类——判断是哪种资产需要配合msg_asset_id()使用。底层实现原理从源码看context.sw 第 77-79 行中msg_amount()是registers模块balance()的直接转发pub fn msg_amount() - u64 { balance() }而balance()sway-lib-std/src/registers.sw 第 248-252 行是一条裸bal汇编指令pub fn balance() - u64 { asm() { bal } }bal是 Fuel VM 的余额寄存器读取指令返回当前调用帧中携带的转入余额。整个调用链为msg_amount()→balance()→bal指令理解这一点有助于你意识到msg_amount的取值来自 VM 寄存器而非合约自身的存储。实战对转入数量做精确校验文档 msg-asset-amount.md 展示了按固定价格销售的典型场景检查转入数量是否等于售价否则回滚代码见 docs/reference/src/code/operations/call_data/src/lib.sw 的import_amount与deposit_amount代码段use std::context::msg_amount; fn purchase() { require(msg_amount() 100_000_000, Incorrect amount sent); // code }这里的100_000_000即按资产最小单位如同燃料币的原子单位计价的售价require会确保任何未足额或超额支付的调用都被拒绝。msg_amount()的另一个高频用法是作为业务计算的输入。在 examples/liquidity_pool/src/main.sw 的withdraw中合约按msg_amount() / 2计算应转出的数量而在 examples/wallet_smart_contract/src/main.sw 的receive_funds中转入额被累加到存储余额storage.balance.write(storage.balance.read() msg_amount())。三个 API 的组合使用call-data的三要素在实际合约中往往组合出现。以 examples/native_asset/src/main.sw 为例它同时暴露get_msg_amount()与get_msg_asset_id()两个查询接口并在内部断言msg_amount() 0再分别把数量和资产 ID 返回给调用方——这相当于把调用元数据透传给外部便于上层SDK 或前端观察一次调用实际携带的资产信息。更完整的组合出现在钱包合约 examples/wallet_smart_contract/src/main.sw 中收款receive_funds用msg_asset_id() AssetId::base()判断资产类型用msg_amount()累加余额转账send_funds用msg_sender().unwrap()校验调用者必须是OWNER_ADDRESS并声明了#[storage(read, write)]注解以读写存储。而 examples/liquidity_pool/src/main.sw 则是资产种类 数量双重校验的范本assert(msg_asset_id() BASE_ASSET)与assert(msg_amount() 0)两道闸门共同确保只有用基础资产且数量为正的调用才能进入铸币逻辑。与调用方配合payable 注解与调用参数传递要让msg_asset_id()和msg_amount()读到有意义的数值调用方需要在发起调用时显式携带资产。Sway 的合约调用语法支持在调用时指定三个可选参数见 contract-call.md 及其实例 docs/reference/src/code/operations/contract_calling/call/src/main.swlet caller abi(Vault, vault_id); caller.deposit { gas: 10000, // 转发给合约的 gas coins: amount, // 转发的币数量 asset_id: asset_id.into(), // 转发币的资产 ID }();对应的合约接口侧需要接收资产的函数必须标注#[payable]注解见 payable 属性文档 与 docs/reference/src/code/language/annotations/src/main.sw 的payable代码段abi Vault { #[payable] fn deposit(); fn withdraw(amount: u64, asset: ContractId); }这个 ABI 接口定义位于 docs/reference/src/code/operations/contract_calling/contract_interface/src/lib.sw。值得注意的是没有#[payable]的函数如果收到资产转入会直接回滚因此在设计接收资产的合约入口时#[payable]与msg_asset_id()/msg_amount()必须成对出现。小结call-data是 Sway 合约感知调用上下文的三元组信息官方文档与标准库sway-lib-std为每个维度都提供了开箱即用的函数msg_sender()来自 auth.swprelude 自动导入返回调用者Identity是访问控制的基础msg_asset_id()来自 call_frames.sw返回转入资产的AssetId用于按资产类型分流msg_amount()来自 context.sw返回转入数量u64用于计价与校验。三者均基于 Fuel VM 的调用帧元数据与寄存器gm、bal指令读取取值天然可信无法由合约逻辑伪造。配合调用方的gas/coins/asset_id参数与接收方的#[payable]注解即可构建出具备权限控制与资产校验能力的完整合约。需要进一步深入时可直接阅读参考文档中的 msg-sender、msg-asset、msg-asset-amount 三个子章节以及在 examples 目录下运行 liquidity_pool 与 wallet_smart_contract 等示例合约做端到端验证。【免费下载链接】sway Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考