@@ -152,10 +152,6 @@ impl Processor {
152
152
let dest_account_info = next_account_info ( account_info_iter) ?;
153
153
let authority_info = next_account_info ( account_info_iter) ?;
154
154
155
- if source_account_info. key == dest_account_info. key {
156
- return Ok ( ( ) ) ;
157
- }
158
-
159
155
let mut source_account = Account :: unpack ( & source_account_info. data . borrow ( ) ) ?;
160
156
let mut dest_account = Account :: unpack ( & dest_account_info. data . borrow ( ) ) ?;
161
157
@@ -180,6 +176,8 @@ impl Processor {
180
176
}
181
177
}
182
178
179
+ let self_transfer = source_account_info. key == dest_account_info. key ;
180
+
183
181
match source_account. delegate {
184
182
COption :: Some ( ref delegate) if authority_info. key == delegate => {
185
183
Self :: validate_owner (
@@ -191,12 +189,14 @@ impl Processor {
191
189
if source_account. delegated_amount < amount {
192
190
return Err ( TokenError :: InsufficientFunds . into ( ) ) ;
193
191
}
194
- source_account. delegated_amount = source_account
195
- . delegated_amount
196
- . checked_sub ( amount)
197
- . ok_or ( TokenError :: Overflow ) ?;
198
- if source_account. delegated_amount == 0 {
199
- source_account. delegate = COption :: None ;
192
+ if !self_transfer {
193
+ source_account. delegated_amount = source_account
194
+ . delegated_amount
195
+ . checked_sub ( amount)
196
+ . ok_or ( TokenError :: Overflow ) ?;
197
+ if source_account. delegated_amount == 0 {
198
+ source_account. delegate = COption :: None ;
199
+ }
200
200
}
201
201
}
202
202
_ => Self :: validate_owner (
@@ -207,6 +207,12 @@ impl Processor {
207
207
) ?,
208
208
} ;
209
209
210
+ // This check MUST occur just before the amounts are manipulated
211
+ // to ensure self-transfers are fully validated
212
+ if self_transfer {
213
+ return Ok ( ( ) ) ;
214
+ }
215
+
210
216
source_account. amount = source_account
211
217
. amount
212
218
. checked_sub ( amount)
@@ -1709,49 +1715,6 @@ mod tests {
1709
1715
let account = Account :: unpack_unchecked ( & account_account. data ) . unwrap ( ) ;
1710
1716
assert_eq ! ( account. amount, 1000 ) ;
1711
1717
1712
- // bogus transfer to self using system accounts.
1713
- //
1714
- // Transfer will succeed if the source and destination accounts have the same address,
1715
- // regardless of whether it is a valid token account.
1716
- //
1717
- // This is probably wrong but transactions in the wild have been observed to do this so
1718
- // this behavior is now part of the token ABI
1719
- {
1720
- let system_account_key = Pubkey :: new_unique ( ) ;
1721
- let mut system_account = SolanaAccount :: new ( 1 , 0 , & Pubkey :: default ( ) ) ;
1722
-
1723
- let instruction = transfer (
1724
- & program_id,
1725
- & system_account_key,
1726
- & system_account_key,
1727
- & owner_key,
1728
- & [ ] ,
1729
- 500 ,
1730
- )
1731
- . unwrap ( ) ;
1732
-
1733
- let account_account_info = AccountInfo :: from ( (
1734
- & instruction. accounts [ 0 ] . pubkey ,
1735
- instruction. accounts [ 0 ] . is_signer ,
1736
- & mut system_account,
1737
- ) ) ;
1738
- let owner_account_info = AccountInfo :: from ( (
1739
- & instruction. accounts [ 2 ] . pubkey ,
1740
- instruction. accounts [ 2 ] . is_signer ,
1741
- & mut owner_account,
1742
- ) ) ;
1743
- Processor :: process (
1744
- & instruction. program_id ,
1745
- & [
1746
- account_account_info. clone ( ) ,
1747
- account_account_info,
1748
- owner_account_info,
1749
- ] ,
1750
- & instruction. data ,
1751
- )
1752
- . unwrap ( )
1753
- }
1754
-
1755
1718
// insufficient funds
1756
1719
assert_eq ! (
1757
1720
Err ( TokenError :: InsufficientFunds . into( ) ) ,
0 commit comments