Skip to content

Commit

Permalink
Draft deposit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ffakenz committed May 2, 2024
1 parent 72baabc commit a7ffa40
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 126 deletions.
14 changes: 12 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,20 @@ test-4: install
.PHONY: test-a
.SILENT: test-a
test-a: install
# Call the bounty canister to get the GitHub issue and capture the output
# Call the bounty canister for healthcheck and capture the output
@echo "Calling healthcheck on bounty canister..."
@TMP_FILE=$$(mktemp); \
dfx canister call bounty healthcheck > $$TMP_FILE; \
echo "healthcheck response:"; \
cat $$TMP_FILE; \
rm -f $$TMP_FILE
rm -f $$TMP_FILE

.PHONY: test-a
.SILENT: test-a
test-deposit: install
$(shell make/test/deposit.sh)

.PHONY: test-a
.SILENT: test-a
test-deposit-direct: install
$(shell make/test/deposit_direct.sh)
3 changes: 2 additions & 1 deletion bounty/bounty.did
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ service : (principal, int32) -> {
"healthcheck": () -> (text);
"accept": (Contributor) -> ();
"deposit": () -> (DepositReceipt);
"deposit_direct": (amount: nat) -> (DepositReceipt);
"deposit_direct": (amount: nat64) -> (DepositReceipt);
}

153 changes: 69 additions & 84 deletions bounty/src/api/deposit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ use num_traits::ToPrimitive;

use super::*;

use icrc1::{ICRC1, ICRC1_FEE, MAINNET_ICRC1_LEDGER_CANISTER_ID, AllowanceArgs, Account, TransferFromArgs, TransferArg};
use icrc1::{
Account, AllowanceArgs, TransferArg, TransferFromArgs, ICRC1,
MAINNET_ICRC1_LEDGER_CANISTER_ID, Tokens
};

use candid::{CandidType, Nat, Principal};
use candid::{CandidType, Principal};

use ic_ledger_types::{Timestamp, Tokens};
use std::convert::From;

use std::time::{SystemTime, UNIX_EPOCH};

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, CandidType, PartialEq)]
Expand All @@ -24,16 +24,15 @@ pub type DepositReceipt = Result<Tokens, DepositErr>;
pub async fn deposit_impl() -> DepositReceipt {
// FIXME check caller equals the owner who initialized the bounty.
let caller = caller();
let icrc1_token_canister_id =
Principal::from_text(MAINNET_ICRC1_LEDGER_CANISTER_ID)
.expect("Wrong MAINNET_ICRC1_LEDGER_CANISTER_ID");

let icrc1_token_canister_id = Principal::from_text(MAINNET_ICRC1_LEDGER_CANISTER_ID)
.expect("Wrong MAINNET_ICRC1_LEDGER_CANISTER_ID");

return deposit_icrc1(caller, icrc1_token_canister_id).await;
}

