Răsfoiți Sursa

spl: Implement token::set_authority (#307)

Michael Huang 4 ani în urmă
părinte
comite
dd64779273

+ 1 - 0
CHANGELOG.md

@@ -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
 

+ 1 - 0
examples/spl/token-proxy/programs/token-proxy/Cargo.toml

@@ -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"] }

+ 56 - 1
examples/spl/token-proxy/programs/token-proxy/src/lib.rs

@@ -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,
+        }
+    }
+}

+ 22 - 0
examples/spl/token-proxy/tests/token-proxy.js

@@ -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;

+ 35 - 0
spl/src/token.rs

@@ -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);