# Wallet canister standards

**URL:** <https://forum.dfinity.org/t/wallet-canister-standards/18695>\
**Category:** Developers\
**Tags:** Discussing\
**Created:** [February 26, 2023, 4:10am UTC](https://forum.dfinity.org/t/wallet-canister-standards/18695 "2023-02-26T04:10:59Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Hazel](https://avatars.discourse-cdn.com/v4/letter/h/e56c9b/32.png) [@Hazel](https://forum.dfinity.org/u/Hazel)\
**Post date:** [February 26, 2023, 4:10am UTC](https://forum.dfinity.org/t/wallet-canister-standards/18695/1 "2023-02-26T04:10:59Z")

</div>

Starting a thread here so we can start figuring out some standards to bring about wallet canisters. For starters I think we need a [ERC-725](https://erc725alliance.org/#:~:text=What%20is%20ERC%20725%3F,keys%20and%20other%20smart%20contracts.) like standard. A common way to make calls and a small key value store is a great foundation to start building wallets / personal canisters / whatever on.

How about the following to kick off discussion 🙂 .

## `icrc5_make_call`

Execute a blocking call against a canister.

```candid
type CanisterCallRequest = record {
  canister : principal;
  method : text;
  cycles : nat64;
  args : vec nat8;
};

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

type CallFailure = variant {
  NotAuthorized: record { reason: text },
  Error: record { code: RejectionCode, message: text },
};

type CallResult = variant {
  Ok: vec nat8;
  Err: CallFailure;
};

service : {
  icrc5_execute_call: (CanisterCallRequest) -> (CallResult);
}

```

## `icrc5_set_data`

Set a value in the store

```candid
type Value = variant {
    Text : text;
    
    Blob : blob;
    Bool : bool;
    
    Option : Value;
    
    Vec : vec Value;
    Record : vec (text, Value);

    Nat : nat;
    Nat8 : nat8;
    Nat16 : nat16;
    Nat32 : nat32;
    Nat64 : nat64;
    
    Int : int;
    Int8 : int8;
    Int16 : int16;
    Int32 : int32;
    Int64 : int64;
    
    Float32 : float32;
    Float64 : float64;
    
    Principal : principal;
};

type SetError = variant {
    NotAuthorized : null;
};

type SetResult = {
    Ok : null;
    Err : set_error;
};

service : {
  icrc5_set_data: (text, Value) -> (SetResult);
}

```

## `icrc5_get_data`

Fetch a value from the store

```candid

type Value = variant {
    Text : text;
    
    Blob : blob;
    Bool : bool;
    
    Option : Value;
    
    Vec : vec Value;
    Record : vec (text, Value);

    Nat : nat;
    Nat8 : nat8;
    Nat16 : nat16;
    Nat32 : nat32;
    Nat64 : nat64;
    
    Int : int;
    Int8 : int8;
    Int16 : int16;
    Int32 : int32;
    Int64 : int64;
    
    Float32 : float32;
    Float64 : float64;
    
    Principal : principal;
};

type FetchError = variant {
    NotAuthorized : null;
    KeyNotFound : null;
};

type FetchResult = variant {
    Ok: Value;
    Err : FetchError;
};

service : {
    icrc5_get_data : (text) -> (FetchResult);
}

```

## `icrc5_authorize_user` & `icrc5_authorized_users`

Authorize a user and fetch authorized users.

Identity is presented as a variant type to allow for additional types of identities to be added.

```candid
type Identity = variant {
    Principal : record {
        p : principal;
    }
};

type AuthRequest = variant {
    Add : record {
        identity : Identity;
    };
    Remove : record {
        identity : Identity;
    };
};

type AuthorizeError = variant {
    NotAuthorized;
};

type AuthorizeResponse = variant {
    Ok : null;
    Err : AuthorizeError;
};

type AuthorizedUser = record {
    identity : Identity;
    created_at : u64;
};

type AuthorizedError = variant {
    NotAuthorized;
};

type AuthorizedUsersResponse = variant {
    Ok : vec AuthorizedUser;
    Err : AuthorizedError;
};

service : {
    icrc5_authorize_user : (AuthRequest) -> (AuthorizeResponse);
    icrc5_authorized_users : () -> (AuthorizedUsersResponse)
}

```

---

<div class="post-metadata">

**Author:** ![skilesare](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/skilesare/32/5609_2.png) [@skilesare](https://forum.dfinity.org/u/skilesare)\
**Post date:** [February 26, 2023, 1:54pm UTC](https://forum.dfinity.org/t/wallet-canister-standards/18695/2 "2023-02-26T13:54:56Z")

</div>

This is super great!

A couple suggestions/questions :

1. Why the extra layer of p?

‘’’  
type Identity = variant {  
Principal : record {  
p : principal;  
}  
};  
‘’’

1. For the value types I’d suggest something like CandyLibrary types [GitHub - icdevs/candy\_library: Library for Converting Types and Creating Workable Motoko Collections](https://github.com/icdevs/candy_library) that are just a bit more dynamic. It includes the #class type and the properties construct that you and @quint used back in the day. We currently have a bounty assigned building a rust version and with a bit of curation from someone with a better computer science background it could become a pretty cool data access paradigm. We already haven engineer building a candy path library as well for deep queries. [GitHub - ZhenyaUsenko/motoko-candy-utils](https://github.com/ZhenyaUsenko/motoko-candy-utils)

This is what we use in the origyn nft standard and with a few conventions we’ve implemented permissioned nodes as well. Maybe we are getting a bit to complex for a first turn here, but at the same time I feel we should start using the power of this baby.

 ![image](https://us1.discourse-cdn.com/flex023/uploads/dfn/original/2X/5/5568a9134d5959d352df07e260d94fa3f390a38d.jpeg)

1. I know composite queries are coming, should we include an endpoint for that as a stub? Maybe one for pure canister to canister queries as well?

2. What do mean “blocking call”? Does this open your canister up to an attacker tricking you into calling long running calls? Does it need to block?

---

<div class="post-metadata">

**Author:** ![Hazel](https://avatars.discourse-cdn.com/v4/letter/h/e56c9b/32.png) [@Hazel](https://forum.dfinity.org/u/Hazel)\
**Post date:** [February 26, 2023, 4:24pm UTC](https://forum.dfinity.org/t/wallet-canister-standards/18695/3 "2023-02-26T16:24:32Z")

</div>

> [@skilesare](#):
>
> 1. Why the extra layer of p?
> 
> ```candid
> type Identity = variant {
> Principal : record {
> p : principal;
> }
> };
> 
> ```

Just leaving space for additional identity types. Maybe requests come packaged with a multi-sig proof?

> [@skilesare](#):
>
> For the value types I’d suggest something like CandyLibrary types

Could you share a candid? But, also leaning towards keeping thinks really simple. My intention here was to expose a KV that would be used for the most simple applications. Maybe some simple client configs, maybe pointing to a more powerful robust store 🙂 .

> [@skilesare](#):
>
> I know composite queries are coming, should we include an endpoint for that as a stub? Maybe one for pure canister to canister queries as well?

I honestly haven’t been following the composite queries discussions. But, this likely makes sense considering the purpose!

> [@skilesare](#):
>
> What do mean “blocking call”? Does this open your canister up to an attacker tricking you into calling long running calls? Does it need to block?

Blocking here means the canister should return the result to the calling client. I’ve read about this a few times, but how big is the actual threat here? The malicious canister would have to eventually return (burning their own cycles for the duration of the attack). Additionally, your canister would still be free to make additional responses. So, we just wouldn’t be able to update the canister for the duration of this, no? (Is this enforced at the protocol level or can we just remove the callback pointer by force if we need to?)

---

<div class="post-metadata">

**Author:** ![Hazel](https://avatars.discourse-cdn.com/v4/letter/h/e56c9b/32.png) [@Hazel](https://forum.dfinity.org/u/Hazel)\
**Post date:** [February 26, 2023, 4:32pm UTC](https://forum.dfinity.org/t/wallet-canister-standards/18695/4 "2023-02-26T16:32:05Z")

</div>

> <https://github.com/dfinity/ICRC/issues/11>
>
> Moving from a \[forum topic\](https://forum.dfinity.org/t/wallet-canister-standard…s/18695/2?u=hazel) to an issue..
> 
> \> For starters I think we need a \[ERC-725\](https://erc725alliance.org/#:~:text=What%20is%20ERC%20725%3F,keys%20and%20other%20smart%20contracts.) like standard. A common way to make calls and a small key value store is a great foundation to start building wallets / personal canisters / whatever on. 
> 
> \## \`icrc11\_make\_call\`
> Execute a blocking call against a canister.
> 
> \`\`\`candid
> type CanisterCallRequest = record {
> canister : principal;
> method : text;
> cycles : nat64;
> args : vec nat8;
> };
> 
> type RejectionCode = variant {
> NoError: null,
> SysFatal: null,
> SysTransient: null,
> DestinationInvalid: null,
> CanisterReject: null,
> CanisterError: null,
> Unknown: null,
> };
> 
> type CallFailure = variant {
> NotAuthorized: record { reason: text },
> Error: record { code: RejectionCode, message: text },
> };
> 
> type CallResult = variant {
> Ok: vec nat8;
> Err: CallFailure;
> };
> 
> service : {
> icrc11\_execute\_call: (CanisterCallRequest) -\> (CallResult);
> }
> \`\`\`
> 
> \## \`icrc11\_set\_data\`
> Set a value in the store
> 
> \`\`\`candid
> type Value = variant {
> Text : text;
>     
> Blob : blob;
> Bool : bool;
>     
> Option : Value;
>     
> Vec : vec Value;
> Record : vec (text, Value);
> 
> Nat : nat;
> Nat8 : nat8;
> Nat16 : nat16;
> Nat32 : nat32;
> Nat64 : nat64;
>     
> Int : int;
> Int8 : int8;
> Int16 : int16;
> Int32 : int32;
> Int64 : int64;
>     
> Float32 : float32;
> Float64 : float64;
>     
> Principal : principal;
> };
> 
> type SetError = variant {
> NotAuthorized : null;
> };
> 
> type SetResult = {
> Ok : null;
> Err : set\_error;
> };
> 
> service : {
> icrc11\_set\_data: (text, Value) -\> (SetResult);
> }
> \`\`\`
> 
> \## \`icrc11\_get\_data\`
> Fetch a value from the store
> 
> \`\`\`candid
> 
> type Value = variant {
> Text : text;
>     
> Blob : blob;
> Bool : bool;
>     
> Option : Value;
>     
> Vec : vec Value;
> Record : vec (text, Value);
> 
> Nat : nat;
> Nat8 : nat8;
> Nat16 : nat16;
> Nat32 : nat32;
> Nat64 : nat64;
>     
> Int : int;
> Int8 : int8;
> Int16 : int16;
> Int32 : int32;
> Int64 : int64;
>     
> Float32 : float32;
> Float64 : float64;
>     
> Principal : principal;
> };
> 
> type FetchError = variant {
> NotAuthorized : null;
> KeyNotFound : null;
> };
> 
> type FetchResult = variant {
> Ok: Value;
> Err : FetchError;
> };
> 
> service : {
> icrc11\_get\_data : (text) -\> (FetchResult);
> }
> \`\`\`
> 
> \## \`icrc11\_authorize\_user\` & \`icrc11\_authorized\_users\`
> 
> Authorize a user and fetch authorized users.
> 
> Identity is presented as a variant type to allow for additional types of identities to be added. 
> 
> \`\`\`candid
> type Identity = variant {
> Principal : record {
> p : principal;
> }
> };
> 
> type AuthRequest = variant {
> Add : record {
> identity : Identity;
> };
> Remove : record {
> identity : Identity;
> };
> };
> 
> type AuthorizeError = variant {
> NotAuthorized;
> };
> 
> type AuthorizeResponse = variant {
> Ok : null;
> Err : AuthorizeError;
> };
> 
> type AuthorizedUser = record {
> identity : Identity;
> created\_at : u64;
> };
> 
> type AuthorizedError = variant {
> NotAuthorized;
> };
> 
> type AuthorizedUsersResponse = variant {
> Ok : vec AuthorizedUser;
> Err : AuthorizedError;
> };
> 
> service : {
> icrc11\_authorize\_user : (AuthRequest) -\> (AuthorizeResponse);
> icrc11\_authorized\_users : () -\> (AuthorizedUsersResponse)
> }
> \`\`\`

---

<div class="post-metadata">

**Author:** ![neeboo](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/neeboo/32/981_2.png) [@neeboo](https://forum.dfinity.org/u/neeboo)\
**Post date:** [February 26, 2023, 6:23pm UTC](https://forum.dfinity.org/t/wallet-canister-standards/18695/5 "2023-02-26T18:23:33Z")

</div>

Thanks for the post.

We have our design, it’s here:

> **[GitHub - AstroxNetwork/wallet\_call: demo of wallet\_call](https://github.com/AstroxNetwork/wallet_call)**
>
> demo of wallet\_call. Contribute to AstroxNetwork/wallet\_call development by creating an account on GitHub.

Interface would be:

```auto
type CallCanisterArgs = record {
  args : vec nat8;
  cycles : nat;
  method_name : text;
  canister : principal;
};
type CallResult = record { return : vec nat8 };
type ExpiryUser = record {
  user : principal;
  expiry_timestamp : nat64;
  timestamp : nat64;
  target_list : vec ProxyActorItem;
};
type Method = record {
  name : text;
  method_type : MethodType;
  key_operation : bool;
};
type MethodType = variant { CALL; OneWay; CompositeQuery; QUERY };
type MethodValidationType = variant { ALL; KEY; UPDATE };
type OwnerReply = variant { Approved : Result_3; NotFound; Rejected : text };
type ProxyActorItem = record {
  methods : vec record { text; Method };
  canister : principal;
};
type ProxyActorTargets = record {
  targets : vec ProxyActorItem;
  expiration : opt nat64;
};
type QueueHash = record { hash : text; user : principal; time_stamp : nat64 };
type Result = variant { Ok : nat; Err : text };
type ProxyCallResult = variant { Ok : CallResult; Err : text };
type RemoveQueueResult = variant { Ok : bool; Err : text };
service : () -> {
  add_expiry_user : (principal, ProxyActorTargets) -> (ExpiryUser);
  add_proxy_black_list : (principal) -> (text);
  balance_get : () -> (Result) query;
  get_queue_reply : (text) -> (opt OwnerReply) query;
  get_queue_unconfirmed : (principal) -> (vec QueueHash) query;
  has_queue_method : (text) -> (bool) query;
  is_proxy_black_list : (principal) -> (bool) query;
  owner_confirm : (text, bool) -> (OwnerReply);
  proxy_call : (CallCanisterArgs) -> (ProxyCallResult);
  remove_proxy_black_list : (principal) -> (opt text);
  remove_queue_method : (text) -> (RemoveQueueResult);
  set_expiry_period : (nat64) -> ();
  set_method_validate_type : (MethodValidationType) -> ();
}

```

I will try and explain the interface design in later post

---

<div class="post-metadata">

**Author:** ![Hazel](https://avatars.discourse-cdn.com/v4/letter/h/e56c9b/32.png) [@Hazel](https://forum.dfinity.org/u/Hazel)\
**Post date:** [February 26, 2023, 6:26pm UTC](https://forum.dfinity.org/t/wallet-canister-standards/18695/6 "2023-02-26T18:26:50Z")

</div>

@neeboo, this looks great. Mind collaborating here: [ICRC-11 Wallet / Personal Canisters · Issue #11 · dfinity/ICRC · GitHub](https://github.com/dfinity/ICRC/issues/11) ?

---

<div class="post-metadata">

**Author:** ![neeboo](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/neeboo/32/981_2.png) [@neeboo](https://forum.dfinity.org/u/neeboo)\
**Post date:** [February 26, 2023, 6:28pm UTC](https://forum.dfinity.org/t/wallet-canister-standards/18695/7 "2023-02-26T18:28:12Z")

</div>

Absolutely, I will move there and LFG

---

<div class="post-metadata">

**Author:** ![skilesare](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/skilesare/32/5609_2.png) [@skilesare](https://forum.dfinity.org/u/skilesare)\
**Post date:** [March 7, 2023, 4:52am UTC](https://forum.dfinity.org/t/wallet-canister-standards/18695/8 "2023-03-07T04:52:06Z")

</div>

> <https://github.com/dfinity/ICRC/issues/11#issuecomment-1457527584>
>
> \`\`\`
> icrc: 11
> title: ICRC-11 Wallet / Personal Canister
> author: Hazel Rowell \<…hazel@departure.dev\>,
> discussions-to: https://github.com/dfinity/ICRC/issues/11status: Deliberating 
> category: ICRC
> requires: None
> created: 2023-02-26
> updated: 2023-02-26
> \`\`\`
> Moving from a \[forum topic\](https://forum.dfinity.org/t/wallet-canister-standards/18695/2?u=hazel) to an issue..
> 
> \> For starters I think we need a \[ERC-725\](https://erc725alliance.org/#:~:text=What%20is%20ERC%20725%3F,keys%20and%20other%20smart%20contracts.) like standard. A common way to make calls and a small key value store is a great foundation to start building wallets / personal canisters / whatever on. 
> 
> \# Summary
> A minimum set of functions to help facilitate the development of wallet / personal canisters and clients.
> 
> \# References 
> \[ERC-725\](https://erc725alliance.org/#:~:text=What%20is%20ERC%20725%3F,keys%20and%20other%20smart%20contracts.) 
> \[Cycles Wallet\](https://github.com/dfinity/cycles-wallet)
> 
> \# Introduction
> 
> This proposal describes \*\*a minimum set of functions and types\*\* which we hope will accelerate the development of wallet canisters and wallet canister clients as well as provide interoperability between the two. The goals of this standard are as follows:
> 
> \- Provide a well known canister method a variety of clients can use execute calls on behalf of a user.
> \- Provide a key-value store for clients to discover associated WApps (Wallet Apps).
> \- Provide a generalized, future proofed, authorization mechanism.
> 
> \## \`icrc11\_make\_call\`
> Execute a blocking call against a canister on behalf of a caller.
> 
> \`\`\`candid
> type CanisterCallRequest = record {
> canister : principal;
> method : text;
> cycles : nat64;
> args : vec nat8;
> };
> 
> type RejectionCode = variant {
> NoError: null,
> SysFatal: null,
> SysTransient: null,
> DestinationInvalid: null,
> CanisterReject: null,
> CanisterError: null,
> Unknown: null,
> };
> 
> type CallFailure = variant {
> NotAuthorized: record { reason: text },
> Error: record { code: RejectionCode, message: text },
> };
> 
> type CallResult = variant {
> Ok: vec nat8;
> Err: CallFailure;
> };
> 
> service : {
> icrc11\_execute\_call: (CanisterCallRequest) -\> (CallResult);
> }
> \`\`\`
> 
> \## \`icrc11\_set\_data\`
> Set a value in the store
> 
> \`\`\`candid
> type Value = variant {
> Text : text;
>     
> Blob : blob;
> Bool : bool;
>     
> Option : Value;
>     
> Vec : vec Value;
> Record : vec (text, Value);
> 
> Nat : nat;
> Nat8 : nat8;
> Nat16 : nat16;
> Nat32 : nat32;
> Nat64 : nat64;
>     
> Int : int;
> Int8 : int8;
> Int16 : int16;
> Int32 : int32;
> Int64 : int64;
>     
> Float32 : float32;
> Float64 : float64;
>     
> Principal : principal;
> };
> 
> type SetError = variant {
> NotAuthorized : null;
> };
> 
> type SetResult = {
> Ok : null;
> Err : set\_error;
> };
> 
> service : {
> icrc11\_set\_data: (text, Value) -\> (SetResult);
> }
> \`\`\`
> 
> \## \`icrc11\_get\_data\`
> Fetch a value from the store
> 
> \`\`\`candid
> 
> type Value = variant {
> Text : text;
>     
> Blob : blob;
> Bool : bool;
>     
> Option : Value;
>     
> Vec : vec Value;
> Record : vec (text, Value);
> 
> Nat : nat;
> Nat8 : nat8;
> Nat16 : nat16;
> Nat32 : nat32;
> Nat64 : nat64;
>     
> Int : int;
> Int8 : int8;
> Int16 : int16;
> Int32 : int32;
> Int64 : int64;
>     
> Float32 : float32;
> Float64 : float64;
>     
> Principal : principal;
> };
> 
> type FetchError = variant {
> NotAuthorized : null;
> KeyNotFound : null;
> };
> 
> type FetchResult = variant {
> Ok: Value;
> Err : FetchError;
> };
> 
> service : {
> icrc11\_get\_data : (text) -\> (FetchResult);
> }
> \`\`\`
> 
> \## \`icrc11\_authorize\_user\` & \`icrc11\_authorized\_users\`
> 
> Authorize a user and fetch authorized users.
> 
> Identity is presented as a variant type to allow for additional types of identities to be added. 
> 
> \`\`\`candid
> type Identity = variant {
> Principal : principal;
> };
> 
> type AuthRequest = variant {
> Add : record {
> identity : opt Identity; // Optional to support backwards compatibility in older clients
> };
> Remove : record {
> identity : opt Identity; // Optional to support backwards compatibility in older clients
> };
> };
> 
> type AuthorizeError = variant {
> NotAuthorized;
> NotSupported;
> };
> 
> type AuthorizeResponse = variant {
> Ok : null;
> Err : AuthorizeError;
> };
> 
> type AuthorizedUser = record {
> identity : Identity;
> created\_at : u64;
> };
> 
> type AuthorizedError = variant {
> NotAuthorized;
> };
> 
> type AuthorizedUsersResponse = variant {
> Ok : vec AuthorizedUser;
> Err : AuthorizedError;
> };
> 
> service : {
> icrc11\_authorize\_user : (AuthRequest) -\> (AuthorizeResponse);
> icrc11\_authorized\_users : () -\> (AuthorizedUsersResponse)
> }
> \`\`\`
