dfinity / cycles-ledger

The cycles ledger is a global ledger canister that enables principal IDs to hold cycles.
Apache License 2.0
8 stars 5 forks source link

feature request: Another alternative withdraw mechanism #144

Open infu opened 1 month ago

infu commented 1 month ago

The cycles ledger would be easier to use by some systems if - to send cycles to canisters we could just put blob representation of the canister principle in memo and transfer to the minting address. Or have a special accounts to which if we send tokens to {owner:"aaaa-aa", subaccount: canister-principal-to-blob} it withdraws cycles to the canister. I think option 2 is actually better. Every canister has a T-Cycles account and doesn't require users to use tools, just sending to it will refill their canisters.

THLO commented 1 month ago

Could you please explain in what sense these proposals are easier than just calling withdraw?

Sure, this is a non-standard endpoint but it is not harder to call than any other endpoint. I think having a separate endpoint is a cleaner approach compared to adding special rules for standard ICRC transfers.

infu commented 1 month ago

Ok let me provide some reasons: 1) Every canister has a TCycles icrc address like 'aaaa-aa-ekibkqy.1cfac4fa3cf3caf3fca12cf334fc34acf2c9ca' and anyone can top-up a canister without special wallet functions. Devs can put the address on the bottom of their pages if someone wants to donate cycles to keep a service up. That should be enough of a reason already. 2) Canisters can top-up other canisters easier, just by utilizing their existing icrc transfer functions that handle request idempotency and other failures. Instead of having to create a lot of additional code to handle the errors coming from this.

type RejectionCode = variant {
  NoError;
  CanisterError;
  SysTransient;
  DestinationInvalid;
  Unknown;
  SysFatal;
  CanisterReject;
};

type WithdrawArgs = record {
    amount : nat;
    from_subaccount : opt vec nat8;
    to : principal;
    created_at_time : opt nat64;
};

type WithdrawError = variant {
  GenericError : record { message : text; error_code : nat };
  TemporarilyUnavailable;
  FailedToWithdraw : record {
    fee_block : opt nat;
    rejection_code : RejectionCode;
    rejection_reason : text;
  };
  Duplicate : record { duplicate_of : nat };
  BadFee : record { expected_fee : nat };
  InvalidReceiver : record { receiver : principal };
  CreatedInFuture : record { ledger_time : nat64 };
  TooOld;
  InsufficientFunds : record { balance : nat };
};

You could also think of it the other way around. We are still sending tokens, but instead of using icrc1 we are using something else that's not standard and if one was to develop a serious canister that uses it, they would need to create a queue and handle all these errors, which are different from icrc1_transfer (while we are still transferring tokens) Is the Cycles ledger keeping that queue if cycle transfer fails or it returns an error?

infu commented 1 month ago

You could conform with icrc1_transfer by dropping the extra errors like: