Wallet Integration

Hi,

I have worked a bit on trying to integrate Oisy and Stoic into the pokedbots racing game.

I tried the NFID IdentityKit, per the Oisy documentation, but I got errors and the issue ended up being compatibility issues between IdentityKit which still uses @dfinity 2.x packages, and my setup which uses new @dfinity 3.x packages.

I then tried integrating Oisy directly, because not supporting Stoic is not too bade because it is outdated and not actively being supported as far as I could see. Once I began working directly with Oisy and signing, it seemed like I now need to rethink how the repo is organized, and possibly implement my ic-js api layer to each extend RelyingParty (oisy-wallet-signer/src/relying-party.ts at main ¡ dfinity/oisy-wallet-signer ¡ GitHub), just as the icrc and icp classes do.

I suppose my question now is how to proceed? Is it more work then its worth to support both delegation and signer type wallets, or is the way forward Oisy only? So far potential users of the app will not use II2 (delegation, requires trust in the app), will not use Plug (delegation, but more so lack of trust due to EXT incident), say they would use Stoic (outdated and not actively updated), and would use Oisy if it was supported.

I was hoping for a simple fix, but like I mentioned above it appears the solution is more complex. I like Oisy and would be fine offering that as the main wallet and refactoring my api layers to use the signing pattern, but wanted to check before going through the work.

Any other devs have advice on how to proceed?

Which frontend framework are you working with?

Using React for the frontend.

Have you tried reaching out to IdentityKit to ask them to bump their dependencies?

Alternatively, have you try to use the latest version of signer-js - the library use underneath IdentityKit? It was recently bumped to the latest version of AgentJS.

What feature are you concretely looking to implement? Interact with an ICP or ICRC ledger or something else?

Have not reached out to IdentityKit yet, will do so though.

I’m not sure about signer-js, I did check out the oisy repo where I found the RelyingParty and the ICP/ICRC implementations.

I’m looking to enable users to play the game while they have their NFTs in the Oisy wallet.

All the mutation type endpoints like “enter_race” or “repair_bot” perform a inter-canister call to the pokedbots EXT canister, so the caller needs to be the owner of the tokens. Additionally, only owners can see private state like current power/charge/condition, so query calls also check if the caller is the owner of the NFT via inter-canister calls.

I think the desired result is that the user can login with Oisy, and approve all the required actions ahead of time (not wanting a wallet popup every 30 seconds when the frontend fetches up-to-date bot info), and then they manage their garage normally from there.

Scratch that. I see David replying will wait for that …

Thanks for the explanation. I understand now that your goal goes (way) beyond interacting with the ICP and ICRC ledgers.

My suggestion - I’m the author of the OISY Wallet signer library, or at least its most active contributor - would be the following, in order of priority:

  1. Reach out to IdentityKit to ask them to bump their dependencies

  2. Try implementing your features using signer-js. It is designed to offer flexibility for relying parties that are looking to implement interaction with a signer.

  3. If the above really doesn’t work - though I doubt it - try to extend the OISY Wallet signer relying party, but note that this library is meant primarily to provide the signer features for OISY or any other apps that aim to be a signer.

Of course, only my two cents. Feel free to ignore, and if you have questions about the OISY Wallet Signer library, happy to try to answer those.

Worth noting that OISY does not support identity delegation, for security reasons. i.e. any canister call has to go through an approval. Therefore, while I cannot be entirely sure about your feature, I just wanted to highlight the fact that “approving all actions ahead of time” regardless of the JS client might not be possible.

This pretty much sums it up. Times like this is when looking at Solana;s “launch of ConnectorKit—a new SDK built to add wallet connection and management functionality to products. Compatible with web3.js and the Kit framework, it’s a headless wallet connection component that includes a framework-agnostic client, numerous composable elements, and user-friendly hook functions.” still has us debating ICRC-28 “Trusted Origin” . Sry for OFF topic.

Ok sounds good. Opened an issue in IdentityKit repo and will start working with the signer-js library to see if it works.

I could be mistaken, but I thought I saw the ‘requestAllPermissions’ from the docs and thought it would remember the users granted permissions for a bit of time.

Is there another solution, or at this time the user would have to confirm the query each time?

Permissions are part of the signer standards (ICRC-25) and not to be confused with providing the relying party a delegation to perform whatever calls without further approval (ICRC-34).

Or in other words, permissions are more like pre-assertions for the user to tell the signer what requests can or cannot be requested, not approved.

Probably too difficult to answer without knowing exactly all your features and requirements. After you implement some proof of concept, you can try to reach out to OISY on the Discord channel to get more detailed answers, I would say.

For toko we use a custom delegation identity when the user is signed in with oisy so you can act inside your app on behalf of the oisy principal but for other canisters it would still require an approval.

If this is what you are after I can probably share the codebase.

Yeah that sounds perfect tbh. All token transfers go through their canisters anyway and we use approval flow for ICP so really we just need to verify the caller has the nft in their Oisy wallet.

To verify if an NFT is held by a given account, you won’t need to make a call through a wallet. You can make an anonymous query call to lookup such public NFT ledger information.

Where possible, only make calls through a wallet when you need to make them actually authenticated e.g. ledger transfer or approval. Meanwhile, most query calls like checking ledger balance or NFT owners are often public information and thus non-authenticated methods than can be called with HttpAgent.

The game canister makes a call to the EXT canister to verify the caller is the owner, since the game logic is in the canister. For example, user who says he owns “Ultimate Master” and tries to enter it into the race should be blocked, so we can’t really just take his word for it.

Also, there is game specific data like current power level that other users shouldn’t be able to see or it ruins the game. This needs to be queried by the owner only.

The game logic is on the backend and uses a random beacon to create a seed that is used for the deterministic simulations. Though we can (and do) have the simulation also run on the frontend, as the random seed is created and saved in the canister when the race is performed.

Would this data be on a canister that you control? if not then this solution isn’t going to work either, else;

This is our login flow which uses @slide-computer/signer, because toko has a lot of scaling components we use canister signatures as well which would be overkill for a single canister approach.

But in the simpelest terms what you can do is;

On the canister;

  • setup a delegation storage which could just be a HashMap<DELEGATION_PRINCIPAL, OISY_PRINCIPAL> (ideally add an expiry as wel)
  • Register an icrc21 method you call via OISY to store the delegation (only approval step you need from oisy)
  • use the delegation to fetch the (stored) oisy principal to use for querying / updating your data (internal canister function)

On the frontend;

  • create a signer agent (like here)
  • generate a random delegation identity (need to be persistent throughout the user session)
  • call the icrc21 registered call with the signer and go through the oisy aproval flow (sending the delegation to the canister)
  • use a regular actor where based on the delegation identity to do authenticated calls.

so down the line you would end up with

#[query]
pub fn foo() -> Result<(), String> {
  let delegation_principal = msg_caller();
  let oisy_principal = use_delegation(delegation_principal); // uses the delegation storage

  let data = get_user_specific_data(oisy_principal);
}

Small note that toko is using Canic which handles most of the icrc21 logic for us and adds a bit of an abstraction layer so if you are looking for it in the toko codebase you will probably hit a dead end.

you can check out the flow in action on https://dev.toko.app/ when you press the “connect wallet” button, the first approval is for approving the right permissions (27, 49), after that the icrc21 will be triggered that you can find here with the consent message at the bottom of the file.

Ok, awesome. I don’t have access to the repo so I can’t see the code (just getting 404s) but I get the idea. Will spend some time today messing around with the idea.