diff --git a/token/js/src/extensions/confidentialTransfer/actions.ts b/token/js/src/extensions/confidentialTransfer/actions.ts new file mode 100644 index 00000000000..cc58ddea784 --- /dev/null +++ b/token/js/src/extensions/confidentialTransfer/actions.ts @@ -0,0 +1,28 @@ +import type { ConfirmOptions, Connection, PublicKey, Signer, TransactionSignature } from '@solana/web3.js'; +import { sendAndConfirmTransaction, Transaction } from '@solana/web3.js'; +import { TOKEN_2022_PROGRAM_ID } from '../../constants.js'; +import { createConfidentialTransferUpdateMintInstruction } from './instructions.js'; +import { PodElGamalPubkey } from 'solana-zk-token-sdk-experimental'; + +export async function updateMint( + connection: Connection, + payer: Signer, + mint: PublicKey, + autoApproveNewAccounts: boolean, + auditorElGamalPubkey: PodElGamalPubkey | null, + authority: Signer, + confirmOptions?: ConfirmOptions, + programId = TOKEN_2022_PROGRAM_ID +): Promise { + const transaction = new Transaction().add( + createConfidentialTransferUpdateMintInstruction( + mint, + authority.publicKey, + autoApproveNewAccounts, + auditorElGamalPubkey, + programId + ) + ); + + return await sendAndConfirmTransaction(connection, transaction, [payer, authority], confirmOptions); +} diff --git a/token/js/src/extensions/confidentialTransfer/index.ts b/token/js/src/extensions/confidentialTransfer/index.ts index d1cedccd563..274bca1b3fe 100644 --- a/token/js/src/extensions/confidentialTransfer/index.ts +++ b/token/js/src/extensions/confidentialTransfer/index.ts @@ -1,2 +1,3 @@ +export * from './actions.js'; export * from './state.js'; export * from './instructions.js'; diff --git a/token/js/src/extensions/confidentialTransfer/instructions.ts b/token/js/src/extensions/confidentialTransfer/instructions.ts index bb586cf52d4..0cd01b7a7d0 100644 --- a/token/js/src/extensions/confidentialTransfer/instructions.ts +++ b/token/js/src/extensions/confidentialTransfer/instructions.ts @@ -9,6 +9,7 @@ import { elgamalPublicKey } from './elgamal.js'; export enum ConfidentialTransferInstruction { InitializeMint = 0, + UpdateMint = 1, } export interface InitializeMintData { @@ -54,3 +55,46 @@ export function createConfidentialTransferInitializeMintInstruction( return new TransactionInstruction({ keys, programId, data }); } + +export interface UpdateMintData { + instruction: TokenInstruction.ConfidentialTransferExtension; + confidentialTransferInstruction: ConfidentialTransferInstruction.UpdateMint; + autoApproveNewAccounts: boolean; + auditorElGamalPubkey: PodElGamalPubkey | null; +} + +export const updateMintData = struct([ + u8('instruction'), + u8('confidentialTransferInstruction'), + bool('autoApproveNewAccounts'), + elgamalPublicKey('auditorElGamalPubkey'), +]); + +export function createConfidentialTransferUpdateMintInstruction( + mint: PublicKey, + confidentialTransferMintAuthority: PublicKey, + autoApproveNewAccounts: boolean, + auditorElGamalPubkey: PodElGamalPubkey | null, + programId = TOKEN_2022_PROGRAM_ID +): TransactionInstruction { + if (!programSupportsExtensions(programId)) { + throw new TokenUnsupportedInstructionError(); + } + const keys = [ + { pubkey: mint, isSigner: false, isWritable: true }, + { pubkey: confidentialTransferMintAuthority, isSigner: true, isWritable: false }, + ]; + + const data = Buffer.alloc(updateMintData.span); + updateMintData.encode( + { + instruction: TokenInstruction.ConfidentialTransferExtension, + confidentialTransferInstruction: ConfidentialTransferInstruction.UpdateMint, + autoApproveNewAccounts: autoApproveNewAccounts, + auditorElGamalPubkey: auditorElGamalPubkey ?? PodElGamalPubkey.default(), + }, + data + ); + + return new TransactionInstruction({ keys, programId, data }); +} diff --git a/token/js/test/e2e-2022/confidentialTransfer.test.ts b/token/js/test/e2e-2022/confidentialTransfer.test.ts index 8544c170efb..df8af693918 100644 --- a/token/js/test/e2e-2022/confidentialTransfer.test.ts +++ b/token/js/test/e2e-2022/confidentialTransfer.test.ts @@ -9,6 +9,7 @@ import { TEST_PROGRAM_ID, newAccountWithLamports, getConnection } from '../commo import { createConfidentialTransferInitializeMintInstruction, getConfidentialTransferMint, + updateMint, } from '../../src/extensions/confidentialTransfer/index'; const TEST_TOKEN_DECIMALS = 2; @@ -85,5 +86,28 @@ describe('confidentialTransfer', () => { expect(confidentialTransferMint.auditorElGamalPubkey.equals(auditorPubkey)); // TODO: equals? } }); + + it('mint updates', async () => { + const newAutoApproveNewAccounts = false; + const newAuditorElGamalKeypair = ElGamalKeypair.new_rand(); + const newAuditorElGamalPubkey = PodElGamalPubkey.encoded(newAuditorElGamalKeypair.pubkey_owned()); + + await updateMint( + connection, + payer, + mint, + newAutoApproveNewAccounts, + newAuditorElGamalPubkey, + confidentialTransferMintAuthority + ); + + const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); + const confidentialTransferMint = getConfidentialTransferMint(mintInfo); + expect(confidentialTransferMint).to.not.be.null; + if (confidentialTransferMint !== null) { + expect(confidentialTransferMint.autoApproveNewAccounts).to.eql(newAutoApproveNewAccounts); + expect(confidentialTransferMint.auditorElGamalPubkey.equals(newAuditorElGamalPubkey)); + } + }); }); });