spl: Implement token::set_authority (#307)

This commit is contained in:
Michael Huang 2021-05-24 19:07:20 -05:00 committed by GitHub
parent 1777ecaee4
commit dd64779273
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 115 additions and 1 deletions

View File

@ -15,6 +15,7 @@ incremented for features.
* ts: Address metadata is now optional for `anchor.workspace` clients ([#310](https://github.com/project-serum/anchor/pull/310)).
* cli: Add global options for override Anchor.toml values ([#313](https://github.com/project-serum/anchor/pull/313)).
* spl: Add `SetAuthority` instruction ([#307](https://github.com/project-serum/anchor/pull/307/files)).
## [0.6.0] - 2021-05-23

View File

@ -15,3 +15,4 @@ cpi = ["no-entrypoint"]
[dependencies]
anchor-lang = { path = "../../../../../lang" }
anchor-spl = { path = "../../../../../spl" }
spl-token = { version = "3.1.1", features = ["no-entrypoint"] }

View File

@ -1,7 +1,7 @@
//! This example demonstrates the use of the `anchor_spl::token` CPI client.
use anchor_lang::prelude::*;
use anchor_spl::token::{self, Burn, MintTo, Transfer};
use anchor_spl::token::{self, Burn, MintTo, SetAuthority, Transfer};
#[program]
mod token_proxy {
@ -18,6 +18,26 @@ mod token_proxy {
pub fn proxy_burn(ctx: Context<ProxyBurn>, amount: u64) -> ProgramResult {
token::burn(ctx.accounts.into(), amount)
}
pub fn proxy_set_authority(
ctx: Context<ProxySetAuthority>,
authority_type: AuthorityType,
new_authority: Option<Pubkey>,
) -> ProgramResult {
token::set_authority(ctx.accounts.into(), authority_type.into(), new_authority)
}
}
#[derive(AnchorSerialize, AnchorDeserialize)]
pub enum AuthorityType {
/// Authority to mint new tokens
MintTokens,
/// Authority to freeze any account associated with the Mint
FreezeAccount,
/// Owner of a given token account
AccountOwner,
/// Authority to close a token account
CloseAccount,
}
#[derive(Accounts)]
@ -53,6 +73,15 @@ pub struct ProxyBurn<'info> {
pub token_program: AccountInfo<'info>,
}
#[derive(Accounts)]
pub struct ProxySetAuthority<'info> {
#[account(signer)]
pub current_authority: AccountInfo<'info>,
#[account(mut)]
pub account_or_mint: AccountInfo<'info>,
pub token_program: AccountInfo<'info>,
}
impl<'a, 'b, 'c, 'info> From<&mut ProxyTransfer<'info>>
for CpiContext<'a, 'b, 'c, 'info, Transfer<'info>>
{
@ -92,3 +121,29 @@ impl<'a, 'b, 'c, 'info> From<&mut ProxyBurn<'info>> for CpiContext<'a, 'b, 'c, '
CpiContext::new(cpi_program, cpi_accounts)
}
}
impl<'a, 'b, 'c, 'info> From<&mut ProxySetAuthority<'info>>
for CpiContext<'a, 'b, 'c, 'info, SetAuthority<'info>>
{
fn from(
accounts: &mut ProxySetAuthority<'info>,
) -> CpiContext<'a, 'b, 'c, 'info, SetAuthority<'info>> {
let cpi_accounts = SetAuthority {
account_or_mint: accounts.account_or_mint.clone(),
current_authority: accounts.current_authority.clone(),
}; // TODO: Support multisig signers
let cpi_program = accounts.token_program.clone();
CpiContext::new(cpi_program, cpi_accounts)
}
}
impl From<AuthorityType> for spl_token::instruction::AuthorityType {
fn from(authority_ty: AuthorityType) -> spl_token::instruction::AuthorityType {
match authority_ty {
AuthorityType::MintTokens => spl_token::instruction::AuthorityType::MintTokens,
AuthorityType::FreezeAccount => spl_token::instruction::AuthorityType::FreezeAccount,
AuthorityType::AccountOwner => spl_token::instruction::AuthorityType::AccountOwner,
AuthorityType::CloseAccount => spl_token::instruction::AuthorityType::CloseAccount,
}
}
}

View File

@ -64,6 +64,24 @@ describe("token", () => {
const toAccount = await getTokenAccount(provider, to);
assert.ok(toAccount.amount.eq(new anchor.BN(1)));
});
it("Set new mint authority", async () => {
const newMintAuthority = anchor.web3.Keypair.generate();
await program.rpc.proxySetAuthority(
{ mintTokens: {} },
newMintAuthority.publicKey,
{
accounts: {
accountOrMint: mint,
currentAuthority: provider.wallet.publicKey,
tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
},
}
);
const mintInfo = await getMintInfo(provider, mint);
assert.ok(mintInfo.mintAuthority.equals(newMintAuthority.publicKey));
});
});
// SPL token client boilerplate for test initialization. Everything below here is
@ -82,6 +100,10 @@ async function getTokenAccount(provider, addr) {
return await serumCmn.getTokenAccount(provider, addr);
}
async function getMintInfo(provider, mintAddr) {
return await serumCmn.getMintInfo(provider, mintAddr);
}
async function createMint(provider, authority) {
if (authority === undefined) {
authority = provider.wallet.publicKey;

View File

@ -126,6 +126,35 @@ pub fn initialize_account<'a, 'b, 'c, 'info>(
)
}
pub fn set_authority<'a, 'b, 'c, 'info>(
ctx: CpiContext<'a, 'b, 'c, 'info, SetAuthority<'info>>,
authority_type: spl_token::instruction::AuthorityType,
new_authority: Option<Pubkey>,
) -> ProgramResult {
let mut spl_new_authority: Option<&Pubkey> = None;
if new_authority.is_some() {
spl_new_authority = new_authority.as_ref()
}
let ix = spl_token::instruction::set_authority(
&spl_token::ID,
ctx.accounts.account_or_mint.key,
spl_new_authority,
authority_type,
ctx.accounts.current_authority.key,
&[], // TODO: Support multisig signers.
)?;
solana_program::program::invoke_signed(
&ix,
&[
ctx.accounts.account_or_mint.clone(),
ctx.accounts.current_authority.clone(),
ctx.program.clone(),
],
ctx.signer_seeds,
)
}
#[derive(Accounts)]
pub struct Transfer<'info> {
pub from: AccountInfo<'info>,
@ -161,6 +190,12 @@ pub struct InitializeAccount<'info> {
pub authority: AccountInfo<'info>,
}
#[derive(Accounts)]
pub struct SetAuthority<'info> {
pub current_authority: AccountInfo<'info>,
pub account_or_mint: AccountInfo<'info>,
}
#[derive(Clone)]
pub struct TokenAccount(spl_token::state::Account);