Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ More examples are available [here](canister/scripts/examples.sh).

#### Prerequisites:

* Add the `sol_rpc_client` library as a dependency in your `Cargo.toml`.
* Add the `sol_rpc_client` and `sol_rpc_types` libraries as dependencies in your `Cargo.toml`.
* Follow the steps outlined [here](libs/client/README.md#build-requirements) to ensure your code compiles.
* If you are running the example locally, follow the instructions [here](README.md#deployment) to deploy a local instance of the SOL RPC canister.

Expand Down
67 changes: 47 additions & 20 deletions examples/basic_solana/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ the [chain fusion overview](https://internetcomputer.org/docs/building-apps/chai
* [ ] Confirm the IC SDK has been installed with the correct version with `dfx --version`.
* [ ] On **macOS**, an `llvm` version that supports the `wasm32-unknown-unknown` target is required. This is because the `zstd` crate (used, for example, to decode `base64+zstd`-encoded responses from Solana's [`getAccountInfo`](https://solana.com/de/docs/rpc/http/getaccountinfo)) depends on LLVM during compilation. The default LLVM bundled with Xcode does not support `wasm32-unknown-unknown`. To fix this, install the [Homebrew version](https://formulae.brew.sh/formula/llvm), using `brew install llvm`.

> ⚠️ **NOTE:** If you wish to use this example as a starting point for your own project, make sure your follow the instructions in the [build requirements](https://github.com/dfinity/sol-rpc-canister/blob/main/libs/client/README.md#build-requirements) for the `sol_rpc_client` crate to ensure that your code compiles.
> [!NOTE]
> If you wish to use this example as a starting point for your own project, make sure your follow the instructions in the [build requirements](https://github.com/dfinity/sol-rpc-canister/blob/main/libs/client/README.md#build-requirements) for the `sol_rpc_client` crate to ensure that your code compiles.

## Step 1: Building and deploying sample code

Expand All @@ -41,7 +42,6 @@ To clone and build the smart contract in **Rust**:
```bash
git clone https://github.com/dfinity/sol-rpc-canister
cd examples/basic_solana
git submodule update --init --recursive
```

**If you are using macOS, you'll need to install Homebrew and run `brew install llvm` to be able to
Expand All @@ -64,7 +64,7 @@ the following command:
dfx deploy --ic
```

This deploys a wallet canister to the ICP Mainnet which is configured to interact with the Solana Devnet via the SOL RPC
This deploys a Solana wallet canister to the ICP Mainnet which is configured to interact with the **Solana Devnet** via the SOL RPC
canister at [`tghme-zyaaa-aaaar-qarca-cai`](https://dashboard.internetcomputer.org/canister/tghme-zyaaa-aaaar-qarca-cai).
Note that you will need to pay for your requests with cycles. If you do not have cycles available for testing, consider
running this example locally as described in the next section.
Expand All @@ -81,14 +81,15 @@ dfx deploy

What this does:
- `dfx start --clean --background` starts a local instance of the ICP blockchain.
- `dfx deploy` deploys a wallet canister as well as a SOL RPC canister, both locally. The wallet canister interacts with
- `dfx deploy` deploys a Solana wallet canister as well as a SOL RPC canister, both locally. The Solana wallet canister interacts with
the Solana Devnet via the local SOL RPC canister.

**NOTE:** If running this example locally, you will need to skip the `--ic` flag in all subsequent `dfx` commands.
> [!TIP]
> To target Solana Mainnet, you will need to change the `init_arg` for the `basic_solana` canister in the `dfx.json` file. To learn more about the initialization arguments, see the `InitArg` type in [`basic_solana.did`](basic_solana.did).

### Getting the canister ID

If the canister deployment is successful (whether on Mainnet of locally), you should see an output that looks like this:
If the canister deployment is successful (whether on Mainnet or locally), you should see an output that looks like this:

```bash
Deploying: basic_solana
Expand All @@ -107,6 +108,9 @@ In the output above, to see the Candid Web UI for your Solana canister, you woul
`https://bd3sg-teaaa-aaaaa-qaaba-cai.raw.icp0.io/?id=<YOUR-CANISTER-ID>`. You should see the methods specified in the
Candid file `basic_solana.did`.

> [!IMPORTANT]
> If running this example locally, you will need to skip the `--ic` flag in all subsequent `dfx` commands.

## Step 2: Generating a Solana account

A Solana account can be derived from an EdDSA public key. To derive a user's specific account, identified on the IC by a
Expand Down Expand Up @@ -134,7 +138,7 @@ This will return a different Solana address as the one above, such as

## Step 3: Receiving SOL

Now that you have your Solana account, let us send some (Devnet) SOL to it:
Now that you have your Solana account, let us send some Devnet SOL to it:

1. Get some Devnet SOL if you don't have any. You can for example use [this faucet](https://faucet.solana.com/).
2. Send some Devnet SOL to the address you obtained in the previous step. You can use any Solana wallet to do so.
Expand All @@ -148,6 +152,9 @@ You can send SOL using the `send_sol` endpoint on your canister, specifying a So
in the smallest unit (Lamport). For example, to send 1 Lamport to `8HNiduWaBanrBv8c2pgGXZWnpKBdEYuQNHnspqto4yyq`, run
the following command:

> [!NOTE]
> If no principal is provided, the caller's principal is used. In this `basic_solana` example, you could replace `null` with another principal to send SOL on their behalf. This is behaviour you would typically not want in production, as it allows anyone to send SOL from any account to any other account. In production, you would typically want to restrict the `send_sol` endpoint to only allow sending SOL from the caller's account.

```shell
dfx canister --ic call basic_solana send_sol '(null, "8HNiduWaBanrBv8c2pgGXZWnpKBdEYuQNHnspqto4yyq", 1)'
```
Expand Down Expand Up @@ -191,6 +198,9 @@ dfx canister --ic call basic_solana get_nonce

To send some SOL using a durable nonce, you can run the following command:

> [!NOTE]
> If no principal is provided, the caller's principal is used. In this `basic_solana` example, you could replace `null` with another principal to send SOL on their behalf. This is behaviour you would typically not want in production, as it allows anyone to send SOL from any account to any other account. In production, you would typically want to restrict the `send_sol_with_durable_nonce` endpoint to only allow sending SOL from the caller's account.

```shell
dfx canister --ic call basic_solana send_sol_with_durable_nonce '(null, "8HNiduWaBanrBv8c2pgGXZWnpKBdEYuQNHnspqto4yyq", 1)'
```
Expand Down Expand Up @@ -223,21 +233,28 @@ example use the USDC token whose mint account on Devnet is `4zMMC9srt5Ri5X14GAgX
You first need to create [Associated Token Accounts (ATA)](https://spl.solana.com/associated-token-account) for the
sender and recipient accounts if they do not exist yet. An ATA is
a [Program Derived Address (PDA)](https://solana.com/docs/core/pda) derived from a Solana account using the token mint
account. An ATA is needed for each type of SPL token held by a Solana account. To create the ATAs for the sender and
recipient, you can run the following commands:
account. An ATA is needed for each type of SPL token held by a Solana account.

We create two new identities, one for the sender and one for the recipient. You can do this by running the following commands:

```bash
dfx identity use sender
dfx canister call basic_solana create_associated_token_account '(null, "<TOKEN MINT ADDRESS`>")'
dfx identity use recipient
dfx canister call basic_solana create_associated_token_account '(null, "<TOKEN MINT ADDRESS`>")'
dfx identity new sender
dfx identity new recipient
```

or
We have to make sure the Solana accounts belonging to the new identities created above actually hold SOL to pay for transaction fees. For this, follow the instructions outlined in [Step 2](#step-2-generating-a-solana-account) and [Step 3](#step-3-receiving-sol) for each identity. You can switch between identities using the `dfx identity use <IDENTITY_NAME>` command or specify the identity to use by adding the `--identity <IDENTITY_NAME>` flag to the `dfx` commands.

To create the ATAs for the sender and
recipient, you can run the following commands:

> [!NOTE]
> If no principal is provided as the first argument, the caller's principal is used.

```bash
dfx canister call basic_solana create_associated_token_account '(opt principal "<SENDER PRINCIPAL>", "<TOKEN MINT ADDRESS`>")'
dfx canister call basic_solana create_associated_token_account '(opt principal "<RECIPIENT PRINCIPAL>", "<TOKEN MINT ADDRESS`>")'
dfx identity use sender
dfx canister --ic call basic_solana create_associated_token_account '(null, "<TOKEN MINT ADDRESS>")'
dfx identity use recipient
dfx canister --ic call basic_solana create_associated_token_account '(null, "<TOKEN MINT ADDRESS>")'
```

This works by sending transactions that instruct the
Expand All @@ -249,26 +266,36 @@ To send some tokens from the sender to the receiver, you will need to obtain som
e.g. [this faucet](https://faucet.circle.com/) for USDC). To do this, you will need the ATA address of the sender. You
can for example get it by running the following command:

> [!NOTE]
> If no principal is provided as the first argument, the caller's principal is used.

```bash
dfx identity use sender
dfx canister call basic_solana associated_token_account '(null, "<TOKEN MINT ADDRESS`>")'
dfx canister --ic call basic_solana associated_token_account '(null, "<TOKEN MINT ADDRESS>")'
```

To transfer some tokens from the sender to the recipient, you can run the following command:

> [!NOTE]
> If no principal is provided as the first argument, the caller's principal is used.
> Make sure to use the `RECIPIENT SOLANA ADDRESS`, not their ATA.

```bash
dfx identity use sender
dfx canister call basic_solana send_spl_token '(null, "<TOKEN MINT ADDRESS>", "<RECIPIENT SOLANA ADDRESS>", <AMOUNT>)'
dfx canister --ic call basic_solana send_spl_token '(null, "<TOKEN MINT ADDRESS>", "<RECIPIENT SOLANA ADDRESS>", <AMOUNT>)'
```

The `send_spl_token` endpoint works similarly to the `send_sol` endpoint, but creates a transaction with the sender and
recipient ATAs instead of their account addresses. You can also inspect the resulting transaction on a Solana explorer,
and verify that the associated token balances were updated accordingly. You can also check the updated token balances by
running the following commands:

> [!NOTE]
> If no ATA is provided, it is derived from the caller's principal.

```bash
dfx canister call basic_solana get_spl_token_balance '(opt principal "<SENDER PRINCIPAL>", "<TOKEN MINT ADDRESS`>")'
dfx canister call basic_solana get_spl_token_balance '(opt principal "<RECIPIENT PRINCIPAL>", "<TOKEN MINT ADDRESS`>")'
dfx canister --ic call basic_solana get_spl_token_balance '(opt "<SENDER ATA>", "<TOKEN MINT ADDRESS>")'
dfx canister --ic call basic_solana get_spl_token_balance '(opt "<RECIPIENT ATA>", "<TOKEN MINT ADDRESS>")'
```

## Conclusion
Expand Down
2 changes: 1 addition & 1 deletion examples/basic_solana/local/canister_ids.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"sol_rpc": {
"local": "lxzze-o7777-77777-aaaaa-cai"
"local": "tghme-zyaaa-aaaar-qarca-cai"
}
}
11 changes: 8 additions & 3 deletions examples/basic_solana/local/dfx.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"canisters": {
"sol_rpc": {
"specified_id": "lxzze-o7777-77777-aaaaa-cai",
"specified_id": "tghme-zyaaa-aaaar-qarca-cai",
"candid": "../../../canister/sol_rpc_canister.did",
"package": "sol_rpc_canister",
"type": "custom",
Expand All @@ -12,7 +12,12 @@
"name": "candid:service"
}
],
"init_arg": "( record { manageApiKeys = null; overrideProvider = null; logFilter = opt variant { ShowAll }; numSubnetNodes = null; mode = opt variant { Demo }; } )"
"init_arg": "( record { manageApiKeys = null; overrideProvider = null; logFilter = opt variant { ShowAll }; numSubnetNodes = null; mode = opt variant { Demo }; } )",
"remote": {
"id": {
"ic": "tghme-zyaaa-aaaar-qarca-cai"
}
}
},
"basic_solana": {
"candid": "../basic_solana.did",
Expand All @@ -25,7 +30,7 @@
"name": "candid:service"
}
],
"init_arg": "( record { solana_network = opt variant { Custom = record { url = \"https://api.devnet.solana.com\"; headers = null } }; ed25519_key_name = opt variant { LocalDevelopment }; sol_rpc_canister_id = opt principal \"lxzze-o7777-77777-aaaaa-cai\" } )"
"init_arg": "( record { solana_network = opt variant { Custom = record { url = \"https://api.devnet.solana.com\"; headers = null } }; ed25519_key_name = opt variant { LocalDevelopment }; sol_rpc_canister_id = opt principal \"tghme-zyaaa-aaaar-qarca-cai\" } )"
}
},
"version": 1
Expand Down