From 08f9463404d31aad34692cd7144bf7e7d64efd9c Mon Sep 17 00:00:00 2001 From: Ilan Gitter <8359193+gitteri@users.noreply.github.com> Date: Tue, 4 Mar 2025 16:47:23 -0700 Subject: [PATCH 1/8] add idl and generate js client --- README.md | 2 + .../js/src/generated/instructions/index.ts | 2 + .../initializeScaledUiAmountMint.ts | 212 +++++++++++++++ .../updateMultiplierScaledUiMint.ts | 240 +++++++++++++++++ .../js/src/generated/programs/token2022.ts | 22 ++ clients/js/src/generated/types/extension.ts | 52 ++++ .../initializeScaledUiAmountMint.test.ts | 75 ++++++ program/idl.json | 248 ++++++++++++++++++ 8 files changed, 853 insertions(+) create mode 100644 clients/js/src/generated/instructions/initializeScaledUiAmountMint.ts create mode 100644 clients/js/src/generated/instructions/updateMultiplierScaledUiMint.ts create mode 100644 clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts diff --git a/README.md b/README.md index 21030169..8bb73da3 100644 --- a/README.md +++ b/README.md @@ -65,8 +65,10 @@ The following clients are available for your programs. You may use the following ## Starting and stopping the local validator The following script is available to start your local validator. +In order to run the local validator you must also have the spl-elgamal-registry program built. ```sh +pnpm confidential-transfer:elgamal-registry:build # you only need to run this once pnpm validator:start ``` diff --git a/clients/js/src/generated/instructions/index.ts b/clients/js/src/generated/instructions/index.ts index c44ba678..a079c572 100644 --- a/clients/js/src/generated/instructions/index.ts +++ b/clients/js/src/generated/instructions/index.ts @@ -56,6 +56,7 @@ export * from './initializeMultisig'; export * from './initializeMultisig2'; export * from './initializeNonTransferableMint'; export * from './initializePermanentDelegate'; +export * from './initializeScaledUiAmountMint'; export * from './initializeTokenGroup'; export * from './initializeTokenGroupMember'; export * from './initializeTokenMetadata'; @@ -80,6 +81,7 @@ export * from './updateDefaultAccountState'; export * from './updateGroupMemberPointer'; export * from './updateGroupPointer'; export * from './updateMetadataPointer'; +export * from './updateMultiplierScaledUiMint'; export * from './updateRateInterestBearingMint'; export * from './updateTokenGroupMaxSize'; export * from './updateTokenGroupUpdateAuthority'; diff --git a/clients/js/src/generated/instructions/initializeScaledUiAmountMint.ts b/clients/js/src/generated/instructions/initializeScaledUiAmountMint.ts new file mode 100644 index 00000000..7a7055c3 --- /dev/null +++ b/clients/js/src/generated/instructions/initializeScaledUiAmountMint.ts @@ -0,0 +1,212 @@ +/** + * This code was AUTOGENERATED using the codama library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun codama to update it. + * + * @see https://github.com/codama-idl/codama + */ + +import { + combineCodec, + getAddressDecoder, + getAddressEncoder, + getF64Decoder, + getF64Encoder, + getOptionDecoder, + getOptionEncoder, + getStructDecoder, + getStructEncoder, + getU8Decoder, + getU8Encoder, + transformEncoder, + type Address, + type Codec, + type Decoder, + type Encoder, + type IAccountMeta, + type IInstruction, + type IInstructionWithAccounts, + type IInstructionWithData, + type Option, + type OptionOrNullable, + type WritableAccount, +} from '@solana/web3.js'; +import { TOKEN_2022_PROGRAM_ADDRESS } from '../programs'; +import { getAccountMetaFactory, type ResolvedAccount } from '../shared'; + +export const INITIALIZE_SCALED_UI_AMOUNT_MINT_DISCRIMINATOR = 42; + +export function getInitializeScaledUiAmountMintDiscriminatorBytes() { + return getU8Encoder().encode(INITIALIZE_SCALED_UI_AMOUNT_MINT_DISCRIMINATOR); +} + +export const INITIALIZE_SCALED_UI_AMOUNT_MINT_SCALED_UI_AMOUNT_MINT_DISCRIMINATOR = 0; + +export function getInitializeScaledUiAmountMintScaledUiAmountMintDiscriminatorBytes() { + return getU8Encoder().encode( + INITIALIZE_SCALED_UI_AMOUNT_MINT_SCALED_UI_AMOUNT_MINT_DISCRIMINATOR + ); +} + +export type InitializeScaledUiAmountMintInstruction< + TProgram extends string = typeof TOKEN_2022_PROGRAM_ADDRESS, + TAccountMint extends string | IAccountMeta = string, + TRemainingAccounts extends readonly IAccountMeta[] = [], +> = IInstruction & + IInstructionWithData & + IInstructionWithAccounts< + [ + TAccountMint extends string + ? WritableAccount + : TAccountMint, + ...TRemainingAccounts, + ] + >; + +export type InitializeScaledUiAmountMintInstructionData = { + discriminator: number; + scaledUiAmountMintDiscriminator: number; + /** The authority that can update the multiplier */ + authority: Option
; + /** The initial multiplier for the scaled UI extension */ + multiplier: number; +}; + +export type InitializeScaledUiAmountMintInstructionDataArgs = { + /** The authority that can update the multiplier */ + authority: OptionOrNullable
; + /** The initial multiplier for the scaled UI extension */ + multiplier: number; +}; + +export function getInitializeScaledUiAmountMintInstructionDataEncoder(): Encoder { + return transformEncoder( + getStructEncoder([ + ['discriminator', getU8Encoder()], + ['scaledUiAmountMintDiscriminator', getU8Encoder()], + [ + 'authority', + getOptionEncoder(getAddressEncoder(), { + prefix: null, + noneValue: 'zeroes', + }), + ], + ['multiplier', getF64Encoder()], + ]), + (value) => ({ + ...value, + discriminator: INITIALIZE_SCALED_UI_AMOUNT_MINT_DISCRIMINATOR, + scaledUiAmountMintDiscriminator: + INITIALIZE_SCALED_UI_AMOUNT_MINT_SCALED_UI_AMOUNT_MINT_DISCRIMINATOR, + }) + ); +} + +export function getInitializeScaledUiAmountMintInstructionDataDecoder(): Decoder { + return getStructDecoder([ + ['discriminator', getU8Decoder()], + ['scaledUiAmountMintDiscriminator', getU8Decoder()], + [ + 'authority', + getOptionDecoder(getAddressDecoder(), { + prefix: null, + noneValue: 'zeroes', + }), + ], + ['multiplier', getF64Decoder()], + ]); +} + +export function getInitializeScaledUiAmountMintInstructionDataCodec(): Codec< + InitializeScaledUiAmountMintInstructionDataArgs, + InitializeScaledUiAmountMintInstructionData +> { + return combineCodec( + getInitializeScaledUiAmountMintInstructionDataEncoder(), + getInitializeScaledUiAmountMintInstructionDataDecoder() + ); +} + +export type InitializeScaledUiAmountMintInput< + TAccountMint extends string = string, +> = { + /** The mint to initialize. */ + mint: Address; + authority: InitializeScaledUiAmountMintInstructionDataArgs['authority']; + multiplier: InitializeScaledUiAmountMintInstructionDataArgs['multiplier']; +}; + +export function getInitializeScaledUiAmountMintInstruction< + TAccountMint extends string, + TProgramAddress extends Address = typeof TOKEN_2022_PROGRAM_ADDRESS, +>( + input: InitializeScaledUiAmountMintInput, + config?: { programAddress?: TProgramAddress } +): InitializeScaledUiAmountMintInstruction { + // Program address. + const programAddress = config?.programAddress ?? TOKEN_2022_PROGRAM_ADDRESS; + + // Original accounts. + const originalAccounts = { + mint: { value: input.mint ?? null, isWritable: true }, + }; + const accounts = originalAccounts as Record< + keyof typeof originalAccounts, + ResolvedAccount + >; + + // Original args. + const args = { ...input }; + + const getAccountMeta = getAccountMetaFactory(programAddress, 'programId'); + const instruction = { + accounts: [getAccountMeta(accounts.mint)], + programAddress, + data: getInitializeScaledUiAmountMintInstructionDataEncoder().encode( + args as InitializeScaledUiAmountMintInstructionDataArgs + ), + } as InitializeScaledUiAmountMintInstruction; + + return instruction; +} + +export type ParsedInitializeScaledUiAmountMintInstruction< + TProgram extends string = typeof TOKEN_2022_PROGRAM_ADDRESS, + TAccountMetas extends readonly IAccountMeta[] = readonly IAccountMeta[], +> = { + programAddress: Address; + accounts: { + /** The mint to initialize. */ + mint: TAccountMetas[0]; + }; + data: InitializeScaledUiAmountMintInstructionData; +}; + +export function parseInitializeScaledUiAmountMintInstruction< + TProgram extends string, + TAccountMetas extends readonly IAccountMeta[], +>( + instruction: IInstruction & + IInstructionWithAccounts & + IInstructionWithData +): ParsedInitializeScaledUiAmountMintInstruction { + if (instruction.accounts.length < 1) { + // TODO: Coded error. + throw new Error('Not enough accounts'); + } + let accountIndex = 0; + const getNextAccount = () => { + const accountMeta = instruction.accounts![accountIndex]!; + accountIndex += 1; + return accountMeta; + }; + return { + programAddress: instruction.programAddress, + accounts: { + mint: getNextAccount(), + }, + data: getInitializeScaledUiAmountMintInstructionDataDecoder().decode( + instruction.data + ), + }; +} diff --git a/clients/js/src/generated/instructions/updateMultiplierScaledUiMint.ts b/clients/js/src/generated/instructions/updateMultiplierScaledUiMint.ts new file mode 100644 index 00000000..a17951bf --- /dev/null +++ b/clients/js/src/generated/instructions/updateMultiplierScaledUiMint.ts @@ -0,0 +1,240 @@ +/** + * This code was AUTOGENERATED using the codama library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun codama to update it. + * + * @see https://github.com/codama-idl/codama + */ + +import { + AccountRole, + combineCodec, + getF64Decoder, + getF64Encoder, + getI64Decoder, + getI64Encoder, + getStructDecoder, + getStructEncoder, + getU8Decoder, + getU8Encoder, + transformEncoder, + type Address, + type Codec, + type Decoder, + type Encoder, + type IAccountMeta, + type IAccountSignerMeta, + type IInstruction, + type IInstructionWithAccounts, + type IInstructionWithData, + type TransactionSigner, + type WritableAccount, + type WritableSignerAccount, +} from '@solana/web3.js'; +import { TOKEN_2022_PROGRAM_ADDRESS } from '../programs'; +import { getAccountMetaFactory, type ResolvedAccount } from '../shared'; + +export const UPDATE_MULTIPLIER_SCALED_UI_MINT_DISCRIMINATOR = 42; + +export function getUpdateMultiplierScaledUiMintDiscriminatorBytes() { + return getU8Encoder().encode(UPDATE_MULTIPLIER_SCALED_UI_MINT_DISCRIMINATOR); +} + +export const UPDATE_MULTIPLIER_SCALED_UI_MINT_SCALED_UI_AMOUNT_MINT_DISCRIMINATOR = 1; + +export function getUpdateMultiplierScaledUiMintScaledUiAmountMintDiscriminatorBytes() { + return getU8Encoder().encode( + UPDATE_MULTIPLIER_SCALED_UI_MINT_SCALED_UI_AMOUNT_MINT_DISCRIMINATOR + ); +} + +export type UpdateMultiplierScaledUiMintInstruction< + TProgram extends string = typeof TOKEN_2022_PROGRAM_ADDRESS, + TAccountMint extends string | IAccountMeta = string, + TAccountAuthority extends string | IAccountMeta = string, + TRemainingAccounts extends readonly IAccountMeta[] = [], +> = IInstruction & + IInstructionWithData & + IInstructionWithAccounts< + [ + TAccountMint extends string + ? WritableAccount + : TAccountMint, + TAccountAuthority extends string + ? WritableAccount + : TAccountAuthority, + ...TRemainingAccounts, + ] + >; + +export type UpdateMultiplierScaledUiMintInstructionData = { + discriminator: number; + scaledUiAmountMintDiscriminator: number; + /** The new multiplier for the scaled UI extension */ + multiplier: number; + /** The timestamp at which the new multiplier will take effect */ + effectiveTimestamp: bigint; +}; + +export type UpdateMultiplierScaledUiMintInstructionDataArgs = { + /** The new multiplier for the scaled UI extension */ + multiplier: number; + /** The timestamp at which the new multiplier will take effect */ + effectiveTimestamp: number | bigint; +}; + +export function getUpdateMultiplierScaledUiMintInstructionDataEncoder(): Encoder { + return transformEncoder( + getStructEncoder([ + ['discriminator', getU8Encoder()], + ['scaledUiAmountMintDiscriminator', getU8Encoder()], + ['multiplier', getF64Encoder()], + ['effectiveTimestamp', getI64Encoder()], + ]), + (value) => ({ + ...value, + discriminator: UPDATE_MULTIPLIER_SCALED_UI_MINT_DISCRIMINATOR, + scaledUiAmountMintDiscriminator: + UPDATE_MULTIPLIER_SCALED_UI_MINT_SCALED_UI_AMOUNT_MINT_DISCRIMINATOR, + }) + ); +} + +export function getUpdateMultiplierScaledUiMintInstructionDataDecoder(): Decoder { + return getStructDecoder([ + ['discriminator', getU8Decoder()], + ['scaledUiAmountMintDiscriminator', getU8Decoder()], + ['multiplier', getF64Decoder()], + ['effectiveTimestamp', getI64Decoder()], + ]); +} + +export function getUpdateMultiplierScaledUiMintInstructionDataCodec(): Codec< + UpdateMultiplierScaledUiMintInstructionDataArgs, + UpdateMultiplierScaledUiMintInstructionData +> { + return combineCodec( + getUpdateMultiplierScaledUiMintInstructionDataEncoder(), + getUpdateMultiplierScaledUiMintInstructionDataDecoder() + ); +} + +export type UpdateMultiplierScaledUiMintInput< + TAccountMint extends string = string, + TAccountAuthority extends string = string, +> = { + /** The mint. */ + mint: Address; + /** The multiplier authority. */ + authority: Address | TransactionSigner; + multiplier: UpdateMultiplierScaledUiMintInstructionDataArgs['multiplier']; + effectiveTimestamp: UpdateMultiplierScaledUiMintInstructionDataArgs['effectiveTimestamp']; + multiSigners?: Array; +}; + +export function getUpdateMultiplierScaledUiMintInstruction< + TAccountMint extends string, + TAccountAuthority extends string, + TProgramAddress extends Address = typeof TOKEN_2022_PROGRAM_ADDRESS, +>( + input: UpdateMultiplierScaledUiMintInput, + config?: { programAddress?: TProgramAddress } +): UpdateMultiplierScaledUiMintInstruction< + TProgramAddress, + TAccountMint, + (typeof input)['authority'] extends TransactionSigner + ? WritableSignerAccount & + IAccountSignerMeta + : TAccountAuthority +> { + // Program address. + const programAddress = config?.programAddress ?? TOKEN_2022_PROGRAM_ADDRESS; + + // Original accounts. + const originalAccounts = { + mint: { value: input.mint ?? null, isWritable: true }, + authority: { value: input.authority ?? null, isWritable: true }, + }; + const accounts = originalAccounts as Record< + keyof typeof originalAccounts, + ResolvedAccount + >; + + // Original args. + const args = { ...input }; + + // Remaining accounts. + const remainingAccounts: IAccountMeta[] = (args.multiSigners ?? []).map( + (signer) => ({ + address: signer.address, + role: AccountRole.READONLY_SIGNER, + signer, + }) + ); + + const getAccountMeta = getAccountMetaFactory(programAddress, 'programId'); + const instruction = { + accounts: [ + getAccountMeta(accounts.mint), + getAccountMeta(accounts.authority), + ...remainingAccounts, + ], + programAddress, + data: getUpdateMultiplierScaledUiMintInstructionDataEncoder().encode( + args as UpdateMultiplierScaledUiMintInstructionDataArgs + ), + } as UpdateMultiplierScaledUiMintInstruction< + TProgramAddress, + TAccountMint, + (typeof input)['authority'] extends TransactionSigner + ? WritableSignerAccount & + IAccountSignerMeta + : TAccountAuthority + >; + + return instruction; +} + +export type ParsedUpdateMultiplierScaledUiMintInstruction< + TProgram extends string = typeof TOKEN_2022_PROGRAM_ADDRESS, + TAccountMetas extends readonly IAccountMeta[] = readonly IAccountMeta[], +> = { + programAddress: Address; + accounts: { + /** The mint. */ + mint: TAccountMetas[0]; + /** The multiplier authority. */ + authority: TAccountMetas[1]; + }; + data: UpdateMultiplierScaledUiMintInstructionData; +}; + +export function parseUpdateMultiplierScaledUiMintInstruction< + TProgram extends string, + TAccountMetas extends readonly IAccountMeta[], +>( + instruction: IInstruction & + IInstructionWithAccounts & + IInstructionWithData +): ParsedUpdateMultiplierScaledUiMintInstruction { + if (instruction.accounts.length < 2) { + // TODO: Coded error. + throw new Error('Not enough accounts'); + } + let accountIndex = 0; + const getNextAccount = () => { + const accountMeta = instruction.accounts![accountIndex]!; + accountIndex += 1; + return accountMeta; + }; + return { + programAddress: instruction.programAddress, + accounts: { + mint: getNextAccount(), + authority: getNextAccount(), + }, + data: getUpdateMultiplierScaledUiMintInstructionDataDecoder().decode( + instruction.data + ), + }; +} diff --git a/clients/js/src/generated/programs/token2022.ts b/clients/js/src/generated/programs/token2022.ts index 07f0b80a..f5b23414 100644 --- a/clients/js/src/generated/programs/token2022.ts +++ b/clients/js/src/generated/programs/token2022.ts @@ -61,6 +61,7 @@ import { type ParsedInitializeMultisigInstruction, type ParsedInitializeNonTransferableMintInstruction, type ParsedInitializePermanentDelegateInstruction, + type ParsedInitializeScaledUiAmountMintInstruction, type ParsedInitializeTokenGroupInstruction, type ParsedInitializeTokenGroupMemberInstruction, type ParsedInitializeTokenMetadataInstruction, @@ -84,6 +85,7 @@ import { type ParsedUpdateGroupMemberPointerInstruction, type ParsedUpdateGroupPointerInstruction, type ParsedUpdateMetadataPointerInstruction, + type ParsedUpdateMultiplierScaledUiMintInstruction, type ParsedUpdateRateInterestBearingMintInstruction, type ParsedUpdateTokenGroupMaxSizeInstruction, type ParsedUpdateTokenGroupUpdateAuthorityInstruction, @@ -198,6 +200,8 @@ export enum Token2022Instruction { UpdateGroupPointer, InitializeGroupMemberPointer, UpdateGroupMemberPointer, + InitializeScaledUiAmountMint, + UpdateMultiplierScaledUiMint, InitializeTokenMetadata, UpdateTokenMetadataField, RemoveTokenMetadataKey, @@ -558,6 +562,18 @@ export function identifyToken2022Instruction( ) { return Token2022Instruction.UpdateGroupMemberPointer; } + if ( + containsBytes(data, getU8Encoder().encode(42), 0) && + containsBytes(data, getU8Encoder().encode(0), 1) + ) { + return Token2022Instruction.InitializeScaledUiAmountMint; + } + if ( + containsBytes(data, getU8Encoder().encode(42), 0) && + containsBytes(data, getU8Encoder().encode(1), 1) + ) { + return Token2022Instruction.UpdateMultiplierScaledUiMint; + } if ( containsBytes( data, @@ -858,6 +874,12 @@ export type ParsedToken2022Instruction< | ({ instructionType: Token2022Instruction.UpdateGroupMemberPointer; } & ParsedUpdateGroupMemberPointerInstruction) + | ({ + instructionType: Token2022Instruction.InitializeScaledUiAmountMint; + } & ParsedInitializeScaledUiAmountMintInstruction) + | ({ + instructionType: Token2022Instruction.UpdateMultiplierScaledUiMint; + } & ParsedUpdateMultiplierScaledUiMintInstruction) | ({ instructionType: Token2022Instruction.InitializeTokenMetadata; } & ParsedInitializeTokenMetadataInstruction) diff --git a/clients/js/src/generated/types/extension.ts b/clients/js/src/generated/types/extension.ts index 2192f5cb..ff86429a 100644 --- a/clients/js/src/generated/types/extension.ts +++ b/clients/js/src/generated/types/extension.ts @@ -16,6 +16,8 @@ import { getBooleanEncoder, getDiscriminatedUnionDecoder, getDiscriminatedUnionEncoder, + getF64Decoder, + getF64Encoder, getI16Decoder, getI16Encoder, getMapDecoder, @@ -160,6 +162,13 @@ export type Extension = lastUpdateTimestamp: bigint; currentRate: number; } + | { + __kind: 'ScaledUiAmountConfig'; + authority: Address; + multiplier: number; + newMultiplierEffectiveTimestamp: bigint; + newMultiplier: number; + } | { __kind: 'CpiGuard'; /** Lock certain token operations from taking place within CPI for this account. */ @@ -363,6 +372,13 @@ export type ExtensionArgs = lastUpdateTimestamp: number | bigint; currentRate: number; } + | { + __kind: 'ScaledUiAmountConfig'; + authority: Address; + multiplier: number; + newMultiplierEffectiveTimestamp: number | bigint; + newMultiplier: number; + } | { __kind: 'CpiGuard'; /** Lock certain token operations from taking place within CPI for this account. */ @@ -579,6 +595,18 @@ export function getExtensionEncoder(): Encoder { getU16Encoder() ), ], + [ + 'ScaledUiAmountConfig', + addEncoderSizePrefix( + getStructEncoder([ + ['authority', getAddressEncoder()], + ['multiplier', getF64Encoder()], + ['newMultiplierEffectiveTimestamp', getU64Encoder()], + ['newMultiplier', getF64Encoder()], + ]), + getU16Encoder() + ), + ], [ 'CpiGuard', addEncoderSizePrefix( @@ -876,6 +904,18 @@ export function getExtensionDecoder(): Decoder { getU16Decoder() ), ], + [ + 'ScaledUiAmountConfig', + addDecoderSizePrefix( + getStructDecoder([ + ['authority', getAddressDecoder()], + ['multiplier', getF64Decoder()], + ['newMultiplierEffectiveTimestamp', getU64Decoder()], + ['newMultiplier', getF64Decoder()], + ]), + getU16Decoder() + ), + ], [ 'CpiGuard', addDecoderSizePrefix( @@ -1162,6 +1202,18 @@ export function extension( '__kind', 'InterestBearingConfig' >; +export function extension( + kind: 'ScaledUiAmountConfig', + data: GetDiscriminatedUnionVariantContent< + ExtensionArgs, + '__kind', + 'ScaledUiAmountConfig' + > +): GetDiscriminatedUnionVariant< + ExtensionArgs, + '__kind', + 'ScaledUiAmountConfig' +>; export function extension( kind: 'CpiGuard', data: GetDiscriminatedUnionVariantContent diff --git a/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts b/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts new file mode 100644 index 00000000..1ea1b690 --- /dev/null +++ b/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts @@ -0,0 +1,75 @@ +import test from 'ava'; +import { + createDefaultSolanaClient, + generateKeyPairSignerWithSol, + getCreateMintInstructions, + sendAndConfirmInstructions, +} from '../../_setup'; +import { Account, generateKeyPairSigner, isSome } from '@solana/web3.js'; +import { + extension, + fetchMint, + getInitializeScaledUiAmountMintInstruction, + Mint, +} from '../../../src'; + +test('it initialize a mint account with an interest bearing mint extension', async (t) => { + const client = createDefaultSolanaClient(); + const [multiplierAuthority, mint] = await Promise.all([ + generateKeyPairSignerWithSol(client), + generateKeyPairSigner(), + ]); + + const newMultiplier = 2; + + // And a scaled ui amount mint extension. + const scaledUiAmountMintExtension = extension('ScaledUiAmountConfig', { + authority: multiplierAuthority.address, + multiplier: 1, + newMultiplierEffectiveTimestamp: BigInt(Math.floor(new Date().getTime() / 1000)), + newMultiplier, + }); + + // When we initialize the mint account with the scaled ui amount mint extension. + const [createMintInstruction, initMintInstruction] = + await getCreateMintInstructions({ + authority: multiplierAuthority.address, + client, + extensions: [scaledUiAmountMintExtension], + mint, + payer: multiplierAuthority, + }); + await sendAndConfirmInstructions(client, multiplierAuthority, [ + createMintInstruction, + getInitializeScaledUiAmountMintInstruction({ + mint: mint.address, + authority: multiplierAuthority.address, + multiplier: 1, + }), + initMintInstruction, + ]); + + const mintAccount = await fetchMint(client.rpc, mint.address); + + const extensions = mintAccount.data.extensions; + + t.true(isSome(extensions)); + t.true( + isSome(extensions) && extensions.value[0].__kind === 'ScaledUiAmountConfig' + ); + + // check without need to check timestamp specifically + if ( + isSome(extensions) && + extensions.value[0].__kind === 'ScaledUiAmountConfig' + ) { + t.is(extensions.value[0].authority, multiplierAuthority.address); + t.is(extensions.value[0].multiplier, 1); + t.true(typeof extensions.value[0].newMultiplierEffectiveTimestamp === 'bigint'); + t.is(extensions.value[0].newMultiplier, newMultiplier); + } + + t.like(mintAccount, >{ + address: mint.address, + }); +}); \ No newline at end of file diff --git a/program/idl.json b/program/idl.json index 8557094f..bf794d47 100644 --- a/program/idl.json +++ b/program/idl.json @@ -7322,6 +7322,199 @@ } ] }, + { + "kind": "instructionNode", + "name": "initializeScaledUiAmountMint", + "docs": [ + "Initialize a new mint with the `ScaledUiAmount` extension.", + "", + "Fails if the mint has already been initialized, so must be called before `InitializeMint`.", + "", + "The mint must have exactly enough space allocated for the base mint (82 bytes),", + "plus 83 bytes of padding, 1 byte reserved for the account type,", + "then space required for this extension, plus any others." + ], + "optionalAccountStrategy": "programId", + "accounts": [ + { + "kind": "instructionAccountNode", + "name": "mint", + "isWritable": true, + "isSigner": false, + "isOptional": false, + "docs": ["The mint to initialize."] + } + ], + "arguments": [ + { + "kind": "instructionArgumentNode", + "name": "discriminator", + "defaultValueStrategy": "omitted", + "docs": [], + "type": { + "kind": "numberTypeNode", + "format": "u8", + "endian": "le" + }, + "defaultValue": { + "kind": "numberValueNode", + "number": 42 + } + }, + { + "kind": "instructionArgumentNode", + "name": "scaledUiAmountMintDiscriminator", + "defaultValueStrategy": "omitted", + "docs": [], + "type": { + "kind": "numberTypeNode", + "format": "u8", + "endian": "le" + }, + "defaultValue": { + "kind": "numberValueNode", + "number": 0 + } + }, + { + "kind": "instructionArgumentNode", + "name": "authority", + "docs": ["The authority that can update the multiplier"], + "type": { + "kind": "zeroableOptionTypeNode", + "item": { + "kind": "publicKeyTypeNode" + } + } + }, + { + "kind": "instructionArgumentNode", + "name": "multiplier", + "docs": ["The initial multiplier for the scaled UI extension"], + "type": { + "kind": "numberTypeNode", + "format": "f64", + "endian": "le" + } + } + ], + "discriminators": [ + { + "kind": "fieldDiscriminatorNode", + "name": "discriminator", + "offset": 0 + }, + { + "kind": "fieldDiscriminatorNode", + "name": "scaledUiAmountMintDiscriminator", + "offset": 1 + } + ] + }, + { + "kind": "instructionNode", + "name": "updateMultiplierScaledUiMint", + "docs": [ + "Update the multiplier. Only supported for mints that include the", + "`ScaledUiAmountConfig` extension.", + "You can set a specific timestamp for the multiplier to take effect." + ], + "optionalAccountStrategy": "programId", + "accounts": [ + { + "kind": "instructionAccountNode", + "name": "mint", + "isWritable": true, + "isSigner": false, + "isOptional": false, + "docs": ["The mint."] + }, + { + "kind": "instructionAccountNode", + "name": "authority", + "isWritable": true, + "isSigner": "either", + "isOptional": false, + "docs": ["The multiplier authority."] + } + ], + "arguments": [ + { + "kind": "instructionArgumentNode", + "name": "discriminator", + "defaultValueStrategy": "omitted", + "docs": [], + "type": { + "kind": "numberTypeNode", + "format": "u8", + "endian": "le" + }, + "defaultValue": { + "kind": "numberValueNode", + "number": 42 + } + }, + { + "kind": "instructionArgumentNode", + "name": "scaledUiAmountMintDiscriminator", + "defaultValueStrategy": "omitted", + "docs": [], + "type": { + "kind": "numberTypeNode", + "format": "u8", + "endian": "le" + }, + "defaultValue": { + "kind": "numberValueNode", + "number": 1 + } + }, + { + "kind": "instructionArgumentNode", + "name": "multiplier", + "docs": ["The new multiplier for the scaled UI extension"], + "type": { + "kind": "numberTypeNode", + "format": "f64", + "endian": "le" + } + }, + { + "kind": "instructionArgumentNode", + "name": "effectiveTimestamp", + "docs": ["The timestamp at which the new multiplier will take effect"], + "type": { + "kind": "numberTypeNode", + "format": "i64", + "endian": "le" + } + } + ], + "remainingAccounts": [ + { + "kind": "instructionRemainingAccountsNode", + "isOptional": true, + "isSigner": true, + "docs": [], + "value": { + "kind": "argumentValueNode", + "name": "multiSigners" + } + } + ], + "discriminators": [ + { + "kind": "fieldDiscriminatorNode", + "name": "discriminator", + "offset": 0 + }, + { + "kind": "fieldDiscriminatorNode", + "name": "scaledUiAmountMintDiscriminator", + "offset": 1 + } + ] + }, { "kind": "instructionNode", "name": "initializeTokenMetadata", @@ -8698,6 +8891,61 @@ } } }, + { + "kind": "enumStructVariantTypeNode", + "name": "scaledUiAmountConfig", + "struct": { + "kind": "sizePrefixTypeNode", + "type": { + "kind": "structTypeNode", + "fields": [ + { + "kind": "structFieldTypeNode", + "name": "authority", + "docs": [], + "type": { + "kind": "publicKeyTypeNode" + } + }, + { + "kind": "structFieldTypeNode", + "name": "multiplier", + "docs": [], + "type": { + "kind": "numberTypeNode", + "format": "f64", + "endian": "le" + } + }, + { + "kind": "structFieldTypeNode", + "name": "newMultiplierEffectiveTimestamp", + "docs": [], + "type": { + "kind": "numberTypeNode", + "format": "u64", + "endian": "le" + } + }, + { + "kind": "structFieldTypeNode", + "name": "newMultiplier", + "docs": [], + "type": { + "kind": "numberTypeNode", + "format": "f64", + "endian": "le" + } + } + ] + }, + "prefix": { + "kind": "numberTypeNode", + "format": "u16", + "endian": "le" + } + } + }, { "kind": "enumStructVariantTypeNode", "name": "cpiGuard", From 859bf3d3e972b058c1cb9b4985cccbb3b8815556 Mon Sep 17 00:00:00 2001 From: Ilan Gitter <8359193+gitteri@users.noreply.github.com> Date: Tue, 4 Mar 2025 17:37:19 -0700 Subject: [PATCH 2/8] update scaled ui discriminator number --- .../initializeScaledUiAmountMint.ts | 2 +- .../updateMultiplierScaledUiMint.ts | 2 +- .../js/src/generated/programs/token2022.ts | 4 +- clients/js/src/generated/types/extension.ts | 100 ++++++------- .../js/src/generated/types/extensionType.ts | 1 + .../initializeScaledUiAmountMint.test.ts | 132 +++++++++--------- program/idl.json | 119 ++++++++-------- 7 files changed, 183 insertions(+), 177 deletions(-) diff --git a/clients/js/src/generated/instructions/initializeScaledUiAmountMint.ts b/clients/js/src/generated/instructions/initializeScaledUiAmountMint.ts index 7a7055c3..f68e7dfb 100644 --- a/clients/js/src/generated/instructions/initializeScaledUiAmountMint.ts +++ b/clients/js/src/generated/instructions/initializeScaledUiAmountMint.ts @@ -34,7 +34,7 @@ import { import { TOKEN_2022_PROGRAM_ADDRESS } from '../programs'; import { getAccountMetaFactory, type ResolvedAccount } from '../shared'; -export const INITIALIZE_SCALED_UI_AMOUNT_MINT_DISCRIMINATOR = 42; +export const INITIALIZE_SCALED_UI_AMOUNT_MINT_DISCRIMINATOR = 43; export function getInitializeScaledUiAmountMintDiscriminatorBytes() { return getU8Encoder().encode(INITIALIZE_SCALED_UI_AMOUNT_MINT_DISCRIMINATOR); diff --git a/clients/js/src/generated/instructions/updateMultiplierScaledUiMint.ts b/clients/js/src/generated/instructions/updateMultiplierScaledUiMint.ts index a17951bf..832ff29e 100644 --- a/clients/js/src/generated/instructions/updateMultiplierScaledUiMint.ts +++ b/clients/js/src/generated/instructions/updateMultiplierScaledUiMint.ts @@ -34,7 +34,7 @@ import { import { TOKEN_2022_PROGRAM_ADDRESS } from '../programs'; import { getAccountMetaFactory, type ResolvedAccount } from '../shared'; -export const UPDATE_MULTIPLIER_SCALED_UI_MINT_DISCRIMINATOR = 42; +export const UPDATE_MULTIPLIER_SCALED_UI_MINT_DISCRIMINATOR = 43; export function getUpdateMultiplierScaledUiMintDiscriminatorBytes() { return getU8Encoder().encode(UPDATE_MULTIPLIER_SCALED_UI_MINT_DISCRIMINATOR); diff --git a/clients/js/src/generated/programs/token2022.ts b/clients/js/src/generated/programs/token2022.ts index f5b23414..74088755 100644 --- a/clients/js/src/generated/programs/token2022.ts +++ b/clients/js/src/generated/programs/token2022.ts @@ -563,13 +563,13 @@ export function identifyToken2022Instruction( return Token2022Instruction.UpdateGroupMemberPointer; } if ( - containsBytes(data, getU8Encoder().encode(42), 0) && + containsBytes(data, getU8Encoder().encode(43), 0) && containsBytes(data, getU8Encoder().encode(0), 1) ) { return Token2022Instruction.InitializeScaledUiAmountMint; } if ( - containsBytes(data, getU8Encoder().encode(42), 0) && + containsBytes(data, getU8Encoder().encode(43), 0) && containsBytes(data, getU8Encoder().encode(1), 1) ) { return Token2022Instruction.UpdateMultiplierScaledUiMint; diff --git a/clients/js/src/generated/types/extension.ts b/clients/js/src/generated/types/extension.ts index ff86429a..9f8c09bc 100644 --- a/clients/js/src/generated/types/extension.ts +++ b/clients/js/src/generated/types/extension.ts @@ -162,13 +162,6 @@ export type Extension = lastUpdateTimestamp: bigint; currentRate: number; } - | { - __kind: 'ScaledUiAmountConfig'; - authority: Address; - multiplier: number; - newMultiplierEffectiveTimestamp: bigint; - newMultiplier: number; - } | { __kind: 'CpiGuard'; /** Lock certain token operations from taking place within CPI for this account. */ @@ -217,6 +210,13 @@ export type Extension = /** Amount withheld during confidential transfers, to be harvest to the mint. */ withheldAmount: EncryptedBalance; } + | { + __kind: 'ScaledUiAmountConfig'; + authority: Address; + multiplier: number; + newMultiplierEffectiveTimestamp: bigint; + newMultiplier: number; + } | { __kind: 'MetadataPointer'; /** Optional authority that can set the metadata address. */ @@ -372,13 +372,6 @@ export type ExtensionArgs = lastUpdateTimestamp: number | bigint; currentRate: number; } - | { - __kind: 'ScaledUiAmountConfig'; - authority: Address; - multiplier: number; - newMultiplierEffectiveTimestamp: number | bigint; - newMultiplier: number; - } | { __kind: 'CpiGuard'; /** Lock certain token operations from taking place within CPI for this account. */ @@ -427,6 +420,13 @@ export type ExtensionArgs = /** Amount withheld during confidential transfers, to be harvest to the mint. */ withheldAmount: EncryptedBalanceArgs; } + | { + __kind: 'ScaledUiAmountConfig'; + authority: Address; + multiplier: number; + newMultiplierEffectiveTimestamp: number | bigint; + newMultiplier: number; + } | { __kind: 'MetadataPointer'; /** Optional authority that can set the metadata address. */ @@ -595,18 +595,6 @@ export function getExtensionEncoder(): Encoder { getU16Encoder() ), ], - [ - 'ScaledUiAmountConfig', - addEncoderSizePrefix( - getStructEncoder([ - ['authority', getAddressEncoder()], - ['multiplier', getF64Encoder()], - ['newMultiplierEffectiveTimestamp', getU64Encoder()], - ['newMultiplier', getF64Encoder()], - ]), - getU16Encoder() - ), - ], [ 'CpiGuard', addEncoderSizePrefix( @@ -667,6 +655,18 @@ export function getExtensionEncoder(): Encoder { getU16Encoder() ), ], + [ + 'ScaledUiAmountConfig', + addEncoderSizePrefix( + getStructEncoder([ + ['authority', getAddressEncoder()], + ['multiplier', getF64Encoder()], + ['newMultiplierEffectiveTimestamp', getU64Encoder()], + ['newMultiplier', getF64Encoder()], + ]), + getU16Encoder() + ), + ], [ 'MetadataPointer', addEncoderSizePrefix( @@ -904,18 +904,6 @@ export function getExtensionDecoder(): Decoder { getU16Decoder() ), ], - [ - 'ScaledUiAmountConfig', - addDecoderSizePrefix( - getStructDecoder([ - ['authority', getAddressDecoder()], - ['multiplier', getF64Decoder()], - ['newMultiplierEffectiveTimestamp', getU64Decoder()], - ['newMultiplier', getF64Decoder()], - ]), - getU16Decoder() - ), - ], [ 'CpiGuard', addDecoderSizePrefix( @@ -976,6 +964,18 @@ export function getExtensionDecoder(): Decoder { getU16Decoder() ), ], + [ + 'ScaledUiAmountConfig', + addDecoderSizePrefix( + getStructDecoder([ + ['authority', getAddressDecoder()], + ['multiplier', getF64Decoder()], + ['newMultiplierEffectiveTimestamp', getU64Decoder()], + ['newMultiplier', getF64Decoder()], + ]), + getU16Decoder() + ), + ], [ 'MetadataPointer', addDecoderSizePrefix( @@ -1202,18 +1202,6 @@ export function extension( '__kind', 'InterestBearingConfig' >; -export function extension( - kind: 'ScaledUiAmountConfig', - data: GetDiscriminatedUnionVariantContent< - ExtensionArgs, - '__kind', - 'ScaledUiAmountConfig' - > -): GetDiscriminatedUnionVariant< - ExtensionArgs, - '__kind', - 'ScaledUiAmountConfig' ->; export function extension( kind: 'CpiGuard', data: GetDiscriminatedUnionVariantContent @@ -1278,6 +1266,18 @@ export function extension( '__kind', 'ConfidentialTransferFeeAmount' >; +export function extension( + kind: 'ScaledUiAmountConfig', + data: GetDiscriminatedUnionVariantContent< + ExtensionArgs, + '__kind', + 'ScaledUiAmountConfig' + > +): GetDiscriminatedUnionVariant< + ExtensionArgs, + '__kind', + 'ScaledUiAmountConfig' +>; export function extension( kind: 'MetadataPointer', data: GetDiscriminatedUnionVariantContent< diff --git a/clients/js/src/generated/types/extensionType.ts b/clients/js/src/generated/types/extensionType.ts index cce1d079..7738d928 100644 --- a/clients/js/src/generated/types/extensionType.ts +++ b/clients/js/src/generated/types/extensionType.ts @@ -42,6 +42,7 @@ export enum ExtensionType { TransferHookAccount, ConfidentialTransferFee, ConfidentialTransferFeeAmount, + ScaledUiAmountConfig, MetadataPointer, TokenMetadata, GroupPointer, diff --git a/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts b/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts index 1ea1b690..0434783a 100644 --- a/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts +++ b/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts @@ -1,75 +1,75 @@ -import test from 'ava'; -import { - createDefaultSolanaClient, - generateKeyPairSignerWithSol, - getCreateMintInstructions, - sendAndConfirmInstructions, -} from '../../_setup'; -import { Account, generateKeyPairSigner, isSome } from '@solana/web3.js'; -import { - extension, - fetchMint, - getInitializeScaledUiAmountMintInstruction, - Mint, -} from '../../../src'; +// import test from 'ava'; +// import { +// createDefaultSolanaClient, +// generateKeyPairSignerWithSol, +// getCreateMintInstructions, +// sendAndConfirmInstructions, +// } from '../../_setup'; +// import { Account, generateKeyPairSigner, isSome } from '@solana/web3.js'; +// import { +// extension, +// fetchMint, +// getInitializeScaledUiAmountMintInstruction, +// Mint, +// } from '../../../src'; -test('it initialize a mint account with an interest bearing mint extension', async (t) => { - const client = createDefaultSolanaClient(); - const [multiplierAuthority, mint] = await Promise.all([ - generateKeyPairSignerWithSol(client), - generateKeyPairSigner(), - ]); +// test('it initialize a mint account with an interest bearing mint extension', async (t) => { +// const client = createDefaultSolanaClient(); +// const [multiplierAuthority, mint] = await Promise.all([ +// generateKeyPairSignerWithSol(client), +// generateKeyPairSigner(), +// ]); - const newMultiplier = 2; +// const newMultiplier = 2; - // And a scaled ui amount mint extension. - const scaledUiAmountMintExtension = extension('ScaledUiAmountConfig', { - authority: multiplierAuthority.address, - multiplier: 1, - newMultiplierEffectiveTimestamp: BigInt(Math.floor(new Date().getTime() / 1000)), - newMultiplier, - }); +// // And a scaled ui amount mint extension. +// const scaledUiAmountMintExtension = extension('ScaledUiAmountConfig', { +// authority: multiplierAuthority.address, +// multiplier: 1, +// newMultiplierEffectiveTimestamp: BigInt(Math.floor(new Date().getTime() / 1000)), +// newMultiplier, +// }); - // When we initialize the mint account with the scaled ui amount mint extension. - const [createMintInstruction, initMintInstruction] = - await getCreateMintInstructions({ - authority: multiplierAuthority.address, - client, - extensions: [scaledUiAmountMintExtension], - mint, - payer: multiplierAuthority, - }); - await sendAndConfirmInstructions(client, multiplierAuthority, [ - createMintInstruction, - getInitializeScaledUiAmountMintInstruction({ - mint: mint.address, - authority: multiplierAuthority.address, - multiplier: 1, - }), - initMintInstruction, - ]); +// // When we initialize the mint account with the scaled ui amount mint extension. +// const [createMintInstruction, initMintInstruction] = +// await getCreateMintInstructions({ +// authority: multiplierAuthority.address, +// client, +// extensions: [scaledUiAmountMintExtension], +// mint, +// payer: multiplierAuthority, +// }); +// await sendAndConfirmInstructions(client, multiplierAuthority, [ +// createMintInstruction, +// getInitializeScaledUiAmountMintInstruction({ +// mint: mint.address, +// authority: multiplierAuthority.address, +// multiplier: 1, +// }), +// initMintInstruction, +// ]); - const mintAccount = await fetchMint(client.rpc, mint.address); +// const mintAccount = await fetchMint(client.rpc, mint.address); - const extensions = mintAccount.data.extensions; +// const extensions = mintAccount.data.extensions; - t.true(isSome(extensions)); - t.true( - isSome(extensions) && extensions.value[0].__kind === 'ScaledUiAmountConfig' - ); +// t.true(isSome(extensions)); +// t.true( +// isSome(extensions) && extensions.value[0].__kind === 'ScaledUiAmountConfig' +// ); - // check without need to check timestamp specifically - if ( - isSome(extensions) && - extensions.value[0].__kind === 'ScaledUiAmountConfig' - ) { - t.is(extensions.value[0].authority, multiplierAuthority.address); - t.is(extensions.value[0].multiplier, 1); - t.true(typeof extensions.value[0].newMultiplierEffectiveTimestamp === 'bigint'); - t.is(extensions.value[0].newMultiplier, newMultiplier); - } +// // check without need to check timestamp specifically +// if ( +// isSome(extensions) && +// extensions.value[0].__kind === 'ScaledUiAmountConfig' +// ) { +// t.is(extensions.value[0].authority, multiplierAuthority.address); +// t.is(extensions.value[0].multiplier, 1); +// t.true(typeof extensions.value[0].newMultiplierEffectiveTimestamp === 'bigint'); +// t.is(extensions.value[0].newMultiplier, newMultiplier); +// } - t.like(mintAccount, >{ - address: mint.address, - }); -}); \ No newline at end of file +// t.like(mintAccount, >{ +// address: mint.address, +// }); +// }); \ No newline at end of file diff --git a/program/idl.json b/program/idl.json index bf794d47..24be4a70 100644 --- a/program/idl.json +++ b/program/idl.json @@ -7358,7 +7358,7 @@ }, "defaultValue": { "kind": "numberValueNode", - "number": 42 + "number": 43 } }, { @@ -7451,7 +7451,7 @@ }, "defaultValue": { "kind": "numberValueNode", - "number": 42 + "number": 43 } }, { @@ -8891,61 +8891,6 @@ } } }, - { - "kind": "enumStructVariantTypeNode", - "name": "scaledUiAmountConfig", - "struct": { - "kind": "sizePrefixTypeNode", - "type": { - "kind": "structTypeNode", - "fields": [ - { - "kind": "structFieldTypeNode", - "name": "authority", - "docs": [], - "type": { - "kind": "publicKeyTypeNode" - } - }, - { - "kind": "structFieldTypeNode", - "name": "multiplier", - "docs": [], - "type": { - "kind": "numberTypeNode", - "format": "f64", - "endian": "le" - } - }, - { - "kind": "structFieldTypeNode", - "name": "newMultiplierEffectiveTimestamp", - "docs": [], - "type": { - "kind": "numberTypeNode", - "format": "u64", - "endian": "le" - } - }, - { - "kind": "structFieldTypeNode", - "name": "newMultiplier", - "docs": [], - "type": { - "kind": "numberTypeNode", - "format": "f64", - "endian": "le" - } - } - ] - }, - "prefix": { - "kind": "numberTypeNode", - "format": "u16", - "endian": "le" - } - } - }, { "kind": "enumStructVariantTypeNode", "name": "cpiGuard", @@ -9185,6 +9130,61 @@ } } }, + { + "kind": "enumStructVariantTypeNode", + "name": "scaledUiAmountConfig", + "struct": { + "kind": "sizePrefixTypeNode", + "type": { + "kind": "structTypeNode", + "fields": [ + { + "kind": "structFieldTypeNode", + "name": "authority", + "docs": [], + "type": { + "kind": "publicKeyTypeNode" + } + }, + { + "kind": "structFieldTypeNode", + "name": "multiplier", + "docs": [], + "type": { + "kind": "numberTypeNode", + "format": "f64", + "endian": "le" + } + }, + { + "kind": "structFieldTypeNode", + "name": "newMultiplierEffectiveTimestamp", + "docs": [], + "type": { + "kind": "numberTypeNode", + "format": "u64", + "endian": "le" + } + }, + { + "kind": "structFieldTypeNode", + "name": "newMultiplier", + "docs": [], + "type": { + "kind": "numberTypeNode", + "format": "f64", + "endian": "le" + } + } + ] + }, + "prefix": { + "kind": "numberTypeNode", + "format": "u16", + "endian": "le" + } + } + }, { "kind": "enumStructVariantTypeNode", "name": "metadataPointer", @@ -9684,6 +9684,11 @@ "name": "confidentialTransferFeeAmount", "docs": ["Includes confidential withheld transfer fees"] }, + { + "kind": "enumEmptyVariantTypeNode", + "name": "scaledUiAmountConfig", + "docs": ["Tokens have a scaled UI amount"] + }, { "kind": "enumEmptyVariantTypeNode", "name": "metadataPointer", From cf287710ced2f2b70adbc53643fb15b274012be3 Mon Sep 17 00:00:00 2001 From: Ilan Gitter <8359193+gitteri@users.noreply.github.com> Date: Tue, 4 Mar 2025 17:55:01 -0700 Subject: [PATCH 3/8] merge broken version to get feedback --- clients/js/src/generated/types/extension.ts | 100 +++++++++--------- program/idl.json | 110 ++++++++++---------- 2 files changed, 105 insertions(+), 105 deletions(-) diff --git a/clients/js/src/generated/types/extension.ts b/clients/js/src/generated/types/extension.ts index 9f8c09bc..bc1be671 100644 --- a/clients/js/src/generated/types/extension.ts +++ b/clients/js/src/generated/types/extension.ts @@ -210,13 +210,6 @@ export type Extension = /** Amount withheld during confidential transfers, to be harvest to the mint. */ withheldAmount: EncryptedBalance; } - | { - __kind: 'ScaledUiAmountConfig'; - authority: Address; - multiplier: number; - newMultiplierEffectiveTimestamp: bigint; - newMultiplier: number; - } | { __kind: 'MetadataPointer'; /** Optional authority that can set the metadata address. */ @@ -239,6 +232,13 @@ export type Extension = /** Any additional metadata about the token as key-value pairs. */ additionalMetadata: Map; } + | { + __kind: 'ScaledUiAmountConfig'; + authority: Address; + multiplier: number; + newMultiplierEffectiveTimestamp: bigint; + newMultiplier: number; + } | { __kind: 'GroupPointer'; /** Optional authority that can set the group address. */ @@ -420,13 +420,6 @@ export type ExtensionArgs = /** Amount withheld during confidential transfers, to be harvest to the mint. */ withheldAmount: EncryptedBalanceArgs; } - | { - __kind: 'ScaledUiAmountConfig'; - authority: Address; - multiplier: number; - newMultiplierEffectiveTimestamp: number | bigint; - newMultiplier: number; - } | { __kind: 'MetadataPointer'; /** Optional authority that can set the metadata address. */ @@ -449,6 +442,13 @@ export type ExtensionArgs = /** Any additional metadata about the token as key-value pairs. */ additionalMetadata: Map; } + | { + __kind: 'ScaledUiAmountConfig'; + authority: Address; + multiplier: number; + newMultiplierEffectiveTimestamp: number | bigint; + newMultiplier: number; + } | { __kind: 'GroupPointer'; /** Optional authority that can set the group address. */ @@ -655,18 +655,6 @@ export function getExtensionEncoder(): Encoder { getU16Encoder() ), ], - [ - 'ScaledUiAmountConfig', - addEncoderSizePrefix( - getStructEncoder([ - ['authority', getAddressEncoder()], - ['multiplier', getF64Encoder()], - ['newMultiplierEffectiveTimestamp', getU64Encoder()], - ['newMultiplier', getF64Encoder()], - ]), - getU16Encoder() - ), - ], [ 'MetadataPointer', addEncoderSizePrefix( @@ -715,6 +703,18 @@ export function getExtensionEncoder(): Encoder { getU16Encoder() ), ], + [ + 'ScaledUiAmountConfig', + addEncoderSizePrefix( + getStructEncoder([ + ['authority', getAddressEncoder()], + ['multiplier', getF64Encoder()], + ['newMultiplierEffectiveTimestamp', getU64Encoder()], + ['newMultiplier', getF64Encoder()], + ]), + getU16Encoder() + ), + ], [ 'GroupPointer', addEncoderSizePrefix( @@ -964,18 +964,6 @@ export function getExtensionDecoder(): Decoder { getU16Decoder() ), ], - [ - 'ScaledUiAmountConfig', - addDecoderSizePrefix( - getStructDecoder([ - ['authority', getAddressDecoder()], - ['multiplier', getF64Decoder()], - ['newMultiplierEffectiveTimestamp', getU64Decoder()], - ['newMultiplier', getF64Decoder()], - ]), - getU16Decoder() - ), - ], [ 'MetadataPointer', addDecoderSizePrefix( @@ -1024,6 +1012,18 @@ export function getExtensionDecoder(): Decoder { getU16Decoder() ), ], + [ + 'ScaledUiAmountConfig', + addDecoderSizePrefix( + getStructDecoder([ + ['authority', getAddressDecoder()], + ['multiplier', getF64Decoder()], + ['newMultiplierEffectiveTimestamp', getU64Decoder()], + ['newMultiplier', getF64Decoder()], + ]), + getU16Decoder() + ), + ], [ 'GroupPointer', addDecoderSizePrefix( @@ -1266,18 +1266,6 @@ export function extension( '__kind', 'ConfidentialTransferFeeAmount' >; -export function extension( - kind: 'ScaledUiAmountConfig', - data: GetDiscriminatedUnionVariantContent< - ExtensionArgs, - '__kind', - 'ScaledUiAmountConfig' - > -): GetDiscriminatedUnionVariant< - ExtensionArgs, - '__kind', - 'ScaledUiAmountConfig' ->; export function extension( kind: 'MetadataPointer', data: GetDiscriminatedUnionVariantContent< @@ -1294,6 +1282,18 @@ export function extension( 'TokenMetadata' > ): GetDiscriminatedUnionVariant; +export function extension( + kind: 'ScaledUiAmountConfig', + data: GetDiscriminatedUnionVariantContent< + ExtensionArgs, + '__kind', + 'ScaledUiAmountConfig' + > +): GetDiscriminatedUnionVariant< + ExtensionArgs, + '__kind', + 'ScaledUiAmountConfig' +>; export function extension( kind: 'GroupPointer', data: GetDiscriminatedUnionVariantContent< diff --git a/program/idl.json b/program/idl.json index 24be4a70..9d905faf 100644 --- a/program/idl.json +++ b/program/idl.json @@ -9130,61 +9130,6 @@ } } }, - { - "kind": "enumStructVariantTypeNode", - "name": "scaledUiAmountConfig", - "struct": { - "kind": "sizePrefixTypeNode", - "type": { - "kind": "structTypeNode", - "fields": [ - { - "kind": "structFieldTypeNode", - "name": "authority", - "docs": [], - "type": { - "kind": "publicKeyTypeNode" - } - }, - { - "kind": "structFieldTypeNode", - "name": "multiplier", - "docs": [], - "type": { - "kind": "numberTypeNode", - "format": "f64", - "endian": "le" - } - }, - { - "kind": "structFieldTypeNode", - "name": "newMultiplierEffectiveTimestamp", - "docs": [], - "type": { - "kind": "numberTypeNode", - "format": "u64", - "endian": "le" - } - }, - { - "kind": "structFieldTypeNode", - "name": "newMultiplier", - "docs": [], - "type": { - "kind": "numberTypeNode", - "format": "f64", - "endian": "le" - } - } - ] - }, - "prefix": { - "kind": "numberTypeNode", - "format": "u16", - "endian": "le" - } - } - }, { "kind": "enumStructVariantTypeNode", "name": "metadataPointer", @@ -9361,6 +9306,61 @@ } } }, + { + "kind": "enumStructVariantTypeNode", + "name": "scaledUiAmountConfig", + "struct": { + "kind": "sizePrefixTypeNode", + "type": { + "kind": "structTypeNode", + "fields": [ + { + "kind": "structFieldTypeNode", + "name": "authority", + "docs": [], + "type": { + "kind": "publicKeyTypeNode" + } + }, + { + "kind": "structFieldTypeNode", + "name": "multiplier", + "docs": [], + "type": { + "kind": "numberTypeNode", + "format": "f64", + "endian": "le" + } + }, + { + "kind": "structFieldTypeNode", + "name": "newMultiplierEffectiveTimestamp", + "docs": [], + "type": { + "kind": "numberTypeNode", + "format": "u64", + "endian": "le" + } + }, + { + "kind": "structFieldTypeNode", + "name": "newMultiplier", + "docs": [], + "type": { + "kind": "numberTypeNode", + "format": "f64", + "endian": "le" + } + } + ] + }, + "prefix": { + "kind": "numberTypeNode", + "format": "u16", + "endian": "le" + } + } + }, { "kind": "enumStructVariantTypeNode", "name": "groupPointer", From 83cfaffdb4f72288d4057a1112aef92a0c7fa022 Mon Sep 17 00:00:00 2001 From: Ilan Gitter <8359193+gitteri@users.noreply.github.com> Date: Wed, 5 Mar 2025 09:54:14 -0700 Subject: [PATCH 4/8] fix type order in idl and add tests --- clients/js/src/generated/types/extension.ts | 111 +++++++------- .../getInitializeInstructionsForExtensions.ts | 9 ++ .../initializeScaledUiAmountMint.test.ts | 135 +++++++++--------- .../updateScaledUiAmountMint.test.ts | 76 ++++++++++ program/idl.json | 114 ++++++++------- 5 files changed, 274 insertions(+), 171 deletions(-) create mode 100644 clients/js/test/extensions/scaledUiAmountMint/updateScaledUiAmountMint.test.ts diff --git a/clients/js/src/generated/types/extension.ts b/clients/js/src/generated/types/extension.ts index bc1be671..3a6e3c2b 100644 --- a/clients/js/src/generated/types/extension.ts +++ b/clients/js/src/generated/types/extension.ts @@ -232,13 +232,6 @@ export type Extension = /** Any additional metadata about the token as key-value pairs. */ additionalMetadata: Map; } - | { - __kind: 'ScaledUiAmountConfig'; - authority: Address; - multiplier: number; - newMultiplierEffectiveTimestamp: bigint; - newMultiplier: number; - } | { __kind: 'GroupPointer'; /** Optional authority that can set the group address. */ @@ -272,6 +265,14 @@ export type Extension = group: Address; /** The member number. */ memberNumber: bigint; + } + | { __kind: 'ConfidentialMintBurn' } + | { + __kind: 'ScaledUiAmountConfig'; + authority: Address; + multiplier: number; + newMultiplierEffectiveTimestamp: bigint; + newMultiplier: number; }; export type ExtensionArgs = @@ -442,13 +443,6 @@ export type ExtensionArgs = /** Any additional metadata about the token as key-value pairs. */ additionalMetadata: Map; } - | { - __kind: 'ScaledUiAmountConfig'; - authority: Address; - multiplier: number; - newMultiplierEffectiveTimestamp: number | bigint; - newMultiplier: number; - } | { __kind: 'GroupPointer'; /** Optional authority that can set the group address. */ @@ -482,6 +476,14 @@ export type ExtensionArgs = group: Address; /** The member number. */ memberNumber: number | bigint; + } + | { __kind: 'ConfidentialMintBurn' } + | { + __kind: 'ScaledUiAmountConfig'; + authority: Address; + multiplier: number; + newMultiplierEffectiveTimestamp: number | bigint; + newMultiplier: number; }; export function getExtensionEncoder(): Encoder { @@ -703,18 +705,6 @@ export function getExtensionEncoder(): Encoder { getU16Encoder() ), ], - [ - 'ScaledUiAmountConfig', - addEncoderSizePrefix( - getStructEncoder([ - ['authority', getAddressEncoder()], - ['multiplier', getF64Encoder()], - ['newMultiplierEffectiveTimestamp', getU64Encoder()], - ['newMultiplier', getF64Encoder()], - ]), - getU16Encoder() - ), - ], [ 'GroupPointer', addEncoderSizePrefix( @@ -788,6 +778,19 @@ export function getExtensionEncoder(): Encoder { getU16Encoder() ), ], + ['ConfidentialMintBurn', getUnitEncoder()], + [ + 'ScaledUiAmountConfig', + addEncoderSizePrefix( + getStructEncoder([ + ['authority', getAddressEncoder()], + ['multiplier', getF64Encoder()], + ['newMultiplierEffectiveTimestamp', getU64Encoder()], + ['newMultiplier', getF64Encoder()], + ]), + getU16Encoder() + ), + ], ], { size: getU16Encoder() } ); @@ -1012,18 +1015,6 @@ export function getExtensionDecoder(): Decoder { getU16Decoder() ), ], - [ - 'ScaledUiAmountConfig', - addDecoderSizePrefix( - getStructDecoder([ - ['authority', getAddressDecoder()], - ['multiplier', getF64Decoder()], - ['newMultiplierEffectiveTimestamp', getU64Decoder()], - ['newMultiplier', getF64Decoder()], - ]), - getU16Decoder() - ), - ], [ 'GroupPointer', addDecoderSizePrefix( @@ -1097,6 +1088,19 @@ export function getExtensionDecoder(): Decoder { getU16Decoder() ), ], + ['ConfidentialMintBurn', getUnitDecoder()], + [ + 'ScaledUiAmountConfig', + addDecoderSizePrefix( + getStructDecoder([ + ['authority', getAddressDecoder()], + ['multiplier', getF64Decoder()], + ['newMultiplierEffectiveTimestamp', getU64Decoder()], + ['newMultiplier', getF64Decoder()], + ]), + getU16Decoder() + ), + ], ], { size: getU16Decoder() } ); @@ -1282,18 +1286,6 @@ export function extension( 'TokenMetadata' > ): GetDiscriminatedUnionVariant; -export function extension( - kind: 'ScaledUiAmountConfig', - data: GetDiscriminatedUnionVariantContent< - ExtensionArgs, - '__kind', - 'ScaledUiAmountConfig' - > -): GetDiscriminatedUnionVariant< - ExtensionArgs, - '__kind', - 'ScaledUiAmountConfig' ->; export function extension( kind: 'GroupPointer', data: GetDiscriminatedUnionVariantContent< @@ -1326,6 +1318,25 @@ export function extension( 'TokenGroupMember' > ): GetDiscriminatedUnionVariant; +export function extension( + kind: 'ConfidentialMintBurn' +): GetDiscriminatedUnionVariant< + ExtensionArgs, + '__kind', + 'ConfidentialMintBurn' +>; +export function extension( + kind: 'ScaledUiAmountConfig', + data: GetDiscriminatedUnionVariantContent< + ExtensionArgs, + '__kind', + 'ScaledUiAmountConfig' + > +): GetDiscriminatedUnionVariant< + ExtensionArgs, + '__kind', + 'ScaledUiAmountConfig' +>; export function extension( kind: K, data?: Data diff --git a/clients/js/src/getInitializeInstructionsForExtensions.ts b/clients/js/src/getInitializeInstructionsForExtensions.ts index 798dc695..8d45e815 100644 --- a/clients/js/src/getInitializeInstructionsForExtensions.ts +++ b/clients/js/src/getInitializeInstructionsForExtensions.ts @@ -25,6 +25,7 @@ import { getInitializeNonTransferableMintInstruction, getInitializeTransferHookInstruction, getInitializePermanentDelegateInstruction, + getInitializeScaledUiAmountMintInstruction, getInitializeConfidentialTransferFeeInstruction, } from './generated'; @@ -80,6 +81,14 @@ export function getPreInitializeInstructionsForMintExtensions( rate: extension.currentRate, }), ]; + case 'ScaledUiAmountConfig': + return [ + getInitializeScaledUiAmountMintInstruction({ + mint, + authority: extension.authority, + multiplier: extension.multiplier, + }), + ]; case 'GroupPointer': return [ getInitializeGroupPointerInstruction({ diff --git a/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts b/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts index 0434783a..0b7a1387 100644 --- a/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts +++ b/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts @@ -1,75 +1,78 @@ -// import test from 'ava'; -// import { -// createDefaultSolanaClient, -// generateKeyPairSignerWithSol, -// getCreateMintInstructions, -// sendAndConfirmInstructions, -// } from '../../_setup'; -// import { Account, generateKeyPairSigner, isSome } from '@solana/web3.js'; -// import { -// extension, -// fetchMint, -// getInitializeScaledUiAmountMintInstruction, -// Mint, -// } from '../../../src'; +import test from 'ava'; +import { + createDefaultSolanaClient, + generateKeyPairSignerWithSol, + getCreateMintInstructions, + sendAndConfirmInstructions, +} from '../../_setup'; +import { Account, address, generateKeyPairSigner, isSome } from '@solana/web3.js'; +import { + extension, + fetchMint, + getInitializeScaledUiAmountMintInstruction, + Mint, +} from '../../../src'; -// test('it initialize a mint account with an interest bearing mint extension', async (t) => { -// const client = createDefaultSolanaClient(); -// const [multiplierAuthority, mint] = await Promise.all([ -// generateKeyPairSignerWithSol(client), -// generateKeyPairSigner(), -// ]); +test('it initialize a mint account with an interest bearing mint extension', async (t) => { + const client = createDefaultSolanaClient(); + const [authority, mint] = await Promise.all([ + generateKeyPairSignerWithSol(client), + generateKeyPairSigner(), + ]); -// const newMultiplier = 2; + const newMultiplier = 2; -// // And a scaled ui amount mint extension. -// const scaledUiAmountMintExtension = extension('ScaledUiAmountConfig', { -// authority: multiplierAuthority.address, -// multiplier: 1, -// newMultiplierEffectiveTimestamp: BigInt(Math.floor(new Date().getTime() / 1000)), -// newMultiplier, -// }); + // And a scaled ui amount mint extension. + const scaledUiAmountMintExtension = extension('ScaledUiAmountConfig', { + authority: authority.address, + multiplier: 1, + newMultiplierEffectiveTimestamp: BigInt(Math.floor(new Date().getTime() / 1000)), + newMultiplier, + }); -// // When we initialize the mint account with the scaled ui amount mint extension. -// const [createMintInstruction, initMintInstruction] = -// await getCreateMintInstructions({ -// authority: multiplierAuthority.address, -// client, -// extensions: [scaledUiAmountMintExtension], -// mint, -// payer: multiplierAuthority, -// }); -// await sendAndConfirmInstructions(client, multiplierAuthority, [ -// createMintInstruction, -// getInitializeScaledUiAmountMintInstruction({ -// mint: mint.address, -// authority: multiplierAuthority.address, -// multiplier: 1, -// }), -// initMintInstruction, -// ]); + // When we initialize the mint account with the scaled ui amount mint extension. -// const mintAccount = await fetchMint(client.rpc, mint.address); + // And a mint close authority extension. + const [createMintInstruction, initMintInstruction] = + await getCreateMintInstructions({ + authority: authority.address, + client, + decimals: 2, + extensions: [scaledUiAmountMintExtension], + mint, + payer: authority, + }); + await sendAndConfirmInstructions(client, authority, [ + createMintInstruction, + getInitializeScaledUiAmountMintInstruction({ + mint: mint.address, + authority: authority.address, + multiplier: 1, + }), + initMintInstruction, + ]); -// const extensions = mintAccount.data.extensions; + const mintAccount = await fetchMint(client.rpc, mint.address); -// t.true(isSome(extensions)); -// t.true( -// isSome(extensions) && extensions.value[0].__kind === 'ScaledUiAmountConfig' -// ); + const extensions = mintAccount.data.extensions; -// // check without need to check timestamp specifically -// if ( -// isSome(extensions) && -// extensions.value[0].__kind === 'ScaledUiAmountConfig' -// ) { -// t.is(extensions.value[0].authority, multiplierAuthority.address); -// t.is(extensions.value[0].multiplier, 1); -// t.true(typeof extensions.value[0].newMultiplierEffectiveTimestamp === 'bigint'); -// t.is(extensions.value[0].newMultiplier, newMultiplier); -// } + t.true(isSome(extensions)); + t.true( + isSome(extensions) && extensions.value[0].__kind === 'ScaledUiAmountConfig' + ); -// t.like(mintAccount, >{ -// address: mint.address, -// }); -// }); \ No newline at end of file + // check without need to check timestamp specifically + if ( + isSome(extensions) && + extensions.value[0].__kind === 'ScaledUiAmountConfig' + ) { + t.is(extensions.value[0].authority, authority.address); + t.is(extensions.value[0].multiplier, 1); + t.true(typeof extensions.value[0].newMultiplierEffectiveTimestamp === 'bigint'); + t.is(extensions.value[0].newMultiplier, 1); + } + + t.like(mintAccount, >{ + address: mint.address, + }); +}); \ No newline at end of file diff --git a/clients/js/test/extensions/scaledUiAmountMint/updateScaledUiAmountMint.test.ts b/clients/js/test/extensions/scaledUiAmountMint/updateScaledUiAmountMint.test.ts new file mode 100644 index 00000000..b5f19d3c --- /dev/null +++ b/clients/js/test/extensions/scaledUiAmountMint/updateScaledUiAmountMint.test.ts @@ -0,0 +1,76 @@ +import test from 'ava'; +import { + createDefaultSolanaClient, + createMint, + generateKeyPairSignerWithSol, + sendAndConfirmInstructions, +} from '../../_setup'; +import { Account, isSome } from '@solana/web3.js'; +import { + extension, + fetchMint, + getUpdateMultiplierScaledUiMintInstruction, + Mint, +} from '../../../src'; + +test('it updates the multiplier of the scaled ui amount mint extension on a mint account', async (t) => { + // Given some signer accounts. + const client = createDefaultSolanaClient(); + const [multiplierAuthority] = await Promise.all([ + generateKeyPairSignerWithSol(client), + ]); + + const oldMultiplier = 1; + const newMultiplier = 2; + + // initialize mint with scaled ui amount mint extension + const mint = await createMint({ + authority: multiplierAuthority, + client, + extensions: [ + extension('ScaledUiAmountConfig', { + authority: multiplierAuthority.address, + multiplier: oldMultiplier, + newMultiplierEffectiveTimestamp: BigInt( + Math.floor(new Date().getTime() * 2) + ), + newMultiplier: oldMultiplier, + }), + ], + payer: multiplierAuthority, + }); + + // then we update the interest bearing mint extension on the mint account + await sendAndConfirmInstructions(client, multiplierAuthority, [ + getUpdateMultiplierScaledUiMintInstruction({ + mint, + authority: multiplierAuthority.address, + multiplier: newMultiplier, + effectiveTimestamp: BigInt(Math.floor(new Date().getTime() / 1000)), + }), + ]); + + const mintAccount = await fetchMint(client.rpc, mint); + + // check without need to check timestamp specifically + const extensions = mintAccount.data.extensions; + + t.true(isSome(extensions)); + t.true( + isSome(extensions) && extensions.value[0].__kind === 'ScaledUiAmountConfig' + ); + + if ( + isSome(extensions) && + extensions.value[0].__kind === 'ScaledUiAmountConfig' + ) { + t.is(extensions.value[0].authority, multiplierAuthority.address); + t.true(typeof extensions.value[0].multiplier === 'number'); + t.true(typeof extensions.value[0].newMultiplierEffectiveTimestamp === 'bigint'); + t.is(extensions.value[0].newMultiplier, newMultiplier); + } + + t.like(mintAccount, >{ + address: mint, + }); +}); diff --git a/program/idl.json b/program/idl.json index 9d905faf..e9b1c48f 100644 --- a/program/idl.json +++ b/program/idl.json @@ -9306,61 +9306,6 @@ } } }, - { - "kind": "enumStructVariantTypeNode", - "name": "scaledUiAmountConfig", - "struct": { - "kind": "sizePrefixTypeNode", - "type": { - "kind": "structTypeNode", - "fields": [ - { - "kind": "structFieldTypeNode", - "name": "authority", - "docs": [], - "type": { - "kind": "publicKeyTypeNode" - } - }, - { - "kind": "structFieldTypeNode", - "name": "multiplier", - "docs": [], - "type": { - "kind": "numberTypeNode", - "format": "f64", - "endian": "le" - } - }, - { - "kind": "structFieldTypeNode", - "name": "newMultiplierEffectiveTimestamp", - "docs": [], - "type": { - "kind": "numberTypeNode", - "format": "u64", - "endian": "le" - } - }, - { - "kind": "structFieldTypeNode", - "name": "newMultiplier", - "docs": [], - "type": { - "kind": "numberTypeNode", - "format": "f64", - "endian": "le" - } - } - ] - }, - "prefix": { - "kind": "numberTypeNode", - "format": "u16", - "endian": "le" - } - } - }, { "kind": "enumStructVariantTypeNode", "name": "groupPointer", @@ -9551,6 +9496,65 @@ "endian": "le" } } + }, + { + "kind": "enumEmptyVariantTypeNode", + "name": "confidentialMintBurn" + }, + { + "kind": "enumStructVariantTypeNode", + "name": "scaledUiAmountConfig", + "struct": { + "kind": "sizePrefixTypeNode", + "type": { + "kind": "structTypeNode", + "fields": [ + { + "kind": "structFieldTypeNode", + "name": "authority", + "docs": [], + "type": { + "kind": "publicKeyTypeNode" + } + }, + { + "kind": "structFieldTypeNode", + "name": "multiplier", + "docs": [], + "type": { + "kind": "numberTypeNode", + "format": "f64", + "endian": "le" + } + }, + { + "kind": "structFieldTypeNode", + "name": "newMultiplierEffectiveTimestamp", + "docs": [], + "type": { + "kind": "numberTypeNode", + "format": "u64", + "endian": "le" + } + }, + { + "kind": "structFieldTypeNode", + "name": "newMultiplier", + "docs": [], + "type": { + "kind": "numberTypeNode", + "format": "f64", + "endian": "le" + } + } + ] + }, + "prefix": { + "kind": "numberTypeNode", + "format": "u16", + "endian": "le" + } + } } ], "size": { From d9ccad6c0ea440cd9d898cb9683b1e2092415617 Mon Sep 17 00:00:00 2001 From: Ilan Gitter <8359193+gitteri@users.noreply.github.com> Date: Wed, 5 Mar 2025 15:08:34 -0700 Subject: [PATCH 5/8] prettier --- .../initializeScaledUiAmountMint.test.ts | 17 +++++++++++++---- .../updateScaledUiAmountMint.test.ts | 4 +++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts b/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts index 0b7a1387..23cc45dc 100644 --- a/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts +++ b/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts @@ -5,7 +5,12 @@ import { getCreateMintInstructions, sendAndConfirmInstructions, } from '../../_setup'; -import { Account, address, generateKeyPairSigner, isSome } from '@solana/web3.js'; +import { + Account, + address, + generateKeyPairSigner, + isSome, +} from '@solana/web3.js'; import { extension, fetchMint, @@ -26,7 +31,9 @@ test('it initialize a mint account with an interest bearing mint extension', asy const scaledUiAmountMintExtension = extension('ScaledUiAmountConfig', { authority: authority.address, multiplier: 1, - newMultiplierEffectiveTimestamp: BigInt(Math.floor(new Date().getTime() / 1000)), + newMultiplierEffectiveTimestamp: BigInt( + Math.floor(new Date().getTime() / 1000) + ), newMultiplier, }); @@ -68,11 +75,13 @@ test('it initialize a mint account with an interest bearing mint extension', asy ) { t.is(extensions.value[0].authority, authority.address); t.is(extensions.value[0].multiplier, 1); - t.true(typeof extensions.value[0].newMultiplierEffectiveTimestamp === 'bigint'); + t.true( + typeof extensions.value[0].newMultiplierEffectiveTimestamp === 'bigint' + ); t.is(extensions.value[0].newMultiplier, 1); } t.like(mintAccount, >{ address: mint.address, }); -}); \ No newline at end of file +}); diff --git a/clients/js/test/extensions/scaledUiAmountMint/updateScaledUiAmountMint.test.ts b/clients/js/test/extensions/scaledUiAmountMint/updateScaledUiAmountMint.test.ts index b5f19d3c..1c68b221 100644 --- a/clients/js/test/extensions/scaledUiAmountMint/updateScaledUiAmountMint.test.ts +++ b/clients/js/test/extensions/scaledUiAmountMint/updateScaledUiAmountMint.test.ts @@ -66,7 +66,9 @@ test('it updates the multiplier of the scaled ui amount mint extension on a mint ) { t.is(extensions.value[0].authority, multiplierAuthority.address); t.true(typeof extensions.value[0].multiplier === 'number'); - t.true(typeof extensions.value[0].newMultiplierEffectiveTimestamp === 'bigint'); + t.true( + typeof extensions.value[0].newMultiplierEffectiveTimestamp === 'bigint' + ); t.is(extensions.value[0].newMultiplier, newMultiplier); } From 6e1a5b3efa39f818efb26709011b56799490d73a Mon Sep 17 00:00:00 2001 From: Ilan Gitter <8359193+gitteri@users.noreply.github.com> Date: Thu, 6 Mar 2025 07:31:58 -0700 Subject: [PATCH 6/8] regenerate client and update tests per comments --- .../initializeScaledUiAmountMint.ts | 2 +- .../updateMultiplierScaledUiMint.ts | 2 +- .../initializeScaledUiAmountMint.test.ts | 31 ++++++++++--------- .../updateScaledUiAmountMint.test.ts | 18 +++++------ 4 files changed, 27 insertions(+), 26 deletions(-) diff --git a/clients/js/src/generated/instructions/initializeScaledUiAmountMint.ts b/clients/js/src/generated/instructions/initializeScaledUiAmountMint.ts index f68e7dfb..10dc7c13 100644 --- a/clients/js/src/generated/instructions/initializeScaledUiAmountMint.ts +++ b/clients/js/src/generated/instructions/initializeScaledUiAmountMint.ts @@ -30,7 +30,7 @@ import { type Option, type OptionOrNullable, type WritableAccount, -} from '@solana/web3.js'; +} from '@solana/kit'; import { TOKEN_2022_PROGRAM_ADDRESS } from '../programs'; import { getAccountMetaFactory, type ResolvedAccount } from '../shared'; diff --git a/clients/js/src/generated/instructions/updateMultiplierScaledUiMint.ts b/clients/js/src/generated/instructions/updateMultiplierScaledUiMint.ts index 832ff29e..da13f146 100644 --- a/clients/js/src/generated/instructions/updateMultiplierScaledUiMint.ts +++ b/clients/js/src/generated/instructions/updateMultiplierScaledUiMint.ts @@ -30,7 +30,7 @@ import { type TransactionSigner, type WritableAccount, type WritableSignerAccount, -} from '@solana/web3.js'; +} from '@solana/kit'; import { TOKEN_2022_PROGRAM_ADDRESS } from '../programs'; import { getAccountMetaFactory, type ResolvedAccount } from '../shared'; diff --git a/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts b/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts index 23cc45dc..02cb15f4 100644 --- a/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts +++ b/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts @@ -7,10 +7,9 @@ import { } from '../../_setup'; import { Account, - address, generateKeyPairSigner, isSome, -} from '@solana/web3.js'; +} from '@solana/kit'; import { extension, fetchMint, @@ -18,19 +17,21 @@ import { Mint, } from '../../../src'; -test('it initialize a mint account with an interest bearing mint extension', async (t) => { +test('it initialize a mint account with a scaled ui amount mint extension', async (t) => { + // Given a fresh client with no state the test cares about. const client = createDefaultSolanaClient(); const [authority, mint] = await Promise.all([ generateKeyPairSignerWithSol(client), generateKeyPairSigner(), ]); + const multiplier = 1; const newMultiplier = 2; // And a scaled ui amount mint extension. const scaledUiAmountMintExtension = extension('ScaledUiAmountConfig', { authority: authority.address, - multiplier: 1, + multiplier, newMultiplierEffectiveTimestamp: BigInt( Math.floor(new Date().getTime() / 1000) ), @@ -38,8 +39,6 @@ test('it initialize a mint account with an interest bearing mint extension', asy }); // When we initialize the mint account with the scaled ui amount mint extension. - - // And a mint close authority extension. const [createMintInstruction, initMintInstruction] = await getCreateMintInstructions({ authority: authority.address, @@ -54,34 +53,38 @@ test('it initialize a mint account with an interest bearing mint extension', asy getInitializeScaledUiAmountMintInstruction({ mint: mint.address, authority: authority.address, - multiplier: 1, + multiplier, }), initMintInstruction, ]); const mintAccount = await fetchMint(client.rpc, mint.address); + // Then the mint account exists. + t.like(mintAccount, >{ + address: mint.address, + }); const extensions = mintAccount.data.extensions; + // And the mint account has a scaled ui amount mint extension. t.true(isSome(extensions)); t.true( isSome(extensions) && extensions.value[0].__kind === 'ScaledUiAmountConfig' ); - // check without need to check timestamp specifically if ( isSome(extensions) && extensions.value[0].__kind === 'ScaledUiAmountConfig' ) { + // And the extension has the correct authority. t.is(extensions.value[0].authority, authority.address); - t.is(extensions.value[0].multiplier, 1); + // And the extension has the correct multiplier. + t.is(extensions.value[0].multiplier, multiplier); + // And the extension has the correct new multiplier effective timestamp. t.true( typeof extensions.value[0].newMultiplierEffectiveTimestamp === 'bigint' ); - t.is(extensions.value[0].newMultiplier, 1); + // And the extension has the correct new multiplier which is not changed due to how the extension is initialized. + t.is(extensions.value[0].newMultiplier, multiplier); } - - t.like(mintAccount, >{ - address: mint.address, - }); }); diff --git a/clients/js/test/extensions/scaledUiAmountMint/updateScaledUiAmountMint.test.ts b/clients/js/test/extensions/scaledUiAmountMint/updateScaledUiAmountMint.test.ts index 1c68b221..56bc319e 100644 --- a/clients/js/test/extensions/scaledUiAmountMint/updateScaledUiAmountMint.test.ts +++ b/clients/js/test/extensions/scaledUiAmountMint/updateScaledUiAmountMint.test.ts @@ -5,12 +5,11 @@ import { generateKeyPairSignerWithSol, sendAndConfirmInstructions, } from '../../_setup'; -import { Account, isSome } from '@solana/web3.js'; +import { isSome } from '@solana/kit'; import { extension, fetchMint, getUpdateMultiplierScaledUiMintInstruction, - Mint, } from '../../../src'; test('it updates the multiplier of the scaled ui amount mint extension on a mint account', async (t) => { @@ -23,7 +22,7 @@ test('it updates the multiplier of the scaled ui amount mint extension on a mint const oldMultiplier = 1; const newMultiplier = 2; - // initialize mint with scaled ui amount mint extension + // And a mint with a scaled ui amount mint extension. const mint = await createMint({ authority: multiplierAuthority, client, @@ -40,7 +39,7 @@ test('it updates the multiplier of the scaled ui amount mint extension on a mint payer: multiplierAuthority, }); - // then we update the interest bearing mint extension on the mint account + // When we update the scaled ui amount mint extension on the mint account await sendAndConfirmInstructions(client, multiplierAuthority, [ getUpdateMultiplierScaledUiMintInstruction({ mint, @@ -52,9 +51,8 @@ test('it updates the multiplier of the scaled ui amount mint extension on a mint const mintAccount = await fetchMint(client.rpc, mint); - // check without need to check timestamp specifically + // Then the mint account has a scaled ui amount mint extension. const extensions = mintAccount.data.extensions; - t.true(isSome(extensions)); t.true( isSome(extensions) && extensions.value[0].__kind === 'ScaledUiAmountConfig' @@ -64,15 +62,15 @@ test('it updates the multiplier of the scaled ui amount mint extension on a mint isSome(extensions) && extensions.value[0].__kind === 'ScaledUiAmountConfig' ) { + // And the extension has the correct authority. t.is(extensions.value[0].authority, multiplierAuthority.address); + // And the extension has the correct multiplier. t.true(typeof extensions.value[0].multiplier === 'number'); + // And the extension has the correct new multiplier effective timestamp. t.true( typeof extensions.value[0].newMultiplierEffectiveTimestamp === 'bigint' ); + // And the extension has the correct new multiplier. t.is(extensions.value[0].newMultiplier, newMultiplier); } - - t.like(mintAccount, >{ - address: mint, - }); }); From 97f7887e53b27a05d4a51a93351430c5204be2cb Mon Sep 17 00:00:00 2001 From: Ilan Gitter <8359193+gitteri@users.noreply.github.com> Date: Thu, 6 Mar 2025 07:35:42 -0700 Subject: [PATCH 7/8] update interest bearing mint test comments to adhere to the same convention --- .../initializeInterestBearingMint.test.ts | 18 ++++++++++++------ .../updateInterestBearingMint.test.ts | 19 +++++++++++-------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/clients/js/test/extensions/interestBearingMint/initializeInterestBearingMint.test.ts b/clients/js/test/extensions/interestBearingMint/initializeInterestBearingMint.test.ts index d7af55f1..640943b8 100644 --- a/clients/js/test/extensions/interestBearingMint/initializeInterestBearingMint.test.ts +++ b/clients/js/test/extensions/interestBearingMint/initializeInterestBearingMint.test.ts @@ -14,6 +14,7 @@ import { } from '../../../src'; test('it initialize a mint account with an interest bearing mint extension', async (t) => { + // Given a fresh client with no state the test cares about. const client = createDefaultSolanaClient(); const [rateAuthority, mint] = await Promise.all([ generateKeyPairSignerWithSol(client), @@ -28,7 +29,7 @@ test('it initialize a mint account with an interest bearing mint extension', asy rateAuthority: rateAuthority.address, initializationTimestamp: BigInt(Math.floor(new Date().getTime() / 1000)), lastUpdateTimestamp: BigInt(Math.floor(new Date().getTime() / 1000)), - preUpdateAverageRate: 10000, + preUpdateAverageRate: rate, currentRate: rate, }); @@ -52,27 +53,32 @@ test('it initialize a mint account with an interest bearing mint extension', asy ]); const mintAccount = await fetchMint(client.rpc, mint.address); + // Then the mint account exists. + t.like(mintAccount, >{ + address: mint.address, + }); const extensions = mintAccount.data.extensions; + // And the mint account has an interest bearing mint extension. t.true(isSome(extensions)); t.true( isSome(extensions) && extensions.value[0].__kind === 'InterestBearingConfig' ); - // check without need to check timestamp specifically if ( isSome(extensions) && extensions.value[0].__kind === 'InterestBearingConfig' ) { + // And the extension has the correct rate authority. t.is(extensions.value[0].rateAuthority, rateAuthority.address); + // And the extension has the correct initialization timestamp. t.true(typeof extensions.value[0].initializationTimestamp === 'bigint'); + // And the extension has the correct last update timestamp. t.true(typeof extensions.value[0].lastUpdateTimestamp === 'bigint'); + // And the extension has the correct pre update average rate. t.is(extensions.value[0].preUpdateAverageRate, rate); + // And the extension has the correct current rate. t.is(extensions.value[0].currentRate, rate); } - - t.like(mintAccount, >{ - address: mint.address, - }); }); diff --git a/clients/js/test/extensions/interestBearingMint/updateInterestBearingMint.test.ts b/clients/js/test/extensions/interestBearingMint/updateInterestBearingMint.test.ts index e38b9704..a62ac184 100644 --- a/clients/js/test/extensions/interestBearingMint/updateInterestBearingMint.test.ts +++ b/clients/js/test/extensions/interestBearingMint/updateInterestBearingMint.test.ts @@ -23,7 +23,7 @@ test('it updates the interest bearing mint extension on a mint account', async ( const oldRate = 10000; const newRate = 20000; - // initialize mint with interest bearing mint extension + // And a mint with an interest bearing mint extension. const mint = await createMint({ authority: rateAuthority, client, @@ -41,7 +41,7 @@ test('it updates the interest bearing mint extension on a mint account', async ( payer: rateAuthority, }); - // then we update the interest bearing mint extension on the mint account + // When we update the interest bearing mint extension on the mint account await sendAndConfirmInstructions(client, rateAuthority, [ getUpdateRateInterestBearingMintInstruction({ rateAuthority: rateAuthority, @@ -51,10 +51,12 @@ test('it updates the interest bearing mint extension on a mint account', async ( ]); const mintAccount = await fetchMint(client.rpc, mint); + t.like(mintAccount, >{ + address: mint, + }); - // check without need to check timestamp specifically + // Then the mint account has an interest bearing mint extension. const extensions = mintAccount.data.extensions; - t.true(isSome(extensions)); t.true( isSome(extensions) && extensions.value[0].__kind === 'InterestBearingConfig' @@ -64,14 +66,15 @@ test('it updates the interest bearing mint extension on a mint account', async ( isSome(extensions) && extensions.value[0].__kind === 'InterestBearingConfig' ) { + // And the extension has the correct rate authority. t.is(extensions.value[0].rateAuthority, rateAuthority.address); + // And the extension has the correct initialization timestamp. t.true(typeof extensions.value[0].initializationTimestamp === 'bigint'); + // And the extension has the correct last update timestamp. t.true(typeof extensions.value[0].lastUpdateTimestamp === 'bigint'); + // And the extension has the correct pre update average rate. t.is(extensions.value[0].preUpdateAverageRate, oldRate); + // And the extension has the correct new current rate. t.is(extensions.value[0].currentRate, newRate); } - - t.like(mintAccount, >{ - address: mint, - }); }); From 24ad6471a08d252e15b3d201dd60be2792d0bba6 Mon Sep 17 00:00:00 2001 From: Ilan Gitter <8359193+gitteri@users.noreply.github.com> Date: Thu, 6 Mar 2025 08:52:59 -0700 Subject: [PATCH 8/8] lint and format --- .../scaledUiAmountMint/initializeScaledUiAmountMint.test.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts b/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts index 02cb15f4..19d8a9e2 100644 --- a/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts +++ b/clients/js/test/extensions/scaledUiAmountMint/initializeScaledUiAmountMint.test.ts @@ -5,11 +5,7 @@ import { getCreateMintInstructions, sendAndConfirmInstructions, } from '../../_setup'; -import { - Account, - generateKeyPairSigner, - isSome, -} from '@solana/kit'; +import { Account, generateKeyPairSigner, isSome } from '@solana/kit'; import { extension, fetchMint,