sRFC 00012: Wallet Delegation Standard for Secure Proof of Ownership

Wallet Delegation Standard

Summary

This RFC introduces a delegation standard to mitigate loss, theft, and unauthorized access to funds stored in cold wallets. The standard maintains the offline nature of cold wallets, allowing them to delegate ownership of assets to dedicated hot wallets, thus allowing secure and controlled proof of ownership of assets.

Motivation: Proof of Ownership

Proof of ownership is a critical yet highly neglected problem in the Solana ecosystem. In its current form, it’s highly insecure

  • users must connect their cold wallets to unverified websites, or
  • transfer their valuable assets to warm wallets

Goals

This standard aims to remedy this by providing a secure way to delegate assets from cold to hot wallets. This allows users to prove ownership of their assets without risking them by connecting to insecure websites or moving them around.

Background

Delegation

Delegating an asset from a cold wallet → hot wallet: The hot wallet is shadowing as the owner of the asset, and the hot wallet does NOT own any write/transfer rights over this asset, nor does it have the ability to transfer these rights to anyone else. The cold wallet remains the true owner of this asset on-chain.

Actions

Two main actions can be performed using the proposed protocol:

  1. Delegation
  2. Revoking Delegation

Delegation Types

  1. Full Wallet Delegation: The entire wallet gets delegated, including all assets that are owned by the wallet.
  2. Token Delegation: Tokens having the same mint address can be delegated. This includes fungibles, semi-fungibles, and non-fungibles.

Proposed Implementation

PDAs can be used to derive delegation accounts for full wallet or individual token Delegation.

DelegationAccount

To delegate wallet A to another wallet B, a DelegationAccount can be derived using the address of wallet A.

#[account]
pub struct DelegateAccount {
    // Cold Wallet
    pub authority: Pubkey,
    pub hot_wallet: Pubkey,
}

where seeds for DelegateAccount:
[
    DelegateAccount::PREFIX.as_bytes(),
    authority.key().as_ref(),
]

TokenDelegationAccount

In the case of token Delegation, i.e., delegating a token in wallet A with ATA (associated token account) T to wallet B, a TokenDelegationAccount can be derived using the address of wallet A and the ATA T.

#[account]
pub struct DelegateTokenAccount {
    pub authority: Pubkey,
    pub hot_wallet: Pubkey,
    pub token_account: Pubkey,
}

where seeds for DelegateTokenAccount:
[
    DelegateTokenAccount::PREFIX.as_bytes(),
    authority.key().as_ref(),
    token_account.key().as_ref(),
]

Revoking Delegations

The authority of the Delegation can revoke delegations by closing the PDA.

#[derive(Accounts)]
pub struct RevokeDelegate<'info> {
    #[account(mut)]
    pub authority: Signer<'info>,
    #[account(mut, has_one=authority, close=authority)]
    pub delegate_account: Account<'info, DelegateAccount>,
}

#[derive(Accounts)]
pub struct RevokeTokenDelegate<'info> {
    pub authority: Signer<'info>,

    #[account(mut, has_one=authority, close=authority)]
    pub delegate_token_account: Account<'info, DelegateTokenAccount>,
}

Fetching Delegations

This standard would require an SDK for client-side fetching delegated wallets. When a user connects their wallet, the SDK can fetch all the assets delegated to the given (connected) hot wallet. Here is a reference implementation of the SDK:

getUserDelegates = async (hotWallet: PublicKey) => {
  const data = await this.delegationProgram.account.DelegateAccount.all([
    {
      memcmp: {
        offset: 8 + 32,
        bytes: hotWallet.toBase58(),
      },
    },
  ]);

  return data;
};

getTokenDelegates = async (hotWallet: PublicKey) => {
  const data = await this.delegationProgram.account.DelegateAccount.all([
    {
      memcmp: {
        offset: 8 + 32,
        bytes: hotWallet.toBase58(),
      },
    },
  ]);

  return data;
};

Potential Use-Cases

  • Claiming Airdrops
  • Claiming Allowlists
  • Token-gated mints
  • Token-gated games
  • DAO Governance
  • On-chain reputation
  • Verifiable Wallet aggregation

Ongoing Investigation

  • Supporting Programmable Wallets: Smart contract wallets like Multisigs are not system accounts; hence, the current implementation for the standard can be changed to support PDAs that represent programmable wallets.
  • Allowing tightly scoped rights to the delegated hot wallet for making transactions on behalf of the cold wallet.
  • Delegation Expiration: Current implementation requires users to manually revoke a delegation if they no longer want a given hot wallet to be the Delegation for a cold wallet. Automatic expiration could be introduced to let users set a dedicated interval, after which the Delegation/TokenDelegationAccount will be closed.

Call For Action

  • Readers: Requesting readers to provide feedback, audit reference implementation, and contribute to the codebase.
  • Dapps: Requesting dapps to explore the SDK reference implementation and provide feedback for making integration of this standard easy.
  • Wallets: Requesting wallets to explore the SDK reference implementation to provide users an easy way to perform the Delegation inside the wallets themselves - connecting cold wallets to any website (even one that is created specifically for this standard) goes against the very core principle behind this standard.

Reference Implementation

Citation

Anvit Mangal <@0xprof_lupin>, Pratik Saria <@PratikSaria>. “sRFC 00012: Wallet Delegation Standard for Secure Proof of Ownership,” https://forum.solana.com/t/srfc-00012-wallet-delegation-standard-for-secure-proof-of-ownership, May 2023.

Why limit delegation to a single delegate?

There’s definitely a case to be made for having multiple delegates for a single cold wallet, especially in this mobile era.

1 Like

One to many delegation was designed for a specific reason: in use cases where a user needs to be airdropped a single token / whitelisted only once per token/wallet, many to many delegation would fail as it the delegate fetcher wont know which wallet to whitelist/airdrop/allowed to enter a token-gated website.

But as you said, there are use cases where many to many mappings would be beneficial.

Is a single delegate to limit delegation?