oracle
模块说明
本文将介绍和使用Sui
官方提供的 天气预言机weather-oracle 进行随机数的创建。
注:因目前官方提供的部署在主网上的天气预言机版本比较老,已经无法正常使用了,故本示例中将在测试网上部署一个简化版本来演示。期待官方后续进行下更新。
源码路径
方法图解
代码示例
以下同样是投骰子的示例,每次调用将通过天气预言机得到一个 1~6 的随机数 NFT。
module cookbook::weather_oracle {
use sui::event;
use simple_weather_oracle::simple_weather::{WeatherOracle};
public struct Dice has key, store {
id: UID,
value: u32,
}
public struct DiceEvent has copy, drop {
value: u32,
}
entry fun roll_dice(weather_oracle: &WeatherOracle, geoname_id: u32): u32 {
let random_pressure_sz =
simple_weather_oracle::simple_weather::city_weather_oracle_pressure(weather_oracle, geoname_id);
let result = random_pressure_sz % 6 + 1;
result
}
entry fun roll_dice_nft(weather_oracle: &WeatherOracle, geoname_id: u32, ctx: &mut TxContext) {
let value = roll_dice(weather_oracle, geoname_id);
let dice = Dice {
id: object::new(ctx),
value,
};
event::emit(DiceEvent { value });
transfer::transfer(dice, tx_context::sender(ctx));
}
}
合约操作
前置准备
因官方的天气预言机已经无法正常使用,故个人在测试网部署一个简化版本,进行测试。待官方升级后,使用方式类似。
具体步骤如下:
(1)编写简化版weather-oracle合约
module oracle::simple_weather {
use std::string::{Self, String};
use sui::dynamic_object_field as dof;
use sui::package;
/// Define a capability for the admin of the oracle.
public struct AdminCap has key, store { id: UID }
/// // Define a one-time witness to create the `Publisher` of the oracle.
public struct SIMPLE_WEATHER has drop {}
// Define a struct for the weather oracle
public struct WeatherOracle has key {
id: UID,
/// The address of the oracle.
address: address,
/// The name of the oracle.
name: String,
/// The description of the oracle.
description: String,
}
// Define a struct for each city that the oracle covers
public struct CityWeatherOracle has key, store {
id: UID,
geoname_id: u32, // The unique identifier of the city
name: String, // The name of the city
country: String, // The country of the city
pressure: u32, // The atmospheric pressure in hPa
// ...
}
/// Module initializer. Uses One Time Witness to create Publisher and transfer it to sender.
fun init(otw: SIMPLE_WEATHER, ctx: &mut TxContext) {
package::claim_and_keep(otw, ctx); // Claim ownership of the one-time witness and keep it
let cap = AdminCap { id: object::new(ctx) }; // Create a new admin capability object
transfer::share_object(WeatherOracle {
id: object::new(ctx),
address: tx_context::sender(ctx),
name: string::utf8(b"SuiMeteo"),
description: string::utf8(b"A weather oracle for posting weather updates (temperature, pressure, humidity, visibility, wind metrics and cloud state) for major cities around the world. Currently the data is fetched from https://openweathermap.org. SuiMeteo provides the best available information, but it does not guarantee its accuracy, completeness, reliability, suitability, or availability. Use it at your own risk and discretion."),
});
transfer::public_transfer(cap, tx_context::sender(ctx)); // Transfer the admin capability to the sender.
}
// Public function for adding a new city to the oracle
public fun add_city(
_: &AdminCap, // The admin capability
oracle: &mut WeatherOracle, // A mutable reference to the oracle object
geoname_id: u32, // The unique identifier of the city
name: String, // The name of the city
country: String, // The country of the city
ctx: &mut TxContext // A mutable reference to the transaction context
) {
dof::add(&mut oracle.id, geoname_id, // Add a new dynamic object field to the oracle object with the geoname ID as the key and a new city weather oracle object as the value.
CityWeatherOracle {
id: object::new(ctx), // Assign a unique ID to the city weather oracle object
geoname_id, // Set the geoname ID of the city weather oracle object
name, // Set the name of the city weather oracle object
country, // Set the country of the city weather oracle object
pressure: 0, // Initialize the pressure to be zero
}
);
}
// Public function for removing an existing city from the oracle
public fun remove_city(_: &AdminCap, oracle: &mut WeatherOracle, geoname_id: u32) {
let CityWeatherOracle { id, geoname_id: _, name: _, country: _, pressure: _} = dof::remove(&mut oracle.id, geoname_id);
object::delete(id);
}
// Public function for updating the weather conditions of a city
public fun update(
_: &AdminCap,
oracle: &mut WeatherOracle,
geoname_id: u32,
pressure: u32,
) {
let city_weather_oracle_mut = dof::borrow_mut<u32, CityWeatherOracle>(&mut oracle.id, geoname_id); // Borrow a mutable reference to the city weather oracle object with the geoname ID as the key
city_weather_oracle_mut.pressure = pressure;
}
/// Returns the `pressure` of the `CityWeatherOracle` with the given `geoname_id`.
public fun city_weather_oracle_pressure(
weather_oracle: &WeatherOracle,
geoname_id: u32
): u32 {
let city_weather_oracle = dof::borrow<u32, CityWeatherOracle>(&weather_oracle.id, geoname_id);
city_weather_oracle.pressure
}
// This function updates the name of a weather oracle contract.
// It takes an admin capability, a mutable reference to the weather oracle, and a new name as arguments.
// It assigns the new name to the weather oracle's name field.
public fun update_name(_: &AdminCap, weather_oracle: &mut WeatherOracle, name: String) {
weather_oracle.name = name;
}
// This function updates the description of a weather oracle contract.
// It takes an admin capability, a mutable reference to the weather oracle, and a new description as arguments.
// It assigns the new description to the weather oracle's description field.
public fun update_description(_: &AdminCap, weather_oracle: &mut WeatherOracle, description: String) {
weather_oracle.description = description;
}
}
(2)部署简化版weather-oracle合约
$ sui client publish
- 记录关键对象
export PACKAGE_ID=0xd12ea4bb23747bbf18b54e3ce4857aa84a89f053e677d79f84642e99a1753f06
export WEATHER_ORACLE=0x50fe253f3913d4c120f1cc96a69db10b9bebed01a4fcb4e3957336f33d9fa4c3
export ADMIN_CAP=0x02714caa89b4d990321035c0d946c650bb5e20f3a73679cfc3ce2de15bb00103
(3)修改Move.toml
- 将PACKAGE_ID更新到Move.toml
[package]
name = "simple_weather_oracle"
edition = "2024.beta"
version = "1.0.0"
published-at = "0xd12ea4bb23747bbf18b54e3ce4857aa84a89f053e677d79f84642e99a1753f06"
[dependencies]
Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" }
[addresses]
oracle = "0xd12ea4bb23747bbf18b54e3ce4857aa84a89f053e677d79f84642e99a1753f06"
(4)提交简化版weather-oracle到github
(5)添加城市
$ export GEO_NAME_ID_SZ=1795566
$ sui client call --package $PACKAGE_ID --module simple_weather --function add_city --args $ADMIN_CAP $WEATHER_ORACLE $GEO_NAME_ID_SZ Shenzhen CN
(6)更新气压值
$ sui client call --package $PACKAGE_ID --module simple_weather --function update --args $ADMIN_CAP $WEATHER_ORACLE $GEO_NAME_ID_SZ 99700
(7)获取气压值
$ sui client call --package $PACKAGE_ID --module simple_weather --function city_weather_oracle_pressure --args $WEATHER_ORACLE $GEO_NAME_ID_SZ
业务合约部署
$ sui client publish
- 记录关键信息
export PACKAGE_ID=0xb5b69526e8c37cc195b4ca881c60a897194ffd11b8adb3638b5909fbdb44119e
浏览器调用
https://suiscan.xyz/testnet/object/0xb5b69526e8c37cc195b4ca881c60a897194ffd11b8adb3638b5909fbdb44119e/contracts
可见得到的骰子点数为:5:
https://suiscan.xyz/testnet/tx/F1XkZ4VmyajJrwuuj6ooQFAnR2sGPna8LMwyf3a3yGuQ
命令行调用
$ sui client call --package $PACKAGE_ID --module weather_oracle --function roll_dice_nft --args $WEATHER_ORACLE $GEO_NAME_ID_SZ
可见得到的骰子点数为:5。
代码调用
import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
import { SuiClient } from "@mysten/sui/client";
import { Transaction } from "@mysten/sui/transactions";
import dotenv from "dotenv";
dotenv.config();
const MNEMONIC = process.env.MNEMONIC!;
const keypair = Ed25519Keypair.deriveKeypair(MNEMONIC);
const FULLNODE_URL = "https://fullnode.testnet.sui.io:443";
const PACKAGE_ID =
"0xb5b69526e8c37cc195b4ca881c60a897194ffd11b8adb3638b5909fbdb44119e";
const WEATHER_ORACLE =
"0x50fe253f3913d4c120f1cc96a69db10b9bebed01a4fcb4e3957336f33d9fa4c3";
const MODULE_NAME = "weather_oracle";
const FUNCTION_NAME = "roll_dice_nft";
const GEO_NAME_ID_SZ = 1795566;
const SUI_CLIENT = new SuiClient({ url: FULLNODE_URL });
(async () => {
let tx = new Transaction();
tx.moveCall({
target: `${PACKAGE_ID}::${MODULE_NAME}::${FUNCTION_NAME}`,
arguments: [tx.object(WEATHER_ORACLE), tx.pure.u32(Number(GEO_NAME_ID_SZ))],
});
try {
const result = await SUI_CLIENT.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
options: {
showEvents: true,
},
});
console.log(
`signAndExecuteTransactionBlock result: ${JSON.stringify(
result,
null,
2
)}`
);
} catch (e) {
console.error(e);
}
})();
- 执行输出
可见得到的骰子点数为:5。
$ ts-node client_weather_oracle.ts
signAndExecuteTransactionBlock result: {
"digest": "E9MYantGeDx4XaWDhpBzCMELsLm3WeB5uq2SqGHfiaWb",
"events": [
{
"id": {
"txDigest": "E9MYantGeDx4XaWDhpBzCMELsLm3WeB5uq2SqGHfiaWb",
"eventSeq": "0"
},
"packageId": "0xb5b69526e8c37cc195b4ca881c60a897194ffd11b8adb3638b5909fbdb44119e",
"transactionModule": "weather_oracle",
"sender": "0xa244617bc05e4122fb825d3b9c63dbad96dd06fae8183c2f03027b1feff12028",
"type": "0xb5b69526e8c37cc195b4ca881c60a897194ffd11b8adb3638b5909fbdb44119e::weather_oracle::DiceEvent",
"parsedJson": {
"value": 5
},
"bcs": "8QwQj"
}
],
"confirmedLocalExecution": false
}
更多资料
https://docs.sui.io/guides/developer/app-examples/weather-oracle
https://blog.sui.io/sui-weather-oracle/