diff --git a/spec/index.md b/spec/index.md index ee37e789..20142bb6 100644 --- a/spec/index.md +++ b/spec/index.md @@ -1428,6 +1428,7 @@ The following sections describe various System API functions, also referred to a ic0.canister_self_copy : (dst : i32, offset : i32, size : i32) -> (); // * ic0.canister_cycle_balance : () -> i64; // * ic0.canister_cycle_balance128 : (dst : i32) -> (); // * + ic0.canister_liquid_cycle_balance128 : (dst : i32) -> (); // * ic0.canister_status : () -> i32; // * ic0.canister_version : () -> i64; // * @@ -1692,9 +1693,7 @@ There must be at most one call to `ic0.call_on_cleanup` between `ic0.call_new` a This system call traps if there is no call under construction, i.e., if not called between `ic0.call_new` and `ic0.call_perform`. - This system call traps if trying to transfer more cycles than are in the current balance of the canister. - - This system call traps if the cycle balance of the canister after transferring cycles decreases below the canister's freezing limit. + This system call traps if trying to transfer more cycles than returned by `ic0.canister_liquid_cycle_balance128`. - `ic0.call_cycles_add128 : (amount_high : i64, amount_low : i64) -> ()` @@ -1702,15 +1701,13 @@ There must be at most one call to `ic0.call_on_cleanup` between `ic0.call_new` a The amount of cycles it moves is represented by a 128-bit value which can be obtained by combining the `amount_high` and `amount_low` parameters. - The cycles are deducted from the balance as shown by `ic0.canister_cycles_balance128` immediately, and moved back if the call cannot be performed (e.g. if `ic0.call_perform` signals an error, if the canister invokes `ic0.call_new`, or returns without calling `ic0.call_perform`). + The cycles are deducted from the balance as shown by `ic0.canister_cycle_balance128` immediately, and moved back if the call cannot be performed (e.g. if `ic0.call_perform` signals an error, if the canister invokes `ic0.call_new`, or returns without calling `ic0.call_perform`). This system call may be called multiple times between `ic0.call_new` and `ic0.call_perform`. This system call traps if there is no call under construction, i.e., if not called between `ic0.call_new` and `ic0.call_perform`. - This system call traps if trying to transfer more cycles than are in the current balance of the canister. - - This system call traps if the cycle balance of the canister after transferring cycles decreases below the canister's freezing limit. + This system call traps if trying to transfer more cycles than returned by `ic0.canister_liquid_cycle_balance128`. - `ic0.call_perform : () -> ( err_code : i32 )` @@ -1736,17 +1733,21 @@ This specification currently does not go into details about which actions cost h - `ic0.canister_cycle_balance : () → i64` - Indicates the current cycle balance of the canister. It is the canister balance before the execution of the current message, minus a reserve to pay for the execution of the current message, minus any cycles queued up to be sent via `ic0.call_cycles_add` and `ic0.call_cycles_add128`. After execution of the message, the IC may add unused cycles from the reserve back to the balance. + Indicates the current cycle balance of the canister. It is the canister balance before the execution of the current message, minus a reserve to pay for the execution of the current message and calls finalized via `ic0.call_perform`, minus any cycles queued up to be sent via `ic0.call_cycles_add` and `ic0.call_cycles_add128`. After execution of the message, the IC may add unused cycles from the reserve back to the balance. :::note -This call traps if the current balance does not fit into a 64-bit value. Canisters that need to deal with larger cycles balances should use `ic0.canister_cycles_balance128` instead. +This call traps if the current balance does not fit into a 64-bit value. Canisters that need to deal with larger cycles balances should use `ic0.canister_cycle_balance128` instead. ::: - `ic0.canister_cycle_balance128 : (dst : i32) → ()` - Indicates the current cycle balance of the canister by copying the value at the location `dst` in the canister memory. It is the canister balance before the execution of the current message, minus a reserve to pay for the execution of the current message, minus any cycles queued up to be sent via `ic0.call_cycles_add` and `ic0.call_cycles_add128`. After execution of the message, the IC may add unused cycles from the reserve back to the balance. + Indicates the current cycle balance of the canister by copying the value at the location `dst` in the canister memory. It is the canister balance before the execution of the current message, minus a reserve to pay for the execution of the current message and calls finalized via `ic0.call_perform`, minus any cycles queued up to be sent via `ic0.call_cycles_add` and `ic0.call_cycles_add128`. After execution of the message, the IC may add unused cycles from the reserve back to the balance. + +- `ic0.canister_liquid_cycle_balance128 : (dst : i32) → ()` + + Indicates the current amount of cycles that is available for spending in calls and execution by copying the value at the location `dst` in the canister memory. This amount of cycles can be safely attached to a call via `ic0.call_cycles_add128` as long as the memory usage of the canister does not increase for the rest of the current message execution. Hence, it is recommended to never attach the entire `ic0.canister_liquid_cycle_balance128` to a call, but leave some slack based on the expected canister memory usage and freezing threshold. - `ic0.msg_cycles_available : () → i64` @@ -7107,11 +7108,15 @@ ic0.canister_cycle_balance() : i64 = if es.balance >= 2^64 then Trap {cycles_used = es.cycles_used;} return es.balance -ic0.canister_cycles_balance128(dst : i32) = +ic0.canister_cycle_balance128(dst : i32) = if es.context = s then Trap {cycles_used = es.cycles_used;} let amount = es.balance copy_cycles_to_canister(dst, amount.to_little_endian_bytes()) +ic0.canister_liquid_cycle_balance128(dst : i32) = + if es.context = s then Trap {cycles_used = es.cycles_used;} + copy_cycles_to_canister(dst, liquid_balance(es).to_little_endian_bytes()) + ic0.canister_status() : i32 = if es.context = s then Trap {cycles_used = es.cycles_used;} match es.params.sysenv.canister_status with