Skip to content

Commit

Permalink
chore!: rename send to withdraw (#101)
Browse files Browse the repository at this point in the history
  • Loading branch information
sesi200 committed Feb 9, 2024
1 parent 1189545 commit 15fb7c9
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 149 deletions.
16 changes: 8 additions & 8 deletions README.md
Expand Up @@ -5,7 +5,7 @@ The cycles ledger is a global ledger canister that enables principal IDs to hold
The cycles ledger complies with the [ICRC-2](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-2/README.md) and [ICRC-1](https://github.com/dfinity/ICRC-1/tree/main/standards/ICRC-1/README.md) token standards.
Additionally, it implements the endpoints defined in the proposed [ICRC-3](https://github.com/dfinity/ICRC-1/pull/128) standard.

The cycles ledger further provides endpoints to deposit and send out cycles, and also
The cycles ledger further provides endpoints to deposit and withdraw cycles, and also
to create canisters using cycles. These custom endpoints are introduced in the following.

## Depositing Cycles
Expand All @@ -27,9 +27,9 @@ When invoked with a particular account (and, optionally, a memo), the balance of

> NOTE: The deposit is rejected if fewer than 100M cycles are attached to the call.
## Sending Cycles
## Withdrawing Cycles

The cycles ledger has the following endpoint to send cycles to other canisters.
The cycles ledger has the following endpoint to withdraw cycles to other canisters.

```
type BlockIndex = nat;
Expand All @@ -44,17 +44,17 @@ type RejectionCode = variant {
CanisterReject;
};
type SendArgs = record {
type WithdrawArgs = record {
amount : nat;
from_subaccount : opt vec nat8;
to : principal;
created_at_time : opt nat64;
};
type SendError = variant {
type WithdrawError = variant {
GenericError : record { message : text; error_code : nat };
TemporarilyUnavailable;
FailedToSend : record {
FailedToWithdraw : record {
fee_block : opt nat;
rejection_code : RejectionCode;
rejection_reason : text;
Expand All @@ -67,14 +67,14 @@ type SendError = variant {
InsufficientFunds : record { balance : nat };
};
send : (SendArgs) -> (variant { Ok : BlockIndex; Err : SendError });
withdraw : (WithdrawArgs) -> (variant { Ok : BlockIndex; Err : WithdrawError });
```

The two required parameters are the amount to be sent and the principal ID of
the targeted canister ID. Optionally, the subaccount from which cycles are
deducted and the time at which the transaction is created can be set as well.

There is a fee of **100M cycles** for sending cycles to another canister.
There is a fee of **100M cycles** for withdrawing cycles to another canister.

> NOTE: The function returns an error when the parameter `to` is not a valid canister ID.
Expand Down
8 changes: 4 additions & 4 deletions cycles-ledger/cycles-ledger.did
Expand Up @@ -37,16 +37,16 @@ type RejectionCode = variant {
SysFatal;
CanisterReject;
};
type SendArgs = record {
type WithdrawArgs = record {
amount : nat;
from_subaccount : opt vec nat8;
to : principal;
created_at_time : opt nat64;
};
type SendError = variant {
type WithdrawError = variant {
GenericError : record { message : text; error_code : nat };
TemporarilyUnavailable;
FailedToSend : record {
FailedToWithdraw : record {
fee_block : opt nat;
rejection_code : RejectionCode;
rejection_reason : text;
Expand Down Expand Up @@ -249,6 +249,6 @@ service : (ledger_args : LedgerArgs) -> {
icrc2_transfer_from : (TransferFromArgs) -> (variant { Ok : nat; Err : TransferFromError });
icrc3_get_tip_certificate : () -> (opt DataCertificate) query;
icrc3_get_blocks : (GetBlocksArgs) -> (GetBlocksResult) query;
send : (SendArgs) -> (variant { Ok : BlockIndex; Err : SendError });
withdraw : (WithdrawArgs) -> (variant { Ok : BlockIndex; Err : WithdrawError });
create_canister : (CreateCanisterArgs) -> (variant { Ok : CreateCanisterSuccess; Err : CreateCanisterError });
};
6 changes: 3 additions & 3 deletions cycles-ledger/src/endpoints.rs
Expand Up @@ -61,7 +61,7 @@ pub struct SupportedStandard {
}

#[derive(CandidType, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct SendArgs {
pub struct WithdrawArgs {
#[serde(default)]
pub from_subaccount: Option<Subaccount>,
pub to: Principal,
Expand All @@ -71,7 +71,7 @@ pub struct SendArgs {
}

#[derive(CandidType, Deserialize, Clone, Debug, PartialEq, Eq)]
pub enum SendError {
pub enum WithdrawError {
BadFee {
expected_fee: NumCycles,
},
Expand All @@ -86,7 +86,7 @@ pub enum SendError {
Duplicate {
duplicate_of: BlockIndex,
},
FailedToSend {
FailedToWithdraw {
fee_block: Option<Nat>,
rejection_code: RejectionCode,
rejection_reason: String,
Expand Down
8 changes: 4 additions & 4 deletions cycles-ledger/src/main.rs
@@ -1,6 +1,6 @@
use candid::{candid_method, Nat};
use cycles_ledger::endpoints::{
DataCertificate, GetBlocksArgs, GetBlocksResult, LedgerArgs, SendError,
DataCertificate, GetBlocksArgs, GetBlocksResult, LedgerArgs, WithdrawError,
};
use cycles_ledger::logs::{Log, LogEntry, Priority};
use cycles_ledger::logs::{P0, P1};
Expand Down Expand Up @@ -236,19 +236,19 @@ fn icrc3_get_blocks(args: GetBlocksArgs) -> GetBlocksResult {

#[update]
#[candid_method]
async fn send(args: endpoints::SendArgs) -> Result<Nat, SendError> {
async fn withdraw(args: endpoints::WithdrawArgs) -> Result<Nat, WithdrawError> {
let from = Account {
owner: ic_cdk::caller(),
subaccount: args.from_subaccount,
};

let Some(amount) = args.amount.0.to_u128() else {
return Err(SendError::InsufficientFunds {
return Err(WithdrawError::InsufficientFunds {
balance: Nat::from(balance_of(&from)),
});
};

storage::send(
storage::withdraw(
from,
args.to,
amount,
Expand Down
8 changes: 4 additions & 4 deletions cycles-ledger/src/memo.rs
Expand Up @@ -5,21 +5,21 @@ use icrc_ledger_types::icrc1::transfer::Memo;
use minicbor::{Decode, Encode, Encoder};

#[derive(Decode, Encode, Debug, Clone, Copy, PartialEq, Eq)]
pub struct SendMemo<'a> {
pub struct WithdrawMemo<'a> {
#[cbor(n(0), with = "minicbor::bytes")]
pub receiver: &'a [u8],
}

impl<'a> From<&'a CanisterId> for SendMemo<'a> {
impl<'a> From<&'a CanisterId> for WithdrawMemo<'a> {
fn from(canister: &'a CanisterId) -> Self {
Self {
receiver: canister.as_slice(),
}
}
}

pub fn encode_send_memo(target_canister: &Principal) -> Memo {
let memo = SendMemo::from(target_canister);
pub fn encode_withdraw_memo(target_canister: &Principal) -> Memo {
let memo = WithdrawMemo::from(target_canister);
let mut encoder = Encoder::new(Vec::new());
encoder.encode(memo).expect("Encoding of memo failed");
encoder.into_writer().into()
Expand Down
47 changes: 26 additions & 21 deletions cycles-ledger/src/storage.rs
@@ -1,10 +1,10 @@
use crate::config::{Config, REMOTE_FUTURE};
use crate::endpoints::{
CmcCreateCanisterArgs, CmcCreateCanisterError, CreateCanisterError, CreateCanisterSuccess,
DataCertificate, DepositResult, SendError,
DataCertificate, DepositResult, WithdrawError,
};
use crate::logs::{P0, P1};
use crate::memo::{encode_send_memo, validate_memo};
use crate::memo::{encode_withdraw_memo, validate_memo};
use crate::{
ciborium_to_generic_value, compact_account,
config::{self, MAX_MEMO_LENGTH},
Expand Down Expand Up @@ -1036,19 +1036,19 @@ mod approve {
}
}

mod send {
mod withdraw {
use candid::Nat;

use crate::endpoints::SendError;
use crate::endpoints::WithdrawError;

use super::transfer_from::UNKNOWN_GENERIC_ERROR;

pub fn anyhow_error(error: anyhow::Error) -> SendError {
pub fn anyhow_error(error: anyhow::Error) -> WithdrawError {
unknown_generic_error(format!("{:#}", error))
}

pub fn unknown_generic_error(message: String) -> SendError {
SendError::GenericError {
pub fn unknown_generic_error(message: String) -> WithdrawError {
WithdrawError::GenericError {
error_code: Nat::from(UNKNOWN_GENERIC_ERROR),
message,
}
Expand Down Expand Up @@ -1108,7 +1108,7 @@ impl From<ProcessTransactionError> for ApproveError {
}
}

impl From<ProcessTransactionError> for SendError {
impl From<ProcessTransactionError> for WithdrawError {
fn from(error: ProcessTransactionError) -> Self {
use ProcessTransactionError::*;

Expand All @@ -1120,7 +1120,7 @@ impl From<ProcessTransactionError> for SendError {
duplicate_of: Nat::from(duplicate_of),
},
InvalidCreatedAtTime(err) => err.into(),
GenericError(err) => send::unknown_generic_error(format!("{:#}", err)),
GenericError(err) => withdraw::unknown_generic_error(format!("{:#}", err)),
}
}
}
Expand Down Expand Up @@ -1286,7 +1286,7 @@ impl From<CreatedAtTimeValidationError> for TransferFromError {
}
}

impl From<CreatedAtTimeValidationError> for SendError {
impl From<CreatedAtTimeValidationError> for WithdrawError {
fn from(value: CreatedAtTimeValidationError) -> Self {
match value {
CreatedAtTimeValidationError::TooOld => Self::TooOld,
Expand Down Expand Up @@ -1408,17 +1408,17 @@ fn is_self_authenticating(principal: &Principal) -> bool {
.is_some_and(|b| *b == CANDID_PRINCIPAL_SELF_AUTHENTICATING_TAG)
}

pub async fn send(
pub async fn withdraw(
from: Account,
to: Principal,
amount: u128,
now: u64,
created_at_time: Option<u64>,
) -> Result<Nat, SendError> {
use SendError::*;
) -> Result<Nat, WithdrawError> {
use WithdrawError::*;

if is_self_authenticating(&to) {
// if it is not an opaque principal ID, the user is trying to send to a non-canister target
// if it is not an opaque principal ID, the user is trying to withdraw to a non-canister target
return Err(InvalidReceiver { receiver: to });
}

Expand All @@ -1432,7 +1432,7 @@ pub async fn send(
let transaction = Transaction {
operation: Operation::Burn { from, amount },
created_at_time,
memo: Some(encode_send_memo(&to)),
memo: Some(encode_withdraw_memo(&to)),
};
check_duplicate(&transaction)?;

Expand All @@ -1445,10 +1445,15 @@ pub async fn send(

// sanity check that the total_supply won't underflow
read_state(|state| state.check_total_supply_decrease(amount_with_fee))
.with_context(|| format!("Unable to send {} cycles from {} to {}", amount, from, to))
.map_err(send::anyhow_error)?;
.with_context(|| {
format!(
"Unable to withdraw {} cycles from {} to {}",
amount, from, to
)
})
.map_err(withdraw::anyhow_error)?;

// The send process involves 3 steps:
// The withdraw process involves 3 steps:
// 1. burn cycles + fee
// 2. call deposit_cycles on the management canister
// 3. if 2. fails then mint cycles
Expand All @@ -1458,7 +1463,7 @@ pub async fn send(
let block_index = process_block(transaction.clone(), now, Some(config::FEE))?;

if let Err(err) = mutate_state(|state| state.debit(&from, amount_with_fee)) {
log_error_and_trap(&err.context(format!("Unable to perform send: {:?}", transaction)))
log_error_and_trap(&err.context(format!("Unable to perform withdraw: {:?}", transaction)))
};

prune(now);
Expand All @@ -1471,7 +1476,7 @@ pub async fn send(
match reimburse(from, amount, now) {
Ok(fee_block) => {
prune(now);
return Err(FailedToSend {
return Err(FailedToWithdraw {
fee_block: Some(Nat::from(fee_block)),
rejection_code,
rejection_reason,
Expand Down Expand Up @@ -1669,7 +1674,7 @@ fn reimburse(acc: Account, amount: u128, now: u64) -> Result<u64, ProcessTransac
let block_index = process_transaction(transaction.clone(), now)?;

if let Err(err) = mutate_state(|state| state.credit(&acc, amount)) {
log_error_and_trap(&err.context(format!("Unable to reimburse send: {:?}", transaction)))
log_error_and_trap(&err.context(format!("Unable to reimburse withdraw: {:?}", transaction)))
};

prune(now);
Expand Down
19 changes: 11 additions & 8 deletions cycles-ledger/tests/client.rs
Expand Up @@ -7,7 +7,7 @@ use cycles_ledger::{
endpoints::{
self, CmcCreateCanisterError, CreateCanisterArgs, CreateCanisterError,
CreateCanisterSuccess, DataCertificate, DepositResult, GetBlocksArg, GetBlocksArgs,
GetBlocksResult, SendArgs,
GetBlocksResult, WithdrawArgs,
},
storage::{Block, CMC_PRINCIPAL},
};
Expand Down Expand Up @@ -100,17 +100,20 @@ pub fn get_block(env: &StateMachine, ledger_id: Principal, block_index: Nat) ->
}
}

pub fn send(
pub fn withdraw(
env: &StateMachine,
ledger_id: Principal,
from: Account,
args: SendArgs,
) -> Result<Nat, endpoints::SendError> {
args: WithdrawArgs,
) -> Result<Nat, endpoints::WithdrawError> {
let arg = Encode!(&args).unwrap();
if let WasmResult::Reply(res) = env.update_call(ledger_id, from.owner, "send", arg).unwrap() {
Decode!(&res, Result<candid::Nat, cycles_ledger::endpoints::SendError>).unwrap()
if let WasmResult::Reply(res) = env
.update_call(ledger_id, from.owner, "withdraw", arg)
.unwrap()
{
Decode!(&res, Result<candid::Nat, cycles_ledger::endpoints::WithdrawError>).unwrap()
} else {
panic!("send rejected")
panic!("withdraw rejected")
}
}

Expand All @@ -127,7 +130,7 @@ pub fn create_canister(
{
Decode!(&res, Result<CreateCanisterSuccess, CreateCanisterError>).unwrap()
} else {
panic!("send rejected")
panic!("create_canister rejected")
}
}

Expand Down

0 comments on commit 15fb7c9

Please sign in to comment.