Skip to content

Commit 2e35c0f

Browse files
[js-client] Add idl.json for the program and add js clients (#129)
add `idl.json` for the program and add js clients
1 parent a92a494 commit 2e35c0f

26 files changed

+2364
-0
lines changed

clients/js/package.json

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
{
2+
"name": "@solana-program/spl-record",
3+
"version": "0.1.0",
4+
"description": "JavaScript client for the SPL Record program",
5+
"sideEffects": false,
6+
"module": "./dist/src/index.mjs",
7+
"main": "./dist/src/index.js",
8+
"types": "./dist/types/index.d.ts",
9+
"type": "commonjs",
10+
"exports": {
11+
".": {
12+
"types": "./dist/types/index.d.ts",
13+
"import": "./dist/src/index.mjs",
14+
"require": "./dist/src/index.js"
15+
}
16+
},
17+
"files": [
18+
"./dist/src",
19+
"./dist/types"
20+
],
21+
"scripts": {
22+
"build": "rimraf dist && tsup && tsc -p ./tsconfig.declarations.json",
23+
"build:docs": "typedoc",
24+
"test": "ava",
25+
"lint": "eslint --ext js,ts,tsx src",
26+
"lint:fix": "eslint --fix --ext js,ts,tsx src",
27+
"format": "prettier --check src test",
28+
"format:fix": "prettier --write src test",
29+
"prepublishOnly": "pnpm build"
30+
},
31+
"publishConfig": {
32+
"access": "public",
33+
"registry": "https://registry.npmjs.org"
34+
},
35+
"license": "Apache-2.0",
36+
"repository": {
37+
"type": "git",
38+
"url": "git+https://github.com/solana-program/token-2022.git"
39+
},
40+
"bugs": {
41+
"url": "https://github.com/solana-program/token-2022/issues"
42+
},
43+
"homepage": "https://github.com/solana-program/token-2022#readme",
44+
"peerDependencies": {
45+
"@solana/kit": "^4.0",
46+
"@solana/sysvars": "^4.0"
47+
},
48+
"devDependencies": {
49+
"@ava/typescript": "^6.0.0",
50+
"@solana-program/system": "^0.9.0",
51+
"@solana/eslint-config-solana": "^3.0.3",
52+
"@solana/kit": "^4.0",
53+
"@types/node": "^24",
54+
"@typescript-eslint/eslint-plugin": "^7.16.1",
55+
"@typescript-eslint/parser": "^7.16.1",
56+
"ava": "^6.1.3",
57+
"eslint": "^8.57.0",
58+
"prettier": "^3.3.3",
59+
"rimraf": "^6.0.1",
60+
"tsup": "^8.1.2",
61+
"typedoc": "^0.28.0",
62+
"typescript": "^5.5.3"
63+
},
64+
"ava": {
65+
"nodeArguments": [
66+
"--no-warnings"
67+
],
68+
"typescript": {
69+
"compile": false,
70+
"rewritePaths": {
71+
"test/": "dist/test/"
72+
}
73+
}
74+
},
75+
"packageManager": "[email protected]"
76+
}

clients/js/src/actions.ts

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import {
2+
Address,
3+
generateKeyPairSigner,
4+
GetBalanceApi,
5+
GetMinimumBalanceForRentExemptionApi,
6+
Instruction,
7+
KeyPairSigner,
8+
Rpc,
9+
TransactionSigner,
10+
} from "@solana/kit";
11+
import {
12+
getCreateAccountInstruction,
13+
getTransferSolInstruction,
14+
} from "@solana-program/system";
15+
import {
16+
getCloseAccountInstruction,
17+
getInitializeInstruction,
18+
getReallocateInstruction,
19+
getSetAuthorityInstruction,
20+
getWriteInstruction,
21+
SPL_RECORD_PROGRAM_ADDRESS,
22+
} from "./generated";
23+
import { RECORD_META_DATA_SIZE } from "./constants";
24+
25+
export interface CreateRecordArgs {
26+
rpc: Rpc<GetMinimumBalanceForRentExemptionApi>;
27+
payer: KeyPairSigner;
28+
authority: Address;
29+
dataLength: number | bigint;
30+
programId?: Address;
31+
/** Optional: Provide your own keypair for the record account. If not provided, one is generated. */
32+
recordKeypair?: KeyPairSigner;
33+
}
34+
35+
export interface CreateRecordResult {
36+
recordKeypair: KeyPairSigner;
37+
ixs: Instruction[];
38+
}
39+
40+
/**
41+
* High-level function to create and initialize a Record Account.
42+
* Handles rent calculation and system account creation.
43+
*/
44+
export async function createRecord({
45+
rpc,
46+
payer,
47+
authority,
48+
dataLength,
49+
programId = SPL_RECORD_PROGRAM_ADDRESS,
50+
recordKeypair,
51+
}: CreateRecordArgs): Promise<CreateRecordResult> {
52+
const recordSigner = recordKeypair ?? (await generateKeyPairSigner());
53+
const space = RECORD_META_DATA_SIZE + BigInt(dataLength);
54+
const lamports = await rpc.getMinimumBalanceForRentExemption(space).send();
55+
56+
const createAccountIx = getCreateAccountInstruction({
57+
payer: payer,
58+
newAccount: recordSigner,
59+
lamports,
60+
space,
61+
programAddress: programId,
62+
});
63+
64+
const initializeIx = getInitializeInstruction(
65+
{
66+
recordAccount: recordSigner.address,
67+
authority,
68+
},
69+
{ programAddress: programId },
70+
);
71+
72+
return {
73+
recordKeypair: recordSigner,
74+
ixs: [createAccountIx, initializeIx],
75+
};
76+
}
77+
78+
export interface WriteRecordArgs {
79+
recordAccount: Address;
80+
authority: TransactionSigner;
81+
offset: number | bigint;
82+
data: Uint8Array;
83+
programId?: Address;
84+
}
85+
86+
/**
87+
* Creates a Write instruction.
88+
* Note: For large data, you should manually chunk this or use a loop helper.
89+
*/
90+
export function createWriteInstruction(args: WriteRecordArgs): Instruction {
91+
return getWriteInstruction(
92+
{
93+
recordAccount: args.recordAccount,
94+
authority: args.authority,
95+
offset: BigInt(args.offset),
96+
data: args.data,
97+
},
98+
{ programAddress: args.programId },
99+
);
100+
}
101+
102+
export interface ReallocateRecordArgs {
103+
rpc: Rpc<GetBalanceApi & GetMinimumBalanceForRentExemptionApi>;
104+
payer: KeyPairSigner;
105+
recordAccount: Address;
106+
authority: TransactionSigner;
107+
newDataLength: number | bigint;
108+
programId?: Address;
109+
}
110+
111+
/**
112+
* High-level function to reallocate a Record Account.
113+
* Checks if additional lamports are needed for the new size and adds a transfer instruction if so.
114+
*/
115+
export async function reallocateRecord({
116+
rpc,
117+
payer,
118+
recordAccount,
119+
authority,
120+
newDataLength,
121+
programId = SPL_RECORD_PROGRAM_ADDRESS,
122+
}: ReallocateRecordArgs): Promise<Instruction[]> {
123+
const ixs: Instruction[] = [];
124+
const newSpace = RECORD_META_DATA_SIZE + BigInt(newDataLength);
125+
const requiredRent = await rpc
126+
.getMinimumBalanceForRentExemption(newSpace)
127+
.send();
128+
const currentBalance = await rpc.getBalance(recordAccount).send();
129+
130+
if (requiredRent > currentBalance.value) {
131+
const lamportsNeeded = requiredRent - currentBalance.value;
132+
ixs.push(
133+
getTransferSolInstruction({
134+
source: payer,
135+
destination: recordAccount,
136+
amount: lamportsNeeded,
137+
}),
138+
);
139+
}
140+
141+
ixs.push(
142+
getReallocateInstruction(
143+
{
144+
recordAccount,
145+
authority,
146+
dataLength: BigInt(newDataLength),
147+
},
148+
{ programAddress: programId },
149+
),
150+
);
151+
152+
return ixs;
153+
}
154+
155+
export interface SetAuthorityArgs {
156+
recordAccount: Address;
157+
authority: TransactionSigner;
158+
newAuthority: Address;
159+
programId?: Address;
160+
}
161+
162+
export function createSetAuthorityInstruction(
163+
args: SetAuthorityArgs,
164+
): Instruction {
165+
return getSetAuthorityInstruction(
166+
{
167+
recordAccount: args.recordAccount,
168+
authority: args.authority,
169+
newAuthority: args.newAuthority,
170+
},
171+
{ programAddress: args.programId },
172+
);
173+
}
174+
175+
export interface CloseRecordArgs {
176+
recordAccount: Address;
177+
authority: TransactionSigner;
178+
receiver: Address;
179+
programId?: Address;
180+
}
181+
182+
export function createCloseRecordInstruction(
183+
args: CloseRecordArgs,
184+
): Instruction {
185+
return getCloseAccountInstruction(
186+
{
187+
recordAccount: args.recordAccount,
188+
authority: args.authority,
189+
receiver: args.receiver,
190+
},
191+
{ programAddress: args.programId },
192+
);
193+
}

clients/js/src/constants.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/** A record account size excluding the record payload */
2+
export const RECORD_META_DATA_SIZE = 33n;
3+
4+
/** Maximum record chunk that can fit inside a transaction when initializing a record account */
5+
export const RECORD_CHUNK_SIZE_PRE_INITIALIZE = 696;
6+
7+
/** Maximum record chunk that can fit inside a transaction when record account already initialized */
8+
export const RECORD_CHUNK_SIZE_POST_INITIALIZE = 917;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* This code was AUTOGENERATED using the Codama library.
3+
* Please DO NOT EDIT THIS FILE, instead use visitors
4+
* to add features, then rerun Codama to update it.
5+
*
6+
* @see https://github.com/codama-idl/codama
7+
*/
8+
9+
export * from './recordData';

0 commit comments

Comments
 (0)