diff --git a/token-metadata/js/test/instruction.test.ts b/token-metadata/js/test/instruction.test.ts index ba7be8164b0..996e2065061 100644 --- a/token-metadata/js/test/instruction.test.ts +++ b/token-metadata/js/test/instruction.test.ts @@ -1,4 +1,3 @@ -import { PublicKey, TransactionInstruction } from '@solana/web3.js'; import { expect } from 'chai'; import { @@ -7,7 +6,29 @@ import { createRemoveKeyInstruction, createUpdateAuthorityInstruction, createUpdateFieldInstruction, + getFieldCodec, + getFieldConfig, } from '../src'; +import type { StructToDecoderTuple } from '@solana/codecs-data-structures'; +import { getBooleanDecoder, getBytesDecoder, getDataEnumCodec, getStructDecoder } from '@solana/codecs-data-structures'; +import { getStringDecoder } from '@solana/codecs-strings'; +import { splDiscriminate } from '@solana/spl-type-length-value'; +import { getU64Decoder } from '@solana/codecs-numbers'; +import type { Option } from '@solana/options'; +import { getOptionDecoder, some } from '@solana/options'; +import { PublicKey, type TransactionInstruction } from '@solana/web3.js'; + +function checkPackUnpack( + instruction: TransactionInstruction, + discriminator: Uint8Array, + layout: StructToDecoderTuple, + values: T +) { + expect(instruction.data.subarray(0, 8)).to.deep.equal(discriminator); + const decoder = getStructDecoder(layout); + const unpacked = decoder.decode(instruction.data.subarray(8)); + expect(unpacked).to.deep.equal(values); +} describe('Token Metadata Instructions', () => { const programId = new PublicKey('22222222222222222222222222222222222222222222'); @@ -17,150 +38,119 @@ describe('Token Metadata Instructions', () => { const mintAuthority = new PublicKey('66666666666666666666666666666666666666666666'); it('Can create Initialize Instruction', () => { - const instruction = createInitializeInstruction({ - programId, - metadata, - updateAuthority, - mint, - mintAuthority, - name: 'My test token', - symbol: 'TEST', - uri: 'http://test.test', - }); - - expect(instruction).to.deep.equal( - new TransactionInstruction({ + const name = 'My test token'; + const symbol = 'TEST'; + const uri = 'http://test.test'; + checkPackUnpack( + createInitializeInstruction({ programId, - keys: [ - { isSigner: false, isWritable: true, pubkey: metadata }, - { isSigner: false, isWritable: false, pubkey: updateAuthority }, - { isSigner: false, isWritable: false, pubkey: mint }, - { isSigner: true, isWritable: false, pubkey: mintAuthority }, - ], - data: Buffer.from([ - // Output of rust implementation - 210, 225, 30, 162, 88, 184, 77, 141, 13, 0, 0, 0, 77, 121, 32, 116, 101, 115, 116, 32, 116, 111, - 107, 101, 110, 4, 0, 0, 0, 84, 69, 83, 84, 16, 0, 0, 0, 104, 116, 116, 112, 58, 47, 47, 116, 101, - 115, 116, 46, 116, 101, 115, 116, - ]), - }) + metadata, + updateAuthority, + mint, + mintAuthority, + name, + symbol, + uri, + }), + splDiscriminate('spl_token_metadata_interface:initialize_account'), + [ + ['name', getStringDecoder()], + ['symbol', getStringDecoder()], + ['uri', getStringDecoder()], + ], + { name, symbol, uri } ); }); it('Can create Update Field Instruction', () => { - const instruction = createUpdateFieldInstruction({ - programId, - metadata, - updateAuthority, - field: 'MyTestField', - value: 'http://test.uri', - }); - - expect(instruction).to.deep.equal( - new TransactionInstruction({ + const field = 'MyTestField'; + const value = 'http://test.uri'; + checkPackUnpack( + createUpdateFieldInstruction({ programId, - keys: [ - { isSigner: false, isWritable: true, pubkey: metadata }, - { isSigner: true, isWritable: false, pubkey: updateAuthority }, - ], - data: Buffer.from([ - // Output of rust implementation - 221, 233, 49, 45, 181, 202, 220, 200, 3, 11, 0, 0, 0, 77, 121, 84, 101, 115, 116, 70, 105, 101, 108, - 100, 15, 0, 0, 0, 104, 116, 116, 112, 58, 47, 47, 116, 101, 115, 116, 46, 117, 114, 105, - ]), - }) + metadata, + updateAuthority, + field, + value, + }), + splDiscriminate('spl_token_metadata_interface:updating_field'), + [ + ['key', getDataEnumCodec(getFieldCodec())], + ['value', getStringDecoder()], + ], + { key: getFieldConfig(field), value } ); }); it('Can create Update Field Instruction with Field Enum', () => { - const instruction = createUpdateFieldInstruction({ - programId, - metadata, - updateAuthority, - field: 'Name', - value: 'http://test.uri', - }); - - expect(instruction).to.deep.equal( - new TransactionInstruction({ + const field = 'Name'; + const value = 'http://test.uri'; + checkPackUnpack( + createUpdateFieldInstruction({ programId, - keys: [ - { isSigner: false, isWritable: true, pubkey: metadata }, - { isSigner: true, isWritable: false, pubkey: updateAuthority }, - ], - data: Buffer.from([ - // Output of rust implementation - 221, 233, 49, 45, 181, 202, 220, 200, 0, 15, 0, 0, 0, 104, 116, 116, 112, 58, 47, 47, 116, 101, 115, - 116, 46, 117, 114, 105, - ]), - }) + metadata, + updateAuthority, + field, + value, + }), + splDiscriminate('spl_token_metadata_interface:updating_field'), + [ + ['key', getDataEnumCodec(getFieldCodec())], + ['value', getStringDecoder()], + ], + { key: getFieldConfig(field), value } ); }); it('Can create Remove Key Instruction', () => { - const instruction = createRemoveKeyInstruction({ - programId, - metadata, - updateAuthority: updateAuthority, - key: 'MyTestField', - idempotent: true, - }); - - expect(instruction).to.deep.equal( - new TransactionInstruction({ + checkPackUnpack( + createRemoveKeyInstruction({ programId, - keys: [ - { isSigner: false, isWritable: true, pubkey: metadata }, - { isSigner: true, isWritable: false, pubkey: updateAuthority }, - ], - data: Buffer.from([ - // Output of rust implementation - 234, 18, 32, 56, 89, 141, 37, 181, 1, 11, 0, 0, 0, 77, 121, 84, 101, 115, 116, 70, 105, 101, 108, - 100, - ]), - }) + metadata, + updateAuthority: updateAuthority, + key: 'MyTestField', + idempotent: true, + }), + splDiscriminate('spl_token_metadata_interface:remove_key_ix'), + [ + ['idempotent', getBooleanDecoder()], + ['key', getStringDecoder()], + ], + { idempotent: true, key: 'MyTestField' } ); }); it('Can create Update Authority Instruction', () => { - const instruction = createUpdateAuthorityInstruction({ - programId, - metadata, - oldAuthority: updateAuthority, - newAuthority: PublicKey.default, - }); - - expect(instruction).to.deep.equal( - new TransactionInstruction({ + const newAuthority = PublicKey.default; + checkPackUnpack( + createUpdateAuthorityInstruction({ programId, - keys: [ - { isSigner: false, isWritable: true, pubkey: metadata }, - { isSigner: true, isWritable: false, pubkey: updateAuthority }, - ], - data: Buffer.from([ - // Output of rust implementation - 215, 228, 166, 228, 84, 100, 86, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]), - }) + metadata, + oldAuthority: updateAuthority, + newAuthority, + }), + splDiscriminate('spl_token_metadata_interface:update_the_authority'), + [['newAuthority', getBytesDecoder({ size: 32 })]], + { newAuthority: Uint8Array.from(newAuthority.toBuffer()) } ); }); - it('Can create Emit Instruction', () => { - const instruction = createEmitInstruction({ - programId, - metadata, - end: BigInt(10), - }); - expect(instruction).to.deep.equal( - new TransactionInstruction({ + it('Can create Emit Instruction', () => { + const start: Option = some(0n); + const end: Option = some(10n); + checkPackUnpack( + createEmitInstruction({ programId, - keys: [{ isSigner: false, isWritable: false, pubkey: metadata }], - data: Buffer.from([ - // Output of rust implementation - 250, 166, 180, 250, 13, 12, 184, 70, 0, 1, 10, 0, 0, 0, 0, 0, 0, 0, - ]), - }) + metadata, + start: 0n, + end: 10n, + }), + splDiscriminate('spl_token_metadata_interface:emitter'), + [ + ['start', getOptionDecoder(getU64Decoder())], + ['end', getOptionDecoder(getU64Decoder())], + ], + { start, end } ); }); }); diff --git a/token-metadata/js/test/state.test.ts b/token-metadata/js/test/state.test.ts index dcadb351913..f55dd04a175 100644 --- a/token-metadata/js/test/state.test.ts +++ b/token-metadata/js/test/state.test.ts @@ -4,29 +4,36 @@ import { expect } from 'chai'; import type { TokenMetadata } from '../src/state'; import { unpack, pack } from '../src'; +function checkPackUnpack(tokenMetadata: TokenMetadata) { + const packed = pack(tokenMetadata); + const unpacked = unpack(packed); + expect(unpacked).to.deep.equal(tokenMetadata); +} + describe('Token Metadata State', () => { - it('Can pack and unpack as rust implementation', () => { - const meta = { + it('Can pack and unpack base token metadata', () => { + checkPackUnpack({ mint: PublicKey.default, name: 'name', symbol: 'symbol', uri: 'uri', additionalMetadata: [], - }; - - // From rust implementation - const bytes = Buffer.from([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 110, 97, - 109, 101, 6, 0, 0, 0, 115, 121, 109, 98, 111, 108, 3, 0, 0, 0, 117, 114, 105, 0, 0, 0, 0, - ]); + }); + }); - expect(pack(meta)).to.deep.equal(bytes); - expect(unpack(bytes)).to.deep.equal(meta); + it('Can pack and unpack with updateAuthority', () => { + checkPackUnpack({ + updateAuthority: new PublicKey('44444444444444444444444444444444444444444444'), + mint: new PublicKey('55555555555555555555555555555555555555555555'), + name: 'name', + symbol: 'symbol', + uri: 'uri', + additionalMetadata: [], + }); }); - it('Can pack and unpack as rust implementation with additionalMetadata', () => { - const meta: TokenMetadata = { + it('Can pack and unpack with additional metadata', () => { + checkPackUnpack({ mint: PublicKey.default, name: 'new_name', symbol: 'new_symbol', @@ -35,43 +42,11 @@ describe('Token Metadata State', () => { ['key1', 'value1'], ['key2', 'value2'], ], - }; - // From rust implementation - const bytes = Buffer.from([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 110, 101, - 119, 95, 110, 97, 109, 101, 10, 0, 0, 0, 110, 101, 119, 95, 115, 121, 109, 98, 111, 108, 7, 0, 0, 0, 110, - 101, 119, 95, 117, 114, 105, 2, 0, 0, 0, 4, 0, 0, 0, 107, 101, 121, 49, 6, 0, 0, 0, 118, 97, 108, 117, 101, - 49, 4, 0, 0, 0, 107, 101, 121, 50, 6, 0, 0, 0, 118, 97, 108, 117, 101, 50, - ]); - - expect(pack(meta)).to.deep.equal(bytes); - expect(unpack(bytes)).to.deep.equal(meta); - }); - - it('Can pack and unpack with mint and updateAuthority', () => { - const meta = { - updateAuthority: new PublicKey('44444444444444444444444444444444444444444444'), - mint: new PublicKey('55555555555555555555555555555555555555555555'), - name: 'name', - symbol: 'symbol', - uri: 'uri', - additionalMetadata: [], - }; - - const bytes = Buffer.from([ - 45, 91, 65, 60, 101, 64, 222, 21, 12, 147, 115, 20, 77, 81, 51, 202, 76, 184, 48, 186, 15, 117, 103, 22, - 172, 234, 14, 80, 215, 148, 53, 229, 60, 121, 172, 80, 135, 1, 40, 28, 16, 196, 153, 112, 103, 22, 239, 184, - 102, 74, 235, 162, 191, 71, 52, 30, 59, 226, 189, 193, 31, 112, 71, 220, 4, 0, 0, 0, 110, 97, 109, 101, 6, - 0, 0, 0, 115, 121, 109, 98, 111, 108, 3, 0, 0, 0, 117, 114, 105, 0, 0, 0, 0, - ]); - - expect(pack(meta)).to.deep.equal(bytes); - expect(unpack(bytes)).to.deep.equal(meta); + }); }); - it('Can pack and unpack with mint, updateAuthority and additional metadata', () => { - const meta: TokenMetadata = { + it('Can pack and unpack with updateAuthority and additional metadata', () => { + checkPackUnpack({ updateAuthority: new PublicKey('44444444444444444444444444444444444444444444'), mint: new PublicKey('55555555555555555555555555555555555555555555'), name: 'name', @@ -81,18 +56,6 @@ describe('Token Metadata State', () => { ['key1', 'value1'], ['key2', 'value2'], ], - }; - - const bytes = Buffer.from([ - 45, 91, 65, 60, 101, 64, 222, 21, 12, 147, 115, 20, 77, 81, 51, 202, 76, 184, 48, 186, 15, 117, 103, 22, - 172, 234, 14, 80, 215, 148, 53, 229, 60, 121, 172, 80, 135, 1, 40, 28, 16, 196, 153, 112, 103, 22, 239, 184, - 102, 74, 235, 162, 191, 71, 52, 30, 59, 226, 189, 193, 31, 112, 71, 220, 4, 0, 0, 0, 110, 97, 109, 101, 6, - 0, 0, 0, 115, 121, 109, 98, 111, 108, 3, 0, 0, 0, 117, 114, 105, 2, 0, 0, 0, 4, 0, 0, 0, 107, 101, 121, 49, - 6, 0, 0, 0, 118, 97, 108, 117, 101, 49, 4, 0, 0, 0, 107, 101, 121, 50, 6, 0, 0, 0, 118, 97, 108, 117, 101, - 50, - ]); - - expect(pack(meta)).to.deep.equal(bytes); - expect(unpack(bytes)).to.deep.equal(meta); + }); }); });