From 8baa2484be2ef02fdc6d9002e865cc3da9c40f10 Mon Sep 17 00:00:00 2001 From: Eric Swanson Date: Wed, 6 Sep 2023 16:11:21 -0700 Subject: [PATCH 01/13] feat: SDK-1163 dfx cycles transfer (top up) --- CHANGELOG.md | 3 +- Cargo.lock | 1 + docs/cli-reference/dfx-cycles.md | 8 +- e2e/tests-dfx/cycles-ledger.bash | 139 +++++++++++++++++++- src/dfx/Cargo.toml | 1 + src/dfx/src/commands/cycles/transfer.rs | 33 ++++- src/dfx/src/lib/cycles_ledger_types/mod.rs | 1 + src/dfx/src/lib/cycles_ledger_types/send.rs | 50 +++++++ src/dfx/src/lib/mod.rs | 1 + src/dfx/src/lib/operations/cycles_ledger.rs | 64 +++++++++ 10 files changed, 295 insertions(+), 6 deletions(-) create mode 100644 src/dfx/src/lib/cycles_ledger_types/mod.rs create mode 100644 src/dfx/src/lib/cycles_ledger_types/send.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index f065842eb8..aa6c401b50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,10 @@ This won't work on mainnet yet, but can work locally after installing the cycles ledger. -Added the following subcommands: +Added the following subcommands and variants: - `dfx cycles balance` - `dfx cycles transfer --to-owner ` (transfer from one account to another account) + - `dfx cycles transfer --top-up ` (transfer from an account to a canister) ## Dependencies diff --git a/Cargo.lock b/Cargo.lock index 2244eed2a5..7936f84b47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1361,6 +1361,7 @@ dependencies = [ "hyper-rustls", "ic-agent", "ic-asset", + "ic-cdk", "ic-identity-hsm", "ic-utils 0.29.0", "ic-wasm", diff --git a/docs/cli-reference/dfx-cycles.md b/docs/cli-reference/dfx-cycles.md index 7b22818345..872616b187 100644 --- a/docs/cli-reference/dfx-cycles.md +++ b/docs/cli-reference/dfx-cycles.md @@ -68,7 +68,7 @@ dfx cycles balance --owner raxcz-bidhr-evrzj-qyivt-nht5a-eltcc-24qfc-o6cvi-hfw7j ## dfx cycles transfer -Use the `dfx cycles transfer` command to transfer cycles from your account to another account. +Use the `dfx cycles transfer` command to transfer cycles from your account to another account. This can be to the account of another owner's principal, or to a canister. ### Basic usage @@ -90,6 +90,7 @@ You can specify the following options for the `dfx cycles transfer` command. | Option | Description | |----------------------------------|--------------------------------------------------------------------| +| `--top-up ` | The canister to which you want to transfer cycles. | | `--to-owner ` | The principal of the account to which you want to transfer cycles. | | `--to-subaccount ` | The subaccount to which you want to transfer cycles. | | `--from-subaccount ` | The subaccount from which you want to transfer cycles. | @@ -111,3 +112,8 @@ Transfer from a subaccount: dfx cycles transfer 1000000000 --from-subaccount 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f --to-owner raxcz-bidhr-evrzj-qyivt-nht5a-eltcc-24qfc-o6cvi-hfw7j-dcecz-kae --network ic ``` +Transfer to (top up) a canister: + +``` bash +dfx cycles transfer 1000000000 --top-up bkyz2-fmaaa-aaaaa-qaaaq-cai --network ic +``` diff --git a/e2e/tests-dfx/cycles-ledger.bash b/e2e/tests-dfx/cycles-ledger.bash index 3bfe2316d3..53ffea14ed 100644 --- a/e2e/tests-dfx/cycles-ledger.bash +++ b/e2e/tests-dfx/cycles-ledger.bash @@ -25,6 +25,10 @@ teardown() { standard_teardown } +add_cycles_ledger_canisters_to_project() { + jq -s '.[0] * .[1]' ../dfx.json dfx.json | sponge dfx.json +} + current_time_nanoseconds() { echo "$(date +%s)"000000000 } @@ -103,7 +107,7 @@ current_time_nanoseconds() { assert_eq "2.900 TC (trillion cycles)." } -@test "transfer" { +@test "transfer to owner" { ALICE=$(dfx identity get-principal --identity alice) ALICE_SUBACCT1="000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" ALICE_SUBACCT1_CANDID="\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" @@ -240,6 +244,135 @@ current_time_nanoseconds() { assert_eq "300000 cycles." } +@test "transfer top up canister principal check" { + BOB=$(dfx identity get-principal --identity bob) + + assert_command dfx deploy cycles-ledger + + assert_command_fail dfx cycles transfer --top-up "$BOB" 600000 --identity alice --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" + assert_contains "Invalid receiver: $BOB. Make sure the receiver is a canister." +} + +@test "transfer top up canister basic" { + dfx_new + add_cycles_ledger_canisters_to_project + install_cycles_ledger_canisters + + BOB=$(dfx identity get-principal --identity bob) + BOB_SUBACCT1="7C7B7A030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" + BOB_SUBACCT1_CANDID="\7C\7B\7A\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" + BOB_SUBACCT2="6C6B6A030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" + BOB_SUBACCT2_CANDID="\6C\6B\6A\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" + + assert_command dfx deploy cycles-ledger + assert_command dfx deploy cycles-depositor --argument "(record {ledger_id = principal \"$(dfx canister id cycles-ledger)\"})" --with-cycles 10000000000000 + + assert_command dfx deploy + + assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$BOB\";};cycles = 2_400_000_000_000;})" --identity cycle-giver + assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$BOB\"; subaccount = opt blob \"$BOB_SUBACCT1_CANDID\"};cycles = 2_600_000_000_000;})" --identity cycle-giver + assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$BOB\"; subaccount = opt blob \"$BOB_SUBACCT2_CANDID\"};cycles = 2_700_000_000_000;})" --identity cycle-giver + + # account to canister + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob + assert_eq "2400000000000 cycles." + assert_command dfx canister status e2e_project_backend + assert_contains "Balance: 3_100_000_000_000 Cycles" + + assert_command dfx cycles transfer --top-up "$(dfx canister id e2e_project_backend)" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob + + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob + assert_eq "2399899900000 cycles." + assert_command dfx canister status e2e_project_backend + assert_contains "Balance: 3_100_000_100_000 Cycles" + + # subaccount to canister + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT1" + assert_eq "2600000000000 cycles." + assert_command dfx canister status e2e_project_backend + assert_contains "Balance: 3_100_000_100_000 Cycles" + + assert_command dfx cycles transfer --top-up "$(dfx canister id e2e_project_backend)" 300000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob --from-subaccount "$BOB_SUBACCT1" + + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT1" + assert_eq "2599899700000 cycles." + assert_command dfx canister status e2e_project_backend + assert_contains "Balance: 3_100_000_400_000 Cycles" + + # subaccount to canister - by canister name + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT2" + assert_eq "2700000000000 cycles." + assert_command dfx canister status e2e_project_backend + assert_contains "Balance: 3_100_000_400_000 Cycles" + + assert_command dfx cycles transfer --top-up e2e_project_backend 600000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob --from-subaccount "$BOB_SUBACCT2" + + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT2" + assert_eq "2699899400000 cycles." + assert_command dfx canister status e2e_project_backend + assert_contains "Balance: 3_100_001_000_000 Cycles" +} + +@test "transfer top up canister deduplication" { + dfx_new + add_cycles_ledger_canisters_to_project + install_cycles_ledger_canisters + + BOB=$(dfx identity get-principal --identity bob) + BOB_SUBACCT1="7C7B7A030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" + BOB_SUBACCT1_CANDID="\7C\7B\7A\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" + BOB_SUBACCT2="6C6B6A030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" + BOB_SUBACCT2_CANDID="\6C\6B\6A\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" + + assert_command dfx deploy cycles-ledger + assert_command dfx deploy cycles-depositor --argument "(record {ledger_id = principal \"$(dfx canister id cycles-ledger)\"})" --with-cycles 10000000000000 + + assert_command dfx deploy + + assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$BOB\";};cycles = 2_400_000_000_000;})" --identity cycle-giver + assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$BOB\"; subaccount = opt blob \"$BOB_SUBACCT1_CANDID\"};cycles = 2_600_000_000_000;})" --identity cycle-giver + assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$BOB\"; subaccount = opt blob \"$BOB_SUBACCT2_CANDID\"};cycles = 2_700_000_000_000;})" --identity cycle-giver + + # account to canister + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob + assert_eq "2400000000000 cycles." + assert_command dfx canister status e2e_project_backend + assert_contains "Balance: 3_100_000_000_000 Cycles" + + t=$(current_time_nanoseconds) + assert_command dfx cycles transfer --top-up "$(dfx canister id e2e_project_backend)" --memo 1 --created-at-time "$t" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob + assert_eq "Transfer sent at block index 3" + + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob + assert_eq "2399899900000 cycles." + assert_command dfx canister status e2e_project_backend + assert_contains "Balance: 3_100_000_100_000 Cycles" + + # same memo and created-at-time: dupe + assert_command dfx cycles transfer --top-up "$(dfx canister id e2e_project_backend)" --memo 1 --created-at-time "$t" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob + assert_contains "transaction is a duplicate of another transaction in block 3" + assert_contains "Transfer sent at block index 3" + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob + assert_eq "2399899900000 cycles." + + # same memo, different created-at-time: not dupe + assert_command dfx cycles transfer --top-up "$(dfx canister id e2e_project_backend)" --memo 1 --created-at-time $((t+1)) 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob + assert_eq "Transfer sent at block index 4" + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob + assert_eq "2399799800000 cycles." + + # different memo, same created-at-time: not dupe + assert_command dfx cycles transfer --top-up "$(dfx canister id e2e_project_backend)" --memo 2 --created-at-time "$t" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob + # actual result: + assert_contains "transaction is a duplicate of another transaction in block 3" + assert_contains "Transfer sent at block index 3" + # expected result: + # assert_eq "Transfer sent at block index 5" + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob + assert_eq "2399799800000 cycles." # expect something else + +} + @test "howto" { # This is the equivalent of https://www.notion.so/dfinityorg/How-to-install-and-test-the-cycles-ledger-521c9f3c410f4a438514a03e35464299 ALICE=$(dfx identity get-principal --identity alice) @@ -281,8 +414,8 @@ current_time_nanoseconds() { assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob --precise assert_eq "100000 cycles." - assert_command dfx canister call cycles-ledger send "(record {amount = 100_000;to = principal \"$(dfx canister id cycles-depositor)\"})" --identity alice - assert_eq "(variant { Ok = 2 : nat })" + assert_command dfx cycles transfer 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --top-up "$(dfx canister id cycles-depositor)" + assert_eq "Transfer sent at block index 2" assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --precise assert_eq "299800000 cycles." diff --git a/src/dfx/Cargo.toml b/src/dfx/Cargo.toml index fbf212e727..11c755ce20 100644 --- a/src/dfx/Cargo.toml +++ b/src/dfx/Cargo.toml @@ -63,6 +63,7 @@ humantime.workspace = true hyper-rustls = { version = "0.24.1", features = ["webpki-roots", "http2"] } ic-agent = { workspace = true, features = ["reqwest"] } ic-asset.workspace = true +ic-cdk.workspace = true ic-identity-hsm = { workspace = true } ic-utils = { workspace = true } ic-wasm = "0.4.0" diff --git a/src/dfx/src/commands/cycles/transfer.rs b/src/dfx/src/commands/cycles/transfer.rs index 384ebed8d6..e70da940f6 100644 --- a/src/dfx/src/commands/cycles/transfer.rs +++ b/src/dfx/src/commands/cycles/transfer.rs @@ -31,13 +31,17 @@ pub struct TransferOpts { #[arg(long, requires("to_owner"))] to_subaccount: Option, + /// Canister to top up. + #[arg(long, group = "target", name = "CANISTER_ID")] + top_up: Option, + /// Transaction timestamp, in nanoseconds, for use in controlling transaction-deduplication, default is system-time. /// https://internetcomputer.org/docs/current/developer-docs/integrations/icrc-1/#transaction-deduplication- #[arg(long)] created_at_time: Option, /// Transfer fee. - #[arg(long, value_parser = cycle_amount_parser)] + #[arg(long, value_parser = cycle_amount_parser, requires("to_owner"))] fee: Option, /// Memo. @@ -86,6 +90,25 @@ pub async fn exec(env: &dyn Environment, opts: TransferOpts) -> DfxResult { created_at_time ) })? + } else if let Some(top_up) = opts.top_up { + let to = get_canister_id(env, &top_up)?; + let from_subaccount = opts.from_subaccount.map(|x| x.0); + cycles_ledger::send( + agent, + to, + amount, + created_at_time, + opts.memo, + from_subaccount, + opts.cycles_ledger_canister_id, + ) + .await + .with_context(|| { + format!( + "If you retry this operation, use --created-at-time {}", + created_at_time + ) + })? } else { unreachable!(); }; @@ -94,3 +117,11 @@ pub async fn exec(env: &dyn Environment, opts: TransferOpts) -> DfxResult { Ok(()) } + +fn get_canister_id(env: &dyn Environment, s: &str) -> DfxResult { + let principal = Principal::from_text(s).or_else(|_| { + env.get_canister_id_store() + .and_then(|canister_id_store| canister_id_store.get(s)) + })?; + Ok(principal) +} diff --git a/src/dfx/src/lib/cycles_ledger_types/mod.rs b/src/dfx/src/lib/cycles_ledger_types/mod.rs new file mode 100644 index 0000000000..5c9ff69435 --- /dev/null +++ b/src/dfx/src/lib/cycles_ledger_types/mod.rs @@ -0,0 +1 @@ +pub mod send; diff --git a/src/dfx/src/lib/cycles_ledger_types/send.rs b/src/dfx/src/lib/cycles_ledger_types/send.rs new file mode 100644 index 0000000000..477c4c356a --- /dev/null +++ b/src/dfx/src/lib/cycles_ledger_types/send.rs @@ -0,0 +1,50 @@ +// Copied from https://github.com/dfinity/cycles-ledger/blob/main/cycles-ledger/src/endpoints.rs +use candid::{CandidType, Nat, Principal}; +use ic_cdk::api::call::RejectionCode; +use icrc_ledger_types::icrc1::account::Subaccount; +use icrc_ledger_types::icrc1::transfer::{BlockIndex, Memo}; +use serde::Deserialize; + +pub type NumCycles = Nat; + +#[derive(CandidType, Deserialize, Clone, Debug, PartialEq, Eq)] +pub struct SendArgs { + #[serde(default)] + pub from_subaccount: Option, + pub to: Principal, + #[serde(default)] + pub created_at_time: Option, + #[serde(default)] + pub memo: Option, + pub amount: NumCycles, +} + +#[derive(CandidType, Deserialize, Clone, Debug, PartialEq, Eq)] +pub enum SendError { + BadFee { + expected_fee: NumCycles, + }, + InsufficientFunds { + balance: NumCycles, + }, + TooOld, + CreatedInFuture { + ledger_time: u64, + }, + TemporarilyUnavailable, + Duplicate { + duplicate_of: BlockIndex, + }, + FailedToSend { + fee_block: Option, + rejection_code: RejectionCode, + rejection_reason: String, + }, + GenericError { + error_code: Nat, + message: String, + }, + InvalidReceiver { + receiver: Principal, + }, +} diff --git a/src/dfx/src/lib/mod.rs b/src/dfx/src/lib/mod.rs index e53ae0828d..2de6df22db 100644 --- a/src/dfx/src/lib/mod.rs +++ b/src/dfx/src/lib/mod.rs @@ -1,6 +1,7 @@ pub mod agent; pub mod builders; pub mod canister_info; +pub mod cycles_ledger_types; pub mod deps; pub mod diagnosis; pub mod dist; diff --git a/src/dfx/src/lib/operations/cycles_ledger.rs b/src/dfx/src/lib/operations/cycles_ledger.rs index 297c8c98e8..59a99a6580 100644 --- a/src/dfx/src/lib/operations/cycles_ledger.rs +++ b/src/dfx/src/lib/operations/cycles_ledger.rs @@ -1,3 +1,5 @@ +use crate::lib::cycles_ledger_types; +use crate::lib::cycles_ledger_types::send::SendError; use crate::lib::error::DfxResult; use crate::lib::retryable::retryable; use anyhow::anyhow; @@ -12,6 +14,7 @@ use icrc_ledger_types::icrc1::transfer::{BlockIndex, TransferError}; const ICRC1_BALANCE_OF_METHOD: &str = "icrc1_balance_of"; const ICRC1_TRANSFER_METHOD: &str = "icrc1_transfer"; +const SEND_METHOD: &str = "send"; pub async fn balance( agent: &Agent, @@ -105,3 +108,64 @@ pub async fn transfer( Ok(block_index) } + +pub async fn send( + agent: &Agent, + to: Principal, + amount: u128, + created_at_time: u64, + memo: Option, + from_subaccount: Option, + cycles_ledger_canister_id: Principal, +) -> DfxResult { + let canister = Canister::builder() + .with_agent(agent) + .with_canister_id(cycles_ledger_canister_id) + .build()?; + + let retry_policy = ExponentialBackoff::default(); + let block_index: BlockIndex = retry(retry_policy, || async { + let arg = cycles_ledger_types::send::SendArgs { + from_subaccount, + to, + created_at_time: Some(created_at_time), + memo: memo.map(|v| v.into()), + amount: Nat::from(amount), + }; + match canister + .update(SEND_METHOD) + .with_arg(arg) + .build() + .map(|result: (Result,)| (result.0,)) + .call_and_wait() + .await + .map(|(result,)| result) + { + Ok(Ok(block_index)) => Ok(block_index), + Ok(Err(SendError::Duplicate { duplicate_of })) => { + println!( + "transaction is a duplicate of another transaction in block {}", + duplicate_of + ); + Ok(duplicate_of) + } + Ok(Err(SendError::InvalidReceiver { receiver })) => { + Err(backoff::Error::permanent(anyhow!( + "Invalid receiver: {}. Make sure the receiver is a canister.", + receiver + ))) + } + Ok(Err(send_err)) => Err(backoff::Error::permanent(anyhow!( + "send error: {:?}", + send_err + ))), + Err(agent_err) if retryable(&agent_err) => { + Err(backoff::Error::transient(anyhow!(agent_err))) + } + Err(agent_err) => Err(backoff::Error::permanent(anyhow!(agent_err))), + } + }) + .await?; + + Ok(block_index) +} From 1aca8a61672743073d25b5b5465d6afb9fb28f8c Mon Sep 17 00:00:00 2001 From: Eric Swanson Date: Fri, 13 Oct 2023 15:26:38 -0700 Subject: [PATCH 02/13] Update with remediation ticket for cycles-ledger --- e2e/tests-dfx/cycles-ledger.bash | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/e2e/tests-dfx/cycles-ledger.bash b/e2e/tests-dfx/cycles-ledger.bash index 53ffea14ed..9a5d4dbfac 100644 --- a/e2e/tests-dfx/cycles-ledger.bash +++ b/e2e/tests-dfx/cycles-ledger.bash @@ -363,13 +363,19 @@ current_time_nanoseconds() { # different memo, same created-at-time: not dupe assert_command dfx cycles transfer --top-up "$(dfx canister id e2e_project_backend)" --memo 2 --created-at-time "$t" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob + # This is expected to be reported as a duplicate, but is not. + # See https://dfinity.atlassian.net/browse/SDK-1289 + # If this part of the test is failing, it probably means the functionality has been fixed in the cycles ledger canister. + + # expected result: + # assert_eq "Transfer sent at block index 5" + # actual result: assert_contains "transaction is a duplicate of another transaction in block 3" assert_contains "Transfer sent at block index 3" - # expected result: - # assert_eq "Transfer sent at block index 5" + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob - assert_eq "2399799800000 cycles." # expect something else + assert_eq "2399799800000 cycles." # expect this to be a different value than above, but it's not } From 086598a2698891d3f20a21683c0a066a64f4cd3a Mon Sep 17 00:00:00 2001 From: Eric Swanson Date: Mon, 16 Oct 2023 10:27:29 -0700 Subject: [PATCH 03/13] Disable memo field when sending to a canister Describe more about difference between sending to an account and sending to a canister. --- docs/cli-reference/dfx-cycles.md | 24 +++++++++++++-------- src/dfx/src/commands/cycles/transfer.rs | 3 +-- src/dfx/src/lib/cycles_ledger_types/send.rs | 4 +--- src/dfx/src/lib/operations/cycles_ledger.rs | 2 -- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/docs/cli-reference/dfx-cycles.md b/docs/cli-reference/dfx-cycles.md index 872616b187..c089b70878 100644 --- a/docs/cli-reference/dfx-cycles.md +++ b/docs/cli-reference/dfx-cycles.md @@ -68,7 +68,13 @@ dfx cycles balance --owner raxcz-bidhr-evrzj-qyivt-nht5a-eltcc-24qfc-o6cvi-hfw7j ## dfx cycles transfer -Use the `dfx cycles transfer` command to transfer cycles from your account to another account. This can be to the account of another owner's principal, or to a canister. +Use the `dfx cycles transfer` command to transfer cycles from your account to another account. + +The source of the transferred cycles is always the cycles ledger account associated with your identity's principal, or one of its subaccounts. + +The destination of the transferred cycles is one of the following: +- A cycles ledger account associated with another identity's principal, or one of its subaccounts. This mode uses the `--to-owner` and `--to-subaccount` options. +- A canister. This mode uses the `--top-up` option. ### Basic usage @@ -88,14 +94,14 @@ You must specify the following argument for the `dfx cycles transfer` command. You can specify the following options for the `dfx cycles transfer` command. -| Option | Description | -|----------------------------------|--------------------------------------------------------------------| -| `--top-up ` | The canister to which you want to transfer cycles. | -| `--to-owner ` | The principal of the account to which you want to transfer cycles. | -| `--to-subaccount ` | The subaccount to which you want to transfer cycles. | -| `--from-subaccount ` | The subaccount from which you want to transfer cycles. | -| `--fee ` | Specifies a transaction fee. | -| `--memo ` | Specifies a numeric memo for this transaction. | +| Option | Description | +|----------------------------------|----------------------------------------------------------------------------------------| +| `--top-up ` | The canister which you want to top up. | +| `--to-owner ` | The principal of the account to which you want to transfer cycles. | +| `--to-subaccount ` | The subaccount to which you want to transfer cycles. | +| `--from-subaccount ` | The subaccount from which you want to transfer cycles. | +| `--fee ` | Specifies a transaction fee. Can only passed in `--to-owner` mode. | +| `--memo ` | Specifies a numeric memo for this transaction. Can only be passed in `--to-owner mode. | | `--created-at-time ` | Specify the timestamp-nanoseconds for the `created_at_time` field on the transfer request. Useful for controlling transaction-de-duplication. https://internetcomputer.org/docs/current/developer-docs/integrations/icrc-1/#transaction-deduplication- | ### Examples diff --git a/src/dfx/src/commands/cycles/transfer.rs b/src/dfx/src/commands/cycles/transfer.rs index e70da940f6..96aceffcc4 100644 --- a/src/dfx/src/commands/cycles/transfer.rs +++ b/src/dfx/src/commands/cycles/transfer.rs @@ -45,7 +45,7 @@ pub struct TransferOpts { fee: Option, /// Memo. - #[arg(long)] + #[arg(long, requires("to_owner"))] memo: Option, /// Canister ID of the cycles ledger canister. @@ -98,7 +98,6 @@ pub async fn exec(env: &dyn Environment, opts: TransferOpts) -> DfxResult { to, amount, created_at_time, - opts.memo, from_subaccount, opts.cycles_ledger_canister_id, ) diff --git a/src/dfx/src/lib/cycles_ledger_types/send.rs b/src/dfx/src/lib/cycles_ledger_types/send.rs index 477c4c356a..20b51228f0 100644 --- a/src/dfx/src/lib/cycles_ledger_types/send.rs +++ b/src/dfx/src/lib/cycles_ledger_types/send.rs @@ -2,7 +2,7 @@ use candid::{CandidType, Nat, Principal}; use ic_cdk::api::call::RejectionCode; use icrc_ledger_types::icrc1::account::Subaccount; -use icrc_ledger_types::icrc1::transfer::{BlockIndex, Memo}; +use icrc_ledger_types::icrc1::transfer::BlockIndex; use serde::Deserialize; pub type NumCycles = Nat; @@ -14,8 +14,6 @@ pub struct SendArgs { pub to: Principal, #[serde(default)] pub created_at_time: Option, - #[serde(default)] - pub memo: Option, pub amount: NumCycles, } diff --git a/src/dfx/src/lib/operations/cycles_ledger.rs b/src/dfx/src/lib/operations/cycles_ledger.rs index 59a99a6580..883c4bf1a5 100644 --- a/src/dfx/src/lib/operations/cycles_ledger.rs +++ b/src/dfx/src/lib/operations/cycles_ledger.rs @@ -114,7 +114,6 @@ pub async fn send( to: Principal, amount: u128, created_at_time: u64, - memo: Option, from_subaccount: Option, cycles_ledger_canister_id: Principal, ) -> DfxResult { @@ -129,7 +128,6 @@ pub async fn send( from_subaccount, to, created_at_time: Some(created_at_time), - memo: memo.map(|v| v.into()), amount: Nat::from(amount), }; match canister From 18f2d4496db3f19b46e9f6dd3514ef20f6ecc70a Mon Sep 17 00:00:00 2001 From: Eric Swanson Date: Mon, 16 Oct 2023 12:30:21 -0700 Subject: [PATCH 04/13] command exclusions --- e2e/tests-dfx/cycles-ledger.bash | 27 +++++++++++-------------- src/dfx/src/commands/cycles/transfer.rs | 15 ++++++-------- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/e2e/tests-dfx/cycles-ledger.bash b/e2e/tests-dfx/cycles-ledger.bash index 9a5d4dbfac..2b67758bf8 100644 --- a/e2e/tests-dfx/cycles-ledger.bash +++ b/e2e/tests-dfx/cycles-ledger.bash @@ -348,35 +348,32 @@ current_time_nanoseconds() { assert_command dfx canister status e2e_project_backend assert_contains "Balance: 3_100_000_100_000 Cycles" - # same memo and created-at-time: dupe + # same created-at-time: dupe assert_command dfx cycles transfer --top-up "$(dfx canister id e2e_project_backend)" --memo 1 --created-at-time "$t" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob assert_contains "transaction is a duplicate of another transaction in block 3" assert_contains "Transfer sent at block index 3" assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob assert_eq "2399899900000 cycles." - # same memo, different created-at-time: not dupe + # different created-at-time: not dupe assert_command dfx cycles transfer --top-up "$(dfx canister id e2e_project_backend)" --memo 1 --created-at-time $((t+1)) 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob assert_eq "Transfer sent at block index 4" assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob assert_eq "2399799800000 cycles." +} - # different memo, same created-at-time: not dupe - assert_command dfx cycles transfer --top-up "$(dfx canister id e2e_project_backend)" --memo 2 --created-at-time "$t" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob - # This is expected to be reported as a duplicate, but is not. - # See https://dfinity.atlassian.net/browse/SDK-1289 - # If this part of the test is failing, it probably means the functionality has been fixed in the cycles ledger canister. - - # expected result: - # assert_eq "Transfer sent at block index 5" +@test "transfer top up canister cli exclusions" { + assert_command_fail dfx cycles transfer --top-up bkyz2-fmaaa-aaaaa-qaaaq-cai 100000 --to-owner raxcz-bidhr-evrzj-qyivt-nht5a-eltcc-24qfc-o6cvi-hfw7j-dcecz-kae --cycles-ledger-canister-id bkyz2-fmaaa-aaaaa-qaaaq-cai + assert_contains "error: the argument '--top-up ' cannot be used with '--to-owner '" - # actual result: - assert_contains "transaction is a duplicate of another transaction in block 3" - assert_contains "Transfer sent at block index 3" + assert_command_fail dfx cycles transfer --top-up bkyz2-fmaaa-aaaaa-qaaaq-cai 100000 --memo 4 --cycles-ledger-canister-id bkyz2-fmaaa-aaaaa-qaaaq-cai + assert_contains "error: the argument '--top-up ' cannot be used with '--memo '" - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob - assert_eq "2399799800000 cycles." # expect this to be a different value than above, but it's not + assert_command_fail dfx cycles transfer --top-up bkyz2-fmaaa-aaaaa-qaaaq-cai 100000 --fee 100000000 --cycles-ledger-canister-id bkyz2-fmaaa-aaaaa-qaaaq-cai --identity bob + assert_contains "error: the argument '--top-up ' cannot be used with '--fee '" + assert_command_fail dfx cycles transfer --top-up bkyz2-fmaaa-aaaaa-qaaaq-cai 100000 --to-subaccount "6C6B6A030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" --cycles-ledger-canister-id bkyz2-fmaaa-aaaaa-qaaaq-cai --identity bob + assert_contains "error: the argument '--top-up ' cannot be used with '--to-subaccount '" } @test "howto" { diff --git a/src/dfx/src/commands/cycles/transfer.rs b/src/dfx/src/commands/cycles/transfer.rs index 96aceffcc4..ce7061da15 100644 --- a/src/dfx/src/commands/cycles/transfer.rs +++ b/src/dfx/src/commands/cycles/transfer.rs @@ -6,14 +6,11 @@ use crate::lib::root_key::fetch_root_key_if_needed; use crate::util::clap::parsers::cycle_amount_parser; use anyhow::Context; use candid::Principal; -use clap::{ArgGroup, Parser}; +use clap::Parser; use std::time::{SystemTime, UNIX_EPOCH}; /// Transfer cycles to another principal. #[derive(Parser)] -#[clap( -group(ArgGroup::new("target").multiple(false).required(true)), -)] pub struct TransferOpts { /// The amount of cycles to send. #[arg(value_parser = cycle_amount_parser)] @@ -24,15 +21,15 @@ pub struct TransferOpts { from_subaccount: Option, /// Transfer cycles to this principal. - #[arg(long, group = "target")] + #[arg(long, conflicts_with("top_up"))] to_owner: Option, /// Transfer cycles to this subaccount. - #[arg(long, requires("to_owner"))] + #[arg(long, conflicts_with("top_up"))] to_subaccount: Option, /// Canister to top up. - #[arg(long, group = "target", name = "CANISTER_ID")] + #[arg(long, conflicts_with("to_owner"))] top_up: Option, /// Transaction timestamp, in nanoseconds, for use in controlling transaction-deduplication, default is system-time. @@ -41,11 +38,11 @@ pub struct TransferOpts { created_at_time: Option, /// Transfer fee. - #[arg(long, value_parser = cycle_amount_parser, requires("to_owner"))] + #[arg(long, value_parser = cycle_amount_parser, conflicts_with("top_up"))] fee: Option, /// Memo. - #[arg(long, requires("to_owner"))] + #[arg(long, conflicts_with("top_up"))] memo: Option, /// Canister ID of the cycles ledger canister. From a614eb6858ea5b4d323f7fb022b0c9509f1d92fb Mon Sep 17 00:00:00 2001 From: Eric Swanson Date: Mon, 16 Oct 2023 14:22:20 -0700 Subject: [PATCH 05/13] no memo with top up --- e2e/tests-dfx/cycles-ledger.bash | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/e2e/tests-dfx/cycles-ledger.bash b/e2e/tests-dfx/cycles-ledger.bash index 2b67758bf8..a680dbbc2e 100644 --- a/e2e/tests-dfx/cycles-ledger.bash +++ b/e2e/tests-dfx/cycles-ledger.bash @@ -340,7 +340,7 @@ current_time_nanoseconds() { assert_contains "Balance: 3_100_000_000_000 Cycles" t=$(current_time_nanoseconds) - assert_command dfx cycles transfer --top-up "$(dfx canister id e2e_project_backend)" --memo 1 --created-at-time "$t" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob + assert_command dfx cycles transfer --top-up "$(dfx canister id e2e_project_backend)" --created-at-time "$t" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob assert_eq "Transfer sent at block index 3" assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob @@ -349,14 +349,14 @@ current_time_nanoseconds() { assert_contains "Balance: 3_100_000_100_000 Cycles" # same created-at-time: dupe - assert_command dfx cycles transfer --top-up "$(dfx canister id e2e_project_backend)" --memo 1 --created-at-time "$t" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob + assert_command dfx cycles transfer --top-up "$(dfx canister id e2e_project_backend)" --created-at-time "$t" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob assert_contains "transaction is a duplicate of another transaction in block 3" assert_contains "Transfer sent at block index 3" assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob assert_eq "2399899900000 cycles." # different created-at-time: not dupe - assert_command dfx cycles transfer --top-up "$(dfx canister id e2e_project_backend)" --memo 1 --created-at-time $((t+1)) 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob + assert_command dfx cycles transfer --top-up "$(dfx canister id e2e_project_backend)" --created-at-time $((t+1)) 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob assert_eq "Transfer sent at block index 4" assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob assert_eq "2399799800000 cycles." From e6f086ba4e437fd64047def3b18ef8167ae9efad Mon Sep 17 00:00:00 2001 From: Eric Swanson Date: Tue, 17 Oct 2023 10:45:40 -0700 Subject: [PATCH 06/13] separate cycles transfer/top-up commands --- e2e/tests-dfx/cycles-ledger.bash | 40 ++++------ src/dfx/src/commands/cycles/mod.rs | 3 + src/dfx/src/commands/cycles/top_up.rs | 85 ++++++++++++++++++++ src/dfx/src/commands/cycles/transfer.rs | 86 ++++++--------------- src/dfx/src/lib/operations/cycles_ledger.rs | 3 +- 5 files changed, 125 insertions(+), 92 deletions(-) create mode 100644 src/dfx/src/commands/cycles/top_up.rs diff --git a/e2e/tests-dfx/cycles-ledger.bash b/e2e/tests-dfx/cycles-ledger.bash index a680dbbc2e..9c23ff061e 100644 --- a/e2e/tests-dfx/cycles-ledger.bash +++ b/e2e/tests-dfx/cycles-ledger.bash @@ -107,7 +107,7 @@ current_time_nanoseconds() { assert_eq "2.900 TC (trillion cycles)." } -@test "transfer to owner" { +@test "transfer" { ALICE=$(dfx identity get-principal --identity alice) ALICE_SUBACCT1="000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" ALICE_SUBACCT1_CANDID="\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" @@ -244,16 +244,16 @@ current_time_nanoseconds() { assert_eq "300000 cycles." } -@test "transfer top up canister principal check" { +@test "top up canister principal check" { BOB=$(dfx identity get-principal --identity bob) assert_command dfx deploy cycles-ledger - assert_command_fail dfx cycles transfer --top-up "$BOB" 600000 --identity alice --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" + assert_command_fail dfx cycles top-up "$BOB" 600000 --identity alice --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" assert_contains "Invalid receiver: $BOB. Make sure the receiver is a canister." } -@test "transfer top up canister basic" { +@test "top-up" { dfx_new add_cycles_ledger_canisters_to_project install_cycles_ledger_canisters @@ -279,7 +279,7 @@ current_time_nanoseconds() { assert_command dfx canister status e2e_project_backend assert_contains "Balance: 3_100_000_000_000 Cycles" - assert_command dfx cycles transfer --top-up "$(dfx canister id e2e_project_backend)" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob + assert_command dfx cycles top-up e2e_project_backend 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob assert_eq "2399899900000 cycles." @@ -292,20 +292,20 @@ current_time_nanoseconds() { assert_command dfx canister status e2e_project_backend assert_contains "Balance: 3_100_000_100_000 Cycles" - assert_command dfx cycles transfer --top-up "$(dfx canister id e2e_project_backend)" 300000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob --from-subaccount "$BOB_SUBACCT1" + assert_command dfx cycles top-up e2e_project_backend 300000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob --from-subaccount "$BOB_SUBACCT1" assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT1" assert_eq "2599899700000 cycles." assert_command dfx canister status e2e_project_backend assert_contains "Balance: 3_100_000_400_000 Cycles" - # subaccount to canister - by canister name + # subaccount to canister - by canister id assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT2" assert_eq "2700000000000 cycles." assert_command dfx canister status e2e_project_backend assert_contains "Balance: 3_100_000_400_000 Cycles" - assert_command dfx cycles transfer --top-up e2e_project_backend 600000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob --from-subaccount "$BOB_SUBACCT2" + assert_command dfx cycles top-up "$(dfx canister id e2e_project_backend)" 600000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob --from-subaccount "$BOB_SUBACCT2" assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT2" assert_eq "2699899400000 cycles." @@ -313,7 +313,7 @@ current_time_nanoseconds() { assert_contains "Balance: 3_100_001_000_000 Cycles" } -@test "transfer top up canister deduplication" { +@test "top-up deduplication" { dfx_new add_cycles_ledger_canisters_to_project install_cycles_ledger_canisters @@ -340,7 +340,7 @@ current_time_nanoseconds() { assert_contains "Balance: 3_100_000_000_000 Cycles" t=$(current_time_nanoseconds) - assert_command dfx cycles transfer --top-up "$(dfx canister id e2e_project_backend)" --created-at-time "$t" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob + assert_command dfx cycles top-up "$(dfx canister id e2e_project_backend)" --created-at-time "$t" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob assert_eq "Transfer sent at block index 3" assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob @@ -349,33 +349,19 @@ current_time_nanoseconds() { assert_contains "Balance: 3_100_000_100_000 Cycles" # same created-at-time: dupe - assert_command dfx cycles transfer --top-up "$(dfx canister id e2e_project_backend)" --created-at-time "$t" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob + assert_command dfx cycles top-up "$(dfx canister id e2e_project_backend)" --created-at-time "$t" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob assert_contains "transaction is a duplicate of another transaction in block 3" assert_contains "Transfer sent at block index 3" assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob assert_eq "2399899900000 cycles." # different created-at-time: not dupe - assert_command dfx cycles transfer --top-up "$(dfx canister id e2e_project_backend)" --created-at-time $((t+1)) 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob + assert_command dfx cycles top-up "$(dfx canister id e2e_project_backend)" --created-at-time $((t+1)) 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob assert_eq "Transfer sent at block index 4" assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob assert_eq "2399799800000 cycles." } -@test "transfer top up canister cli exclusions" { - assert_command_fail dfx cycles transfer --top-up bkyz2-fmaaa-aaaaa-qaaaq-cai 100000 --to-owner raxcz-bidhr-evrzj-qyivt-nht5a-eltcc-24qfc-o6cvi-hfw7j-dcecz-kae --cycles-ledger-canister-id bkyz2-fmaaa-aaaaa-qaaaq-cai - assert_contains "error: the argument '--top-up ' cannot be used with '--to-owner '" - - assert_command_fail dfx cycles transfer --top-up bkyz2-fmaaa-aaaaa-qaaaq-cai 100000 --memo 4 --cycles-ledger-canister-id bkyz2-fmaaa-aaaaa-qaaaq-cai - assert_contains "error: the argument '--top-up ' cannot be used with '--memo '" - - assert_command_fail dfx cycles transfer --top-up bkyz2-fmaaa-aaaaa-qaaaq-cai 100000 --fee 100000000 --cycles-ledger-canister-id bkyz2-fmaaa-aaaaa-qaaaq-cai --identity bob - assert_contains "error: the argument '--top-up ' cannot be used with '--fee '" - - assert_command_fail dfx cycles transfer --top-up bkyz2-fmaaa-aaaaa-qaaaq-cai 100000 --to-subaccount "6C6B6A030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" --cycles-ledger-canister-id bkyz2-fmaaa-aaaaa-qaaaq-cai --identity bob - assert_contains "error: the argument '--top-up ' cannot be used with '--to-subaccount '" -} - @test "howto" { # This is the equivalent of https://www.notion.so/dfinityorg/How-to-install-and-test-the-cycles-ledger-521c9f3c410f4a438514a03e35464299 ALICE=$(dfx identity get-principal --identity alice) @@ -417,7 +403,7 @@ current_time_nanoseconds() { assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob --precise assert_eq "100000 cycles." - assert_command dfx cycles transfer 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --top-up "$(dfx canister id cycles-depositor)" + assert_command dfx cycles top-up cycles-depositor 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice assert_eq "Transfer sent at block index 2" assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --precise diff --git a/src/dfx/src/commands/cycles/mod.rs b/src/dfx/src/commands/cycles/mod.rs index d42f2c7364..8e1b14228a 100644 --- a/src/dfx/src/commands/cycles/mod.rs +++ b/src/dfx/src/commands/cycles/mod.rs @@ -6,6 +6,7 @@ use clap::Parser; use tokio::runtime::Runtime; mod balance; +pub mod top_up; mod transfer; /// Helper commands to manage the user's cycles. @@ -22,6 +23,7 @@ pub struct CyclesOpts { #[derive(Parser)] enum SubCommand { Balance(balance::CyclesBalanceOpts), + TopUp(top_up::TopUpOpts), Transfer(transfer::TransferOpts), } @@ -31,6 +33,7 @@ pub fn exec(env: &dyn Environment, opts: CyclesOpts) -> DfxResult { runtime.block_on(async { match opts.subcmd { SubCommand::Balance(v) => balance::exec(&agent_env, v).await, + SubCommand::TopUp(v) => top_up::exec(&agent_env, v).await, SubCommand::Transfer(v) => transfer::exec(&agent_env, v).await, } }) diff --git a/src/dfx/src/commands/cycles/top_up.rs b/src/dfx/src/commands/cycles/top_up.rs new file mode 100644 index 0000000000..3d481e435b --- /dev/null +++ b/src/dfx/src/commands/cycles/top_up.rs @@ -0,0 +1,85 @@ +use crate::lib::environment::Environment; +use crate::lib::error::DfxResult; +use crate::lib::nns_types::account_identifier::Subaccount; +use crate::lib::operations::cycles_ledger; +use crate::lib::root_key::fetch_root_key_if_needed; +use crate::util::clap::parsers::cycle_amount_parser; +use anyhow::Context; +use candid::Principal; +use clap::Parser; +use std::time::{SystemTime, UNIX_EPOCH}; + +/// Send cycles to a canister. +#[derive(Parser)] +pub struct TopUpOpts { + /// Transfer cycles to this canister. + to: String, + + /// The amount of cycles to send. + #[arg(value_parser = cycle_amount_parser)] + amount: u128, + + /// Transfer cycles from this subaccount. + #[arg(long)] + from_subaccount: Option, + + /// Transfer cycles to this subaccount. + #[arg(long, conflicts_with("top_up"))] + to_subaccount: Option, + + /// Transaction timestamp, in nanoseconds, for use in controlling transaction-deduplication, default is system-time. + /// https://internetcomputer.org/docs/current/developer-docs/integrations/icrc-1/#transaction-deduplication- + #[arg(long)] + created_at_time: Option, + + /// Canister ID of the cycles ledger canister. + /// If not specified, the default cycles ledger canister ID will be used. + // todo: remove this. See https://dfinity.atlassian.net/browse/SDK-1262 + #[arg(long)] + cycles_ledger_canister_id: Principal, +} + +pub async fn exec(env: &dyn Environment, opts: TopUpOpts) -> DfxResult { + let agent = env.get_agent(); + + let amount = opts.amount; + + fetch_root_key_if_needed(env).await?; + + let created_at_time = opts.created_at_time.unwrap_or( + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() as u64, + ); + + let to = get_canister_id(env, &opts.to)?; + let from_subaccount = opts.from_subaccount.map(|x| x.0); + let block_index = cycles_ledger::send( + agent, + to, + amount, + created_at_time, + from_subaccount, + opts.cycles_ledger_canister_id, + ) + .await + .with_context(|| { + format!( + "If you retry this operation, use --created-at-time {}", + created_at_time + ) + })?; + + println!("Transfer sent at block index {block_index}"); + + Ok(()) +} + +fn get_canister_id(env: &dyn Environment, s: &str) -> DfxResult { + let principal = Principal::from_text(s).or_else(|_| { + env.get_canister_id_store() + .and_then(|canister_id_store| canister_id_store.get(s)) + })?; + Ok(principal) +} diff --git a/src/dfx/src/commands/cycles/transfer.rs b/src/dfx/src/commands/cycles/transfer.rs index ce7061da15..5c50cfa6a8 100644 --- a/src/dfx/src/commands/cycles/transfer.rs +++ b/src/dfx/src/commands/cycles/transfer.rs @@ -12,6 +12,9 @@ use std::time::{SystemTime, UNIX_EPOCH}; /// Transfer cycles to another principal. #[derive(Parser)] pub struct TransferOpts { + /// Transfer cycles to this principal. + to: Principal, + /// The amount of cycles to send. #[arg(value_parser = cycle_amount_parser)] amount: u128, @@ -20,29 +23,17 @@ pub struct TransferOpts { #[arg(long)] from_subaccount: Option, - /// Transfer cycles to this principal. - #[arg(long, conflicts_with("top_up"))] - to_owner: Option, - /// Transfer cycles to this subaccount. - #[arg(long, conflicts_with("top_up"))] + #[arg(long)] to_subaccount: Option, - /// Canister to top up. - #[arg(long, conflicts_with("to_owner"))] - top_up: Option, - /// Transaction timestamp, in nanoseconds, for use in controlling transaction-deduplication, default is system-time. /// https://internetcomputer.org/docs/current/developer-docs/integrations/icrc-1/#transaction-deduplication- #[arg(long)] created_at_time: Option, - /// Transfer fee. - #[arg(long, value_parser = cycle_amount_parser, conflicts_with("top_up"))] - fee: Option, - /// Memo. - #[arg(long, conflicts_with("top_up"))] + #[arg(long)] memo: Option, /// Canister ID of the cycles ledger canister. @@ -66,58 +57,27 @@ pub async fn exec(env: &dyn Environment, opts: TransferOpts) -> DfxResult { .as_nanos() as u64, ); - let block_index = if let Some(to_owner) = opts.to_owner { - let from_subaccount = opts.from_subaccount.map(|x| x.0); - let to_subaccount = opts.to_subaccount.map(|x| x.0); - cycles_ledger::transfer( - agent, - amount, - from_subaccount, - to_owner, - to_subaccount, - created_at_time, - opts.fee, - opts.memo, - opts.cycles_ledger_canister_id, - ) - .await - .with_context(|| { - format!( - "If you retry this operation, use --created-at-time {}", - created_at_time - ) - })? - } else if let Some(top_up) = opts.top_up { - let to = get_canister_id(env, &top_up)?; - let from_subaccount = opts.from_subaccount.map(|x| x.0); - cycles_ledger::send( - agent, - to, - amount, - created_at_time, - from_subaccount, - opts.cycles_ledger_canister_id, + let from_subaccount = opts.from_subaccount.map(|x| x.0); + let to_subaccount = opts.to_subaccount.map(|x| x.0); + let block_index = cycles_ledger::transfer( + agent, + amount, + from_subaccount, + opts.to, + to_subaccount, + created_at_time, + opts.memo, + opts.cycles_ledger_canister_id, + ) + .await + .with_context(|| { + format!( + "If you retry this operation, use --created-at-time {}", + created_at_time ) - .await - .with_context(|| { - format!( - "If you retry this operation, use --created-at-time {}", - created_at_time - ) - })? - } else { - unreachable!(); - }; + })?; println!("Transfer sent at block index {block_index}"); Ok(()) } - -fn get_canister_id(env: &dyn Environment, s: &str) -> DfxResult { - let principal = Principal::from_text(s).or_else(|_| { - env.get_canister_id_store() - .and_then(|canister_id_store| canister_id_store.get(s)) - })?; - Ok(principal) -} diff --git a/src/dfx/src/lib/operations/cycles_ledger.rs b/src/dfx/src/lib/operations/cycles_ledger.rs index 883c4bf1a5..72700117e0 100644 --- a/src/dfx/src/lib/operations/cycles_ledger.rs +++ b/src/dfx/src/lib/operations/cycles_ledger.rs @@ -55,7 +55,6 @@ pub async fn transfer( owner: Principal, to_subaccount: Option, created_at_time: u64, - fee: Option, memo: Option, cycles_ledger_canister_id: Principal, ) -> DfxResult { @@ -73,7 +72,7 @@ pub async fn transfer( owner, subaccount: to_subaccount, }, - fee: fee.map(Nat::from), + fee: None, created_at_time: Some(created_at_time), memo: memo.map(|v| v.into()), amount: Nat::from(amount), From b022e493dbfa79bf40776c277cbd2860e4b734ad Mon Sep 17 00:00:00 2001 From: Eric Swanson Date: Tue, 17 Oct 2023 11:38:38 -0700 Subject: [PATCH 07/13] format, update docs --- CHANGELOG.md | 6 +- docs/cli-reference/dfx-cycles.md | 62 ++- e2e/tests-dfx/cycles-ledger.bash | 588 ++++++++++---------- src/dfx/src/commands/cycles/top_up.rs | 27 +- src/dfx/src/commands/cycles/transfer.rs | 22 +- src/dfx/src/lib/operations/cycles_ledger.rs | 5 +- 6 files changed, 371 insertions(+), 339 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dbe9c4b45..d4c3c7487f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,10 +10,10 @@ The message "transaction is a duplicate of another transaction in block ...", pr This won't work on mainnet yet, but can work locally after installing the cycles ledger. -Added the following subcommands and variants: +Added the following subcommands: - `dfx cycles balance` - - `dfx cycles transfer --to-owner ` (transfer from one account to another account) - - `dfx cycles transfer --top-up ` (transfer from an account to a canister) + - `dfx cycles transfer ` (transfer from one account to another account) + - `dfx cycles top-up ` (transfer from an account to a canister) ## Dependencies diff --git a/docs/cli-reference/dfx-cycles.md b/docs/cli-reference/dfx-cycles.md index c089b70878..7398fc60cf 100644 --- a/docs/cli-reference/dfx-cycles.md +++ b/docs/cli-reference/dfx-cycles.md @@ -70,25 +70,20 @@ dfx cycles balance --owner raxcz-bidhr-evrzj-qyivt-nht5a-eltcc-24qfc-o6cvi-hfw7j Use the `dfx cycles transfer` command to transfer cycles from your account to another account. -The source of the transferred cycles is always the cycles ledger account associated with your identity's principal, or one of its subaccounts. - -The destination of the transferred cycles is one of the following: -- A cycles ledger account associated with another identity's principal, or one of its subaccounts. This mode uses the `--to-owner` and `--to-subaccount` options. -- A canister. This mode uses the `--top-up` option. - ### Basic usage ``` bash -dfx cycles transfer [options] +dfx cycles transfer [options] ``` ### Arguments You must specify the following argument for the `dfx cycles transfer` command. -| Argument | Description | -|--------------|-----------------------------------| -| `` | The number of cycles to transfer. | +| Argument | Description | +|------------|-----------------------------------| +| `` | The principal of the account to which you want to transfer cycles. | +| `` | The number of cycles to transfer. | ### Options @@ -96,11 +91,8 @@ You can specify the following options for the `dfx cycles transfer` command. | Option | Description | |----------------------------------|----------------------------------------------------------------------------------------| -| `--top-up ` | The canister which you want to top up. | -| `--to-owner ` | The principal of the account to which you want to transfer cycles. | | `--to-subaccount ` | The subaccount to which you want to transfer cycles. | | `--from-subaccount ` | The subaccount from which you want to transfer cycles. | -| `--fee ` | Specifies a transaction fee. Can only passed in `--to-owner` mode. | | `--memo ` | Specifies a numeric memo for this transaction. Can only be passed in `--to-owner mode. | | `--created-at-time ` | Specify the timestamp-nanoseconds for the `created_at_time` field on the transfer request. Useful for controlling transaction-de-duplication. https://internetcomputer.org/docs/current/developer-docs/integrations/icrc-1/#transaction-deduplication- | @@ -109,17 +101,53 @@ You can specify the following options for the `dfx cycles transfer` command. Transfer 1 billion cycles to another account: ``` bash -dfx cycles transfer 1000000000 --to-owner raxcz-bidhr-evrzj-qyivt-nht5a-eltcc-24qfc-o6cvi-hfw7j-dcecz-kae --network ic +dfx cycles transfer raxcz-bidhr-evrzj-qyivt-nht5a-eltcc-24qfc-o6cvi-hfw7j-dcecz-kae 1000000000 --network ic ``` Transfer from a subaccount: ``` bash -dfx cycles transfer 1000000000 --from-subaccount 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f --to-owner raxcz-bidhr-evrzj-qyivt-nht5a-eltcc-24qfc-o6cvi-hfw7j-dcecz-kae --network ic +dfx cycles transfer raxcz-bidhr-evrzj-qyivt-nht5a-eltcc-24qfc-o6cvi-hfw7j-dcecz-kae 1000000000 --from-subaccount 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f --network ic +``` + +## dfx cycles top-up + +Use the `dfx cycles top-up` command to send cycles from your account to a canister. + +### Basic usage + +``` bash +dfx cycles top-up [options] +``` + +### Arguments + +You must specify the following argument for the `dfx cycles transfer` command. + +| Argument | Description | +|------------|-------------------------------------------------------------------------| +| `` | The name of a canister in the current project, or a canister principal. | +| `` | The number of cycles to transfer. | + +### Options + +You can specify the following options for the `dfx cycles top-up` command. + +| Option | Description | +|----------------------------------|----------------------------------------------------------------------------------------| +| `--from-subaccount ` | The subaccount from which you want to transfer cycles. | +| `--created-at-time ` | Specify the timestamp-nanoseconds for the `created_at_time` field on the transfer request. Useful for controlling transaction-de-duplication. https://internetcomputer.org/docs/current/developer-docs/integrations/icrc-1/#transaction-deduplication- | + +### Examples + +Send cycles to a canister in your project: + +``` bash +dfx cycles top-up my_backend 1000000000 --network ic ``` -Transfer to (top up) a canister: +Send cycles to a canister by principal: ``` bash -dfx cycles transfer 1000000000 --top-up bkyz2-fmaaa-aaaaa-qaaaq-cai --network ic +dfx cycles top-up bkyz2-fmaaa-aaaaa-qaaaq-cai 1000000000 --network ic ``` diff --git a/e2e/tests-dfx/cycles-ledger.bash b/e2e/tests-dfx/cycles-ledger.bash index 9c23ff061e..685ac4f072 100644 --- a/e2e/tests-dfx/cycles-ledger.bash +++ b/e2e/tests-dfx/cycles-ledger.bash @@ -4,29 +4,29 @@ load ../utils/_ load ../utils/cycles-ledger setup() { - standard_setup - install_asset cycles-ledger - install_shared_asset subnet_type/shared_network_settings/system - install_cycles_ledger_canisters + standard_setup + install_asset cycles-ledger + install_shared_asset subnet_type/shared_network_settings/system + install_cycles_ledger_canisters - dfx identity new --storage-mode plaintext cycle-giver - dfx identity new --storage-mode plaintext alice - dfx identity new --storage-mode plaintext bob + dfx identity new --storage-mode plaintext cycle-giver + dfx identity new --storage-mode plaintext alice + dfx identity new --storage-mode plaintext bob - dfx_start_for_nns_install + dfx_start_for_nns_install - dfx extension install nns --version 0.2.1 || true - dfx nns install --ledger-accounts "$(dfx ledger account-id --identity cycle-giver)" + dfx extension install nns --version 0.2.1 || true + dfx nns install --ledger-accounts "$(dfx ledger account-id --identity cycle-giver)" } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } add_cycles_ledger_canisters_to_project() { - jq -s '.[0] * .[1]' ../dfx.json dfx.json | sponge dfx.json + jq -s '.[0] * .[1]' ../dfx.json dfx.json | sponge dfx.json } current_time_nanoseconds() { @@ -34,381 +34,383 @@ current_time_nanoseconds() { } @test "balance" { - ALICE=$(dfx identity get-principal --identity alice) - ALICE_SUBACCT1="000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" - ALICE_SUBACCT1_CANDID="\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" - ALICE_SUBACCT2="9C9B9A030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" - ALICE_SUBACCT2_CANDID="\9C\9B\9A\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" - BOB=$(dfx identity get-principal --identity bob) + ALICE=$(dfx identity get-principal --identity alice) + ALICE_SUBACCT1="000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" + ALICE_SUBACCT1_CANDID="\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" + ALICE_SUBACCT2="9C9B9A030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" + ALICE_SUBACCT2_CANDID="\9C\9B\9A\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" + BOB=$(dfx identity get-principal --identity bob) - assert_command dfx deploy cycles-ledger - assert_command dfx deploy cycles-depositor --argument "(record {ledger_id = principal \"$(dfx canister id cycles-ledger)\"})" --with-cycles 10000000000000 + assert_command dfx deploy cycles-ledger + assert_command dfx deploy cycles-depositor --argument "(record {ledger_id = principal \"$(dfx canister id cycles-ledger)\"})" --with-cycles 10000000000000 - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --precise - assert_eq "0 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --precise + assert_eq "0 cycles." - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice - assert_eq "0.000 TC (trillion cycles)." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice + assert_eq "0.000 TC (trillion cycles)." - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob --precise - assert_eq "0 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob --precise + assert_eq "0 cycles." - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob - assert_eq "0.000 TC (trillion cycles)." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob + assert_eq "0.000 TC (trillion cycles)." - assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$ALICE\";};cycles = 1_700_400_200_150;})" --identity cycle-giver - assert_eq "(record { balance = 1_700_400_200_150 : nat; txid = 0 : nat })" + assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$ALICE\";};cycles = 1_700_400_200_150;})" --identity cycle-giver + assert_eq "(record { balance = 1_700_400_200_150 : nat; txid = 0 : nat })" - assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$ALICE\"; subaccount = opt blob \"$ALICE_SUBACCT1_CANDID\"};cycles = 3_750_000_000_000;})" --identity cycle-giver - assert_eq "(record { balance = 3_750_000_000_000 : nat; txid = 1 : nat })" + assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$ALICE\"; subaccount = opt blob \"$ALICE_SUBACCT1_CANDID\"};cycles = 3_750_000_000_000;})" --identity cycle-giver + assert_eq "(record { balance = 3_750_000_000_000 : nat; txid = 1 : nat })" - assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$ALICE\"; subaccount = opt blob \"$ALICE_SUBACCT2_CANDID\"};cycles = 760_500_000_000;})" --identity cycle-giver - assert_eq "(record { balance = 760_500_000_000 : nat; txid = 2 : nat })" + assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$ALICE\"; subaccount = opt blob \"$ALICE_SUBACCT2_CANDID\"};cycles = 760_500_000_000;})" --identity cycle-giver + assert_eq "(record { balance = 760_500_000_000 : nat; txid = 2 : nat })" - assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$BOB\";};cycles = 2_900_000_000_000;})" --identity cycle-giver - assert_eq "(record { balance = 2_900_000_000_000 : nat; txid = 3 : nat })" + assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$BOB\";};cycles = 2_900_000_000_000;})" --identity cycle-giver + assert_eq "(record { balance = 2_900_000_000_000 : nat; txid = 3 : nat })" - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity alice - assert_eq "1700400200150 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity alice + assert_eq "1700400200150 cycles." - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity alice --subaccount "$ALICE_SUBACCT1" - assert_eq "3750000000000 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity alice --subaccount "$ALICE_SUBACCT1" + assert_eq "3750000000000 cycles." - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity alice --subaccount "$ALICE_SUBACCT2" - assert_eq "760500000000 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity alice --subaccount "$ALICE_SUBACCT2" + assert_eq "760500000000 cycles." - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob - assert_eq "2900000000000 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob + assert_eq "2900000000000 cycles." - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice - assert_eq "1.700 TC (trillion cycles)." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice + assert_eq "1.700 TC (trillion cycles)." - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --subaccount "$ALICE_SUBACCT1" - assert_eq "3.750 TC (trillion cycles)." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --subaccount "$ALICE_SUBACCT1" + assert_eq "3.750 TC (trillion cycles)." - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --subaccount "$ALICE_SUBACCT2" - assert_eq "0.760 TC (trillion cycles)." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --subaccount "$ALICE_SUBACCT2" + assert_eq "0.760 TC (trillion cycles)." - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob - assert_eq "2.900 TC (trillion cycles)." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob + assert_eq "2.900 TC (trillion cycles)." - # can see cycles balance of other accounts - assert_command dfx cycles balance --owner "$ALICE" --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob - assert_eq "1.700 TC (trillion cycles)." + # can see cycles balance of other accounts + assert_command dfx cycles balance --owner "$ALICE" --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob + assert_eq "1.700 TC (trillion cycles)." - assert_command dfx cycles balance --owner "$ALICE" --subaccount "$ALICE_SUBACCT1" --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob - assert_eq "3.750 TC (trillion cycles)." + assert_command dfx cycles balance --owner "$ALICE" --subaccount "$ALICE_SUBACCT1" --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob + assert_eq "3.750 TC (trillion cycles)." - assert_command dfx cycles balance --owner "$BOB" --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity anonymous - assert_eq "2.900 TC (trillion cycles)." + assert_command dfx cycles balance --owner "$BOB" --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity anonymous + assert_eq "2.900 TC (trillion cycles)." } @test "transfer" { - ALICE=$(dfx identity get-principal --identity alice) - ALICE_SUBACCT1="000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" - ALICE_SUBACCT1_CANDID="\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" - ALICE_SUBACCT2="9C9B9A030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" - ALICE_SUBACCT2_CANDID="\9C\9B\9A\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" - BOB=$(dfx identity get-principal --identity bob) - BOB_SUBACCT1="7C7B7A030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" + ALICE=$(dfx identity get-principal --identity alice) + ALICE_SUBACCT1="000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" + ALICE_SUBACCT1_CANDID="\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" + ALICE_SUBACCT2="9C9B9A030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" + ALICE_SUBACCT2_CANDID="\9C\9B\9A\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" + BOB=$(dfx identity get-principal --identity bob) + BOB_SUBACCT1="7C7B7A030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" - assert_command dfx deploy cycles-ledger - assert_command dfx deploy cycles-depositor --argument "(record {ledger_id = principal \"$(dfx canister id cycles-ledger)\"})" --with-cycles 10000000000000 + assert_command dfx deploy cycles-ledger + assert_command dfx deploy cycles-depositor --argument "(record {ledger_id = principal \"$(dfx canister id cycles-ledger)\"})" --with-cycles 10000000000000 - assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$ALICE\";};cycles = 3_000_000_000_000;})" --identity cycle-giver - assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$ALICE\"; subaccount = opt blob \"$ALICE_SUBACCT1_CANDID\"};cycles = 2_000_000_000_000;})" --identity cycle-giver - assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$ALICE\"; subaccount = opt blob \"$ALICE_SUBACCT2_CANDID\"};cycles = 1_000_000_000_000;})" --identity cycle-giver + assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$ALICE\";};cycles = 3_000_000_000_000;})" --identity cycle-giver + assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$ALICE\"; subaccount = opt blob \"$ALICE_SUBACCT1_CANDID\"};cycles = 2_000_000_000_000;})" --identity cycle-giver + assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$ALICE\"; subaccount = opt blob \"$ALICE_SUBACCT2_CANDID\"};cycles = 1_000_000_000_000;})" --identity cycle-giver - # account to account - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity alice - assert_eq "3000000000000 cycles." - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob - assert_eq "0 cycles." + # account to account + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity alice + assert_eq "3000000000000 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob + assert_eq "0 cycles." - assert_command dfx cycles transfer 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --to-owner "$BOB" - assert_eq "Transfer sent at block index 3" + assert_command dfx cycles transfer "$BOB" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice + assert_eq "Transfer sent at block index 3" - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity alice - assert_eq "2999899900000 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity alice + assert_eq "2999899900000 cycles." - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob - assert_eq "100000 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob + assert_eq "100000 cycles." - # account to subaccount - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity alice - assert_eq "2999899900000 cycles." - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT1" - assert_eq "0 cycles." + # account to subaccount + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity alice + assert_eq "2999899900000 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT1" + assert_eq "0 cycles." - assert_command dfx cycles transfer 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --to-owner "$BOB" --to-subaccount "$BOB_SUBACCT1" - assert_eq "Transfer sent at block index 4" + assert_command dfx cycles transfer "$BOB" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --to-subaccount "$BOB_SUBACCT1" + assert_eq "Transfer sent at block index 4" - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity alice - assert_eq "2999799800000 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity alice + assert_eq "2999799800000 cycles." - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT1" - assert_eq "100000 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT1" + assert_eq "100000 cycles." - # subaccount to account - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity alice --subaccount "$ALICE_SUBACCT2" - assert_eq "1000000000000 cycles." - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob - assert_eq "100000 cycles." + # subaccount to account + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity alice --subaccount "$ALICE_SUBACCT2" + assert_eq "1000000000000 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob + assert_eq "100000 cycles." - # assert_command dfx canister call cycles-ledger icrc1_transfer "(record {to = record{owner = principal \"$BOB\"}; amount = 100_000;})" --identity alice - assert_command dfx cycles transfer 700000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --to-owner "$BOB" --from-subaccount "$ALICE_SUBACCT2" + assert_command dfx cycles transfer "$BOB" 700000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --from-subaccount "$ALICE_SUBACCT2" + assert_eq "Transfer sent at block index 5" - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity alice --subaccount "$ALICE_SUBACCT2" - assert_eq "999899300000 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity alice --subaccount "$ALICE_SUBACCT2" + assert_eq "999899300000 cycles." - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob - assert_eq "800000 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob + assert_eq "800000 cycles." - # subaccount to subaccount - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity alice --subaccount "$ALICE_SUBACCT2" - assert_eq "999899300000 cycles." - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT1" - assert_eq "100000 cycles." + # subaccount to subaccount + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity alice --subaccount "$ALICE_SUBACCT2" + assert_eq "999899300000 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT1" + assert_eq "100000 cycles." - assert_command dfx cycles transfer 400000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --to-owner "$BOB" --to-subaccount "$BOB_SUBACCT1" --from-subaccount "$ALICE_SUBACCT2" - assert_eq "Transfer sent at block index 6" + assert_command dfx cycles transfer "$BOB" 400000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --to-subaccount "$BOB_SUBACCT1" --from-subaccount "$ALICE_SUBACCT2" + assert_eq "Transfer sent at block index 6" - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity alice --subaccount "$ALICE_SUBACCT2" - assert_eq "999798900000 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity alice --subaccount "$ALICE_SUBACCT2" + assert_eq "999798900000 cycles." - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT1" - assert_eq "500000 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT1" + assert_eq "500000 cycles." } @test "transfer deduplication" { - ALICE=$(dfx identity get-principal --identity alice) - BOB=$(dfx identity get-principal --identity bob) + ALICE=$(dfx identity get-principal --identity alice) + BOB=$(dfx identity get-principal --identity bob) - assert_command dfx deploy cycles-ledger - assert_command dfx deploy cycles-depositor --argument "(record {ledger_id = principal \"$(dfx canister id cycles-ledger)\"})" --with-cycles 10000000000000 + assert_command dfx deploy cycles-ledger + assert_command dfx deploy cycles-depositor --argument "(record {ledger_id = principal \"$(dfx canister id cycles-ledger)\"})" --with-cycles 10000000000000 - assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$ALICE\";};cycles = 3_000_000_000_000;})" --identity cycle-giver + assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$ALICE\";};cycles = 3_000_000_000_000;})" --identity cycle-giver - assert_command dfx cycles balance --precise --identity alice --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" - assert_eq "3000000000000 cycles." + assert_command dfx cycles balance --precise --identity alice --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" + assert_eq "3000000000000 cycles." - assert_command dfx cycles balance --precise --identity bob --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" - assert_eq "0 cycles." + assert_command dfx cycles balance --precise --identity bob --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" + assert_eq "0 cycles." - t=$(current_time_nanoseconds) + t=$(current_time_nanoseconds) - assert_command dfx cycles transfer 100000 --created-at-time "$t" --memo 1 --identity alice --to-owner "$BOB" --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" - assert_eq "Transfer sent at block index 1" + assert_command dfx cycles transfer "$BOB" 100000 --created-at-time "$t" --memo 1 --identity alice --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" + assert_eq "Transfer sent at block index 1" - assert_command dfx cycles balance --precise --identity alice --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" - assert_eq "2999899900000 cycles." + assert_command dfx cycles balance --precise --identity alice --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" + assert_eq "2999899900000 cycles." - assert_command dfx cycles balance --precise --identity bob --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" - assert_eq "100000 cycles." + assert_command dfx cycles balance --precise --identity bob --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" + assert_eq "100000 cycles." - # same memo and created-at-time: dupe - assert_command dfx cycles transfer 100000 --created-at-time "$t" --memo 1 --identity alice --to-owner "$BOB" --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" - assert_contains "transaction is a duplicate of another transaction in block 1" - assert_contains "Transfer sent at block index 1" + # same memo and created-at-time: dupe + assert_command dfx cycles transfer "$BOB" 100000 --created-at-time "$t" --memo 1 --identity alice --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" + # shellcheck disable=SC2154 + assert_contains "transaction is a duplicate of another transaction in block 1" "$stderr" + # shellcheck disable=SC2154 + assert_eq "Transfer sent at block index 1" "$stdout" - assert_command dfx cycles balance --precise --identity alice --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" - assert_eq "2999899900000 cycles." + assert_command dfx cycles balance --precise --identity alice --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" + assert_eq "2999899900000 cycles." - assert_command dfx cycles balance --precise --identity bob --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" - assert_eq "100000 cycles." + assert_command dfx cycles balance --precise --identity bob --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" + assert_eq "100000 cycles." - # different memo and same created-at-time same: not dupe - assert_command dfx cycles transfer 100000 --created-at-time "$t" --memo 2 --identity alice --to-owner "$BOB" --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" - assert_contains "Transfer sent at block index 2" + # different memo and same created-at-time same: not dupe + assert_command dfx cycles transfer "$BOB" 100000 --created-at-time "$t" --memo 2 --identity alice --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" + assert_contains "Transfer sent at block index 2" - assert_command dfx cycles balance --precise --identity alice --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" - assert_eq "2999799800000 cycles." + assert_command dfx cycles balance --precise --identity alice --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" + assert_eq "2999799800000 cycles." - assert_command dfx cycles balance --precise --identity bob --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" - assert_eq "200000 cycles." + assert_command dfx cycles balance --precise --identity bob --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" + assert_eq "200000 cycles." - # same memo and different created-at-time same: not dupe - assert_command dfx cycles transfer 100000 --created-at-time $((t+1)) --memo 1 --identity alice --to-owner "$BOB" --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" - assert_contains "Transfer sent at block index 3" + # same memo and different created-at-time same: not dupe + assert_command dfx cycles transfer "$BOB" 100000 --created-at-time $((t+1)) --memo 1 --identity alice --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" + assert_contains "Transfer sent at block index 3" - assert_command dfx cycles balance --precise --identity alice --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" - assert_eq "2999699700000 cycles." + assert_command dfx cycles balance --precise --identity alice --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" + assert_eq "2999699700000 cycles." - assert_command dfx cycles balance --precise --identity bob --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" - assert_eq "300000 cycles." + assert_command dfx cycles balance --precise --identity bob --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" + assert_eq "300000 cycles." } @test "top up canister principal check" { - BOB=$(dfx identity get-principal --identity bob) + BOB=$(dfx identity get-principal --identity bob) - assert_command dfx deploy cycles-ledger + assert_command dfx deploy cycles-ledger - assert_command_fail dfx cycles top-up "$BOB" 600000 --identity alice --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" - assert_contains "Invalid receiver: $BOB. Make sure the receiver is a canister." + assert_command_fail dfx cycles top-up "$BOB" 600000 --identity alice --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" + assert_contains "Invalid receiver: $BOB. Make sure the receiver is a canister." } @test "top-up" { - dfx_new - add_cycles_ledger_canisters_to_project - install_cycles_ledger_canisters - - BOB=$(dfx identity get-principal --identity bob) - BOB_SUBACCT1="7C7B7A030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" - BOB_SUBACCT1_CANDID="\7C\7B\7A\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" - BOB_SUBACCT2="6C6B6A030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" - BOB_SUBACCT2_CANDID="\6C\6B\6A\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" - - assert_command dfx deploy cycles-ledger - assert_command dfx deploy cycles-depositor --argument "(record {ledger_id = principal \"$(dfx canister id cycles-ledger)\"})" --with-cycles 10000000000000 - - assert_command dfx deploy - - assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$BOB\";};cycles = 2_400_000_000_000;})" --identity cycle-giver - assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$BOB\"; subaccount = opt blob \"$BOB_SUBACCT1_CANDID\"};cycles = 2_600_000_000_000;})" --identity cycle-giver - assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$BOB\"; subaccount = opt blob \"$BOB_SUBACCT2_CANDID\"};cycles = 2_700_000_000_000;})" --identity cycle-giver - - # account to canister - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob - assert_eq "2400000000000 cycles." - assert_command dfx canister status e2e_project_backend - assert_contains "Balance: 3_100_000_000_000 Cycles" - - assert_command dfx cycles top-up e2e_project_backend 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob - - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob - assert_eq "2399899900000 cycles." - assert_command dfx canister status e2e_project_backend - assert_contains "Balance: 3_100_000_100_000 Cycles" - - # subaccount to canister - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT1" - assert_eq "2600000000000 cycles." - assert_command dfx canister status e2e_project_backend - assert_contains "Balance: 3_100_000_100_000 Cycles" - - assert_command dfx cycles top-up e2e_project_backend 300000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob --from-subaccount "$BOB_SUBACCT1" - - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT1" - assert_eq "2599899700000 cycles." - assert_command dfx canister status e2e_project_backend - assert_contains "Balance: 3_100_000_400_000 Cycles" - - # subaccount to canister - by canister id - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT2" - assert_eq "2700000000000 cycles." - assert_command dfx canister status e2e_project_backend - assert_contains "Balance: 3_100_000_400_000 Cycles" - - assert_command dfx cycles top-up "$(dfx canister id e2e_project_backend)" 600000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob --from-subaccount "$BOB_SUBACCT2" - - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT2" - assert_eq "2699899400000 cycles." - assert_command dfx canister status e2e_project_backend - assert_contains "Balance: 3_100_001_000_000 Cycles" + dfx_new + add_cycles_ledger_canisters_to_project + install_cycles_ledger_canisters + + BOB=$(dfx identity get-principal --identity bob) + BOB_SUBACCT1="7C7B7A030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" + BOB_SUBACCT1_CANDID="\7C\7B\7A\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" + BOB_SUBACCT2="6C6B6A030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" + BOB_SUBACCT2_CANDID="\6C\6B\6A\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" + + assert_command dfx deploy cycles-ledger + assert_command dfx deploy cycles-depositor --argument "(record {ledger_id = principal \"$(dfx canister id cycles-ledger)\"})" --with-cycles 10000000000000 + + assert_command dfx deploy + + assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$BOB\";};cycles = 2_400_000_000_000;})" --identity cycle-giver + assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$BOB\"; subaccount = opt blob \"$BOB_SUBACCT1_CANDID\"};cycles = 2_600_000_000_000;})" --identity cycle-giver + assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$BOB\"; subaccount = opt blob \"$BOB_SUBACCT2_CANDID\"};cycles = 2_700_000_000_000;})" --identity cycle-giver + + # account to canister + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob + assert_eq "2400000000000 cycles." + assert_command dfx canister status e2e_project_backend + assert_contains "Balance: 3_100_000_000_000 Cycles" + + assert_command dfx cycles top-up e2e_project_backend 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob + + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob + assert_eq "2399899900000 cycles." + assert_command dfx canister status e2e_project_backend + assert_contains "Balance: 3_100_000_100_000 Cycles" + + # subaccount to canister + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT1" + assert_eq "2600000000000 cycles." + assert_command dfx canister status e2e_project_backend + assert_contains "Balance: 3_100_000_100_000 Cycles" + + assert_command dfx cycles top-up e2e_project_backend 300000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob --from-subaccount "$BOB_SUBACCT1" + + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT1" + assert_eq "2599899700000 cycles." + assert_command dfx canister status e2e_project_backend + assert_contains "Balance: 3_100_000_400_000 Cycles" + + # subaccount to canister - by canister id + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT2" + assert_eq "2700000000000 cycles." + assert_command dfx canister status e2e_project_backend + assert_contains "Balance: 3_100_000_400_000 Cycles" + + assert_command dfx cycles top-up "$(dfx canister id e2e_project_backend)" 600000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob --from-subaccount "$BOB_SUBACCT2" + + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob --subaccount "$BOB_SUBACCT2" + assert_eq "2699899400000 cycles." + assert_command dfx canister status e2e_project_backend + assert_contains "Balance: 3_100_001_000_000 Cycles" } @test "top-up deduplication" { - dfx_new - add_cycles_ledger_canisters_to_project - install_cycles_ledger_canisters - - BOB=$(dfx identity get-principal --identity bob) - BOB_SUBACCT1="7C7B7A030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" - BOB_SUBACCT1_CANDID="\7C\7B\7A\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" - BOB_SUBACCT2="6C6B6A030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" - BOB_SUBACCT2_CANDID="\6C\6B\6A\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" - - assert_command dfx deploy cycles-ledger - assert_command dfx deploy cycles-depositor --argument "(record {ledger_id = principal \"$(dfx canister id cycles-ledger)\"})" --with-cycles 10000000000000 - - assert_command dfx deploy - - assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$BOB\";};cycles = 2_400_000_000_000;})" --identity cycle-giver - assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$BOB\"; subaccount = opt blob \"$BOB_SUBACCT1_CANDID\"};cycles = 2_600_000_000_000;})" --identity cycle-giver - assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$BOB\"; subaccount = opt blob \"$BOB_SUBACCT2_CANDID\"};cycles = 2_700_000_000_000;})" --identity cycle-giver - - # account to canister - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob - assert_eq "2400000000000 cycles." - assert_command dfx canister status e2e_project_backend - assert_contains "Balance: 3_100_000_000_000 Cycles" - - t=$(current_time_nanoseconds) - assert_command dfx cycles top-up "$(dfx canister id e2e_project_backend)" --created-at-time "$t" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob - assert_eq "Transfer sent at block index 3" - - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob - assert_eq "2399899900000 cycles." - assert_command dfx canister status e2e_project_backend - assert_contains "Balance: 3_100_000_100_000 Cycles" - - # same created-at-time: dupe - assert_command dfx cycles top-up "$(dfx canister id e2e_project_backend)" --created-at-time "$t" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob - assert_contains "transaction is a duplicate of another transaction in block 3" - assert_contains "Transfer sent at block index 3" - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob - assert_eq "2399899900000 cycles." - - # different created-at-time: not dupe - assert_command dfx cycles top-up "$(dfx canister id e2e_project_backend)" --created-at-time $((t+1)) 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob - assert_eq "Transfer sent at block index 4" - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob - assert_eq "2399799800000 cycles." + dfx_new + add_cycles_ledger_canisters_to_project + install_cycles_ledger_canisters + + BOB=$(dfx identity get-principal --identity bob) + BOB_SUBACCT1="7C7B7A030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" + BOB_SUBACCT1_CANDID="\7C\7B\7A\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" + BOB_SUBACCT2="6C6B6A030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" + BOB_SUBACCT2_CANDID="\6C\6B\6A\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" + + assert_command dfx deploy cycles-ledger + assert_command dfx deploy cycles-depositor --argument "(record {ledger_id = principal \"$(dfx canister id cycles-ledger)\"})" --with-cycles 10000000000000 + + assert_command dfx deploy + + assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$BOB\";};cycles = 2_400_000_000_000;})" --identity cycle-giver + assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$BOB\"; subaccount = opt blob \"$BOB_SUBACCT1_CANDID\"};cycles = 2_600_000_000_000;})" --identity cycle-giver + assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$BOB\"; subaccount = opt blob \"$BOB_SUBACCT2_CANDID\"};cycles = 2_700_000_000_000;})" --identity cycle-giver + + # account to canister + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob + assert_eq "2400000000000 cycles." + assert_command dfx canister status e2e_project_backend + assert_contains "Balance: 3_100_000_000_000 Cycles" + + t=$(current_time_nanoseconds) + assert_command dfx cycles top-up "$(dfx canister id e2e_project_backend)" --created-at-time "$t" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob + assert_eq "Transfer sent at block index 3" + + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob + assert_eq "2399899900000 cycles." + assert_command dfx canister status e2e_project_backend + assert_contains "Balance: 3_100_000_100_000 Cycles" + + # same created-at-time: dupe + assert_command dfx cycles top-up "$(dfx canister id e2e_project_backend)" --created-at-time "$t" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob + assert_contains "transaction is a duplicate of another transaction in block 3" + assert_contains "Transfer sent at block index 3" + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob + assert_eq "2399899900000 cycles." + + # different created-at-time: not dupe + assert_command dfx cycles top-up "$(dfx canister id e2e_project_backend)" --created-at-time $((t+1)) 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob + assert_eq "Transfer sent at block index 4" + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob + assert_eq "2399799800000 cycles." } @test "howto" { - # This is the equivalent of https://www.notion.so/dfinityorg/How-to-install-and-test-the-cycles-ledger-521c9f3c410f4a438514a03e35464299 - ALICE=$(dfx identity get-principal --identity alice) - BOB=$(dfx identity get-principal --identity bob) + # This is the equivalent of https://www.notion.so/dfinityorg/How-to-install-and-test-the-cycles-ledger-521c9f3c410f4a438514a03e35464299 + ALICE=$(dfx identity get-principal --identity alice) + BOB=$(dfx identity get-principal --identity bob) - assert_command dfx deploy cycles-ledger - assert_command dfx deploy cycles-depositor --argument "(record {ledger_id = principal \"$(dfx canister id cycles-ledger)\"})" --with-cycles 10000000000000 + assert_command dfx deploy cycles-ledger + assert_command dfx deploy cycles-depositor --argument "(record {ledger_id = principal \"$(dfx canister id cycles-ledger)\"})" --with-cycles 10000000000000 - assert_command dfx ledger balance --identity cycle-giver - assert_eq "1000000000.00000000 ICP" + assert_command dfx ledger balance --identity cycle-giver + assert_eq "1000000000.00000000 ICP" - assert_command dfx canister status cycles-depositor - assert_contains "Balance: 10_000_000_000_000 Cycles" + assert_command dfx canister status cycles-depositor + assert_contains "Balance: 10_000_000_000_000 Cycles" - dfx canister status cycles-depositor + dfx canister status cycles-depositor - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --precise - assert_eq "0 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --precise + assert_eq "0 cycles." - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob --precise - assert_eq "0 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob --precise + assert_eq "0 cycles." - assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$ALICE\";};cycles = 500_000_000;})" --identity cycle-giver - assert_eq "(record { balance = 500_000_000 : nat; txid = 0 : nat })" + assert_command dfx canister call cycles-depositor deposit "(record {to = record{owner = principal \"$ALICE\";};cycles = 500_000_000;})" --identity cycle-giver + assert_eq "(record { balance = 500_000_000 : nat; txid = 0 : nat })" - assert_command dfx canister status cycles-depositor - assert_contains "Balance: 9_999_500_000_000 Cycles" + assert_command dfx canister status cycles-depositor + assert_contains "Balance: 9_999_500_000_000 Cycles" - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --precise - assert_eq "500000000 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --precise + assert_eq "500000000 cycles." - assert_command dfx cycles transfer 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --to-owner "$BOB" - assert_eq "Transfer sent at block index 1" + assert_command dfx cycles transfer "$BOB" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice + assert_eq "Transfer sent at block index 1" - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --precise - assert_eq "399900000 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --precise + assert_eq "399900000 cycles." - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob --precise - assert_eq "100000 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob --precise + assert_eq "100000 cycles." - assert_command dfx cycles top-up cycles-depositor 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice - assert_eq "Transfer sent at block index 2" + assert_command dfx cycles top-up cycles-depositor 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice + assert_eq "Transfer sent at block index 2" - assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --precise - assert_eq "299800000 cycles." + assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity alice --precise + assert_eq "299800000 cycles." - assert_command dfx canister status cycles-depositor - assert_contains "Balance: 9_999_500_100_000 Cycles" + assert_command dfx canister status cycles-depositor + assert_contains "Balance: 9_999_500_100_000 Cycles" } diff --git a/src/dfx/src/commands/cycles/top_up.rs b/src/dfx/src/commands/cycles/top_up.rs index 3d481e435b..ac2cfe9222 100644 --- a/src/dfx/src/commands/cycles/top_up.rs +++ b/src/dfx/src/commands/cycles/top_up.rs @@ -4,18 +4,18 @@ use crate::lib::nns_types::account_identifier::Subaccount; use crate::lib::operations::cycles_ledger; use crate::lib::root_key::fetch_root_key_if_needed; use crate::util::clap::parsers::cycle_amount_parser; -use anyhow::Context; use candid::Principal; use clap::Parser; +use slog::warn; use std::time::{SystemTime, UNIX_EPOCH}; /// Send cycles to a canister. #[derive(Parser)] pub struct TopUpOpts { - /// Transfer cycles to this canister. + /// Send cycles to this canister. to: String, - /// The amount of cycles to send. + /// The number of cycles to send. #[arg(value_parser = cycle_amount_parser)] amount: u128, @@ -23,10 +23,6 @@ pub struct TopUpOpts { #[arg(long)] from_subaccount: Option, - /// Transfer cycles to this subaccount. - #[arg(long, conflicts_with("top_up"))] - to_subaccount: Option, - /// Transaction timestamp, in nanoseconds, for use in controlling transaction-deduplication, default is system-time. /// https://internetcomputer.org/docs/current/developer-docs/integrations/icrc-1/#transaction-deduplication- #[arg(long)] @@ -55,7 +51,7 @@ pub async fn exec(env: &dyn Environment, opts: TopUpOpts) -> DfxResult { let to = get_canister_id(env, &opts.to)?; let from_subaccount = opts.from_subaccount.map(|x| x.0); - let block_index = cycles_ledger::send( + let result = cycles_ledger::send( agent, to, amount, @@ -63,13 +59,14 @@ pub async fn exec(env: &dyn Environment, opts: TopUpOpts) -> DfxResult { from_subaccount, opts.cycles_ledger_canister_id, ) - .await - .with_context(|| { - format!( - "If you retry this operation, use --created-at-time {}", - created_at_time - ) - })?; + .await; + if result.is_err() && opts.created_at_time.is_none() { + warn!( + env.get_logger(), + "If you retry this operation, use --created-at-time {}", created_at_time + ); + } + let block_index = result?; println!("Transfer sent at block index {block_index}"); diff --git a/src/dfx/src/commands/cycles/transfer.rs b/src/dfx/src/commands/cycles/transfer.rs index 5c50cfa6a8..97c1cf9d01 100644 --- a/src/dfx/src/commands/cycles/transfer.rs +++ b/src/dfx/src/commands/cycles/transfer.rs @@ -4,9 +4,9 @@ use crate::lib::nns_types::account_identifier::Subaccount; use crate::lib::operations::cycles_ledger; use crate::lib::root_key::fetch_root_key_if_needed; use crate::util::clap::parsers::cycle_amount_parser; -use anyhow::Context; use candid::Principal; use clap::Parser; +use slog::warn; use std::time::{SystemTime, UNIX_EPOCH}; /// Transfer cycles to another principal. @@ -15,7 +15,7 @@ pub struct TransferOpts { /// Transfer cycles to this principal. to: Principal, - /// The amount of cycles to send. + /// The number of cycles to send. #[arg(value_parser = cycle_amount_parser)] amount: u128, @@ -59,8 +59,9 @@ pub async fn exec(env: &dyn Environment, opts: TransferOpts) -> DfxResult { let from_subaccount = opts.from_subaccount.map(|x| x.0); let to_subaccount = opts.to_subaccount.map(|x| x.0); - let block_index = cycles_ledger::transfer( + let result = cycles_ledger::transfer( agent, + env.get_logger(), amount, from_subaccount, opts.to, @@ -69,13 +70,14 @@ pub async fn exec(env: &dyn Environment, opts: TransferOpts) -> DfxResult { opts.memo, opts.cycles_ledger_canister_id, ) - .await - .with_context(|| { - format!( - "If you retry this operation, use --created-at-time {}", - created_at_time - ) - })?; + .await; + if result.is_err() && opts.created_at_time.is_none() { + warn!( + env.get_logger(), + "If you retry this operation, use --created-at-time {}", created_at_time + ); + } + let block_index = result?; println!("Transfer sent at block index {block_index}"); diff --git a/src/dfx/src/lib/operations/cycles_ledger.rs b/src/dfx/src/lib/operations/cycles_ledger.rs index 72700117e0..0ef996f188 100644 --- a/src/dfx/src/lib/operations/cycles_ledger.rs +++ b/src/dfx/src/lib/operations/cycles_ledger.rs @@ -11,6 +11,7 @@ use ic_utils::call::SyncCall; use ic_utils::Canister; use icrc_ledger_types::icrc1; use icrc_ledger_types::icrc1::transfer::{BlockIndex, TransferError}; +use slog::{info, Logger}; const ICRC1_BALANCE_OF_METHOD: &str = "icrc1_balance_of"; const ICRC1_TRANSFER_METHOD: &str = "icrc1_transfer"; @@ -50,6 +51,7 @@ pub async fn balance( pub async fn transfer( agent: &Agent, + logger: &Logger, amount: u128, from_subaccount: Option, owner: Principal, @@ -88,7 +90,8 @@ pub async fn transfer( { Ok(Ok(block_index)) => Ok(block_index), Ok(Err(TransferError::Duplicate { duplicate_of })) => { - println!( + info!( + logger, "{}", TransferError::Duplicate { duplicate_of: duplicate_of.clone() From 89d65c9d6651b414c1d7dbe7bcfa5d3d1b378dc8 Mon Sep 17 00:00:00 2001 From: Eric Swanson Date: Tue, 17 Oct 2023 14:04:18 -0700 Subject: [PATCH 08/13] terminology --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4c3c7487f..2bfffccb00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,8 @@ This won't work on mainnet yet, but can work locally after installing the cycles Added the following subcommands: - `dfx cycles balance` - - `dfx cycles transfer ` (transfer from one account to another account) - - `dfx cycles top-up ` (transfer from an account to a canister) + - `dfx cycles transfer ` (transfer cycles from one account to another account) + - `dfx cycles top-up ` (send cycles from an account to a canister) ## Dependencies From 8ce780f8cd9cde5d1b3a6135d9fa0faffc21f011 Mon Sep 17 00:00:00 2001 From: Eric Swanson Date: Tue, 17 Oct 2023 14:05:13 -0700 Subject: [PATCH 09/13] plural --- docs/cli-reference/dfx-cycles.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/cli-reference/dfx-cycles.md b/docs/cli-reference/dfx-cycles.md index 7398fc60cf..283dd46e25 100644 --- a/docs/cli-reference/dfx-cycles.md +++ b/docs/cli-reference/dfx-cycles.md @@ -78,7 +78,7 @@ dfx cycles transfer [options] ### Arguments -You must specify the following argument for the `dfx cycles transfer` command. +You must specify the following arguments for the `dfx cycles transfer` command. | Argument | Description | |------------|-----------------------------------| @@ -122,7 +122,7 @@ dfx cycles top-up [options] ### Arguments -You must specify the following argument for the `dfx cycles transfer` command. +You must specify the following arguments for the `dfx cycles transfer` command. | Argument | Description | |------------|-------------------------------------------------------------------------| From cb16ef7884d2988c0be532b5c28af8c3f2459b57 Mon Sep 17 00:00:00 2001 From: Eric Swanson Date: Tue, 17 Oct 2023 14:37:14 -0700 Subject: [PATCH 10/13] send: log, not println, duplicate message --- e2e/tests-dfx/cycles-ledger.bash | 6 ++++-- src/dfx/src/commands/cycles/top_up.rs | 1 + src/dfx/src/lib/operations/cycles_ledger.rs | 7 ++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/e2e/tests-dfx/cycles-ledger.bash b/e2e/tests-dfx/cycles-ledger.bash index 685ac4f072..a878e8cc60 100644 --- a/e2e/tests-dfx/cycles-ledger.bash +++ b/e2e/tests-dfx/cycles-ledger.bash @@ -352,8 +352,10 @@ current_time_nanoseconds() { # same created-at-time: dupe assert_command dfx cycles top-up "$(dfx canister id e2e_project_backend)" --created-at-time "$t" 100000 --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --identity bob - assert_contains "transaction is a duplicate of another transaction in block 3" - assert_contains "Transfer sent at block index 3" + # shellcheck disable=SC2154 + assert_contains "transaction is a duplicate of another transaction in block 3" "$stderr" + # shellcheck disable=SC2154 + assert_contains "Transfer sent at block index 3" "$stdout" assert_command dfx cycles balance --cycles-ledger-canister-id "$(dfx canister id cycles-ledger)" --precise --identity bob assert_eq "2399899900000 cycles." diff --git a/src/dfx/src/commands/cycles/top_up.rs b/src/dfx/src/commands/cycles/top_up.rs index ac2cfe9222..bf6a72789e 100644 --- a/src/dfx/src/commands/cycles/top_up.rs +++ b/src/dfx/src/commands/cycles/top_up.rs @@ -53,6 +53,7 @@ pub async fn exec(env: &dyn Environment, opts: TopUpOpts) -> DfxResult { let from_subaccount = opts.from_subaccount.map(|x| x.0); let result = cycles_ledger::send( agent, + env.get_logger(), to, amount, created_at_time, diff --git a/src/dfx/src/lib/operations/cycles_ledger.rs b/src/dfx/src/lib/operations/cycles_ledger.rs index 0ef996f188..c82a561760 100644 --- a/src/dfx/src/lib/operations/cycles_ledger.rs +++ b/src/dfx/src/lib/operations/cycles_ledger.rs @@ -113,6 +113,7 @@ pub async fn transfer( pub async fn send( agent: &Agent, + logger: &Logger, to: Principal, amount: u128, created_at_time: u64, @@ -143,9 +144,9 @@ pub async fn send( { Ok(Ok(block_index)) => Ok(block_index), Ok(Err(SendError::Duplicate { duplicate_of })) => { - println!( - "transaction is a duplicate of another transaction in block {}", - duplicate_of + info!( + logger, + "transaction is a duplicate of another transaction in block {}", duplicate_of ); Ok(duplicate_of) } From 530534118a9d65c842fca2c92d9a6ee028b6e23b Mon Sep 17 00:00:00 2001 From: Eric Swanson <64809312+ericswanson-dfinity@users.noreply.github.com> Date: Wed, 18 Oct 2023 09:36:02 -0700 Subject: [PATCH 11/13] Remove reference to to-owner mode Co-authored-by: Thomas Locher --- docs/cli-reference/dfx-cycles.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cli-reference/dfx-cycles.md b/docs/cli-reference/dfx-cycles.md index 283dd46e25..d7067ea287 100644 --- a/docs/cli-reference/dfx-cycles.md +++ b/docs/cli-reference/dfx-cycles.md @@ -93,7 +93,7 @@ You can specify the following options for the `dfx cycles transfer` command. |----------------------------------|----------------------------------------------------------------------------------------| | `--to-subaccount ` | The subaccount to which you want to transfer cycles. | | `--from-subaccount ` | The subaccount from which you want to transfer cycles. | -| `--memo ` | Specifies a numeric memo for this transaction. Can only be passed in `--to-owner mode. | +| `--memo ` | Specifies a numeric memo for this transaction. | | `--created-at-time ` | Specify the timestamp-nanoseconds for the `created_at_time` field on the transfer request. Useful for controlling transaction-de-duplication. https://internetcomputer.org/docs/current/developer-docs/integrations/icrc-1/#transaction-deduplication- | ### Examples From 200dff5db261ef1a42fe123aa6a3232c96bf31eb Mon Sep 17 00:00:00 2001 From: Eric Swanson <64809312+ericswanson-dfinity@users.noreply.github.com> Date: Wed, 18 Oct 2023 09:38:34 -0700 Subject: [PATCH 12/13] Update docs/cli-reference/dfx-cycles.md Co-authored-by: Thomas Locher --- docs/cli-reference/dfx-cycles.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cli-reference/dfx-cycles.md b/docs/cli-reference/dfx-cycles.md index d7067ea287..936abdc986 100644 --- a/docs/cli-reference/dfx-cycles.md +++ b/docs/cli-reference/dfx-cycles.md @@ -136,7 +136,7 @@ You can specify the following options for the `dfx cycles top-up` command. | Option | Description | |----------------------------------|----------------------------------------------------------------------------------------| | `--from-subaccount ` | The subaccount from which you want to transfer cycles. | -| `--created-at-time ` | Specify the timestamp-nanoseconds for the `created_at_time` field on the transfer request. Useful for controlling transaction-de-duplication. https://internetcomputer.org/docs/current/developer-docs/integrations/icrc-1/#transaction-deduplication- | +| `--created-at-time ` | Specify the timestamp-nanoseconds for the `created_at_time` field on the transfer request. Useful for controlling transaction deduplication. https://internetcomputer.org/docs/current/developer-docs/integrations/icrc-1/#transaction-deduplication- | ### Examples From f1b7e8ce9abb5424f62e2df398d9a2373950d58c Mon Sep 17 00:00:00 2001 From: Eric Swanson <64809312+ericswanson-dfinity@users.noreply.github.com> Date: Wed, 18 Oct 2023 09:38:55 -0700 Subject: [PATCH 13/13] CR suggestion Co-authored-by: Thomas Locher --- src/dfx/src/commands/cycles/top_up.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dfx/src/commands/cycles/top_up.rs b/src/dfx/src/commands/cycles/top_up.rs index bf6a72789e..70e49bcc4f 100644 --- a/src/dfx/src/commands/cycles/top_up.rs +++ b/src/dfx/src/commands/cycles/top_up.rs @@ -23,7 +23,7 @@ pub struct TopUpOpts { #[arg(long)] from_subaccount: Option, - /// Transaction timestamp, in nanoseconds, for use in controlling transaction-deduplication, default is system-time. + /// Transaction timestamp, in nanoseconds, for use in controlling transaction deduplication, default is system time. /// https://internetcomputer.org/docs/current/developer-docs/integrations/icrc-1/#transaction-deduplication- #[arg(long)] created_at_time: Option,