@@ -152,10 +152,6 @@ impl Processor {
152152 let dest_account_info = next_account_info ( account_info_iter) ?;
153153 let authority_info = next_account_info ( account_info_iter) ?;
154154
155- if source_account_info. key == dest_account_info. key {
156- return Ok ( ( ) ) ;
157- }
158-
159155 let mut source_account = Account :: unpack ( & source_account_info. data . borrow ( ) ) ?;
160156 let mut dest_account = Account :: unpack ( & dest_account_info. data . borrow ( ) ) ?;
161157
@@ -180,6 +176,8 @@ impl Processor {
180176 }
181177 }
182178
179+ let self_transfer = source_account_info. key == dest_account_info. key ;
180+
183181 match source_account. delegate {
184182 COption :: Some ( ref delegate) if authority_info. key == delegate => {
185183 Self :: validate_owner (
@@ -191,12 +189,14 @@ impl Processor {
191189 if source_account. delegated_amount < amount {
192190 return Err ( TokenError :: InsufficientFunds . into ( ) ) ;
193191 }
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+ }
200200 }
201201 }
202202 _ => Self :: validate_owner (
@@ -207,6 +207,12 @@ impl Processor {
207207 ) ?,
208208 } ;
209209
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+
210216 source_account. amount = source_account
211217 . amount
212218 . checked_sub ( amount)
@@ -1709,49 +1715,6 @@ mod tests {
17091715 let account = Account :: unpack_unchecked ( & account_account. data ) . unwrap ( ) ;
17101716 assert_eq ! ( account. amount, 1000 ) ;
17111717
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-
17551718 // insufficient funds
17561719 assert_eq ! (
17571720 Err ( TokenError :: InsufficientFunds . into( ) ) ,
0 commit comments