Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions metadata/js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@bbachain/spl-token-metadata",
"version": "0.0.2",
"version": "0.0.6",
"author": "BBAChain Labs <developers@bbachain.com>",
"repository": "https://github.com/bbachain/program-executor",
"license": "Apache-2.0",
Expand All @@ -27,11 +27,10 @@
"clean": "shx rm -rf lib",
"build": "yarn clean && tsc -p tsconfig.json; tsc-esm -p tsconfig.json && tsc -p tsconfig.cjs.json",
"postbuild": "echo '{\"type\":\"commonjs\"}' > lib/cjs/package.json && echo '{\"type\":\"module\"}' > lib/esm/package.json",
"deploy": "yarn docs && gh-pages --dist docs --dest token/js --dotfiles",
"deploy": "yarn docs && gh-pages --dist docs --dest metadata/js --dotfiles",
"test": "yarn test:unit && yarn test:e2e-built && yarn test:e2e-native && yarn test:e2e-2022",
"test:unit": "mocha test/unit",
"test:e2e-built": "start-server-and-test 'solana-test-validator --bpf-program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA ../../target/deploy/spl_token.so --reset --quiet' http://localhost:8899/health 'mocha test/e2e'",
"test:e2e-2022": "TEST_PROGRAM_ID=TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb start-server-and-test 'solana-test-validator --bpf-program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL ../../target/deploy/spl_associated_token_account.so --bpf-program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb ../../target/deploy/spl_token_2022.so --reset --quiet' http://localhost:8899/health 'mocha test/e2e*'",
"test:e2e-native": "start-server-and-test 'solana-test-validator --reset --quiet' http://localhost:8899/health 'mocha test/e2e'",
"test:build-programs": "cargo build-bpf --manifest-path ../program/Cargo.toml && cargo build-bpf --manifest-path ../program-2022/Cargo.toml && cargo build-bpf --manifest-path ../../associated-token-account/program/Cargo.toml",
"docs": "shx rm -rf docs && NODE_OPTIONS=--max_old_space_size=4096 typedoc && shx cp .nojekyll docs/",
Expand Down
75 changes: 0 additions & 75 deletions metadata/js/src/client.ts

This file was deleted.

16 changes: 15 additions & 1 deletion metadata/js/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
import { PublicKey } from '@bbachain/web3.js';

/** Program ID (replace with your actual address if it changes). */
export const PROGRAM_ID = new PublicKey('metaAig5QsCBSfstkwqPQxzdjXdUB8JxjfvtvEPNe3F');
export const METADATA_SEED = 'metadata';

export const PDA_SEED = 'metadata';

/** Maximum on‑chain string lengths (bytes, not UTF‑16 code units). */
export const MAX_NAME_LEN = 32;
export const MAX_SYMBOL_LEN = 10;
export const MAX_URI_LEN = 200;

/**
* The Rust program over‑allocates one byte when it creates the account
* (`processor.rs::data_size`), but never writes to it.
* We drop that trailing byte when decoding.
*/
export const TAIL_PADDING = 1;
48 changes: 0 additions & 48 deletions metadata/js/src/deserialize.ts

This file was deleted.

22 changes: 22 additions & 0 deletions metadata/js/src/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { PublicKey } from '@bbachain/web3.js';
import { serialize } from 'borsh';
import { PROGRAM_ID, PDA_SEED, MAX_NAME_LEN, MAX_SYMBOL_LEN, MAX_URI_LEN } from './constants';
import { InitializeArgs, UpdateArgs, TokenInstructionKind } from './types';
import { INSTRUCTION_SCHEMA } from './schema';

/** PDA derivation helper (sync, because web3.js v2). */
export const deriveMetadataPDA = (mint: PublicKey): [PublicKey, number] =>
PublicKey.findProgramAddressSync([Buffer.from(PDA_SEED), mint.toBuffer()], PROGRAM_ID);

/** Length guards (throws early on oversized strings). */
export function assertFieldLengths(obj: { name: string; symbol: string; uri: string }): void {
if (obj.name.length > MAX_NAME_LEN) throw new Error('name too long');
if (obj.symbol.length > MAX_SYMBOL_LEN) throw new Error('symbol too long');
if (obj.uri.length > MAX_URI_LEN) throw new Error('uri too long');
}

/** Prepends the enum discriminant to a Borsh‑encoded payload. */
export function encodeInstructionData(kind: TokenInstructionKind, args: InitializeArgs | UpdateArgs): Buffer {
const data = serialize(INSTRUCTION_SCHEMA, args);
return Buffer.concat([Buffer.from([kind]), data]);
}
7 changes: 5 additions & 2 deletions metadata/js/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export * from './client';
export * from './deserialize';
export * from './constants';
export * from './types';
export * from './schema';
export * from './helpers';
export * from './instructions';
export * from './read';
95 changes: 37 additions & 58 deletions metadata/js/src/instructions.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,41 @@
import * as borsh from 'borsh';
import { PublicKey, SystemProgram, TransactionInstruction } from '@bbachain/web3.js';
import { InitializeArgs, UpdateArgs, TokenInstructionKind } from './types';
import { PROGRAM_ID } from './constants';
import { assertFieldLengths, deriveMetadataPDA, encodeInstructionData } from './helpers';

