Skip to content

add examples to demonstrate access canister using wallet canister #41

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
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
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ config := agent.Config{
}
```

### Using WalletCanister as proxy
Please refer ic/wallet/README.md for detail.



## Packages

You can find the documentation for each package in the links below. Examples can be found throughout the documentation.
Expand Down
41 changes: 41 additions & 0 deletions agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"net/url"
"testing"

"github.com/aviate-labs/agent-go"
Expand Down Expand Up @@ -340,3 +341,43 @@ type testLogger struct{}
func (t testLogger) Printf(format string, v ...any) {
fmt.Printf("[TEST]"+format+"\n", v...)
}

// Refer ic/wallet/README.md for more information
func Test_Agent_LocalNet(t *testing.T) {
host, err := url.Parse("http://localhost:4943")
if err != nil {
panic(err)
}
cfg := agent.Config{
ClientConfig: &agent.ClientConfig{Host: host},
FetchRootKey: true,
DisableSignedQueryVerification: true, //MUST BE TRUE TO ACCESS LOCAL REPLICA
}
a, err := agent.New(cfg)
if err != nil {
panic(err)
}

principal := principal.MustDecode("bkyz2-fmaaa-aaaaa-qaaaq-cai")

var s1 string
err = a.Query(principal, "greet", []any{}, []any{&s1})
if err != nil {
panic(err)
}
fmt.Printf("s1:%v\n", s1)

var s2 string
err = a.Query(principal, "concat", []any{"hello", "world"}, []any{&s2})
if err != nil {
panic(err)
}
fmt.Printf("s2:%v\n", s2)

var s3 string
err = a.Call(principal, "sha256", []any{"hello, world", uint32(2)}, []any{&s3}) //2's type should match with taht defined in hasher canister.
if err != nil {
panic(err)
}
fmt.Printf("s3:%v\n", s3)
}
84 changes: 84 additions & 0 deletions ic/wallet/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
## 2 methods to access a canister:
- agent: directly access, normal purpose.
- wallet: use wallet canister as proxy, user can provide cycles for canister execution, demonstrating in Test_WalletCanister_LocalNet()

## Prerequisite
For test agent-go/agent_test.go/Test_Agent_LocalNet() and agent-go/ic/wallet/agent_test.go/Test_WalletCanister_LocalNet(), a hasher canister deployed on local replica for demonstration, its code shown below.
```rust

use ic_cdk_macros::{init, post_upgrade, pre_upgrade, query, update};
use ic_cdk::api::call::{msg_cycles_accept, msg_cycles_available};
use sha2::{Digest, Sha256};

const SINGLE_SHA256_COST_CYCLES :u64 = 200_000_000;
const CONCAT_STRING_COST_CYCLES :u64 = 200_000_000;

#[query]
pub fn greet()->String{
format!("Hello, World!")
}

#[update]
pub fn sha256_with_cycles(msg :String, n :u32) -> String {
let available_cycles = msg_cycles_available();
let needed_cycles:u64 = SINGLE_SHA256_COST_CYCLES*(n as u64);
ic_cdk::println!("needed_cycles: {}", needed_cycles);
if available_cycles < needed_cycles{
ic_cdk::eprintln!("Not enough cycles provided for sha256 operation.");
return String::from("Not enough cycles provided for sha256 operation.");
}
let _ = msg_cycles_accept(needed_cycles);
let mut input = msg.clone().into_bytes();
ic_cdk::println!("input: {}", msg);
for _ in 0..n {
let mut hasher = Sha256::new();
hasher.update(input);
input = hasher.finalize().to_vec();
}
ic_cdk::println!("result: {}", hex::encode(input.clone()));
hex::encode(input)
}

#[update]
pub fn sha256(msg :String, n :u32) -> String {
let mut input = msg.clone().into_bytes();
ic_cdk::println!("input: {}", msg);
for _ in 0..n {
let mut hasher = Sha256::new();
hasher.update(input);
input = hasher.finalize().to_vec();
}
ic_cdk::println!("result: {}", hex::encode(input.clone()));
hex::encode(input)
}

#[query]
pub fn concat(s1:String, s2:String) -> String {
format!("{} {}", s1, s2)
}

