Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions pinocchio/interface/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,11 @@ pub enum TokenInstruction {
///
/// * Single owner
/// 0. `[writable]` The source account.
/// 1. `[signer]` The source account owner.
/// 1. `[signer]` The source account's owner/delegate.
///
/// * Multisignature owner
/// * Multisignature owner/delegate
/// 0. `[writable]` The source account.
/// 1. `[]` The source account's multisignature owner.
/// 1. `[]` The source account's multisignature owner/delegate.
/// 2. `..+M` `[signer]` M signer accounts.
Revoke,

Expand Down
22 changes: 18 additions & 4 deletions pinocchio/program/src/processor/revoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,32 @@ pub fn process_revoke(accounts: &[AccountInfo]) -> ProgramResult {
let source_account =
unsafe { load_mut::<Account>(source_account_info.borrow_mut_data_unchecked())? };

// Unpacking the remaining accounts to get the owner account at this point
// Unpacking the remaining accounts to get the authority account at this point
// to maintain the same order as SPL Token.
let [owner_info, remaining @ ..] = remaining else {
let [authority_info, remaining @ ..] = remaining else {
return Err(ProgramError::NotEnoughAccountKeys);
};

if source_account.is_frozen()? {
return Err(TokenError::AccountFrozen.into());
}

// SAFETY: `owner_info` is not currently borrowed.
unsafe { validate_owner(&source_account.owner, owner_info, remaining)? }
// Validates the owner or delegate.

// SAFETY: `authority_info` is not currently borrowed; in the case
// `authority_info` is the same as `source_account_info`, then it cannot be
// a multisig.
unsafe {
validate_owner(
if source_account.delegate() == Some(authority_info.key()) {
authority_info.key()
Comment thread
febo marked this conversation as resolved.
} else {
&source_account.owner
},
authority_info,
remaining,
)?
};

source_account.clear_delegate();
source_account.set_delegated_amount(0);
Expand Down
83 changes: 83 additions & 0 deletions pinocchio/program/tests/revoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,86 @@ async fn revoke() {
assert!(account.delegate.is_none());
assert!(account.delegated_amount == 0);
}

#[tokio::test]
async fn revoke_with_delegate() {
let mut context = ProgramTest::new("pinocchio_token_program", TOKEN_PROGRAM_ID, None)
.start_with_context()
.await;

// Given a mint account.

let mint_authority = Keypair::new();
let freeze_authority = Pubkey::new_unique();

let mint = mint::initialize(
&mut context,
mint_authority.pubkey(),
Some(freeze_authority),
&TOKEN_PROGRAM_ID,
)
.await
.unwrap();

// And a token account with 100 tokens.

let owner = Keypair::new();

let account =
account::initialize(&mut context, &mint, &owner.pubkey(), &TOKEN_PROGRAM_ID).await;

mint::mint(
&mut context,
&mint,
&account,
&mint_authority,
100,
&TOKEN_PROGRAM_ID,
)
.await
.unwrap();

// And 50 tokens delegated.

let delegate = Keypair::new();

account::approve(
&mut context,
&account,
&delegate.pubkey(),
&owner,
50,
&TOKEN_PROGRAM_ID,
)
.await;

// When we revoke the delegation with the delegate as authority.

let revoke_ix = spl_token_interface::instruction::revoke(
&spl_token_interface::ID,
&account,
&delegate.pubkey(),
&[],
)
.unwrap();

let tx = Transaction::new_signed_with_payer(
&[revoke_ix],
Some(&context.payer.pubkey()),
&[&context.payer, &delegate],
context.last_blockhash,
);
context.banks_client.process_transaction(tx).await.unwrap();

// Then the account should not have a delegate nor delegated amount.

let account = context.banks_client.get_account(account).await.unwrap();

assert!(account.is_some());

let account = account.unwrap();
Comment thread
febo marked this conversation as resolved.
Outdated
let account = spl_token_interface::state::Account::unpack(&account.data).unwrap();

assert!(account.delegate.is_none());
assert!(account.delegated_amount == 0);
}