ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Sway 智能合约实战:用 Fuel 原生资产机制实现最小化流动性池(Liquidity Pool)

Sway 智能合约实战:用 Fuel 原生资产机制实现最小化流动性池(Liquidity Pool) Sway 智能合约实战用 Fuel 原生资产机制实现最小化流动性池Liquidity Pool【免费下载链接】sway Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway本篇指南围绕 Sway 官方示例仓库中的流动性池Liquidity Pool合约展开讲解如何在 Fuel 网络中以「合约原生资产」为核心机制通过mint_to、transfer、msg_asset_id、msg_amount等标准库 API 实现存入deposit与提取withdraw两个核心操作并揭示「无需持久化存储记账、由 FuelVM 自动跟踪合约资产余额」这一关键设计。读完本文你将掌握 Fuel 原生资产模型的基本用法并能够基于此模式编写自己的单边资产池或 LP 凭证类合约。一、Fuel 原生资产模型为什么流动性池不需要手工记账在绝大多数 EVM 系合约中代币余额由合约自身的 storage 映射维护例如mapping(address uint256) balanceOf任何转账都必须在合约代码中同步更新存储否则账目就会错乱。Fuel 网络采用了截然不同的模型所有合约都可以铸造mint和销毁burn自己的原生资产合约既可以接收、也可以转出任意原生资产包括自己的资产所有被调用或铸造的原生资产内部余额由 FuelVM 直接跟踪任意时刻都可以通过std标准库的balance_of函数查询。这意味着流动性池合约无需用持久化存储手工记录「谁存了多少、池子里还剩多少」——余额本身就是链上事实读取即得。这一点在官方文档 docs/book/src/examples/liquidity_pool.md 中被明确强调there is no need for any manual accounting of the contracts balances using persistent storage无需使用持久化存储对合约余额做任何手工记账。二、示例工程结构与配置流动性池示例位于 examples/liquidity_pool是一个标准的 Sway 合约工程examples/liquidity_pool/ ├── Forc.toml └── src/ └── main.sw其Forc.toml声明了工程元信息与标准库依赖[project] authors [Fuel Labs contactfuel.sh] entry main.sw license Apache-2.0 name liquidity_pool [dependencies] std { path ../../sway-lib-std }要点entry main.sw指定合约入口文件为src/main.swstd通过本地路径依赖仓库内的 sway-lib-std 标准库因此在仓库根目录下执行forc build在examples/liquidity_pool目录下执行即可直接编译无需联网拉取依赖。三、完整合约代码逐段拆解完整实现位于 examples/liquidity_pool/src/main.sw官方文档通过{{#include ../../../../examples/liquidity_pool/src/main.sw}}将其原样嵌入 docs/book/src/examples/liquidity_pool.md。下面逐段分析。3.1 导入标准库资产操作contract; use std::{ asset::{ mint_to, transfer, }, call_frames::msg_asset_id, constants::DEFAULT_SUB_ID, context::msg_amount, hash::*, };asset::{mint_to, transfer}铸造并转移 LP 资产、转移基础资产的核心函数call_frames::msg_asset_id读取当前调用call frame中随调用附带的资产类型context::msg_amount读取本次调用附带的数量constants::DEFAULT_SUB_ID铸造 LP 资产时使用的默认子标识hash::*为AssetId等类型提供哈希/相等性等 trait 支持。3.2 定义 ABIabi LiquidityPool { fn deposit(recipient: Address); fn withdraw(recipient: Address); }ABI 仅暴露两个方法deposit(recipient)用于存入基础资产并铸造 LP 凭证withdraw(recipient)用于销毁 LP 凭证并转出基础资产。3.3 声明基础资产常量const BASE_ASSET: AssetId AssetId::from(0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c);BASE_ASSET是该池接受的基础资产如原生 ETH 或其他已发行资产的AssetId。AssetId::from(b256)由标准库提供将原始b256数据包装为AssetId类型见 sway-lib-std/src/asset_id.sw。注意实际部署时该常量应替换为你所在链上目标资产的真实AssetId主网 Ether 可通过AssetId::base()获取标准库对此有专门封装。3.4 deposit铸造双倍 LP 资产impl LiquidityPool for Contract { 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 some LP assets based upon the amount of the base asset. mint_to(Identity::Address(recipient), DEFAULT_SUB_ID, amount_to_mint); }deposit的语义校验资产类型assert(msg_asset_id() BASE_ASSET)确保调用者附带的必须是池接受的基础资产否则合约回滚revert校验数量assert(msg_amount() 0)拒绝零金额调用计算铸造量amount_to_mint msg_amount() * 2即按 1:2 的比例铸造 LP 资产示例为教学简化逻辑非真实 AMM 定价铸造并转账mint_to(Identity::Address(recipient), DEFAULT_SUB_ID, amount_to_mint)把 LP 资产铸造到recipient名下。关键点存入的基础资产并没有在合约存储里登记它只是随调用进入合约余额由 FuelVM 跟踪合约在收到基础资产的同时铸造出等量的 LP 凭证资产。3.5 withdraw销毁 LP 凭证并返还基础资产fn withdraw(recipient: Address) { let asset_id AssetId::default(); assert(msg_asset_id() asset_id); assert(msg_amount() 0); // Amount to withdraw. let amount_to_transfer msg_amount() / 2; // Transfer base asset to recipient. transfer(Identity::Address(recipient), BASE_ASSET, amount_to_transfer); } }withdraw的语义确定 LP 资产AssetId::default()返回当前合约在默认SubIdb256::zero()下铸造的资产 ID校验资产类型assert(msg_asset_id() asset_id)要求调用者附带的必须是本合约的 LP 资产等价于「先销毁凭证再取款」校验数量assert(msg_amount() 0)计算返还量amount_to_transfer msg_amount() / 2与存款时 1:2 的比例对称转账基础资产transfer(Identity::Address(recipient), BASE_ASSET, amount_to_transfer)。用户把 LP 资产发给合约后合约按 2:1 比例把基础资产转回给recipient。LP 资产由 FuelVM 从合约余额中扣除隐式销毁基础资产则转出整个流程同样不涉及任何持久化记账。四、标准库源码级解析这些 API 在底层做了什么示例合约用到的每个标准库函数都能在 sway-lib-std 中找到实现理解它们有助于把握 Fuel 原生资产模型的实际运作方式。4.1 mint / mint_to铸造并转移资产sway-lib-std/src/asset.sw 中pub fn mint(sub_id: SubId, amount: u64) { asm(r1: amount, r2: sub_id) { mint r1 r2; } } pub fn mint_to(to: Identity, sub_id: SubId, amount: u64) { mint(sub_id, amount); transfer(to, AssetId::new(ContractId::this(), sub_id), amount); }mint直接内联 FuelVM 的MINT指令铸造指定SubId与数量的资产铸造出的资产先归合约所有mint_to在铸造后立即调用transfer将新铸造资产转账给目标Identity是「铸即转」的便捷封装铸造目标资产 ID 由AssetId::new(ContractId::this(), sub_id)计算其本质是sha256(contract_id, sub_id)见 sway-lib-std/src/asset_id.sw 中的实现注释与AssetId::new源码。4.2 transfer按目标类型分发转账同文件中的transfer按接收方类型分流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), }; }转给Address时走transfer_to_address遍历交易输出寻找金额为 0 的空闲OutputVariable通过TRO指令执行转账若找不到空闲输出则 revert相关常量见 sway-lib-std/src/error_signals.sw 中的FAILED_TRANSFER_TO_ADDRESS_SIGNAL转给合约时走force_transfer_to_contract直接使用TR指令。标准库对两种转账目标都有注释警告若接收方是合约且没有取回机制资产可能永久丢失。4.3 msg_asset_id 与 msg_amount读取调用附带信息msg_asset_id()实现在 sway-lib-std/src/call_frames.sw通过读取当前调用帧call frame中固定偏移量处的 32 字节数据得到本次调用附带的资产 IDmsg_amount()实现在 sway-lib-std/src/context.sw本质是读取当前调用的余额寄存器balance()。4.4 balance_of / this_balance查询任意合约的资产余额sway-lib-std/src/context.sw 中pub fn this_balance(asset_id: AssetId) - u64 { balance_of(ContractId::this(), asset_id) } pub fn balance_of(target: ContractId, asset_id: AssetId) - u64 { asm(balance, asset: asset_id.bits(), id: target.bits()) { bal balance asset id; balance: u64 } }这正是文档所说「FuelVM 自动跟踪内部余额、随时可用balance_of查询」的底层证据balance_of直接内联 FuelVM 的BAL指令由虚拟机按(合约, 资产ID)查询余额不需要合约自己维护任何计数器。你可以在自己的合约里用this_balance(BASE_ASSET)随时验证池子实际持有量。4.5 DEFAULT_SUB_ID 与 AssetId::defaultDEFAULT_SUB_ID定义于 sway-lib-std/src/constants.sw值为b256::zero()表示合约铸造资产时的默认子标识AssetId::default()实现于 sway-lib-std/src/asset_id.sw等价于sha256(当前合约, b256::zero())即「本合约默认 SubId 下的资产 ID」——这正是withdraw中校验 LP 资产所用的值。五、运行与验证方式5.1 编译在仓库内执行forc为 Sway 官方工具链cd examples/liquidity_pool forc build编译通过后forc会输出合约字节码与 ABI JSON。5.2 单元测试本示例合约以assert完成运行时校验你可以在此基础上用#[test]编写测试或参考仓库 test 目录下大量e2e_vm_tests用例的写法通过forc test在 FuelVM 上模拟调用deposit/withdraw并断言余额变化。5.3 调用语义调用deposit时交易必须携带BASE_ASSET且金额大于 0调用withdraw时交易必须携带本合约的 LP 资产AssetId::default()且金额大于 0金额为 0 或资产类型不符的调用都会被assert中断并整体回滚保证池子账目安全。六、扩展思考在此基础上可以做什么把msg_amount() * 2换成基于恒定乘积的定价公式如x * y k即可演化为真正的单边/双边 AMM 流动性池引入多个SubIdmint_to接受任意SubId同一合约可发行多种不同凭证资产只需在铸造时指定不同子标识利用this_balance/balance_of做审计由于余额由 FuelVM 保证任何时刻查询到的余额都与链上状态一致天然适合作为价格或流动性数据的输入源。七、总结这个流动性池示例是理解 Fuel 原生资产模型的理想起点合约铸造自己的 LP 凭证资产、接收并转出任意资产、全程依赖 FuelVM 自动跟踪余额代码简洁到只有几十行却不失完整的安全校验。核心 APImint_to、transfer、msg_asset_id、msg_amount、balance_of都有标准库源码可查位于 sway-lib-std/src/asset.sw、sway-lib-std/src/context.sw 与 sway-lib-std/src/call_frames.sw可作为你编写任何「发行原生资产型」合约的直接参考模板。【免费下载链接】sway Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表