delegation_manager/
lib.rs

1//! # Unique Delegation Manager
2//!
3//! Unique Delegation Manager is a toolset built for managing a "master-delegate" relationship
4//! between 1-to-many wallets. Protocols that implement it can allow safe execution of numerous
5//! actions for users without exposing their assets to any risks.
6
7use anchor_lang::prelude::*;
8declare_id!("UPLdquGEBVnVK5TmccSue5gyPkxSRT4poezHShoEzg8");
9
10#[constant]
11pub const AUTHORIZE_SEED: &'static [u8] = b"authorize";
12/// Unique program library's Delegation Manager program.
13#[program]
14pub mod delegation_manager {
15    use super::*;
16
17    /// Initializes delegate ix is used by a wallet to initialize the Delegation
18    /// account.
19    pub fn initialize_delegate(ctx: Context<InitializeDelegation>) -> Result<()> {
20        let delegation = &mut ctx.accounts.delegation;
21        delegation.master = ctx.accounts.master.key();
22        delegation.representative = ctx.accounts.representative.key();
23        delegation.authorised = false;
24        Ok(())
25    }
26
27    /// Confirm delegate ix is used by the representative to confirm the delegation
28    /// by setting the authorised flag to true.
29    pub fn confirm_delegate(ctx: Context<ConfirmDelegation>) -> Result<()> {
30        let delegation = &mut ctx.accounts.delegation;
31        require!(
32            ctx.accounts.representative.key() == delegation.representative,
33            DelegationError::WrongRepresentative
34        );
35        require!(!delegation.authorised, DelegationError::AlreadyAuthorised);
36        delegation.authorised = true;
37        Ok(())
38    }
39
40    /// Cancel delegate is used to revoke the authorisation given to the representative by
41    /// erasing the Delegation account. It can be invoked by both master and representative,
42    /// but the rent SOLs go to the master account.
43    pub fn cancel_delegate<'a, 'b, 'c, 'info>(
44        ctx: Context<'a, 'b, 'c, 'info, CancelDelegation<'info>>,
45    ) -> Result<()> {
46        let delegation = &mut ctx.accounts.delegation;
47        let remaining_accounts = &mut ctx.remaining_accounts.iter();
48        let master = remaining_accounts
49            .next()
50            .expect("Expected master as remaining account");
51        let representative = remaining_accounts
52            .next()
53            .expect("Expected representative as remaining account");
54        require!(
55            master.key() == delegation.master,
56            DelegationError::WrongMaster
57        );
58        require!(
59            representative.key() == delegation.representative,
60            DelegationError::WrongRepresentative
61        );
62        require!(
63            master.is_signer || representative.is_signer,
64            DelegationError::WrongSigner
65        );
66
67        delegation.close(master.to_account_info())?;
68
69        Ok(())
70    }
71}
72
73/// Accounts passed to InitializeDelegation instruction
74#[derive(Accounts)]
75pub struct InitializeDelegation<'info> {
76    #[account(mut)]
77    /// The one invoking the instruction to create Delegation
78    pub master: Signer<'info>,
79    ///CHECK: can be any account which can sign confirmation
80    pub representative: UncheckedAccount<'info>,
81    #[account(
82        init,
83        seeds = [AUTHORIZE_SEED, master.key().as_ref(), representative.key().as_ref()],
84        bump,
85        space = 8 + 32 + 32 + 1,
86        payer = master
87    )]
88    /// The Delegation PDA account derived from the master and representativ pubkeys
89    pub delegation: Box<Account<'info, Delegation>>,
90    pub system_program: Program<'info, System>,
91}
92
93/// Accounts passed to ConfirmDelegation instruction
94#[derive(Accounts)]
95pub struct ConfirmDelegation<'info> {
96    #[account(mut)]
97    pub representative: Signer<'info>,
98    #[account(mut)]
99    pub delegation: Box<Account<'info, Delegation>>,
100    pub system_program: Program<'info, System>,
101}
102
103/// Accounts passed to CancelDelegation instruction
104#[derive(Accounts)]
105pub struct CancelDelegation<'info> {
106    #[account(mut)]
107    pub delegation: Box<Account<'info, Delegation>>,
108    pub system_program: Program<'info, System>,
109}
110
111/// State account storing the delegation
112#[account]
113#[derive(Debug)]
114pub struct Delegation {
115    /// The creator of the delegation
116    pub master: Pubkey,
117    /// The wallet who delegates
118    pub representative: Pubkey,
119    /// Confirmation flag
120    pub authorised: bool,
121}
122
123/// Program errors
124#[error_code]
125pub enum DelegationError {
126    #[msg("Wrong representative!")]
127    WrongRepresentative,
128    #[msg("Wrong authority!")]
129    WrongMaster,
130    #[msg("Wrong signer!")]
131    WrongSigner,
132    #[msg("Authorization already approved!")]
133    AlreadyAuthorised,
134    #[msg("The account provided has no authority!")]
135    NotAuthorized,
136}
137
138/// Function used to determine if a representative is authorised by master.
139/// If the master is the same as a representative, the delegation_option argument can be None.
140/// If the master is not the same as a representative, Delegation account needs to be passed
141pub fn check_authorization(
142    master: &AccountInfo,
143    representative: &AccountInfo,
144    delegation_option: Option<&AccountInfo>,
145) -> Result<()> {
146    if master.key() != representative.key() {
147        let delegation_info = delegation_option.expect("Missing Delegation Account");
148        require_keys_eq!(*delegation_info.owner, ID);
149        let delegation = Box::new(
150            Account::<Delegation>::try_from(delegation_info)
151                .expect("Wrong account passed as Delegation account"),
152        );
153        require_keys_eq!(master.key(), delegation.master);
154        require_keys_eq!(representative.key(), delegation.representative);
155        require!(delegation.authorised, DelegationError::NotAuthorized);
156    }
157    Ok(())
158}
159
160pub fn get_delegation_address(master: &Pubkey, representative: &Pubkey) -> Pubkey {
161    Pubkey::find_program_address(&get_delegation_address_seeds(master, representative), &ID).0
162}
163
164pub fn get_delegation_address_seeds<'a>(
165    master: &'a Pubkey,
166    representative: &'a Pubkey,
167) -> [&'a [u8]; 3] {
168    [AUTHORIZE_SEED, &master.as_ref(), &representative.as_ref()]
169}