#[update]
pub fn concat_with_cycles(s1:String, s2:String) -> String {
let available_cycles = msg_cycles_available();
let needed_cycles:u64 = CONCAT_STRING_COST_CYCLES;
if available_cycles < needed_cycles{
ic_cdk::eprintln!("Not enough cycles provided for concat operation.");
return String::from("Not enough cycles provided for concat operation.");
}
let _ = msg_cycles_accept(needed_cycles);
format!("{} {}", s1, s2)
}
```
## Tips
- export identity .pem
```
dfx identity list
dfx identity use xxx // one of the listed idnetity
dfx identity export xxx
```

- get the identity's wallet
```
dfx identity get-wallet
```

130 changes: 130 additions & 0 deletions ic/wallet/agent_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package wallet

import (
"fmt"
"net/url"
"testing"
"time"

"github.com/aviate-labs/agent-go"
"github.com/aviate-labs/agent-go/candid/idl"
"github.com/aviate-labs/agent-go/identity"
"github.com/aviate-labs/agent-go/principal"
)

// replace with "dfx identity export xxx" result
var pem = []byte(`
-----BEGIN EC PRIVATE KEY-----
-----END EC PRIVATE KEY-----`)

func Test_WalletCanister_LocalNet(t *testing.T) {
id, err := identity.NewSecp256k1IdentityFromPEMWithoutParameters(pem)
if err != nil {
panic(err)
}

host, err := url.Parse("http://localhost:4943")
if err != nil {
panic(err)
}
cfg := agent.Config{
Identity: id,
ClientConfig: &agent.ClientConfig{Host: host},
FetchRootKey: true,
PollTimeout: 30 * time.Second,
DisableSignedQueryVerification: true, //MUST BE TRUE TO ACCESS LOCAL REPLICA
}
a, err := NewAgent(principal.MustDecode("bnz7o-iuaaa-aaaaa-qaaaa-cai"), cfg)
if err != nil {
panic(err)
}

balance, err := a.WalletBalance()
if err != nil {
panic(err)
}
fmt.Printf("balance:%v\n", balance)

canisterId := principal.MustDecode("bkyz2-fmaaa-aaaaa-qaaaq-cai")

var s1 string
err = a.Query(canisterId, "greet", []any{}, []any{&s1})
if err != nil {
panic(err)
}
fmt.Printf("s1:%v\n", s1)

var s2 string
err = a.Query(canisterId, "concat", []any{"hello", "world"}, []any{&s2})
if err != nil {
panic(err)
}
fmt.Printf("s2:%v\n", s2)

var s3 string
err = a.Call(canisterId, "sha256", []any{"hello, world", uint32(2)}, []any{&s3}) //2's type should match with taht defined in hasher canister.
if err != nil {
panic(err)
}
fmt.Printf("s3:%v\n", s3)

//step4: concat_with_cycles
input4, err := idl.Marshal([]any{"hello", "world"})
if err != nil {
panic(err)
}
fmt.Printf("input4:%v\n", input4)

arg4 := struct {
Canister principal.Principal `ic:"canister" json:"canister"`
MethodName string `ic:"method_name" json:"method_name"`
Args []byte `ic:"args" json:"args"`
Cycles uint64 `ic:"cycles" json:"cycles"`
}{
Canister: principal.MustDecode("bkyz2-fmaaa-aaaaa-qaaaq-cai"),
MethodName: "concat_with_cycles",
Args: []byte(input4),
Cycles: 200_000_000,
}
res4, err := a.WalletCall(arg4)
if err != nil {
panic(err)
}
var s4 string
err = idl.Unmarshal(res4.Ok.Return, []any{&s4})
if err != nil {
panic(err)
}
fmt.Printf("s4:%v\n", s4)

//step5, sha256_with_cycles
n := uint32(2)
input5, err := idl.Marshal([]any{"hello, world", n})
if err != nil {
panic(err)
}
fmt.Printf("input5:%v\n", input5)

arg5 := struct {
Canister principal.Principal `ic:"canister" json:"canister"`
MethodName string `ic:"method_name" json:"method_name"`
Args []byte `ic:"args" json:"args"`
Cycles uint64 `ic:"cycles" json:"cycles"`
}{
Canister: principal.MustDecode("bkyz2-fmaaa-aaaaa-qaaaq-cai"),
MethodName: "sha256_with_cycles",
Args: []byte(input5),
Cycles: uint64(200_000_000 * n),
}
res5, err := a.WalletCall(arg5)
if err != nil {
panic(err)
}

var s5 string
err = idl.Unmarshal(res5.Ok.Return, []any{&s5})
if err != nil {
panic(err)
}
fmt.Printf("s5:%v\n", s5)
}