smart_wallet/
state.rs

1//! State structs.
2#![deny(missing_docs)]
3
4use anchor_lang::prelude::*;
5use anchor_lang::solana_program;
6use vipers::prelude::*;
7
8/// A [SmartWallet] is a multisig wallet with Timelock capabilities.
9#[account]
10#[derive(Default, Debug, PartialEq)]
11pub struct SmartWallet {
12    /// Base used to derive.
13    pub base: Pubkey,
14    /// Bump seed for deriving PDA seeds.
15    pub bump: u8,
16
17    /// Minimum number of owner approvals needed to sign a [Transaction].
18    pub threshold: u64,
19    /// Minimum delay between approval and execution, in seconds.
20    pub minimum_delay: i64,
21    /// Time after the ETA until a [Transaction] expires.
22    pub grace_period: i64,
23
24    /// Sequence of the ownership set.
25    ///
26    /// This may be used to see if the owners on the multisig have changed
27    /// since the last time the owners were checked. This is used on
28    /// [Transaction] approval to ensure that owners cannot approve old
29    /// transactions.
30    pub owner_set_seqno: u32,
31    /// Total number of [Transaction]s on this [SmartWallet].
32    pub num_transactions: u64,
33
34    /// Owners of the [SmartWallet].
35    pub owners: Vec<Pubkey>,
36
37    /// Extra space for program upgrades.
38    pub reserved: [u64; 16],
39}
40
41impl SmartWallet {
42    /// Computes the space a [SmartWallet] uses.
43    pub fn space(max_owners: u8) -> usize {
44        4 // Anchor discriminator
45            + std::mem::size_of::<SmartWallet>()
46            + 4 // 4 = the Vec discriminator
47            + std::mem::size_of::<Pubkey>() * (max_owners as usize)
48    }
49
50    /// Gets the index of the key in the owners Vec, or None
51    pub fn owner_index_opt(&self, key: Pubkey) -> Option<usize> {
52        self.owners.iter().position(|a| *a == key)
53    }
54
55    /// Gets the index of the key in the owners Vec, or error
56    pub fn try_owner_index(&self, key: Pubkey) -> Result<usize> {
57        Ok(unwrap_opt!(self.owner_index_opt(key), InvalidOwner))
58    }
59}
60
61/// A [Transaction] is a series of instructions that may be executed
62/// by a [SmartWallet].
63#[account]
64#[derive(Debug, Default, PartialEq)]
65pub struct Transaction {
66    /// The [SmartWallet] account this transaction belongs to.
67    pub smart_wallet: Pubkey,
68    /// The auto-incremented integer index of the transaction.
69    /// All transactions on the [SmartWallet] can be looked up via this index,
70    /// allowing for easier browsing of a wallet's historical transactions.
71    pub index: u64,
72    /// Bump seed.
73    pub bump: u8,
74
75    /// The proposer of the [Transaction].
76    pub proposer: Pubkey,
77    /// The instruction.
78    pub instructions: Vec<TXInstruction>,
79    /// `signers[index]` is true iff `[SmartWallet]::owners[index]` signed the transaction.
80    pub signers: Vec<bool>,
81    /// Owner set sequence number.
82    pub owner_set_seqno: u32,
83    /// Estimated time the [Transaction] will be executed.
84    ///
85    /// - If set to [crate::NO_ETA], the transaction may be executed at any time.
86    /// - Otherwise, the [Transaction] may be executed at any point after the ETA has elapsed.
87    pub eta: i64,
88
89    /// The account that executed the [Transaction].
90    pub executor: Pubkey,
91    /// When the transaction was executed. -1 if not executed.
92    pub executed_at: i64,
93}
94
95impl Transaction {
96    /// Computes the space a [Transaction] uses.
97    pub fn space(instructions: Vec<TXInstruction>) -> usize {
98        4  // Anchor discriminator
99            + std::mem::size_of::<Transaction>()
100            + 4 // Vec discriminator
101            + (instructions.iter().map(|ix| ix.space()).sum::<usize>())
102    }
103
104    /// Number of signers.
105    pub fn num_signers(&self) -> usize {
106        self.signers.iter().filter(|&did_sign| *did_sign).count()
107    }
108}
109
110/// Instruction.
111#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, Default, PartialEq)]
112pub struct TXInstruction {
113    /// Pubkey of the instruction processor that executes this instruction
114    pub program_id: Pubkey,
115    /// Metadata for what accounts should be passed to the instruction processor
116    pub keys: Vec<TXAccountMeta>,
117    /// Opaque data passed to the instruction processor
118    pub data: Vec<u8>,
119}
120
121impl TXInstruction {
122    /// Space that a [TXInstruction] takes up.
123    pub fn space(&self) -> usize {
124        std::mem::size_of::<Pubkey>()
125            + (self.keys.len() as usize) * std::mem::size_of::<TXAccountMeta>()
126            + (self.data.len() as usize)
127    }
128}
129
130/// Account metadata used to define [TXInstruction]s
131#[derive(AnchorSerialize, AnchorDeserialize, Debug, PartialEq, Copy, Clone)]
132pub struct TXAccountMeta {
133    /// An account's public key
134    pub pubkey: Pubkey,
135    /// True if an Instruction requires a Transaction signature matching `pubkey`.
136    pub is_signer: bool,
137    /// True if the `pubkey` can be loaded as a read-write account.
138    pub is_writable: bool,
139}
140
141impl From<&TXInstruction> for solana_program::instruction::Instruction {
142    fn from(tx: &TXInstruction) -> solana_program::instruction::Instruction {
143        solana_program::instruction::Instruction {
144            program_id: tx.program_id,
145            accounts: tx.keys.clone().into_iter().map(Into::into).collect(),
146            data: tx.data.clone(),
147        }
148    }
149}
150
151impl From<TXAccountMeta> for solana_program::instruction::AccountMeta {
152    fn from(
153        TXAccountMeta {
154            pubkey,
155            is_signer,
156            is_writable,
157        }: TXAccountMeta,
158    ) -> solana_program::instruction::AccountMeta {
159        solana_program::instruction::AccountMeta {
160            pubkey,
161            is_signer,
162            is_writable,
163        }
164    }
165}
166
167/// Type of Subaccount.
168#[derive(
169    AnchorSerialize, AnchorDeserialize, Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord,
170)]
171#[repr(u8)]
172pub enum SubaccountType {
173    /// Requires the normal multisig approval process.
174    Derived = 0,
175    /// Any owner may sign an instruction  as this address.
176    OwnerInvoker = 1,
177}
178
179impl Default for SubaccountType {
180    fn default() -> Self {
181        SubaccountType::Derived
182    }
183}
184
185/// Mapping of a Subaccount to its [SmartWallet].
186#[account]
187#[derive(Copy, Default, Debug, PartialEq, Eq)]
188pub struct SubaccountInfo {
189    /// Smart wallet of the sub-account.
190    pub smart_wallet: Pubkey,
191    /// Type of sub-account.
192    pub subaccount_type: SubaccountType,
193    /// Index of the sub-account.
194    pub index: u64,
195}
196
197impl SubaccountInfo {
198    /// Number of bytes that a [SubaccountInfo] uses.
199    pub const LEN: usize = 32 + 1 + 8;
200}