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}