tl;dr: Do not implement ICRC-28 on defi canisters. If you want to use ICRC-34 in defi canisters, use relying-party delegations, not account delegations.
Authenticated sessions vs. transaction approval
Web2 applications are based on the concept of authenticated sessions. The user initially logs in, the authenticity of the user is checked once, and both client and server maintain some data that represents the active session. While the session is active, the user can perform many individual actions without the requirement of (explicitly) authenticating again.
In Gen-1 and Gen-2 blockchains such as Bitcoin and Ethereum, transactions are slow and expensive, and state stored on the blockchain is limited. Thus, transactions are used sparsely and generally only to move significant value. An explicit approval of the user for each action (e.g., using a hardware wallet) is thus appropriate.
Due to the efficiency and decentralization of ICP, dapps built on ICP may share characteristics with both of the above cases. When participating in a discussion on OpenChat, I want the characteristics of a session; I do not want to explicitly approve each and every emoji reaction I make on other people’s posts. On the other hand, when managing my liquidity pool positions on ICP AMMs, I definitely want the additional safety of a dedicated wallet with transaction approval. Some dapps – like OpenChat with its inbuilt swap options – even share both characteristics of sessions and value transactions simultaneously.
II has been widely used for implementing session-like behavior in many ICP dapps, although admittedly some characteristics of traditional sessions are lacking. (One cannot securely “terminate” a session other than waiting for it to expire.) On the other end, wallets like Plug and more recently the ICRC-21 wallet standard have embodied the transaction approval characteristics.
Defi on ICP
In (most) defi applications built on ICP, the backend application canister will technically control user tokens for at least some duration. This is obvious in AMMs, where the liquidity pools that consist of users’ tokens are maintained by a canister. But due to the lack of cross-canister atomicity, it’s also required for any type of atomic swap (like exchanging an NFT for tokens); some canister has to technically control both assets for some amount of time, in order to guarantee that both parties in the trade receive their desired outcome.
I deliberately use the term technically control here since I want to highlight that the canister is a smart contract and will only act on my behalf, I as a user still ultimately own (and indirectly, by instructing the canister, control) the tokens. The canister technically controlling the tokens on the ledger is just a means to an end.
While the canister technically controls (or is authorized to do so, e.g. via an ICRC-2 approval) the user’s assets, the user will often need to call that canister with additional instructions. For instance, after sending some ckBTC to a DEX, do I want to add it to a liquidity pool? If so, which one? Do I want to swap it for some other token? Which one, with what slippage? And to what address do I want to send the tokens I receive in exchange?
The wallet standards
The main purpose of a dedicated wallet is to securely hold assets and be able to use them across different applications; in ICP lingo, the assets are controlled by the (main) wallet principal. ICRC-21 then allows transaction-approval semantics for that main wallet principal.
ICRC-34, by contrast, uses the delegation mechanisms underlying II to build session semantics. It comes in two flavors:
- In relying-party delegation mode, an ICRC-34 wallet will provide session-like semantics to applications, while using – just like II – a different principal for each application.
- In account delegation mode, an ICRC-34 wallet will provide session-like semantics to applications, while using the main wallet principal.
Note that, without further safety measures, account delegations would be horrendously insecure: the application could use the delegation to steal all tokens owned by the user on any ledger. For this reason, ICRC-34 account delegations require canisters to implement ICRC-28, which basically states that the canister opts into the use of account delegations. (Delegations can be scoped to specific target canisters.)
Why ICRC-28 is should not be used for defi
To understand why I advise against the use of ICRC-28 in defi canisters, consider the concrete example of a DEX. The use of ICRC-28/34 on a DEX could look as follows: The DEX backend, which maintains the liquidity pools, implements ICRC-28 and authorizes the DEX frontend to receive an account delegation from an ICRC-34 compliant wallet. Now a swap would work as follows:
- The user sends tokens via ICRC-1 transfer or ICRC-2 approval. This requires an explicit wallet interaction.
- The front end uses the ICRC-34 delegation to call the swap function on the backend canister. The function call specifies the target token, the slippage, and the address the received tokens are supposed to be received at. (This could be one or multiple calls.)
The problem with the above flow is that, with the account delegation, the front-end application obtains full control over the user’s assets technically controlled by the DEX canister! No further user interaction would be required for, for instance, liquidating all of the user’s liquidity pool positions and sending all tokens to some external address. That violates two major objectives of using a wallet:
- Control/transparency: The user no longer has full control and transparency over what happens to their tokens, the front-end application can move them without user consent.
- Security: The security of the user’s tokens degrades to the security level of a web front end, whereas a dedicated wallet (especially a hardware wallet) can offer a significantly higher level of security.
Such a use of account delegation also contradicts ICRC-34, which defines:
Account Delegation: an identity that has restricted access to the signer’s identity, such that it is stable across many relying parties but cannot be used to operate on tradable assets and shared infrastructure.
At least in my reading, issuing an account delegation scoped to the DEX backend canister provides access to the tradable assets held by the user on that exchange (while technically controlled by the canister), and thus contradicts the above definition.
So how should we do things?
If you are building a defi application on ICP, I advise against implementing ICRC-28 endpoints on any canister that controls (significant) value on behalf of a user. For these cases, ICRC-21 is the appropriate mechanism, and in many defi applications such as AMMs all authenticated calls will (potentially) move significant value, and thus deserve explicit approval via ICRC-21. For applications that share both session and transaction semantics, make the difference explicit: Implement session semantics via one dedicated mechanism (e.g. II), and have the user explicitly maintain their assets by an ICRC-21 wallet.
In my opinion, in order to smoothen defi flows that rely on a sequence of multiple ICRC-21 transactions, it also seems promising to proceed with (at least a restricted version of) ICRC-39 for batch approvals, in which at least sequences of the type “ICRC-2, …, ICRC-2, something else” can be approved in one go, since that seems to be the most common case in defi and failure/success can be handled gracefully by the wallet.