diff --git a/cycles-ledger/src/storage.rs b/cycles-ledger/src/storage.rs index bf24e09..2089e19 100644 --- a/cycles-ledger/src/storage.rs +++ b/cycles-ledger/src/storage.rs @@ -243,9 +243,7 @@ pub fn transfer( .expect("failed to update 'from' balance"); let to_balance = s.balances.get(&to_key).unwrap_or_default(); - s.balances - .insert(to_key, to_balance + amount) - .expect("failed to update 'to' balance"); + s.balances.insert(to_key, to_balance + amount); let phash = s.last_block_hash(); let block_hash = s.emit_block(Block { diff --git a/cycles-ledger/tests/client.rs b/cycles-ledger/tests/client.rs index 1bc2ef3..c3fddb7 100644 --- a/cycles-ledger/tests/client.rs +++ b/cycles-ledger/tests/client.rs @@ -2,7 +2,10 @@ use candid::{Decode, Encode, Nat, Principal}; use cycles_ledger::endpoints::{self, DepositResult, SendArg}; use depositor::endpoints::DepositArg; use ic_test_state_machine_client::{StateMachine, WasmResult}; -use icrc_ledger_types::icrc1::account::Account; +use icrc_ledger_types::icrc1::{ + account::Account, + transfer::{TransferArg, TransferError}, +}; use num_traits::ToPrimitive; pub fn deposit( @@ -52,3 +55,32 @@ pub fn send( panic!("send rejected") } } + +pub fn transfer( + env: &StateMachine, + ledger_id: Principal, + from: Account, + args: TransferArg, +) -> Result { + let arg = Encode!(&args).unwrap(); + if let WasmResult::Reply(res) = env + .update_call(ledger_id, from.owner, "icrc1_transfer", arg) + .unwrap() + { + Decode!(&res, Result).unwrap() + } else { + panic!("transfer rejected") + } +} + +pub fn fee(env: &StateMachine, ledger_id: Principal) -> Nat { + let arg = Encode!(&()).unwrap(); + if let WasmResult::Reply(res) = env + .query_call(ledger_id, Principal::anonymous(), "icrc1_fee", arg) + .unwrap() + { + Decode!(&res, Nat).unwrap() + } else { + panic!("fee call rejected") + } +} diff --git a/cycles-ledger/tests/tests.rs b/cycles-ledger/tests/tests.rs index 24e91ba..176e609 100644 --- a/cycles-ledger/tests/tests.rs +++ b/cycles-ledger/tests/tests.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use candid::{Encode, Nat, Principal}; -use client::deposit; +use client::{deposit, transfer}; use cycles_ledger::{ config::{self, FEE}, endpoints::{SendArg, SendErrorReason}, @@ -10,10 +10,13 @@ use depositor::endpoints::InitArg as DepositorInitArg; use escargot::CargoBuild; use ic_cdk::api::call::RejectionCode; use ic_test_state_machine_client::StateMachine; -use icrc_ledger_types::icrc1::{account::Account, transfer::Memo}; +use icrc_ledger_types::icrc1::{ + account::Account, + transfer::{Memo, TransferArg, TransferError}, +}; use serde_bytes::ByteBuf; -use crate::client::{balance_of, send}; +use crate::client::{balance_of, fee, send}; mod client; @@ -459,3 +462,85 @@ fn test_send_fails() { .unwrap_err(); assert_eq!(0, balance_of(env, ledger_id, user_2)); } + +#[test] +fn test_transfer() { + let env = &new_state_machine(); + let ledger_id = install_ledger(env); + let depositor_id = install_depositor(env, ledger_id); + let user1 = Account { + owner: Principal::from_slice(&[1]), + subaccount: None, + }; + let user2: Account = Account { + owner: Principal::from_slice(&[2]), + subaccount: None, + }; + let deposit_amount = 1_000_000_000; + deposit(env, depositor_id, user1, deposit_amount); + let fee = fee(env, ledger_id); + + let transfer_amount = Nat::from(100_000); + transfer( + env, + ledger_id, + user1, + TransferArg { + from_subaccount: None, + to: user2, + fee: None, + created_at_time: None, + memo: None, + amount: transfer_amount.clone(), + }, + ) + .unwrap(); + + assert_eq!(balance_of(env, ledger_id, user2), transfer_amount.clone()); + assert_eq!( + balance_of(env, ledger_id, user1), + Nat::from(deposit_amount) - fee.clone() - transfer_amount.clone() + ); + + // Should not be able to send back the full amount as the user2 cannot pay the fee + assert_eq!( + TransferError::InsufficientFunds { + balance: transfer_amount.clone() + }, + transfer( + env, + ledger_id, + user2, + TransferArg { + from_subaccount: None, + to: user2, + fee: None, + created_at_time: None, + memo: None, + amount: transfer_amount.clone(), + }, + ) + .unwrap_err() + ); + + // Should not be able to set a fee that is incorrect + assert_eq!( + TransferError::BadFee { + expected_fee: fee.clone() + }, + transfer( + env, + ledger_id, + user2, + TransferArg { + from_subaccount: None, + to: user1, + fee: Some(Nat::from(0)), + created_at_time: None, + memo: None, + amount: transfer_amount.clone(), + }, + ) + .unwrap_err() + ); +}