1
+ use code_vm_api:: prelude:: * ;
2
+ use steel:: * ;
3
+
4
+ use crate :: ExecContext ;
5
+
6
+ /*
7
+ This instruction is used to transfer tokens from *one* virtual account to a
8
+ number of virtual accounts. The signature of the source account is required
9
+ to authorize the transfer.
10
+
11
+ Extra accounts required by this instruction:
12
+
13
+ | # | R/W | Type | Req | PDA | Name | Description |
14
+ |---|-----|------------- |-----|-----|--------|--------------|
15
+ |...| The same as the vm_exec instruction. |
16
+ |---|-----|------------- |-----|-----|--------|--------------|
17
+ | 6 | | <None> | | | | |
18
+ | 7 | | <None> | | | | |
19
+ | 8 | | <None> | | | | |
20
+ | 9 | | <None> | | | | |
21
+ |10 | | <None> | | | | |
22
+
23
+
24
+ Instruction data:
25
+
26
+ 0. signature: [u8;64] - The opcode to execute.
27
+ 1. amount: [u64] - The account_indicies of the virtual accounts to use.
28
+ 2. count: [u8] - The number of destinations.
29
+ */
30
+ pub fn process_airdrop (
31
+ ctx : & ExecContext ,
32
+ data : & ExecIxData ,
33
+ ) -> ProgramResult {
34
+
35
+ let vm = load_vm ( ctx. vm_info ) ?;
36
+ let args = AirdropOp :: try_from_bytes ( & data. data ) ?. to_struct ( ) ?;
37
+
38
+ let mem_indicies = & data. mem_indicies ;
39
+ let mem_banks = & data. mem_banks ;
40
+ let num_accounts = 2 + ( args. count as usize ) ;
41
+
42
+ check_condition (
43
+ mem_indicies. len ( ) == num_accounts,
44
+ "invalid number of memory indicies" ,
45
+ ) ?;
46
+
47
+ check_condition (
48
+ mem_banks. len ( ) == num_accounts,
49
+ "invalid number of memory banks" ,
50
+ ) ?;
51
+
52
+ let nonce_index = mem_indicies[ 0 ] ;
53
+ let nonce_mem = mem_banks[ 0 ] ;
54
+
55
+ let src_index = mem_indicies[ 1 ] ;
56
+ let src_mem = mem_banks[ 1 ] ;
57
+
58
+ let vm_mem = ctx. get_banks ( ) ;
59
+
60
+ check_condition (
61
+ vm_mem[ nonce_mem as usize ] . is_some ( ) ,
62
+ "the nonce memory account must be provided" ,
63
+ ) ?;
64
+
65
+ check_condition (
66
+ vm_mem[ src_mem as usize ] . is_some ( ) ,
67
+ "the source memory account must be provided" ,
68
+ ) ?;
69
+
70
+ let nonce_mem_info = vm_mem[ nonce_mem as usize ] . unwrap ( ) ;
71
+ let src_mem_info = vm_mem[ src_mem as usize ] . unwrap ( ) ;
72
+
73
+ let va = try_read ( & nonce_mem_info, nonce_index) ?;
74
+ let mut vdn = va. into_inner_nonce ( ) . unwrap ( ) ;
75
+
76
+ let va = try_read ( & src_mem_info, src_index) ?;
77
+ let mut src_vta = va. into_inner_timelock ( ) . unwrap ( ) ;
78
+
79
+ let total_amount = args. amount
80
+ . checked_mul ( args. count as u64 )
81
+ . ok_or ( ProgramError :: ArithmeticOverflow ) ?;
82
+
83
+ if src_vta. balance < total_amount {
84
+ return Err ( ProgramError :: InsufficientFunds ) ;
85
+ }
86
+
87
+ src_vta. balance = src_vta. balance
88
+ . checked_sub ( total_amount)
89
+ . ok_or ( ProgramError :: ArithmeticOverflow ) ?;
90
+
91
+ let mut dst_pubkeys = Vec :: new ( ) ;
92
+ for i in 0 ..args. count as usize {
93
+ let dst_index = mem_indicies[ 2 + i] ;
94
+ let dst_mem = mem_banks[ 2 + i] ;
95
+
96
+ check_condition (
97
+ vm_mem[ dst_mem as usize ] . is_some ( ) ,
98
+ "a destination memory account must be provided" ,
99
+ ) ?;
100
+
101
+ let dst_mem_info = vm_mem[ dst_mem as usize ] . unwrap ( ) ;
102
+
103
+ let va = try_read ( & dst_mem_info, dst_index) ?;
104
+ let mut dst_vta = va. into_inner_timelock ( ) . unwrap ( ) ;
105
+
106
+ // Check if this destination is actually the source.
107
+ let is_same_account = ( src_mem == dst_mem) && ( src_index == dst_index) ;
108
+ if is_same_account {
109
+ // If the source is also in the destinations list, it receives the airdrop as well.
110
+ src_vta. balance = src_vta. balance
111
+ . checked_add ( args. amount )
112
+ . ok_or ( ProgramError :: ArithmeticOverflow ) ?;
113
+
114
+ } else {
115
+ // Normal destination: add the airdrop to its balance
116
+ dst_vta. balance = dst_vta. balance
117
+ . checked_add ( args. amount )
118
+ . ok_or ( ProgramError :: ArithmeticOverflow ) ?;
119
+
120
+ // Write the updated destination back
121
+ try_write (
122
+ dst_mem_info,
123
+ dst_index,
124
+ & VirtualAccount :: Timelock ( dst_vta)
125
+ ) ?;
126
+ }
127
+
128
+ dst_pubkeys. push ( dst_vta. owner ) ;
129
+ }
130
+
131
+ let hash = create_airdrop_message (
132
+ & vm,
133
+ & src_vta,
134
+ & dst_pubkeys,
135
+ args. amount ,
136
+ & vdn,
137
+ ) ;
138
+
139
+ sig_verify (
140
+ src_vta. owner . as_ref ( ) ,
141
+ args. signature . as_ref ( ) ,
142
+ hash. as_ref ( ) ,
143
+ ) ?;
144
+
145
+ vdn. value = vm. get_current_poh ( ) ;
146
+
147
+ // Finally, write back the updated source (which now includes
148
+ // any airdrop shares if the source was also in the destination list).
149
+ try_write (
150
+ src_mem_info,
151
+ src_index,
152
+ & VirtualAccount :: Timelock ( src_vta)
153
+ ) ?;
154
+
155
+ try_write (
156
+ nonce_mem_info,
157
+ nonce_index,
158
+ & VirtualAccount :: Nonce ( vdn)
159
+ ) ?;
160
+
161
+ Ok ( ( ) )
162
+ }
0 commit comments