Skip to content

Commit 12e70fc

Browse files
add createsplinterface to token-interface sdk
1 parent a7336af commit 12e70fc

8 files changed

Lines changed: 191 additions & 4 deletions

File tree

js/token-interface/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@
4949
"buffer": "6.0.3"
5050
},
5151
"devDependencies": {
52-
"@lightprotocol/compressed-token": "workspace:*",
5352
"@eslint/js": "9.36.0",
5453
"@rollup/plugin-commonjs": "^26.0.1",
5554
"@rollup/plugin-node-resolve": "^15.2.3",

js/token-interface/src/instructions/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ export * from './load';
77
export * from './burn';
88
export * from './freeze';
99
export * from './thaw';
10+
export * from './spl-interface';
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import {
2+
AccountMeta,
3+
PublicKey,
4+
SystemProgram,
5+
TransactionInstruction,
6+
} from '@solana/web3.js';
7+
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
8+
import { Buffer } from 'buffer';
9+
import {
10+
COMPRESSED_TOKEN_PROGRAM_ID,
11+
deriveCpiAuthorityPda,
12+
deriveSplInterfacePdaWithIndex,
13+
} from '../constants';
14+
15+
const CREATE_SPL_INTERFACE_DISCRIMINATOR = Buffer.from([
16+
23, 169, 27, 122, 147, 169, 209, 152,
17+
]);
18+
19+
interface CreateSplInterfaceAccountsLayoutParams {
20+
feePayer: PublicKey;
21+
mint: PublicKey;
22+
splInterfacePda: PublicKey;
23+
tokenProgramId: PublicKey;
24+
cpiAuthorityPda: PublicKey;
25+
}
26+
27+
function createSplInterfaceAccountsLayout({
28+
feePayer,
29+
mint,
30+
splInterfacePda,
31+
tokenProgramId,
32+
cpiAuthorityPda,
33+
}: CreateSplInterfaceAccountsLayoutParams): AccountMeta[] {
34+
return [
35+
{ pubkey: feePayer, isSigner: true, isWritable: true },
36+
{ pubkey: splInterfacePda, isSigner: false, isWritable: true },
37+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
38+
{ pubkey: mint, isSigner: false, isWritable: true },
39+
{ pubkey: tokenProgramId, isSigner: false, isWritable: false },
40+
{ pubkey: cpiAuthorityPda, isSigner: false, isWritable: false },
41+
];
42+
}
43+
44+
export interface CreateSplInterfaceInstructionInput {
45+
feePayer: PublicKey;
46+
mint: PublicKey;
47+
index: number;
48+
tokenProgramId?: PublicKey;
49+
}
50+
51+
/**
52+
* Create SPL interface (omnibus account) for an existing SPL mint.
53+
*
54+
* @param input SPL interface instruction input.
55+
* @param input.feePayer Fee payer.
56+
* @param input.mint SPL mint address.
57+
* @param input.index SPL interface derivation index (single required index).
58+
* @param input.tokenProgramId Token program id (defaults to TOKEN_PROGRAM_ID).
59+
* @returns Create SPL interface instruction.
60+
*/
61+
export function createSplInterfaceInstruction({
62+
feePayer,
63+
mint,
64+
index,
65+
tokenProgramId = TOKEN_PROGRAM_ID,
66+
}: CreateSplInterfaceInstructionInput): TransactionInstruction {
67+
if (!Number.isInteger(index) || index < 0 || index > 255) {
68+
throw new Error(
69+
`Invalid SPL interface index ${index}. Expected integer in [0, 255].`,
70+
);
71+
}
72+
73+
const [splInterfacePda] = deriveSplInterfacePdaWithIndex(mint, index);
74+
const keys = createSplInterfaceAccountsLayout({
75+
feePayer,
76+
mint,
77+
splInterfacePda,
78+
tokenProgramId,
79+
cpiAuthorityPda: deriveCpiAuthorityPda(),
80+
});
81+
82+
return new TransactionInstruction({
83+
programId: COMPRESSED_TOKEN_PROGRAM_ID,
84+
keys,
85+
data: CREATE_SPL_INTERFACE_DISCRIMINATOR,
86+
});
87+
}

js/token-interface/src/kit/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ import {
1212
createLoadInstructions as createLoadInstructionsTx,
1313
createRevokeInstructions as createRevokeInstructionsTx,
1414
createRevokeInstructionsNowrap as createRevokeInstructionsNowrapTx,
15+
createSplInterfaceInstruction as createSplInterfaceInstructionTx,
1516
createThawInstructions as createThawInstructionsTx,
1617
createThawInstructionsNowrap as createThawInstructionsNowrapTx,
1718
} from '../instructions';
1819
import type { KitInstruction } from '../instructions/_plan';
1920
import { toKitInstructions } from '../instructions/_plan';
21+
import type { CreateSplInterfaceInstructionInput } from '../instructions/spl-interface';
2022
import type {
2123
CreateApproveInstructionsInput,
2224
CreateAtaInstructionsInput,
@@ -133,13 +135,20 @@ export async function createBurnInstructionsNowrap(
133135
return wrap(createBurnInstructionsNowrapTx(input));
134136
}
135137

138+
export function createSplInterfaceInstruction(
139+
input: CreateSplInterfaceInstructionInput,
140+
): KitInstruction {
141+
return toKitInstructions([createSplInterfaceInstructionTx(input)])[0];
142+
}
143+
136144
export type {
137145
CreateApproveInstructionsInput,
138146
CreateAtaInstructionsInput,
139147
CreateBurnInstructionsInput,
140148
CreateFreezeInstructionsInput,
141149
CreateLoadInstructionsInput,
142150
CreateRevokeInstructionsInput,
151+
CreateSplInterfaceInstructionInput,
143152
CreateThawInstructionsInput,
144153
CreateTransferInstructionsInput,
145154
};

js/token-interface/tests/e2e/helpers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
selectStateTreeInfo,
1818
sendAndConfirmTx,
1919
} from '@lightprotocol/stateless.js';
20-
import { createMint, mintTo } from '@lightprotocol/compressed-token';
20+
import { createMint, mintTo } from '../../../compressed-token/src';
2121
import { parseLightTokenHot } from '../../src/read';
2222
import { getSplInterfaces } from '../../src/spl-interface';
2323

js/token-interface/tests/unit/instruction-builders.test.ts

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { describe, expect, it } from 'vitest';
2-
import { Keypair } from '@solana/web3.js';
2+
import { Keypair, SystemProgram } from '@solana/web3.js';
33
import { Buffer } from 'buffer';
44
import { LIGHT_TOKEN_PROGRAM_ID, bn } from '@lightprotocol/stateless.js';
5-
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
5+
import { TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID } from '@solana/spl-token';
66
import {
77
createApproveInstruction,
88
createAtaInstruction,
@@ -14,7 +14,13 @@ import {
1414
createThawInstruction,
1515
createTransferCheckedInstruction,
1616
createDecompressInstruction,
17+
createSplInterfaceInstruction,
1718
} from '../../src/instructions';
19+
import {
20+
COMPRESSED_TOKEN_PROGRAM_ID,
21+
deriveCpiAuthorityPda,
22+
deriveSplInterfacePdaWithIndex,
23+
} from '../../src/constants';
1824

1925
describe('instruction builders', () => {
2026
function buildParsedCompressedAccount(params?: {
@@ -349,4 +355,72 @@ describe('instruction builders', () => {
349355
expect(instruction.keys[2].pubkey.equals(owner)).toBe(true);
350356
expect(instruction.keys[2].isSigner).toBe(true);
351357
});
358+
359+
it('creates spl interface instruction with required index', () => {
360+
const feePayer = Keypair.generate().publicKey;
361+
const mint = Keypair.generate().publicKey;
362+
const index = 0;
363+
364+
const instruction = createSplInterfaceInstruction({
365+
feePayer,
366+
mint,
367+
index,
368+
});
369+
370+
const [splInterfacePda] = deriveSplInterfacePdaWithIndex(mint, index);
371+
372+
expect(instruction.programId.equals(COMPRESSED_TOKEN_PROGRAM_ID)).toBe(
373+
true,
374+
);
375+
expect(
376+
instruction.data.equals(
377+
Buffer.from([23, 169, 27, 122, 147, 169, 209, 152]),
378+
),
379+
).toBe(true);
380+
expect(instruction.keys).toHaveLength(6);
381+
expect(instruction.keys[0].pubkey.equals(feePayer)).toBe(true);
382+
expect(instruction.keys[0].isSigner).toBe(true);
383+
expect(instruction.keys[0].isWritable).toBe(true);
384+
expect(instruction.keys[1].pubkey.equals(splInterfacePda)).toBe(true);
385+
expect(instruction.keys[1].isWritable).toBe(true);
386+
expect(instruction.keys[2].pubkey.equals(SystemProgram.programId)).toBe(
387+
true,
388+
);
389+
expect(instruction.keys[3].pubkey.equals(mint)).toBe(true);
390+
expect(instruction.keys[3].isWritable).toBe(true);
391+
expect(instruction.keys[4].pubkey.equals(TOKEN_PROGRAM_ID)).toBe(true);
392+
expect(instruction.keys[5].pubkey.equals(deriveCpiAuthorityPda())).toBe(
393+
true,
394+
);
395+
});
396+
397+
it('creates spl interface instruction with custom token program', () => {
398+
const feePayer = Keypair.generate().publicKey;
399+
const mint = Keypair.generate().publicKey;
400+
const index = 1;
401+
402+
const instruction = createSplInterfaceInstruction({
403+
feePayer,
404+
mint,
405+
index,
406+
tokenProgramId: TOKEN_2022_PROGRAM_ID,
407+
});
408+
409+
expect(
410+
instruction.keys[4].pubkey.equals(TOKEN_2022_PROGRAM_ID),
411+
).toBe(true);
412+
});
413+
414+
it('throws when spl interface index is out of range', () => {
415+
const feePayer = Keypair.generate().publicKey;
416+
const mint = Keypair.generate().publicKey;
417+
418+
expect(() =>
419+
createSplInterfaceInstruction({
420+
feePayer,
421+
mint,
422+
index: 256,
423+
}),
424+
).toThrow(/integer in \[0, 255\]/i);
425+
});
352426
});

js/token-interface/tests/unit/kit.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { createAtaInstruction } from '../../src/instructions';
44
import {
55
createTransferInstructions,
66
createAtaInstructions,
7+
createSplInterfaceInstruction,
78
createTransferInstructionPlan,
89
toKitInstructions,
910
} from '../../src/kit';
@@ -38,4 +39,15 @@ describe('kit adapter', () => {
3839
expect(typeof createTransferInstructions).toBe('function');
3940
expect(typeof createTransferInstructionPlan).toBe('function');
4041
});
42+
43+
it('adapts createSplInterfaceInstruction to kit format', () => {
44+
const instruction = createSplInterfaceInstruction({
45+
feePayer: Keypair.generate().publicKey,
46+
mint: Keypair.generate().publicKey,
47+
index: 0,
48+
});
49+
50+
expect(instruction).toBeDefined();
51+
expect(typeof instruction).toBe('object');
52+
});
4153
});

js/token-interface/tests/unit/public-api.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
createAtaInstructions,
99
createFreezeInstruction,
1010
createThawInstruction,
11+
createSplInterfaceInstruction,
1112
getAtaAddress,
1213
} from '../../src';
1314

@@ -74,4 +75,8 @@ describe('public api', () => {
7475
it('exports canonical transfer builder', () => {
7576
expect(typeof createTransferInstructions).toBe('function');
7677
});
78+
79+
it('exports createSplInterfaceInstruction builder', () => {
80+
expect(typeof createSplInterfaceInstruction).toBe('function');
81+
});
7782
});

0 commit comments

Comments
 (0)