ic_canister_kit/token/ledger.rs
1//! icp 类的账本罐子接口
2
3use candid::CandidType;
4use serde::{Deserialize, Serialize};
5
6use crate::{
7 canister::{call::call_canister, types::CanisterCallResult},
8 identity::{AccountIdentifier, CanisterId, Subaccount},
9};
10
11// Ledger 标准
12// name : () -> (Name) query;
13// symbol : () -> (Symbol) query;
14// decimals : () -> (Decimals) query;
15// account_balance : (BinaryAccountBalanceArgs) -> (Tokens) query;
16// transfer_fee : (record {}) -> (TransferFee) query;
17// transfer : (TransferArgs) -> (Result_1);
18// 下面的接口应该用不到
19// archives : () -> (Archives) query;
20// query_blocks : (GetBlocksArgs) -> (QueryBlocksResponse) query;
21// query_encoded_blocks : (GetBlocksArgs) -> (QueryEncodedBlocksResponse) query;
22
23// =================== 账本方法 ===================
24
25// https://dashboard.internetcomputer.org/canister/ryjl3-tyaaa-aaaaa-aaaba-cai
26
27// ============== 查询名称 ==============
28// name : () -> (Name) query;
29// type Name = record { name : text };
30
31/// 账本名称
32#[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
33pub struct LedgerName {
34 name: String,
35}
36
37/// 查询名称
38#[allow(unused)]
39pub async fn ledger_name(canister_id: CanisterId) -> CanisterCallResult<LedgerName> {
40 call_canister::<_, LedgerName>(canister_id, "name", ()).await
41}
42/// 查询名称
43#[allow(unused)]
44pub async fn ledger_name_by(canister_id: CanisterId) -> CanisterCallResult<String> {
45 ledger_name(canister_id).await.map(|n| n.name)
46}
47
48// ============== 查询符号 ==============
49// symbol : () -> (Symbol) query;
50// type Symbol = record { symbol : text };
51
52/// symbol
53#[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
54pub struct LedgerSymbol {
55 symbol: String,
56}
57/// 查询 symbol
58#[allow(unused)]
59pub async fn ledger_symbol(canister_id: CanisterId) -> CanisterCallResult<LedgerSymbol> {
60 call_canister::<_, LedgerSymbol>(canister_id, "symbol", ()).await
61}
62/// 查询 symbol
63#[allow(unused)]
64pub async fn ledger_symbol_by(canister_id: CanisterId) -> CanisterCallResult<String> {
65 ledger_symbol(canister_id).await.map(|s| s.symbol)
66}
67
68// ============== 查询精度 ==============
69// decimals : () -> (Decimals) query;
70// type Decimals = record { decimals : nat32 };
71
72/// 精度
73#[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
74pub struct LedgerDecimals {
75 decimals: u32,
76}
77/// 查询精度
78#[allow(unused)]
79pub async fn ledger_decimals(canister_id: CanisterId) -> CanisterCallResult<LedgerDecimals> {
80 call_canister::<_, LedgerDecimals>(canister_id, "decimals", ()).await
81}
82/// 查询精度
83#[allow(unused)]
84pub async fn ledger_decimals_by(canister_id: CanisterId) -> CanisterCallResult<u32> {
85 ledger_decimals(canister_id).await.map(|d| d.decimals)
86}
87
88// ============== 查询余额 ==============
89// account_balance : (BinaryAccountBalanceArgs) -> (Tokens) query;
90// type BinaryAccountBalanceArgs = record { account : vec nat8 };
91// type Tokens = record { e8s : nat64 };
92
93/// 查询余额参数
94pub type LedgerBinaryAccountBalanceArgs = ic_ledger_types::AccountBalanceArgs;
95// #[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
96// pub struct LedgerBinaryAccountBalanceArgs {
97// /// 账户识别
98// pub account: LedgerAccountIdentifier,
99// }
100/// 余额
101pub type LedgerTokens = ic_ledger_types::Tokens;
102// #[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
103// pub struct LedgerTokens {
104// /// ICP 接口指定 8 位精度的数值
105// pub e8s: u64,
106// }
107/// 查询余额
108#[allow(unused)]
109pub async fn ledger_account_balance(
110 canister_id: CanisterId,
111 args: LedgerBinaryAccountBalanceArgs,
112) -> CanisterCallResult<LedgerTokens> {
113 call_canister::<_, LedgerTokens>(canister_id, "account_balance", (args,)).await
114}
115/// 查询余额
116#[allow(unused)]
117pub async fn ledger_account_balance_by(
118 canister_id: CanisterId,
119 account: LedgerAccountIdentifier,
120) -> CanisterCallResult<u64> {
121 ledger_account_balance(canister_id, LedgerBinaryAccountBalanceArgs { account })
122 .await
123 .map(|b| b.e8s())
124}
125
126// ============== 查询转账费用 ==============
127// transfer_fee : (record {}) -> (TransferFee) query;
128// type TransferFee = record { transfer_fee : Tokens };
129
130/// 查询转账费用参数
131#[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
132pub struct LedgerTransferFeeArg {}
133
134/// 转账费用
135#[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
136pub struct LedgerTransferFee {
137 /// The fee to pay to perform a transfer
138 pub transfer_fee: LedgerTokens,
139}
140/// 查询转账费用
141#[allow(unused)]
142pub async fn ledger_transfer_fee(
143 canister_id: CanisterId,
144 // args: LedgerTransferFeeArg,
145) -> CanisterCallResult<LedgerTransferFee> {
146 call_canister::<_, LedgerTransferFee>(
147 canister_id,
148 "transfer_fee",
149 // (args,),
150 (LedgerTransferFeeArg {},),
151 )
152 .await
153}
154/// 查询转账费用, 简化参数
155#[allow(unused)]
156pub async fn ledger_transfer_fee_by(canister_id: CanisterId) -> CanisterCallResult<u64> {
157 ledger_transfer_fee(
158 canister_id,
159 // LedgerTransferFeeArg {}
160 )
161 .await
162 .map(|f| f.transfer_fee.e8s())
163}
164
165// ============== 转账 ==============
166// transfer : (TransferArgs) -> (Result_1);
167// type TimeStamp = record { timestamp_nanos : nat64 };
168// type Tokens = record { e8s : nat64 };
169// type TransferArgs = record {
170// to : vec nat8;
171// fee : Tokens;
172// memo : nat64;
173// from_subaccount : opt vec nat8;
174// created_at_time : opt TimeStamp;
175// amount : Tokens;
176// };
177// type Result_1 = variant { Ok : nat64; Err : TransferError_1 };
178// type TransferError_1 = variant {
179// TxTooOld : record { allowed_window_nanos : nat64 };
180// BadFee : record { expected_fee : Tokens };
181// TxDuplicate : record { duplicate_of : nat64 };
182// TxCreatedInFuture;
183// InsufficientFunds : record { balance : Tokens };
184// };
185
186/// 转账标识
187pub type LedgerMemo = u64; // 转账需要记录的标识码
188/// 账本时间戳
189#[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
190pub struct LedgerTimestamp {
191 /// 时间戳, 纳秒
192 pub timestamp_nanos: u64,
193}
194/// 账户 ID 是长度为 32 的byte数组
195/// 前 4 位是大端法编码的后面 28 位数字的 CRC32 校验码
196/// The first 4 bytes is big-endian encoding of a CRC32 checksum of the last 28 bytes.
197// pub type LedgerAccountIdentifier = Vec<u8>;
198pub type LedgerAccountIdentifier = AccountIdentifier; // ! 修改为安全的参数
199
200/// 子账户是任意长度为 32 的byte数组
201/// 使用子账户机制, 让一个用户 principal 控制大量的账本账户
202/// Ledger uses subaccounts to compute the source address, which enables one
203/// principal to control multiple ledger accounts.
204// pub type LedgerSubaccount = Vec<u8>;
205pub type LedgerSubaccount = Subaccount; // ! 修改为安全的参数
206
207/// 转账参数
208pub type LedgerTransferArgs = ic_ledger_types::TransferArgs;
209// #[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
210// pub struct LedgerTransferArgs {
211// /// 调用者指定的使用的子账户地址
212// /// 如果没有, 则默认全 0 的子账户
213// /// The subaccount from which the caller wants to transfer funds.
214// /// If null, the ledger uses the default (all zeros) subaccount to compute the source address.
215// pub from_subaccount: Option<LedgerSubaccount>,
216// /// 目标地址, 长度为 32 的byte数组, 转账成功, 目标地址的余额会增加 amount 的数量
217// /// The destination account. If the transfer is successful, the balance of this address increases by `amount`.
218// pub to: LedgerAccountIdentifier,
219// /// 想要转给目标地址的数量
220// pub amount: LedgerTokens,
221// /// 调用者必须支付的交易费, 必须是 10000 e8s
222// /// The amount that the caller pays for the transaction. Must be 10000 e8s.
223// pub fee: LedgerTokens,
224// /// 交易标识码 u64的数字
225// pub memo: LedgerMemo,
226// /// 请求的时间节点, 如果是空, 则默认 IC 系统当前时间
227// /// The point in time when the caller created this request. If null, the ledger uses current IC time as the timestamp.
228// pub created_at_time: Option<LedgerTimestamp>,
229// }
230
231/// 转账成功后返回的交易高度
232pub type LedgerBlockIndex = u64;
233
234/// 转账可能出现的错误
235pub type LedgerTransferError = ic_ledger_types::TransferError;
236// #[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
237// pub enum LedgerTransferError {
238// /// 手续费不正确
239// /// The fee that the caller specified in the transfer request was not the one that ledger expects.
240// /// The caller can change the transfer fee to the `expected_fee` and retry the request.
241// BadFee {
242// /// 期望的手续费
243// expected_fee: LedgerTokens,
244// },
245// /// 余额不足
246// /// The account specified by the caller doesn't have enough funds.
247// InsufficientFunds {
248// /// 余额不足
249// balance: LedgerTokens,
250// },
251// /// 交易过期了, 请求时间太早了, 距离 IC 系统当前时间 24 小时内的请求可以被接受
252// /// The request is too old.
253// /// The ledger only accepts requests created within 24 hours window.
254// /// This is a non-recoverable error.
255// TxTooOld {
256// /// 允许的时间窗口
257// allowed_window_nanos: u64,
258// },
259// /// 未来的交易, 指定交易时间在未来
260// /// The caller specified `created_at_time` that is too far in future.
261// /// The caller can retry the request later.
262// TxCreatedInFuture,
263// /// 重复交易 // ! 猜测是通过交易请求时间判断是否重复的
264// /// The ledger has already executed the request.
265// /// `duplicate_of` field is equal to the index of the block containing the original transaction.
266// TxDuplicate {
267// /// 重复的交易
268// duplicate_of: LedgerBlockIndex,
269// },
270// }
271/// 转账结果
272pub type LedgerTransferResult = Result<LedgerBlockIndex, LedgerTransferError>;
273
274/// 进行转账
275#[allow(unused)]
276pub async fn ledger_transfer(
277 canister_id: CanisterId,
278 args: LedgerTransferArgs,
279) -> CanisterCallResult<LedgerTransferResult> {
280 call_canister::<_, LedgerTransferResult>(canister_id, "transfer", (args,)).await
281}
282/// 进行转账
283#[allow(unused)]
284pub async fn ledger_transfer_by(
285 canister_id: CanisterId,
286 to: LedgerAccountIdentifier,
287 amount: u64,
288 fee: u64,
289 memo: u64,
290) -> CanisterCallResult<LedgerTransferResult> {
291 ledger_transfer(
292 canister_id,
293 LedgerTransferArgs {
294 from_subaccount: None,
295 to,
296 amount: LedgerTokens::from_e8s(amount),
297 fee: LedgerTokens::from_e8s(fee),
298 memo: ic_ledger_types::Memo(memo),
299 created_at_time: None,
300 },
301 )
302 .await
303}