class InitializeInstruction {
instruction = 0;
name: string;
symbol: string;
uri: string;
/** Builds an `Initialize` instruction. */
export const createInitializeMetadataIx = (
payer: PublicKey,
mint: PublicKey,
args: InitializeArgs
): TransactionInstruction => {
assertFieldLengths(args);

constructor(props: { name: string; symbol: string; uri: string }) {
this.name = props.name;
this.symbol = props.symbol;
this.uri = props.uri;
}
}
const [metadataPda] = deriveMetadataPDA(mint);
const keys = [
{ pubkey: metadataPda, isSigner: false, isWritable: true },
{ pubkey: mint, isSigner: false, isWritable: false },
{ pubkey: payer, isSigner: true, isWritable: true },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
];
const data = encodeInstructionData(TokenInstructionKind.Initialize, args);
return new TransactionInstruction({ programId: PROGRAM_ID, keys, data });
};

class UpdateInstruction {
instruction = 1;
name: string;
symbol: string;
uri: string;
/** Builds an `Update` instruction. */
export const createUpdateMetadataIx = (
authority: PublicKey,
mint: PublicKey,
args: UpdateArgs
): TransactionInstruction => {
assertFieldLengths(args);

constructor(props: { name: string; symbol: string; uri: string }) {
this.name = props.name;
this.symbol = props.symbol;
this.uri = props.uri;
}
}

const InstructionSchema = new Map<any, any>([
[
InitializeInstruction,
{
kind: 'struct',
fields: [
['instruction', 'u8'],
['name', 'string'],
['symbol', 'string'],
['uri', 'string'],
],
},
],
[
UpdateInstruction,
{
kind: 'struct',
fields: [
['instruction', 'u8'],
['name', 'string'],
['symbol', 'string'],
['uri', 'string'],
],
},
],
]);

export function encodeInitializeInstruction(name: string, symbol: string, uri: string): Buffer {
return Buffer.from(borsh.serialize(InstructionSchema, new InitializeInstruction({ name, symbol, uri })));
}

export function encodeUpdateInstruction(name: string, symbol: string, uri: string): Buffer {
return Buffer.from(borsh.serialize(InstructionSchema, new UpdateInstruction({ name, symbol, uri })));
}
const [metadataPda] = deriveMetadataPDA(mint);
const keys = [
{ pubkey: metadataPda, isSigner: false, isWritable: true },
{ pubkey: mint, isSigner: false, isWritable: false },
{ pubkey: authority, isSigner: true, isWritable: false },
];
const data = encodeInstructionData(TokenInstructionKind.Update, args);
return new TransactionInstruction({ programId: PROGRAM_ID, keys, data });
};
25 changes: 25 additions & 0 deletions metadata/js/src/read.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Connection, PublicKey } from '@bbachain/web3.js';
import { deserializeUnchecked } from 'borsh';
import { STATE_SCHEMA } from './schema';
import { TokenMetadata } from './types';
import { deriveMetadataPDA } from './helpers';
import { TAIL_PADDING } from './constants';

/** Deserialises raw account data → `TokenMetadata`, forgiving the 1‑byte tail. */
export const decodeMetadataAccount = (data: Buffer): TokenMetadata => {
try {
return deserializeUnchecked(STATE_SCHEMA, TokenMetadata, data);
} catch {
if (data.length > TAIL_PADDING) {
return deserializeUnchecked(STATE_SCHEMA, TokenMetadata, data.subarray(0, data.length - TAIL_PADDING));
}
throw new Error('Failed to decode TokenMetadata');
}
};

/** Fetches & decodes the account; returns `null` if it doesn’t exist. */
export const fetchMetadata = async (connection: Connection, mint: PublicKey): Promise<TokenMetadata | null> => {
const [pda] = deriveMetadataPDA(mint);
const info = await connection.getAccountInfo(pda);
return info ? decodeMetadataAccount(info.data) : null;
};
45 changes: 45 additions & 0 deletions metadata/js/src/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Schema } from 'borsh';
import { InitializeArgs, UpdateArgs, TokenMetadata } from './types';

/** Borsh schema for account state. */
export const STATE_SCHEMA: Schema = new Map([
[
TokenMetadata,
{
kind: 'struct',
fields: [
['mint', [32]],
['name', 'string'],
['symbol', 'string'],
['uri', 'string'],
['authority', [32]],
],
},
],
]);

/** Borsh schema for instruction payloads. */
export const INSTRUCTION_SCHEMA: Schema = new Map([
[
InitializeArgs,
{
kind: 'struct',
fields: [
['name', 'string'],
['symbol', 'string'],
['uri', 'string'],
],
},
],
[
UpdateArgs,
{
kind: 'struct',
fields: [
['name', 'string'],
['symbol', 'string'],
['uri', 'string'],
],
},
],
]);
Loading
Loading