AptosMove实战:从零构建一个链上价格预言机(含源码和测试)在区块链的世界里,智能合约如何与外部真实世界的数据交互,始终是一个核心命题。而“预言机”正是连接链上与链下世界的关键桥梁。本文将带你深入Aptos生态,使用强类型、高安全性的Move语言,从零开始构建一个实用的链上价格
在区块链的世界里,智能合约如何与外部真实世界的数据交互,始终是一个核心命题。而“预言机”正是连接链上与链下世界的关键桥梁。本文将带你深入 Aptos 生态,使用强类型、高安全性的 Move 语言,从零开始构建一个实用的链上价格预言机。我们将重点利用 Move 中强大的 Vector
(动态数组)数据类型来灵活地存储和管理多种代币的价格信息。无论你是 Move 新手还是有经验的开发者,通过这个实战案例,你都将对 Aptos 智能合约的开发模式有更深刻的理解。
Aptos Move Vectors - Token Price Feeds Module
以下是本次项目所需的核心 Move 代码。
module net2dev_addr::price_feeds {
use std::vector;
use std::string::{String, utf8};
use std::timestamp;
use std::signer;
struct TokenFeed has store, drop, copy {
last_price: u64,
timestamp: u64
}
struct PriceFeeds has key, store, drop, copy {
symbols: vector<String>,
data: vector<TokenFeed>
}
/// Error Not Owner
const ENOT_OWNER: u64 = 101;
fun init_module(owner: &signer) {
let symbols = vector::empty<String>();
symbols.push_back(utf8(b"BTC"));
let new_feed = TokenFeed { last_price: 0, timestamp: 0 };
let data_feed = PriceFeeds {
symbols,
data: (vector[new_feed])
};
move_to(owner, data_feed)
}
fun update_feed(owner: &signer, last_price: u64, symbol: String) acquires PriceFeeds {
let signer_addr = signer::address_of(owner);
assert!(signer_addr == @net2dev_addr, ENOT_OWNER);
assert!(exists<PriceFeeds>(signer_addr) == true, 101);
let time = timestamp::now_seconds();
let data_store = borrow_global_mut<PriceFeeds>(signer_addr);
let new_feed = TokenFeed { last_price, timestamp: time };
let (result, index) = data_store.symbols.index_of(&symbol);
if (result) {
// data_store.data[index] = new_feed;
data_store.data.remove(index);
data_store.data.insert(index, new_feed);
} else {
data_store.data.push_back(new_feed);
data_store.symbols.push_back(symbol);
}
}
fun get_token_price(symbol: String): TokenFeed acquires PriceFeeds {
let symbols = borrow_global<PriceFeeds>(@net2dev_addr).symbols;
let (result, index) = symbols.index_of(&symbol);
if (result) {
borrow_global<PriceFeeds>(@net2dev_addr).data[index]
} else {
TokenFeed { last_price: 0, timestamp: 0 }
}
}
#[test_only]
use std::debug::print;
#[test(owner = @net2dev_addr, init_addr = @0x1)]
fun test_function(owner: &signer, init_addr: signer) acquires PriceFeeds {
timestamp::set_time_has_started_for_testing(&init_addr);
init_module(owner);
update_feed(owner, 63400, utf8(b"BTC"));
let result = get_token_price(utf8(b"BTC"));
assert!(result.last_price == 63400, 0);
print(&result);
update_feed(owner, 1000, utf8(b"ETH"));
result = get_token_price(utf8(b"ETH"));
assert!(result.last_price == 1000, 0);
print(&result);
update_feed(owner, 62411, utf8(b"BTC"));
result = get_token_price(utf8(b"BTC"));
assert!(result.last_price == 62411, 0);
print(&result);
}
}
这段代码实现了一个简单的链上价格预言机(Price Oracle)*智能合约。它的核心功能是在 Aptos 区块链上安全地存储和提供不同代币(Token)的价格信息。合约首先定义了一个名为 PriceFeeds
的核心数据结构,它就像一个链上的数据库,用来存放一系列代币的符号(如 "BTC")以及它们对应的最新价格和更新时间戳。合约部署时会进行初始化,默认添加 "BTC" 作为第一个代币。此后,只有\合约的所有者有权限调用 update_feed
函数来更新某个代币的价格,或者添加一个新的代币及其价格。最后,任何人都可以通过公开的 get_token_price
函数,传入代币符号来查询其在链上记录的最新价格数据。整个设计确保了价格数据的写入是受控且安全的,而读取则是公开透明的**,为其他去中心化应用提供了可信的价格参考。
运行 aptos move test
命令,验证合约的逻辑正确性。
➜ aptos move test
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING my-dapp
Running Move unit tests
[debug] 0x48daaa7d16f1a59929ece03157b8f3e4625bc0a201988ac6fea9b38f50db5ef3::price_feeds::TokenFeed {
last_price: 63400,
timestamp: 0
}
[debug] 0x48daaa7d16f1a59929ece03157b8f3e4625bc0a201988ac6fea9b38f50db5ef3::price_feeds::TokenFeed {
last_price: 1000,
timestamp: 0
}
[debug] 0x48daaa7d16f1a59929ece03157b8f3e4625bc0a201988ac6fea9b38f50db5ef3::price_feeds::TokenFeed {
last_price: 62411,
timestamp: 0
}
[ PASS ] 0x48daaa7d16f1a59929ece03157b8f3e4625bc0a201988ac6fea9b38f50db5ef3::price_feeds::test_function
Test result: OK. Total tests: 1; passed: 1; failed: 0
{
"Result": "Success"
}
这段终端输出是运行 aptos move test
命令后的成功反馈,它表明我们编写的智能合约单元测试已经顺利通过。这就像是产品发布前的自动化质量检测。前面三段 [debug]
开头的打印信息,是我们测试代码中 print
函数输出的结果,它们清晰地展示了测试的执行步骤:首先将 BTC 价格更新为 63400
,接着添加了 ETH 并设置价格为 1000
,最后又将 BTC 价格更新为 62411
。最关键的是 [ PASS ]
和 "Result": "Success"
这两行,它们明确地告诉我们,测试中所有的断言(assert)都已验证成功,证明了我们合约的初始化、更新和查询价格等核心功能完全符合预期,代码逻辑是正确且可靠的。
通过本文从合约设计、代码实现到单元测试的完整流程,我们不仅成功构建了一个功能完备的 Aptos 价格预言机,更重要的是,我们深入实践了 Move 语言中 Vector
这一核心数据结构在管理动态链上数据时的强大作用。Vector
的灵活性与 Move 语言自身的安全特性相结合,为开发者在 Aptos 上构建复杂应用提供了坚实的基础。希望这个实例能为你打开一扇门,激发更多关于 Aptos 和 Move 的创造性想法。区块链开发之路,始于实践,贵在坚持。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!