Skip to main content

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}