async fn deposit_icrc1(
caller: Principal,
icrc1_token_canister_id: Principal
icrc1_token_canister_id: Principal,
) -> Result<Tokens, DepositErr> {
let icrc1_token = ICRC1::new(icrc1_token_canister_id);
let icrc1_token_fee = icrc1_token.get_fee().await;
Expand All @@ -42,103 +41,89 @@ async fn deposit_icrc1(

// depends on:
// > dfx canister call icrc1_ledger_canister icrc2_approve "(record { amount = 100_000; spender = record{owner = principal \"SPENDER_PRINCIPAL\";} })"
let allowance_args =
AllowanceArgs {
account: Account { owner: caller, subaccount : None },
spender : Account { owner: id(), subaccount : None }
};
let allowance_args = AllowanceArgs {
account: Account {
owner: caller,
subaccount: None,
},
spender: Account {
owner: id(),
subaccount: None,
},
};
let allowance = icrc1_token.allowance(allowance_args).await;

let available =
ToPrimitive::to_u64(&allowance.allowance.0)
.expect("Nat value is too large for u64")
- Tokens::e8s(&icrc1_token_fee);

let nanoseconds_since_epoch =
SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_nanos() as u64;

let transfer_from_args =
TransferFromArgs{
// TODO check or FIXME
spender_subaccount: Some(From::from(caller)),
from: Account {
owner: caller,
subaccount : Some(From::from(caller))
},
to: Account {
owner : bounty_canister_id,
subaccount : Some(From::from(bounty_canister_id))
},
amount: Tokens::from_e8s(available),
fee: Some(Tokens::from_e8s(ICRC1_FEE)),
// TODO enhance memo text
memo: Some(String::from("deposit_icrc1").as_bytes().to_vec()),
created_at_time: Some(Timestamp { timestamp_nanos: nanoseconds_since_epoch})
};

let available = allowance.allowance - icrc1_token_fee.clone();
let amount = ToPrimitive::to_u64(&available.0).expect("amount: Nat value is too large for u64");

let transfer_from_args = TransferFromArgs {
// TODO check or FIXME
spender_subaccount: Some(From::from(caller)),
from: Account {
owner: caller,
subaccount: Some(From::from(caller)),
},
to: Account {
owner: bounty_canister_id,
subaccount: Some(From::from(bounty_canister_id)),
},
amount: From::from(amount),
fee: Some(icrc1_token_fee),
// TODO enhance memo text
memo: None,
created_at_time: None,
};

return icrc1_token
.transfer_from(transfer_from_args)
.await
.map(|_| Tokens::from_e8s(available))
.map_err(|error|
DepositErr::TransferFailure{reason: error.to_string()}
);
.map(|_| From::from(amount))
.map_err(|error| DepositErr::TransferFailure {
reason: error.to_string(),
});
}

pub async fn deposit_direct_impl(amount: Nat) -> DepositReceipt {
pub async fn deposit_direct_impl(amount: u64) -> DepositReceipt {
// FIXME check caller equals the owner who initialized the bounty.
let caller = caller();
let icrc1_ledger_canister_id =
Principal::from_text(MAINNET_ICRC1_LEDGER_CANISTER_ID)
.expect("Wrong MAINNET_ICRC1_LEDGER_CANISTER_ID");
let icrc1_ledger_canister_id = Principal::from_text(MAINNET_ICRC1_LEDGER_CANISTER_ID)
.expect("Wrong MAINNET_ICRC1_LEDGER_CANISTER_ID");

return deposit_direct_icrc1(caller, icrc1_ledger_canister_id, amount).await;
}

async fn deposit_direct_icrc1(
caller: Principal,
icrc1_token_canister_id: Principal,
amount: Nat
amount: u64,
) -> Result<Tokens, DepositErr> {
let icrc1_token = ICRC1::new(icrc1_token_canister_id);
let icrc1_token_fee = icrc1_token.get_fee().await;

let bounty_canister_id = id();

let available =
ToPrimitive::to_u64(&amount.0)
.expect("Nat value is too large for u64")
- Tokens::e8s(&icrc1_token_fee);

let nanoseconds_since_epoch =
SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_nanos() as u64;

let transfer_args =
TransferArg{
// TODO check or FIXME
from_subaccount: Some(From::from(caller)),
to: Account {
owner : bounty_canister_id,
subaccount : Some(From::from(bounty_canister_id))
},
amount: Tokens::from_e8s(available),
fee: Some(Tokens::from_e8s(ICRC1_FEE)),
// TODO enhance memo text
memo: Some(String::from("deposit_icrc1").as_bytes().to_vec()),
created_at_time: Some(Timestamp { timestamp_nanos: nanoseconds_since_epoch})
};

let available = amount - icrc1_token_fee.clone();
let amount = ToPrimitive::to_u64(&available.0).expect("amount: Nat value is too large for u64");

let transfer_args = TransferArg {
// TODO check or FIXME
from_subaccount: Some(From::from(caller)),
to: Account {
owner: bounty_canister_id,
subaccount: Some(From::from(bounty_canister_id)),
},
amount: From::from(amount),
fee: Some(icrc1_token_fee),
// TODO enhance memo text
memo: None,
created_at_time: None,
};

return icrc1_token
.transfer(transfer_args)
.await
.map(|_| Tokens::from_e8s(available))
.map_err(|error|
DepositErr::TransferFailure{reason: error.to_string()}
);
.map(|_| From::from(amount))
.map_err(|error| DepositErr::TransferFailure {
reason: error.to_string(),
});
}
57 changes: 21 additions & 36 deletions bounty/src/api/icrc1.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
use candid::{CandidType, Nat, Principal};
use ic_ledger_types::{Memo, Subaccount};
use serde::{Deserialize, Serialize};
use ic_ledger_types::{Timestamp, Subaccount, Tokens};
use ic_cdk::api::call::call;

type BlockIndex = Nat;

use std::fmt::{Display, Formatter};

// pub type Blob = Vec<u8>;

pub type Tokens = Nat;

pub type Timestamp = u64;

#[derive(Debug, Serialize, Deserialize, CandidType, PartialEq)]
pub enum TransferError {
BadFee { expected_fee : Tokens },
Expand All @@ -31,7 +37,7 @@ impl TransferError {
TransferError::TooOld =>
String::from("Transaction too old"),
TransferError::CreatedInFuture { ledger_time } =>
format!("Created in the future: {}", ledger_time.timestamp_nanos.to_string()),
format!("Created in the future: {}", ledger_time.to_string()),
TransferError::TemporarilyUnavailable =>
String::from("Ledger temporarily unavailable"),
TransferError::Duplicate { duplicate_of } =>
Expand Down Expand Up @@ -71,7 +77,7 @@ impl TransferFromError {
TransferFromError::TooOld =>
String::from("Transaction too old"),
TransferFromError::CreatedInFuture { ledger_time } =>
format!("Created in the future: {}", ledger_time.timestamp_nanos.to_string()),
format!("Created in the future: {}", ledger_time.to_string()),
TransferFromError::TemporarilyUnavailable =>
String::from("Ledger temporarily unavailable"),
TransferFromError::Duplicate { duplicate_of } =>
Expand Down Expand Up @@ -99,7 +105,7 @@ impl Display for TransferFromError {
}
TransferFromError::TooOld => write!(f, "Transaction too old"),
TransferFromError::CreatedInFuture { ledger_time } => {
write!(f, "Created in the future: {}", ledger_time.timestamp_nanos.to_string())
write!(f, "Created in the future: {}", ledger_time.to_string())
}
TransferFromError::TemporarilyUnavailable => {
write!(f, "Ledger temporarily unavailable")
Expand All @@ -116,8 +122,6 @@ impl Display for TransferFromError {

pub type TransferFromResult = Result<BlockIndex, TransferFromError>;

pub type Blob = Vec<u8>;

#[derive(Debug, Serialize, Deserialize, CandidType)]
pub struct Account {
pub owner : Principal,
Expand All @@ -130,7 +134,7 @@ pub struct TransferArg {
pub to : Account,
pub amount : Tokens,
pub fee : Option<Tokens>,
pub memo : Option<Blob>,
pub memo : Option<Memo>,
pub created_at_time: Option<Timestamp>
}

Expand All @@ -141,7 +145,7 @@ pub struct TransferFromArgs {
pub to : Account,
pub amount : Tokens,
pub fee : Option<Tokens>,
pub memo : Option<Blob>,
pub memo : Option<Memo>,
pub created_at_time: Option<Timestamp>
}

Expand All @@ -157,7 +161,6 @@ pub struct Allowance {
pub expires_at : Option<Timestamp>
}

pub const ICRC1_FEE: u64 = 10_000;
pub const MAINNET_ICRC1_LEDGER_CANISTER_ID: &str = "mxzaz-hqaaa-aaaar-qaada-cai";

pub struct ICRC1 {
Expand All @@ -170,48 +173,30 @@ impl ICRC1 {
}

pub async fn transfer(&self, args: TransferArg) -> TransferResult {
let argument = (
args.from_subaccount,
args.to,
args.amount,
args.fee,
args.memo,
args.created_at_time
);
let call_result: Result<(TransferResult,), _> =
call(self.principal, "icrc1_transfer", argument).await;

call_result.unwrap().0
call(self.principal, "icrc1_transfer", (args,)).await;
return call_result.unwrap().0
}

pub async fn transfer_from(&self, args: TransferFromArgs) -> TransferFromResult {
let argument = (
args.spender_subaccount,
args.from,
args.to,
args.amount,
args.fee,
args.memo,
args.created_at_time
);
let call_result: Result<(TransferFromResult,), _> =
call(self.principal, "icrc2_transfer_from", argument).await;
call(self.principal, "icrc2_transfer_from", (args,)).await;

call_result.unwrap().0
return call_result.unwrap().0
}

pub async fn allowance(&self, args: AllowanceArgs) -> Allowance {
let argument = (args.account, args.spender);
let call_result: Result<(Allowance, ), _> =
call(self.principal, "icrc2_allowance", argument).await;
call(self.principal, "icrc2_allowance", (args,)).await;

return call_result.unwrap().0;
}

pub async fn get_fee(&self) -> Tokens {
let call_result: Result<(Tokens,), _> =
pub async fn get_fee(&self) -> Nat {
let call_result: Result<(Nat,), _> =
call(self.principal, "icrc1_fee", ()).await;

call_result.unwrap().0
return call_result.unwrap().0
}
}
4 changes: 2 additions & 2 deletions bounty/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use candid::{Nat, Principal};
use candid::Principal;

mod api {
pub mod state;
Expand Down Expand Up @@ -29,7 +29,7 @@ async fn deposit() -> DepositReceipt {
}

#[ic_cdk::update]
async fn deposit_direct(amount: Nat) -> DepositReceipt {
async fn deposit_direct(amount: u64) -> DepositReceipt {
return deposit_direct_impl(amount).await;
}

Expand Down
Loading

0 comments on commit a7ffa40

Please sign in to comment.