Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit c6f147e

Browse files
t-nelsonmergify[bot]
authored andcommitted
token: Fully check self-transfers
1 parent f61d7a8 commit c6f147e

File tree

1 file changed

+16
-53
lines changed

1 file changed

+16
-53
lines changed

token/program/src/processor.rs

Lines changed: 16 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)