|
1 | 1 | # Hello, cycles! |
2 | 2 |
|
3 | | -The `hello_cycles` sample project provides a simple example to illustrate how you might add functions to receive cycles, transfer cycles, and check your cycle balance with a simple Motoko actor (canister). |
| 3 | +On the Internet Computer, canisters pay for computation and storage with cycles. This example demonstrates the three fundamental cycle management operations in Motoko: |
4 | 4 |
|
5 | | -This sample project assumes that you are using the default cycles wallet canister that is created for you. |
| 5 | +1. **Inspect the balance** — read how many cycles the canister currently holds. |
| 6 | +2. **Accept incoming cycles** — when a caller attaches cycles to a call, the canister must explicitly claim them. Unclaimed cycles are automatically refunded to the caller. |
| 7 | +3. **Send cycles to another canister** — attach cycles to an outgoing inter-canister call and learn how many were refunded (not accepted by the receiver). |
6 | 8 |
|
7 | | -This example consists of the following functions (see `src/hello_cycles/main.mo`): |
| 9 | +Operations 2 and 3 are two perspectives on the same transaction: |
| 10 | +- The **receiver** calls `Cycles.accept()` and returns how many it took (`accepted`). |
| 11 | +- The **sender** reads `Cycles.refunded()` after the call returns to learn how many cycles came back. |
8 | 12 |
|
9 | | -- The `wallet_balance : () -> async Nat`: enables you to check the current cycle balance for the canister. |
| 13 | +## Functions |
10 | 14 |
|
11 | | -- The `wallet_receive : () -> { amount : Nat64 }`: enables the program to accept cycles that are sent to the canister from a wallet. Both the name and type of this function are dictated by the wallet's implementation (so don't mess with them). |
| 15 | +- `getBalance()` — returns the canister's current cycle balance as `Nat`. |
| 16 | +- `acceptCycles()` — accepts up to 10 million cycles from the caller; returns `{ accepted : Nat64 }`. Any excess is refunded automatically. |
| 17 | +- `sendCycles(receiver, amount)` — forwards `amount` cycles from this canister's balance to `receiver`; returns `{ refunded : Nat }`. A non-zero `refunded` means the receiver did not accept all of the offered cycles. |
12 | 18 |
|
13 | | -- The `transfer : (shared () -> (), Nat) -> async { refunded : Nat }`: enables the program to transfer cycles to any shared function with candid signature `"() -> ()"` (assuming it accepts cycles). One example is the wallet's own `wallet_receive : () -> ()` function. |
| 19 | +## Build and deploy from the command line |
14 | 20 |
|
15 | | -:::caution |
16 | | -The wallet's `wallet_receive` return type differs from hello_cycle's `wallet_receive`. |
17 | | -::: |
| 21 | +### Prerequisites |
18 | 22 |
|
19 | | -## Deploying from ICP Ninja |
| 23 | +- Node.js |
| 24 | +- icp-cli: `npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm` |
| 25 | +- ic-mops: `npm install -g ic-mops` |
| 26 | +- jq (used in `make test` to read the proxy canister principal) |
20 | 27 |
|
21 | | -[](https://icp.ninja/editor?g=https://github.com/dfinity/examples/tree/master/motoko/hello_cycles) |
| 28 | +### Install |
22 | 29 |
|
23 | | -## Build and deploy from the command-line |
24 | | - |
25 | | -### 1. [Download and install the IC SDK.](https://internetcomputer.org/docs/building-apps/getting-started/install) |
| 30 | +```bash |
| 31 | +git clone https://github.com/dfinity/examples |
| 32 | +cd examples/motoko/hello_cycles |
| 33 | +``` |
26 | 34 |
|
27 | | -### 2. Download your project from ICP Ninja using the 'Download files' button on the upper left corner, or [clone the GitHub examples repository.](https://github.com/dfinity/examples/) |
| 35 | +### Deploy and test |
28 | 36 |
|
29 | | -### 3. Navigate into the project's directory. |
| 37 | +```bash |
| 38 | +icp network start -d |
| 39 | +icp deploy |
| 40 | +make test |
| 41 | +icp network stop |
| 42 | +``` |
30 | 43 |
|
31 | | -### 4. Deploy the project to your local environment: |
| 44 | +The tests cover both perspectives: |
32 | 45 |
|
33 | | -``` |
34 | | -dfx start --background --clean && dfx deploy |
35 | | -``` |
| 46 | +- **Test 2 (receiver side)**: calls `acceptCycles` through the local [proxy canister](https://cli.internetcomputer.org/0.3/guides/proxy-canister/) with 1M attached cycles — verifies `accepted > 0`. External callers cannot attach cycles directly; icp-cli routes the cycles via the proxy, which is automatically deployed on the local network. |
| 47 | +- **Tests 3 & 4 (sender side)**: calls `sendCycles` with the canister's **own** `acceptCycles` as the receiver (an inter-canister self-call — no second canister needed). The 5M/15M cycles leave the canister via `sendCycles` and arrive at `acceptCycles`, which accepts up to the 10M limit. Test 3 sends 5M → `refunded = 0` (within limit). Test 4 sends 15M → `refunded = 5_000_000` (5M over the limit). In practice, `sendCycles` would target a different canister. |
36 | 48 |
|
37 | 49 | ## Security considerations and best practices |
38 | 50 |
|
39 | | -If you base your application on this example, it is recommended that you familiarize yourself with and adhere to the [security best practices](https://internetcomputer.org/docs/building-apps/security/overview) for developing on ICP. This example may not implement all the best practices. |
40 | | - |
| 51 | +Refer to the [security best practices](https://docs.internetcomputer.org/guides/security/overview) for information on security and best practices for your ICP app. |
0 commit comments