diff --git a/rs/ethereum/cketh/minter/cketh_minter.did b/rs/ethereum/cketh/minter/cketh_minter.did index 3821a5c7225..4520f06d165 100644 --- a/rs/ethereum/cketh/minter/cketh_minter.did +++ b/rs/ethereum/cketh/minter/cketh_minter.did @@ -362,6 +362,12 @@ type WithdrawErc20Arg = record { // Ethereum address to withdraw to. recipient : text; + + // The subaccount to burn ckETH from to pay for the transaction fee. + from_cketh_subaccount : opt Subaccount; + + // The subaccount to burn ckERC20 from. + from_ckerc20_subaccount : opt Subaccount; }; type RetrieveErc20Request = record { diff --git a/rs/ethereum/cketh/minter/src/endpoints/ckerc20.rs b/rs/ethereum/cketh/minter/src/endpoints/ckerc20.rs index c65c488e2e7..8cff2fb141c 100644 --- a/rs/ethereum/cketh/minter/src/endpoints/ckerc20.rs +++ b/rs/ethereum/cketh/minter/src/endpoints/ckerc20.rs @@ -1,12 +1,15 @@ use crate::ledger_client::LedgerBurnError; use crate::state::transactions::Erc20WithdrawalRequest; use candid::{CandidType, Deserialize, Nat, Principal}; +use icrc_ledger_types::icrc1::account::Subaccount; #[derive(CandidType, Deserialize)] pub struct WithdrawErc20Arg { pub amount: Nat, pub ckerc20_ledger_id: Principal, pub recipient: String, + pub from_cketh_subaccount: Option, + pub from_ckerc20_subaccount: Option, } #[derive(Clone, PartialEq, Debug, CandidType, Deserialize)] diff --git a/rs/ethereum/cketh/minter/src/main.rs b/rs/ethereum/cketh/minter/src/main.rs index a9f9679be07..adade9bec7d 100644 --- a/rs/ethereum/cketh/minter/src/main.rs +++ b/rs/ethereum/cketh/minter/src/main.rs @@ -399,6 +399,8 @@ async fn withdraw_erc20( amount, ckerc20_ledger_id, recipient, + from_cketh_subaccount, + from_ckerc20_subaccount, }: WithdrawErc20Arg, ) -> Result { validate_ckerc20_active(); @@ -436,11 +438,24 @@ async fn withdraw_erc20( let erc20_tx_fee = estimate_erc20_transaction_fee().await.ok_or_else(|| { WithdrawErc20Error::TemporarilyUnavailable("Failed to retrieve current gas fee".to_string()) })?; + let cketh_account = Account { + owner: caller, + subaccount: from_cketh_subaccount, + }; + let ckerc20_account = Account { + owner: caller, + subaccount: from_ckerc20_subaccount, + }; let now = ic_cdk::api::time(); - log!(INFO, "[withdraw_erc20]: burning {:?} ckETH", erc20_tx_fee); + log!( + INFO, + "[withdraw_erc20]: burning {:?} ckETH from account {}", + erc20_tx_fee, + cketh_account + ); match cketh_ledger .burn_from( - caller.into(), + cketh_account, erc20_tx_fee, BurnMemo::Erc20GasFee { ckerc20_token_symbol: ckerc20_token.ckerc20_token_symbol.clone(), @@ -453,13 +468,14 @@ async fn withdraw_erc20( Ok(cketh_ledger_burn_index) => { log!( INFO, - "[withdraw_erc20]: burning {} {}", + "[withdraw_erc20]: burning {} {} from account {}", ckerc20_withdrawal_amount, - ckerc20_token.ckerc20_token_symbol + ckerc20_token.ckerc20_token_symbol, + ckerc20_account ); match LedgerClient::ckerc20_ledger(&ckerc20_token) .burn_from( - caller.into(), + ckerc20_account, ckerc20_withdrawal_amount, BurnMemo::Erc20Convert { ckerc20_withdrawal_id: cketh_ledger_burn_index.get(), @@ -478,7 +494,8 @@ async fn withdraw_erc20( ckerc20_ledger_burn_index, erc20_contract_address: ckerc20_token.erc20_contract_address, from: caller, - from_subaccount: None, + from_subaccount: from_ckerc20_subaccount + .and_then(LedgerSubaccount::from_bytes), created_at: now, }; log!( @@ -507,8 +524,10 @@ async fn withdraw_erc20( let reimbursement_request = ReimbursementRequest { ledger_burn_index: cketh_ledger_burn_index, reimbursed_amount: reimbursed_amount.change_units(), - to: caller, - to_subaccount: None, + to: cketh_account.owner, + to_subaccount: cketh_account + .subaccount + .and_then(LedgerSubaccount::from_bytes), transaction_hash: None, }; mutate_state(|s| { diff --git a/rs/ethereum/cketh/minter/tests/ckerc20.rs b/rs/ethereum/cketh/minter/tests/ckerc20.rs index a2c20354bc9..4021d99e2cc 100644 --- a/rs/ethereum/cketh/minter/tests/ckerc20.rs +++ b/rs/ethereum/cketh/minter/tests/ckerc20.rs @@ -10,8 +10,13 @@ use ic_cketh_minter::endpoints::{ use ic_cketh_minter::memo::MintMemo; use ic_cketh_minter::numeric::BlockNumber; use ic_cketh_minter::{MINT_RETRY_DELAY, SCRAPING_ETH_LOGS_INTERVAL}; -use ic_cketh_test_utils::ckerc20::{CkErc20Setup, DepositCkErc20Params, Erc20Token, ONE_USDC}; -use ic_cketh_test_utils::flow::DepositParams; +use ic_cketh_test_utils::ckerc20::{ + CkErc20Setup, DepositCkErc20, DepositCkErc20Params, DepositCkErc20WithSubaccountParams, + Erc20Token, ONE_USDC, +}; +use ic_cketh_test_utils::flow::{ + DepositCkEthParams, DepositCkEthWithSubaccountParams, DepositParams, +}; use ic_cketh_test_utils::mock::{JsonRpcMethod, MockJsonRpcProviders}; use ic_cketh_test_utils::response::{ block_response, empty_logs, multi_logs_for_single_transaction, @@ -20,8 +25,9 @@ use ic_cketh_test_utils::{ format_ethereum_address_to_eip_55, CkEthSetup, CKETH_MINIMUM_WITHDRAWAL_AMOUNT, DEFAULT_DEPOSIT_BLOCK_NUMBER, DEFAULT_DEPOSIT_FROM_ADDRESS, DEFAULT_DEPOSIT_LOG_INDEX, DEFAULT_DEPOSIT_TRANSACTION_HASH, DEFAULT_ERC20_DEPOSIT_LOG_INDEX, - DEFAULT_ERC20_DEPOSIT_TRANSACTION_HASH, EFFECTIVE_GAS_PRICE, ERC20_HELPER_CONTRACT_ADDRESS, - ETH_HELPER_CONTRACT_ADDRESS, GAS_USED, LAST_SCRAPED_BLOCK_NUMBER_AT_INSTALL, MINTER_ADDRESS, + DEFAULT_ERC20_DEPOSIT_TRANSACTION_HASH, DEFAULT_USER_SUBACCOUNT, EFFECTIVE_GAS_PRICE, + ERC20_HELPER_CONTRACT_ADDRESS, ETH_HELPER_CONTRACT_ADDRESS, GAS_USED, + LAST_SCRAPED_BLOCK_NUMBER_AT_INSTALL, MINTER_ADDRESS, }; use ic_ethereum_types::Address; use ic_ledger_suite_orchestrator_test_utils::flow::call_ledger_icrc1_total_supply; @@ -141,7 +147,7 @@ mod withdraw_erc20 { use super::*; use ic_base_types::PrincipalId; use ic_cketh_minter::endpoints::ckerc20::{ - LedgerError, RetrieveErc20Request, WithdrawErc20Error, + LedgerError, RetrieveErc20Request, WithdrawErc20Arg, WithdrawErc20Error, }; use ic_cketh_minter::endpoints::events::{ TransactionReceipt, TransactionStatus, UnsignedTransaction, @@ -626,42 +632,54 @@ mod withdraw_erc20 { #[test] fn should_withdraw_ckusdc() { - fn test(transaction_status: TransactionStatus) { - let ckerc20 = CkErc20Setup::default().add_supported_erc20_tokens(); + fn test>( + ckerc20: CkErc20Setup, + deposit_params: T, + ckerc20_withdrawal_amount: u64, + transaction_status: TransactionStatus, + ) { let minter = ckerc20.cketh.minter_id; - let ckusdc = ckerc20.find_ckerc20_token("ckUSDC"); - let caller = ckerc20.caller(); + let deposit_params = deposit_params.into(); + let cketh_deposit_params = deposit_params + .cketh_deposit() + .expect("missing ckETH deposit params"); + let cketh_account = cketh_deposit_params.recipient(); + let ckerc20_account = deposit_params.recipient(); + assert_eq!(cketh_account.owner, ckerc20_account.owner); let ckerc20_tx_fee = DEFAULT_CKERC20_WITHDRAWAL_TRANSACTION_FEE; let ckerc20 = ckerc20 - .deposit_cketh_and_ckerc20( - EXPECTED_BALANCE, - TWO_USDC + CKERC20_TRANSFER_FEE, - ckusdc.clone(), - caller, - ) + .deposit(deposit_params.clone()) .expect_mint() - .call_cketh_ledger_approve_minter(caller, ckerc20_tx_fee, None) + .call_cketh_ledger_approve_minter( + cketh_account.owner, + ckerc20_tx_fee, + cketh_account.subaccount, + ) .call_ckerc20_ledger_approve_minter( - ckusdc.ledger_canister_id, - caller, - TWO_USDC, - None, + deposit_params.token().ledger_canister_id, + ckerc20_account.owner, + ckerc20_withdrawal_amount, + ckerc20_account.subaccount, ) - .call_minter_withdraw_erc20( - caller, - TWO_USDC, - ckusdc.ledger_canister_id, - DEFAULT_ERC20_WITHDRAWAL_DESTINATION_ADDRESS, + .call_minter_withdraw_erc20_with( + ckerc20_account.owner, + WithdrawErc20Arg { + amount: ckerc20_withdrawal_amount.into(), + ckerc20_ledger_id: deposit_params.token().ledger_canister_id, + recipient: DEFAULT_ERC20_WITHDRAWAL_DESTINATION_ADDRESS.to_string(), + from_cketh_subaccount: cketh_account.subaccount, + from_ckerc20_subaccount: ckerc20_account.subaccount, + }, ) .expect_refresh_gas_fee_estimate(identity) .expect_withdrawal_request_accepted(); assert_eq!( - ckerc20 - .setup - .erc20_balance_from_get_minter_info(&ckusdc.erc20_contract_address), - TWO_USDC + CKERC20_TRANSFER_FEE + ckerc20.setup.erc20_balance_from_get_minter_info( + &deposit_params.token().erc20_contract_address + ), + deposit_params.ckerc20_amount() ); let time = ckerc20.setup.env.get_time().as_nanos_since_unix_epoch(); @@ -691,17 +709,14 @@ mod withdraw_erc20 { .call_cketh_ledger_get_transaction(cketh_block_index.clone()) .expect_burn(Burn { amount: ckerc20_tx_fee.into(), - from: Account { - owner: caller, - subaccount: None, - }, + from: cketh_account, spender: Some(Account { owner: minter.into(), subaccount: None, }), memo: Some(Memo::from(BurnMemo::Erc20GasFee { ckerc20_token_symbol: "ckUSDC".parse().unwrap(), - ckerc20_withdrawal_amount: TWO_USDC.into(), + ckerc20_withdrawal_amount: ckerc20_withdrawal_amount.into(), to_address: DEFAULT_ERC20_WITHDRAWAL_DESTINATION_ADDRESS .parse() .unwrap(), @@ -709,15 +724,12 @@ mod withdraw_erc20 { created_at_time: None, }) .call_ckerc20_ledger_get_transaction( - ckusdc.ledger_canister_id, + deposit_params.token().ledger_canister_id, ckerc20_block_index.clone(), ) .expect_burn(Burn { - amount: TWO_USDC.into(), - from: Account { - owner: caller, - subaccount: None, - }, + amount: ckerc20_withdrawal_amount.into(), + from: ckerc20_account, spender: Some(Account { owner: minter.into(), subaccount: None, @@ -732,21 +744,25 @@ mod withdraw_erc20 { }); let expected_cketh_balance_after_withdrawal = - Nat::from(EXPECTED_BALANCE - CKETH_TRANSFER_FEE - ckerc20_tx_fee); + Nat::from(cketh_deposit_params.amount() - CKETH_TRANSFER_FEE - ckerc20_tx_fee); assert_eq!( - ckerc20.cketh.balance_of(caller), + ckerc20.cketh.balance_of(cketh_account), expected_cketh_balance_after_withdrawal ); let expected_ckerc20_balance_after_withdrawal = Nat::from(0_u8); assert_eq!( - ckerc20.balance_of_ledger(ckusdc.ledger_canister_id, caller), + ckerc20 + .balance_of_ledger(deposit_params.token().ledger_canister_id, ckerc20_account), expected_ckerc20_balance_after_withdrawal ); assert_eq!( - ckerc20.erc20_balance_from_get_minter_info(&ckusdc.erc20_contract_address), + ckerc20.erc20_balance_from_get_minter_info( + &deposit_params.token().erc20_contract_address + ), match transaction_status { - TransactionStatus::Success => CKERC20_TRANSFER_FEE, - TransactionStatus::Failure => TWO_USDC + CKERC20_TRANSFER_FEE, + TransactionStatus::Success => + deposit_params.ckerc20_amount() - ckerc20_withdrawal_amount, + TransactionStatus::Failure => deposit_params.ckerc20_amount(), } ); @@ -757,14 +773,17 @@ mod withdraw_erc20 { .assert_has_unique_events_in_order(&vec![ EventPayload::AcceptedErc20WithdrawalRequest { max_transaction_fee: ckerc20_tx_fee.into(), - withdrawal_amount: TWO_USDC.into(), - erc20_contract_address: ckusdc.erc20_contract_address.clone(), + withdrawal_amount: ckerc20_withdrawal_amount.into(), + erc20_contract_address: deposit_params + .token() + .erc20_contract_address + .clone(), destination: DEFAULT_ERC20_WITHDRAWAL_DESTINATION_ADDRESS.to_string(), cketh_ledger_burn_index: cketh_block_index.clone(), - ckerc20_ledger_id: ckusdc.ledger_canister_id, + ckerc20_ledger_id: deposit_params.token().ledger_canister_id, ckerc20_ledger_burn_index: ckerc20_block_index.clone(), - from: caller, - from_subaccount: None, + from: ckerc20_account.owner, + from_subaccount: ckerc20_account.subaccount, created_at: time, }, EventPayload::CreatedTransaction { @@ -775,13 +794,13 @@ mod withdraw_erc20 { max_priority_fee_per_gas: 1_500_000_000_u64.into(), max_fee_per_gas: estimated_max_fee_per_gas.clone(), gas_limit: estimated_gas_limit.clone(), - destination: ckusdc.erc20_contract_address, + destination: deposit_params.token().erc20_contract_address.clone(), value: 0_u8.into(), data: ByteBuf::from(erc20_transfer_data( &DEFAULT_ERC20_WITHDRAWAL_DESTINATION_ADDRESS .parse() .unwrap(), - &TWO_USDC.into(), + &ckerc20_withdrawal_amount.into(), )), access_list: vec![], }, @@ -807,7 +826,7 @@ mod withdraw_erc20 { ckerc20.env.advance_time(PROCESS_REIMBURSEMENT); let cketh_balance_after_reimbursement = ckerc20.wait_for_updated_ledger_balance( ckerc20.cketh_ledger_id(), - caller, + cketh_account, &expected_cketh_balance_after_withdrawal, ); assert_eq!( @@ -817,11 +836,14 @@ mod withdraw_erc20 { if transaction_status == TransactionStatus::Failure { let ckerc20_balance_after_reimbursement = ckerc20.wait_for_updated_ledger_balance( - ckusdc.ledger_canister_id, - caller, + deposit_params.token().ledger_canister_id, + ckerc20_account, &expected_ckerc20_balance_after_withdrawal, ); - assert_eq!(ckerc20_balance_after_reimbursement, Nat::from(TWO_USDC)); + assert_eq!( + ckerc20_balance_after_reimbursement, + Nat::from(ckerc20_withdrawal_amount) + ); ckerc20 .check_events() .assert_has_unique_events_in_order(&vec![ @@ -829,20 +851,20 @@ mod withdraw_erc20 { withdrawal_id: cketh_block_index.clone(), burn_in_block: ckerc20_block_index.clone(), reimbursed_in_block: Nat::from(3_u8), - ledger_id: ckusdc.ledger_canister_id, - reimbursed_amount: TWO_USDC.into(), + ledger_id: deposit_params.token().ledger_canister_id, + reimbursed_amount: ckerc20_withdrawal_amount.into(), transaction_hash: Some( DEFAULT_CKERC20_WITHDRAWAL_TRANSACTION_HASH.to_string(), ), }, ]) - .call_ckerc20_ledger_get_transaction(ckusdc.ledger_canister_id, 3_u8) + .call_ckerc20_ledger_get_transaction( + deposit_params.token().ledger_canister_id, + 3_u8, + ) .expect_mint(Mint { - amount: TWO_USDC.into(), - to: Account { - owner: caller, - subaccount: None, - }, + amount: ckerc20_withdrawal_amount.into(), + to: ckerc20_account, memo: Some(Memo::from(MintMemo::ReimburseTransaction { withdrawal_id: ckerc20_block_index.0.to_u64().unwrap(), tx_hash: DEFAULT_CKERC20_WITHDRAWAL_TRANSACTION_HASH.parse().unwrap(), @@ -852,8 +874,53 @@ mod withdraw_erc20 { } } - test(TransactionStatus::Success); - test(TransactionStatus::Failure); + for tx_status in [TransactionStatus::Success, TransactionStatus::Failure] { + let ckerc20 = CkErc20Setup::default().add_supported_erc20_tokens(); + let ckusdc = ckerc20.find_ckerc20_token("ckUSDC"); + test( + ckerc20, + DepositCkErc20Params { + cketh_deposit: Some(DepositParams::from(DepositCkEthParams { + amount: EXPECTED_BALANCE, + ..Default::default() + })), + ..DepositCkErc20Params::new(TWO_USDC + CKERC20_TRANSFER_FEE, ckusdc) + }, + TWO_USDC, + tx_status.clone(), + ); + + let ckerc20 = CkErc20Setup::default() + .add_supported_erc20_tokens() + .add_support_for_subaccount(); + let ckusdc = ckerc20.find_ckerc20_token("ckUSDC"); + let caller = ckerc20.caller(); + let cketh_subaccount = Some(DEFAULT_USER_SUBACCOUNT); + let ckusdc_subaccount = Some([43; 32]); + assert_ne!(cketh_subaccount, ckusdc_subaccount); + + test( + ckerc20, + DepositCkErc20WithSubaccountParams { + cketh_deposit: Some(DepositParams::from(DepositCkEthWithSubaccountParams { + recipient: caller, + recipient_subaccount: cketh_subaccount, + amount: EXPECTED_BALANCE, + ..Default::default() + })), + ..DepositCkErc20WithSubaccountParams::new( + TWO_USDC + CKERC20_TRANSFER_FEE, + ckusdc.clone(), + Account { + owner: caller, + subaccount: ckusdc_subaccount, + }, + ) + }, + TWO_USDC, + tx_status, + ); + } } #[test] @@ -1256,69 +1323,147 @@ mod withdraw_erc20 { fn should_deposit_ckerc20() { let ckerc20 = CkErc20Setup::default().add_supported_erc20_tokens(); let ckusdc = ckerc20.find_ckerc20_token("ckUSDC"); - let caller: Principal = ckerc20.caller(); + test_deposit_ckerc20(ckerc20, DepositCkErc20Params::new(ONE_USDC, ckusdc)); - ckerc20 - .deposit_ckerc20(ONE_USDC, ckusdc.clone(), caller) - .expect_mint() - .call_ckerc20_ledger_get_transaction(ckusdc.ledger_canister_id, 0_u8) - .expect_mint(Mint { - amount: Nat::from(ONE_USDC), - to: Account { + let ckerc20 = CkErc20Setup::default() + .add_supported_erc20_tokens() + .add_support_for_subaccount(); + let ckusdc = ckerc20.find_ckerc20_token("ckUSDC"); + let caller = ckerc20.caller(); + test_deposit_ckerc20( + ckerc20, + DepositCkErc20WithSubaccountParams::new( + ONE_USDC, + ckusdc, + Account { owner: caller, - subaccount: None, + subaccount: Some(DEFAULT_USER_SUBACCOUNT), }, - memo: Some(Memo::from(MintMemo::Convert { - from_address: DEFAULT_DEPOSIT_FROM_ADDRESS.parse().unwrap(), - tx_hash: DEFAULT_ERC20_DEPOSIT_TRANSACTION_HASH.parse().unwrap(), - log_index: DEFAULT_ERC20_DEPOSIT_LOG_INDEX.into(), - })), - created_at_time: None, - }); + ), + ); + + fn test_deposit_ckerc20>(ckerc20: CkErc20Setup, params: T) { + let params = params.into(); + + ckerc20 + .deposit(params.clone()) + .expect_mint() + .call_ckerc20_ledger_get_transaction(params.token().ledger_canister_id, 0_u8) + .expect_mint(Mint { + amount: Nat::from(params.ckerc20_amount()), + to: params.recipient(), + memo: Some(Memo::from(MintMemo::Convert { + from_address: *params.from_address(), + tx_hash: params.transaction_data().transaction_hash.parse().unwrap(), + log_index: params.transaction_data().log_index.into(), + })), + created_at_time: None, + }); + } } #[test] fn should_deposit_cketh_and_ckerc20() { let ckerc20 = CkErc20Setup::default().add_supported_erc20_tokens(); let ckusdc = ckerc20.find_ckerc20_token("ckUSDC"); - let caller = ckerc20.caller(); + test_deposit_cketh_and_ckerc20( + ckerc20, + DepositCkErc20Params { + cketh_deposit: Some(DepositParams::from(DepositCkEthParams { + amount: CKETH_MINIMUM_WITHDRAWAL_AMOUNT, + ..Default::default() + })), + ..DepositCkErc20Params::new(ONE_USDC, ckusdc) + }, + ); - ckerc20 - .deposit_cketh_and_ckerc20( - CKETH_MINIMUM_WITHDRAWAL_AMOUNT, - ONE_USDC, - ckusdc.clone(), - caller, - ) - .expect_mint() - .call_cketh_ledger_get_transaction(0_u8) - .expect_mint(Mint { - amount: CKETH_MINIMUM_WITHDRAWAL_AMOUNT.into(), - to: Account { - owner: caller, - subaccount: None, - }, - memo: Some(Memo::from(MintMemo::Convert { - from_address: DEFAULT_DEPOSIT_FROM_ADDRESS.parse().unwrap(), - tx_hash: DEFAULT_DEPOSIT_TRANSACTION_HASH.parse().unwrap(), - log_index: DEFAULT_DEPOSIT_LOG_INDEX.into(), + let ckerc20 = CkErc20Setup::default() + .add_supported_erc20_tokens() + .add_support_for_subaccount(); + let ckusdc = ckerc20.find_ckerc20_token("ckUSDC"); + let caller = ckerc20.caller(); + let ckusdc_subaccount = Some([43; 32]); + test_deposit_cketh_and_ckerc20( + ckerc20, + DepositCkErc20WithSubaccountParams { + cketh_deposit: Some(DepositParams::from(DepositCkEthParams { + recipient: caller, + ..Default::default() })), - created_at_time: None, - }) - .call_ckerc20_ledger_get_transaction(ckusdc.ledger_canister_id, 0_u8) - .expect_mint(Mint { - amount: ONE_USDC.into(), - to: Account { - owner: caller, - subaccount: None, - }, - memo: Some(Memo::from(MintMemo::Convert { - from_address: DEFAULT_DEPOSIT_FROM_ADDRESS.parse().unwrap(), - tx_hash: DEFAULT_ERC20_DEPOSIT_TRANSACTION_HASH.parse().unwrap(), - log_index: DEFAULT_ERC20_DEPOSIT_LOG_INDEX.into(), + ..DepositCkErc20WithSubaccountParams::new( + ONE_USDC, + ckusdc.clone(), + Account { + owner: caller, + subaccount: ckusdc_subaccount, + }, + ) + }, + ); + + let ckerc20 = CkErc20Setup::default() + .add_supported_erc20_tokens() + .add_support_for_subaccount(); + let ckusdc = ckerc20.find_ckerc20_token("ckUSDC"); + let caller = ckerc20.caller(); + let cketh_subaccount = Some(DEFAULT_USER_SUBACCOUNT); + let ckusdc_subaccount = Some([43; 32]); + assert_ne!(cketh_subaccount, ckusdc_subaccount); + test_deposit_cketh_and_ckerc20( + ckerc20, + DepositCkErc20WithSubaccountParams { + cketh_deposit: Some(DepositParams::from(DepositCkEthWithSubaccountParams { + recipient: caller, + recipient_subaccount: cketh_subaccount, + ..Default::default() })), - created_at_time: None, - }); + ..DepositCkErc20WithSubaccountParams::new( + ONE_USDC, + ckusdc.clone(), + Account { + owner: caller, + subaccount: ckusdc_subaccount, + }, + ) + }, + ); + + fn test_deposit_cketh_and_ckerc20>(ckerc20: CkErc20Setup, params: T) { + let params = params.into(); + let cketh_params = params + .cketh_deposit() + .expect("missing ckETH deposit params"); + + ckerc20 + .deposit(params.clone()) + .expect_mint() + .call_cketh_ledger_get_transaction(0_u8) + .expect_mint(Mint { + amount: Nat::from(cketh_params.amount()), + to: cketh_params.recipient(), + memo: Some(Memo::from(MintMemo::Convert { + from_address: *cketh_params.from_address(), + tx_hash: cketh_params + .transaction_data() + .transaction_hash + .parse() + .unwrap(), + log_index: cketh_params.transaction_data().log_index.into(), + })), + created_at_time: None, + }) + .call_ckerc20_ledger_get_transaction(params.token().ledger_canister_id, 0_u8) + .expect_mint(Mint { + amount: Nat::from(params.ckerc20_amount()), + to: params.recipient(), + memo: Some(Memo::from(MintMemo::Convert { + from_address: *params.from_address(), + tx_hash: params.transaction_data().transaction_hash.parse().unwrap(), + log_index: params.transaction_data().log_index.into(), + })), + created_at_time: None, + }); + } } #[test] diff --git a/rs/ethereum/cketh/minter/tests/subaccount.rs b/rs/ethereum/cketh/minter/tests/subaccount.rs deleted file mode 100644 index 9da7d8173dd..00000000000 --- a/rs/ethereum/cketh/minter/tests/subaccount.rs +++ /dev/null @@ -1,164 +0,0 @@ -use candid::Nat; -use ic_cketh_minter::memo::MintMemo; -use ic_cketh_test_utils::ckerc20::{CkErc20Setup, DepositCkErc20WithSubaccountParams, ONE_USDC}; -use ic_cketh_test_utils::flow::{ - DepositCkEthParams, DepositCkEthWithSubaccountParams, DepositParams, -}; -use ic_cketh_test_utils::{ - DEFAULT_DEPOSIT_FROM_ADDRESS, DEFAULT_DEPOSIT_LOG_INDEX, DEFAULT_DEPOSIT_TRANSACTION_HASH, - DEFAULT_ERC20_DEPOSIT_LOG_INDEX, DEFAULT_ERC20_DEPOSIT_TRANSACTION_HASH, - DEFAULT_USER_SUBACCOUNT, EXPECTED_BALANCE, -}; -use icrc_ledger_types::icrc1::account::Account; -use icrc_ledger_types::icrc1::transfer::Memo; -use icrc_ledger_types::icrc3::transactions::Mint; - -#[test] -fn should_deposit_ckerc20() { - let ckerc20 = CkErc20Setup::default() - .add_supported_erc20_tokens() - .add_support_for_subaccount(); - let ckusdc = ckerc20.find_ckerc20_token("ckUSDC"); - let caller = ckerc20.caller(); - - ckerc20 - .deposit(DepositCkErc20WithSubaccountParams::new( - ONE_USDC, - ckusdc.clone(), - Account { - owner: caller, - subaccount: Some(DEFAULT_USER_SUBACCOUNT), - }, - )) - .expect_mint() - .call_ckerc20_ledger_get_transaction(ckusdc.ledger_canister_id, 0_u8) - .expect_mint(Mint { - amount: Nat::from(ONE_USDC), - to: Account { - owner: caller, - subaccount: Some(DEFAULT_USER_SUBACCOUNT), - }, - memo: Some(Memo::from(MintMemo::Convert { - from_address: DEFAULT_DEPOSIT_FROM_ADDRESS.parse().unwrap(), - tx_hash: DEFAULT_ERC20_DEPOSIT_TRANSACTION_HASH.parse().unwrap(), - log_index: DEFAULT_ERC20_DEPOSIT_LOG_INDEX.into(), - })), - created_at_time: None, - }); - //TODO XC-221: continue test to withdraw from subaccount -} - -#[test] -fn should_deposit_cketh_without_subaccount_and_ckerc20_with_subaccount() { - let ckerc20 = CkErc20Setup::default() - .add_supported_erc20_tokens() - .add_support_for_subaccount(); - let ckusdc = ckerc20.find_ckerc20_token("ckUSDC"); - let caller = ckerc20.caller(); - let ckusdc_subaccount = Some([43; 32]); - - ckerc20 - .deposit(DepositCkErc20WithSubaccountParams { - cketh_deposit: Some(DepositParams::from(DepositCkEthParams { - recipient: caller, - ..Default::default() - })), - ..DepositCkErc20WithSubaccountParams::new( - ONE_USDC, - ckusdc.clone(), - Account { - owner: caller, - subaccount: ckusdc_subaccount, - }, - ) - }) - .expect_mint() - .call_cketh_ledger_get_transaction(0_u8) - .expect_mint(Mint { - amount: EXPECTED_BALANCE.into(), - to: Account { - owner: caller, - subaccount: None, - }, - memo: Some(Memo::from(MintMemo::Convert { - from_address: DEFAULT_DEPOSIT_FROM_ADDRESS.parse().unwrap(), - tx_hash: DEFAULT_DEPOSIT_TRANSACTION_HASH.parse().unwrap(), - log_index: DEFAULT_DEPOSIT_LOG_INDEX.into(), - })), - created_at_time: None, - }) - .call_ckerc20_ledger_get_transaction(ckusdc.ledger_canister_id, 0_u8) - .expect_mint(Mint { - amount: ONE_USDC.into(), - to: Account { - owner: caller, - subaccount: ckusdc_subaccount, - }, - memo: Some(Memo::from(MintMemo::Convert { - from_address: DEFAULT_DEPOSIT_FROM_ADDRESS.parse().unwrap(), - tx_hash: DEFAULT_ERC20_DEPOSIT_TRANSACTION_HASH.parse().unwrap(), - log_index: DEFAULT_ERC20_DEPOSIT_LOG_INDEX.into(), - })), - created_at_time: None, - }); - //TODO XC-221: continue test to withdraw from subaccount -} - -#[test] -fn should_deposit_cketh_with_subaccount_and_ckerc20_with_subaccount() { - let ckerc20 = CkErc20Setup::default() - .add_supported_erc20_tokens() - .add_support_for_subaccount(); - let ckusdc = ckerc20.find_ckerc20_token("ckUSDC"); - let caller = ckerc20.caller(); - let cketh_subaccount = Some(DEFAULT_USER_SUBACCOUNT); - let ckusdc_subaccount = Some([43; 32]); - assert_ne!(cketh_subaccount, ckusdc_subaccount); - - ckerc20 - .deposit(DepositCkErc20WithSubaccountParams { - cketh_deposit: Some(DepositParams::from(DepositCkEthWithSubaccountParams { - recipient: caller, - recipient_subaccount: cketh_subaccount, - ..Default::default() - })), - ..DepositCkErc20WithSubaccountParams::new( - ONE_USDC, - ckusdc.clone(), - Account { - owner: caller, - subaccount: ckusdc_subaccount, - }, - ) - }) - .expect_mint() - .call_cketh_ledger_get_transaction(0_u8) - .expect_mint(Mint { - amount: EXPECTED_BALANCE.into(), - to: Account { - owner: caller, - subaccount: cketh_subaccount, - }, - memo: Some(Memo::from(MintMemo::Convert { - from_address: DEFAULT_DEPOSIT_FROM_ADDRESS.parse().unwrap(), - tx_hash: DEFAULT_DEPOSIT_TRANSACTION_HASH.parse().unwrap(), - log_index: DEFAULT_DEPOSIT_LOG_INDEX.into(), - })), - created_at_time: None, - }) - .call_ckerc20_ledger_get_transaction(ckusdc.ledger_canister_id, 0_u8) - .expect_mint(Mint { - amount: ONE_USDC.into(), - to: Account { - owner: caller, - subaccount: ckusdc_subaccount, - }, - memo: Some(Memo::from(MintMemo::Convert { - from_address: DEFAULT_DEPOSIT_FROM_ADDRESS.parse().unwrap(), - tx_hash: DEFAULT_ERC20_DEPOSIT_TRANSACTION_HASH.parse().unwrap(), - log_index: DEFAULT_ERC20_DEPOSIT_LOG_INDEX.into(), - })), - created_at_time: None, - }); - //TODO XC-221: continue test to withdraw from subaccount -} diff --git a/rs/ethereum/cketh/test_utils/src/ckerc20.rs b/rs/ethereum/cketh/test_utils/src/ckerc20.rs index 5aa97c55e53..1b712d7651f 100644 --- a/rs/ethereum/cketh/test_utils/src/ckerc20.rs +++ b/rs/ethereum/cketh/test_utils/src/ckerc20.rs @@ -324,12 +324,22 @@ impl CkErc20Setup { amount: amount.into(), ckerc20_ledger_id, recipient: recipient.into(), + from_cketh_subaccount: None, + from_ckerc20_subaccount: None, }; + self.call_minter_withdraw_erc20_with(from, arg) + } + + pub fn call_minter_withdraw_erc20_with( + self, + from: Principal, + withdraw_erc20_arg: WithdrawErc20Arg, + ) -> RefreshGasFeeEstimate { let message_id = self.env.send_ingress( PrincipalId::from(from), self.cketh.minter_id, "withdraw_erc20", - Encode!(&arg).expect("failed to encode withdraw args"), + Encode!(&withdraw_erc20_arg).expect("failed to encode withdraw args"), ); RefreshGasFeeEstimate { setup: self,