1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
use {
crate::{state::AccountState, ID},
solana_account_view::{AccountView, Ref},
solana_address::Address,
solana_program_error::ProgramError,
};
/// Token account data.
#[repr(C)]
pub struct Account {
/// The mint associated with this account
mint: Address,
/// The owner of this account.
owner: Address,
/// The amount of tokens this account holds.
amount: [u8; 8],
/// Indicates whether the delegate is present or not.
delegate_flag: [u8; 4],
/// If `delegate` is `Some` then `delegated_amount` represents
/// the amount authorized by the delegate.
delegate: Address,
/// The account's state.
state: u8,
/// Indicates whether this account represents a native token or not.
is_native: [u8; 4],
/// When `is_native.is_some()` is `true`, this is a native token, and the
/// value logs the rent-exempt reserve. An Account is required to be
/// rent-exempt, so the value is used by the Processor to ensure that
/// wrapped SOL accounts do not drop below this threshold.
native_amount: [u8; 8],
/// The amount delegated.
delegated_amount: [u8; 8],
/// Indicates whether the close authority is present or not.
close_authority_flag: [u8; 4],
/// Optional authority to close the account.
close_authority: Address,
}
impl Account {
pub const LEN: usize = core::mem::size_of::<Account>();
/// Return a `TokenAccount` from the given account view.
///
/// This method performs owner and length validation on `AccountView`, safe
/// borrowing the account data.
#[inline]
pub fn from_account_view(account_view: &AccountView) -> Result<Ref<'_, Account>, ProgramError> {
if account_view.data_len() != Self::LEN {
return Err(ProgramError::InvalidAccountData);
}
if !account_view.owned_by(&ID) {
return Err(ProgramError::InvalidAccountData);
}
Ok(Ref::map(account_view.try_borrow()?, |data| unsafe {
Self::from_bytes_unchecked(data)
}))
}
/// Return a `TokenAccount` from the given account view.
///
/// This method performs owner and length validation on `AccountView`, but
/// does not perform the borrow check.
///
/// # Safety
///
/// The caller must ensure that it is safe to borrow the account data (e.g.,
/// there are no mutable borrows of the account data).
#[inline]
pub unsafe fn from_account_view_unchecked(
account_view: &AccountView,
) -> Result<&Account, ProgramError> {
if account_view.data_len() != Self::LEN {
return Err(ProgramError::InvalidAccountData);
}
if account_view.owner() != &ID {
return Err(ProgramError::InvalidAccountData);
}
Ok(Self::from_bytes_unchecked(account_view.borrow_unchecked()))
}
/// Return a `TokenAccount` from the given bytes.
///
/// # Safety
///
/// The caller must ensure that `bytes` contains a valid representation of
/// `TokenAccount`, and it is properly aligned to be interpreted as an
/// instance of `TokenAccount`. At the moment `TokenAccount` has an
/// alignment of 1 byte. This method does not perform a length
/// validation.
#[inline(always)]
pub unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
&*(bytes.as_ptr() as *const Account)
}
pub fn mint(&self) -> &Address {
&self.mint
}
pub fn owner(&self) -> &Address {
&self.owner
}
pub fn amount(&self) -> u64 {
u64::from_le_bytes(self.amount)
}
#[inline(always)]
pub fn has_delegate(&self) -> bool {
self.delegate_flag[0] == 1
}
pub fn delegate(&self) -> Option<&Address> {
if self.has_delegate() {
Some(self.delegate_unchecked())
} else {
None
}
}
/// Use this when you know the account will have a delegate and want to skip
/// the `Option` check.
#[inline(always)]
pub fn delegate_unchecked(&self) -> &Address {
&self.delegate
}
#[inline(always)]
pub fn state(&self) -> AccountState {
self.state.into()
}
#[inline(always)]
pub fn is_native(&self) -> bool {
self.is_native[0] == 1
}
pub fn native_amount(&self) -> Option<u64> {
if self.is_native() {
Some(self.native_amount_unchecked())
} else {
None
}
}
/// Return the native amount.
///
/// This method should be used when the caller knows that the token is
/// native since it skips the `Option` check.
#[inline(always)]
pub fn native_amount_unchecked(&self) -> u64 {
u64::from_le_bytes(self.native_amount)
}
pub fn delegated_amount(&self) -> u64 {
u64::from_le_bytes(self.delegated_amount)
}
#[inline(always)]
pub fn has_close_authority(&self) -> bool {
self.close_authority_flag[0] == 1
}
pub fn close_authority(&self) -> Option<&Address> {
if self.has_close_authority() {
Some(self.close_authority_unchecked())
} else {
None
}
}
/// Return the close authority.
///
/// This method should be used when the caller knows that the token will
/// have a close authority set since it skips the `Option` check.
#[inline(always)]
pub fn close_authority_unchecked(&self) -> &Address {
&self.close_authority
}
#[inline(always)]
pub fn is_initialized(&self) -> bool {
self.state != AccountState::Uninitialized as u8
}
#[inline(always)]
pub fn is_frozen(&self) -> bool {
self.state == AccountState::Frozen as u8
}
}