@typesol/cli 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +61 -0
- package/README.md +10 -6
- package/dist/cli.js +147 -20
- package/dist/compiler/analyze.d.ts +4 -0
- package/dist/compiler/analyze.js +51 -9
- package/dist/compiler/emit/base.js +1 -1
- package/dist/compiler/emit/calls.js +7 -0
- package/dist/compiler/emit/cpi.js +188 -9
- package/dist/compiler/emit/expressions.d.ts +11 -0
- package/dist/compiler/emit/expressions.js +107 -2
- package/dist/compiler/emit/function.js +11 -1
- package/dist/compiler/emit/shared.js +3 -2
- package/dist/compiler/emit/statements.d.ts +13 -0
- package/dist/compiler/emit/statements.js +131 -5
- package/dist/compiler/emit/storage.d.ts +2 -1
- package/dist/compiler/emit/storage.js +24 -7
- package/dist/compiler/idl.js +28 -14
- package/dist/compiler/model.d.ts +16 -3
- package/dist/compiler/toolchain.d.ts +2 -0
- package/dist/compiler/toolchain.js +27 -1
- package/dist/onchain.d.ts +30 -0
- package/dist/onchain.js +53 -0
- package/dist/portgen.d.ts +87 -0
- package/dist/portgen.js +311 -0
- package/dist/templates.js +49 -154
- package/dist/testing.d.ts +66 -0
- package/dist/testing.js +337 -0
- package/dist/workspace.js +11 -1
- package/docs/architecture.md +3 -0
- package/docs/cli.md +16 -2
- package/docs/language.md +11 -10
- package/docs/mainnet.md +5 -4
- package/docs/migrate-from-anchor.md +104 -0
- package/docs/test-your-program.md +52 -0
- package/docs/testing.md +1 -1
- package/docs/troubleshooting.md +7 -0
- package/lib/typesol.d.ts +68 -12
- package/package.json +19 -1
- package/runtime/typesol_rt.h +25 -5
- package/ts-plugin/index.js +112 -0
- package/ts-plugin/package.json +4 -0
package/dist/templates.js
CHANGED
|
@@ -2,71 +2,16 @@
|
|
|
2
2
|
// main flow and at least one refused call, so a new project starts from something that passes and shows how to test.
|
|
3
3
|
// The programs are copies of examples/ (test/workspace.test.ts checks they still match).
|
|
4
4
|
export const TEMPLATE_NAMES = ["counter", "vault", "escrow"];
|
|
5
|
-
/**
|
|
5
|
+
/** Every test starts the same way: build the project's program into a LiteSVM with `typesol/testing`. */
|
|
6
6
|
const testFile = (name, o) => `// Runs the program in LiteSVM (an in-process Solana runtime): \`npm test\`.
|
|
7
|
-
import {
|
|
8
|
-
${o.imports}import {
|
|
9
|
-
${[
|
|
10
|
-
"AccountRole",
|
|
11
|
-
"appendTransactionMessageInstructions",
|
|
12
|
-
"createTransactionMessage",
|
|
13
|
-
...o.kit,
|
|
14
|
-
"pipe",
|
|
15
|
-
"setTransactionMessageFeePayerSigner",
|
|
16
|
-
"signTransactionMessageWithSigners",
|
|
17
|
-
"type Address",
|
|
18
|
-
"type Instruction",
|
|
19
|
-
"type KeyPairSigner",
|
|
20
|
-
]
|
|
21
|
-
.map((x) => ` ${x},`)
|
|
22
|
-
.join("\n")}
|
|
23
|
-
} from "@solana/kit";
|
|
24
|
-
import { FailedTransactionMetadata, LiteSVM, TransactionMetadata } from "litesvm";
|
|
25
|
-
import path from "node:path";
|
|
26
|
-
import { fileURLToPath } from "node:url";
|
|
27
|
-
import { buildWorkspace, findWorkspace } from "typesol/workspace";
|
|
7
|
+
${o.imports ?? ""}import { testProgram, type TestProgram } from "typesol/testing";
|
|
28
8
|
import { beforeAll, describe, expect, it } from "vitest";
|
|
29
9
|
|
|
30
|
-
const SYSTEM = "11111111111111111111111111111111" as Address;
|
|
31
|
-
const ws = findWorkspace(path.dirname(fileURLToPath(import.meta.url)))!;
|
|
32
|
-
|
|
33
10
|
describe("${name}", () => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
beforeAll(() => {
|
|
38
|
-
const [built] = buildWorkspace(ws, ["${name}"]);
|
|
39
|
-
programId = built.address as Address;
|
|
40
|
-
svm.addProgramFromFile(programId, built.result.soPath);
|
|
41
|
-
coder = new BorshCoder(built.result.idl as unknown as Idl);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
const ix = (name: string, args: object, accounts: { address: Address; signer?: KeyPairSigner; writable?: boolean }[]): Instruction => ({
|
|
45
|
-
programAddress: programId,
|
|
46
|
-
accounts: accounts.map((a) => ({
|
|
47
|
-
address: a.address,
|
|
48
|
-
role: a.signer ? (a.writable ? AccountRole.WRITABLE_SIGNER : AccountRole.READONLY_SIGNER) : a.writable ? AccountRole.WRITABLE : AccountRole.READONLY,
|
|
49
|
-
...(a.signer ? { signer: a.signer } : {}),
|
|
50
|
-
})) as Instruction["accounts"],
|
|
51
|
-
data: new Uint8Array(coder.instruction.encode(name, args)),
|
|
11
|
+
let t: TestProgram;
|
|
12
|
+
beforeAll(async () => {
|
|
13
|
+
t = await testProgram("${name}", import.meta.url);
|
|
52
14
|
});
|
|
53
|
-
const send = async (payer: KeyPairSigner, ixs: Instruction[]) => {
|
|
54
|
-
const tx = await pipe(
|
|
55
|
-
createTransactionMessage({ version: 0 }),
|
|
56
|
-
(t) => setTransactionMessageFeePayerSigner(payer, t),
|
|
57
|
-
(t) => svm.setTransactionMessageLifetimeUsingLatestBlockhash(t),
|
|
58
|
-
(t) => appendTransactionMessageInstructions(ixs, t),
|
|
59
|
-
(t) => signTransactionMessageWithSigners(t),
|
|
60
|
-
);
|
|
61
|
-
const res = svm.sendTransaction(tx);
|
|
62
|
-
svm.expireBlockhash();
|
|
63
|
-
return res;
|
|
64
|
-
};
|
|
65
|
-
/** The custom error code of a failed transaction: 6000 + the member's index in the program's error enum. */
|
|
66
|
-
const errorCode = (res: TransactionMetadata | FailedTransactionMetadata) => {
|
|
67
|
-
if (!(res instanceof FailedTransactionMetadata)) throw new Error("expected the transaction to fail");
|
|
68
|
-
return Number(/Custom\\D*(\\d+)/.exec(String(res.err()))?.[1]);
|
|
69
|
-
};
|
|
70
15
|
${o.body}});
|
|
71
16
|
`;
|
|
72
17
|
export const TEMPLATES = {
|
|
@@ -101,25 +46,17 @@ export function assertAtLeast(counter: Account<Counter>, min: u64): void {
|
|
|
101
46
|
}
|
|
102
47
|
`,
|
|
103
48
|
test: (name) => testFile(name, {
|
|
104
|
-
imports: "",
|
|
105
|
-
kit: ["generateKeyPairSigner", "getAddressEncoder", "getProgramDerivedAddress", "lamports"],
|
|
106
49
|
body: `
|
|
107
50
|
it("initializes and increments the caller's counter; refuses anyone else", async () => {
|
|
108
|
-
const alice = await
|
|
109
|
-
const mallory = await
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
expect(await send(alice, [ix("initialize", {}, [me, { address: counter, writable: true }, { address: SYSTEM }])])).toBeInstanceOf(TransactionMetadata);
|
|
115
|
-
expect(await send(alice, [ix("increment", { by: new BN(5) }, [{ address: alice.address, signer: alice }, { address: counter, writable: true }])])).toBeInstanceOf(TransactionMetadata);
|
|
116
|
-
const account = svm.getAccount(counter);
|
|
117
|
-
if (!account.exists) throw new Error("counter was not created");
|
|
118
|
-
expect(coder.accounts.decode("Counter", Buffer.from(account.data)).count.toString()).toBe("5");
|
|
51
|
+
const alice = await t.wallet();
|
|
52
|
+
const mallory = await t.wallet();
|
|
53
|
+
const counter = await t.pda("counter", alice);
|
|
54
|
+
t.ok(await t.send(alice, t.ix("initialize", {}, { authority: alice, counter })));
|
|
55
|
+
t.ok(await t.send(alice, t.ix("increment", { by: 5n }, { authority: alice, counter })));
|
|
56
|
+
expect((await t.account<{ count: bigint }>("Counter", counter))?.count).toBe(5n);
|
|
119
57
|
|
|
120
58
|
// CounterError.Unauthorized is the enum's first member: 6000.
|
|
121
|
-
|
|
122
|
-
expect(errorCode(await send(mallory, [stranger]))).toBe(6000);
|
|
59
|
+
expect(t.errorCode(await t.send(mallory, t.ix("increment", { by: 1n }, { authority: mallory, counter })))).toBe(6000);
|
|
123
60
|
});
|
|
124
61
|
`,
|
|
125
62
|
}),
|
|
@@ -182,41 +119,30 @@ export function shut(owner: Mut<Signer>, vault: Mut<Vault>): void {
|
|
|
182
119
|
}
|
|
183
120
|
`,
|
|
184
121
|
test: (name) => testFile(name, {
|
|
185
|
-
imports: "",
|
|
186
|
-
kit: ["generateKeyPairSigner", "getAddressEncoder", "getProgramDerivedAddress", "lamports"],
|
|
187
122
|
body: `
|
|
188
123
|
it("deposits and withdraws SOL, keeps the rent reserve, then closes", async () => {
|
|
189
|
-
const owner = await
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
expect(
|
|
196
|
-
const rent = svm.getBalance(vault)!;
|
|
197
|
-
expect(await send(owner, [ix("deposit", { amount: new BN(3_000_000_000) }, [me, v, { address: SYSTEM }])])).toBeInstanceOf(TransactionMetadata);
|
|
198
|
-
expect(await send(owner, [ix("withdraw", { amount: new BN(1_000_000_000) }, [me, v])])).toBeInstanceOf(TransactionMetadata);
|
|
199
|
-
expect(svm.getBalance(vault)).toBe(rent + 2_000_000_000n);
|
|
124
|
+
const owner = await t.wallet();
|
|
125
|
+
const vault = await t.pda("vault", owner);
|
|
126
|
+
t.ok(await t.send(owner, t.ix("open", {}, { owner, vault })));
|
|
127
|
+
const rent = t.balance(vault);
|
|
128
|
+
t.ok(await t.send(owner, t.ix("deposit", { amount: 3_000_000_000n }, { owner, vault })));
|
|
129
|
+
t.ok(await t.send(owner, t.ix("withdraw", { amount: 1_000_000_000n }, { owner, vault })));
|
|
130
|
+
expect(t.balance(vault)).toBe(rent + 2_000_000_000n);
|
|
200
131
|
|
|
201
132
|
// VaultError.InsufficientFunds (third member, 6002): the rent reserve stays.
|
|
202
|
-
expect(errorCode(await send(owner,
|
|
133
|
+
expect(t.errorCode(await t.send(owner, t.ix("withdraw", { amount: 2_000_000_001n }, { owner, vault })))).toBe(6002);
|
|
203
134
|
|
|
204
|
-
|
|
205
|
-
expect(
|
|
135
|
+
t.ok(await t.send(owner, t.ix("shut", {}, { owner, vault })));
|
|
136
|
+
expect(await t.account("Vault", vault)).toBeNull();
|
|
206
137
|
});
|
|
207
138
|
|
|
208
139
|
it("refuses a stranger", async () => {
|
|
209
|
-
const owner = await
|
|
210
|
-
const thief = await
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
const [vault] = await getProgramDerivedAddress({ programAddress: programId, seeds: ["vault", getAddressEncoder().encode(owner.address)] });
|
|
214
|
-
const v = { address: vault, writable: true };
|
|
215
|
-
expect(await send(owner, [ix("open", {}, [{ address: owner.address, signer: owner, writable: true }, v, { address: SYSTEM }])])).toBeInstanceOf(
|
|
216
|
-
TransactionMetadata,
|
|
217
|
-
);
|
|
140
|
+
const owner = await t.wallet();
|
|
141
|
+
const thief = await t.wallet();
|
|
142
|
+
const vault = await t.pda("vault", owner);
|
|
143
|
+
t.ok(await t.send(owner, t.ix("open", {}, { owner, vault })));
|
|
218
144
|
// VaultError.NotOwner: 6000.
|
|
219
|
-
expect(errorCode(await send(thief,
|
|
145
|
+
expect(t.errorCode(await t.send(thief, t.ix("shut", {}, { owner: thief, vault })))).toBe(6000);
|
|
220
146
|
});
|
|
221
147
|
`,
|
|
222
148
|
}),
|
|
@@ -330,18 +256,17 @@ export function refund(
|
|
|
330
256
|
}
|
|
331
257
|
`,
|
|
332
258
|
test: (name) => testFile(name, {
|
|
333
|
-
imports: `import { AccountState, getMintEncoder, getMintSize, getTokenDecoder, getTokenEncoder, getTokenSize } from "@solana-program/token";\n`,
|
|
334
|
-
kit: ["generateKeyPairSigner", "getAddressEncoder", "getProgramDerivedAddress", "lamports"],
|
|
259
|
+
imports: `import { AccountState, getMintEncoder, getMintSize, getTokenDecoder, getTokenEncoder, getTokenSize } from "@solana-program/token";\nimport { type Address, type KeyPairSigner, generateKeyPairSigner, lamports } from "@solana/kit";\n`,
|
|
335
260
|
body: `
|
|
336
261
|
const TOKEN = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" as Address;
|
|
337
262
|
/** A mint, written straight into the VM (a real client sends System CreateAccount + InitializeMint2). */
|
|
338
263
|
const mint = async (authority: Address) => {
|
|
339
264
|
const address = (await generateKeyPairSigner()).address;
|
|
340
265
|
const data = getMintEncoder().encode({ mintAuthority: authority, supply: 0n, decimals: 6, isInitialized: true, freezeAuthority: null });
|
|
341
|
-
svm.setAccount({ address, data: new Uint8Array(data), executable: false, lamports: lamports(1_461_600n), programAddress: TOKEN, space: BigInt(getMintSize()) });
|
|
266
|
+
t.svm.setAccount({ address, data: new Uint8Array(data), executable: false, lamports: lamports(1_461_600n), programAddress: TOKEN, space: BigInt(getMintSize()) });
|
|
342
267
|
return address;
|
|
343
268
|
};
|
|
344
|
-
/** A token account of
|
|
269
|
+
/** A token account of owner for mint holding amount, written straight into the VM. */
|
|
345
270
|
const tokenAccount = async (owner: Address, mintAddress: Address, amount: bigint) => {
|
|
346
271
|
const address = (await generateKeyPairSigner()).address;
|
|
347
272
|
const data = getTokenEncoder().encode({
|
|
@@ -354,73 +279,43 @@ export function refund(
|
|
|
354
279
|
delegatedAmount: 0n,
|
|
355
280
|
closeAuthority: null,
|
|
356
281
|
});
|
|
357
|
-
svm.setAccount({ address, data: new Uint8Array(data), executable: false, lamports: lamports(2_039_280n), programAddress: TOKEN, space: BigInt(getTokenSize()) });
|
|
282
|
+
t.svm.setAccount({ address, data: new Uint8Array(data), executable: false, lamports: lamports(2_039_280n), programAddress: TOKEN, space: BigInt(getTokenSize()) });
|
|
358
283
|
return address;
|
|
359
284
|
};
|
|
360
285
|
const balance = (address: Address) => {
|
|
361
|
-
const account = svm.getAccount(address);
|
|
286
|
+
const account = t.svm.getAccount(address);
|
|
362
287
|
return account.exists ? getTokenDecoder().decode(account.data).amount : null;
|
|
363
288
|
};
|
|
364
289
|
|
|
365
290
|
it("escrows mint A for mint B; the taker swaps, the maker can refund", async () => {
|
|
366
|
-
const maker = await
|
|
367
|
-
const taker = await
|
|
368
|
-
svm.airdrop(maker.address, lamports(1_000_000_000n));
|
|
369
|
-
svm.airdrop(taker.address, lamports(1_000_000_000n));
|
|
291
|
+
const maker = await t.wallet();
|
|
292
|
+
const taker = await t.wallet();
|
|
370
293
|
const mintA = await mint(maker.address);
|
|
371
294
|
const mintB = await mint(taker.address);
|
|
372
|
-
const
|
|
373
|
-
const
|
|
374
|
-
const
|
|
375
|
-
const
|
|
295
|
+
const makerTokenA = await tokenAccount(maker.address, mintA, 1_000n);
|
|
296
|
+
const makerTokenB = await tokenAccount(maker.address, mintB, 0n);
|
|
297
|
+
const takerTokenA = await tokenAccount(taker.address, mintA, 0n);
|
|
298
|
+
const takerTokenB = await tokenAccount(taker.address, mintB, 500n);
|
|
376
299
|
|
|
377
|
-
const open = async (seed:
|
|
378
|
-
const
|
|
379
|
-
programAddress: programId,
|
|
380
|
-
seeds: ["offer", getAddressEncoder().encode(maker.address), new Uint8Array(new BN(seed).toArray("le", 8))],
|
|
381
|
-
});
|
|
300
|
+
const open = async (seed: bigint) => {
|
|
301
|
+
const offer = await t.pda("offer", maker, seed);
|
|
382
302
|
// The client creates the vault: an empty token account for mint A owned by the offer PDA.
|
|
383
303
|
const vault = await tokenAccount(offer, mintA, 0n);
|
|
384
|
-
|
|
385
|
-
{ address: maker.address, signer: maker, writable: true },
|
|
386
|
-
{ address: offer, writable: true },
|
|
387
|
-
{ address: makerA, writable: true },
|
|
388
|
-
{ address: vault, writable: true },
|
|
389
|
-
{ address: SYSTEM },
|
|
390
|
-
{ address: TOKEN },
|
|
391
|
-
]);
|
|
392
|
-
expect(await send(maker, [make])).toBeInstanceOf(TransactionMetadata);
|
|
304
|
+
t.ok(await t.send(maker, t.ix("make", { seed, amountA: 400n, wantB: 300n, mintB }, { maker, offer, makerTokenA, vault })));
|
|
393
305
|
return { offer, vault };
|
|
394
306
|
};
|
|
395
307
|
|
|
396
|
-
const first = await open(
|
|
308
|
+
const first = await open(1n);
|
|
397
309
|
expect(balance(first.vault)).toBe(400n);
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
{ address: maker.address, writable: true },
|
|
401
|
-
{ address: first.offer, writable: true },
|
|
402
|
-
{ address: takerA, writable: true },
|
|
403
|
-
{ address: takerB, writable: true },
|
|
404
|
-
{ address: makerB, writable: true },
|
|
405
|
-
{ address: first.vault, writable: true },
|
|
406
|
-
{ address: TOKEN },
|
|
407
|
-
]);
|
|
408
|
-
expect(await send(taker, [take])).toBeInstanceOf(TransactionMetadata);
|
|
409
|
-
expect([balance(takerA), balance(makerB), balance(first.vault)]).toEqual([400n, 300n, null]);
|
|
310
|
+
t.ok(await t.send(taker, t.ix("take", {}, { taker, maker: maker.address, offer: first.offer, takerTokenA, takerTokenB, makerTokenB, vault: first.vault })));
|
|
311
|
+
expect([balance(takerTokenA), balance(makerTokenB), balance(first.vault)]).toEqual([400n, 300n, null]);
|
|
410
312
|
|
|
411
|
-
const second = await open(
|
|
412
|
-
const refund = (
|
|
413
|
-
ix("refund", {}, [
|
|
414
|
-
{ address: signer.address, signer, writable: true },
|
|
415
|
-
{ address: second.offer, writable: true },
|
|
416
|
-
{ address: makerA, writable: true },
|
|
417
|
-
{ address: second.vault, writable: true },
|
|
418
|
-
{ address: TOKEN },
|
|
419
|
-
]);
|
|
313
|
+
const second = await open(2n);
|
|
314
|
+
const refund = (who: KeyPairSigner) => t.ix("refund", {}, { maker: who, offer: second.offer, makerTokenA, vault: second.vault });
|
|
420
315
|
// EscrowError.WrongMaker (third member, 6002): only the maker refunds.
|
|
421
|
-
expect(errorCode(await send(taker,
|
|
422
|
-
|
|
423
|
-
expect([balance(
|
|
316
|
+
expect(t.errorCode(await t.send(taker, refund(taker)))).toBe(6002);
|
|
317
|
+
t.ok(await t.send(maker, refund(maker)));
|
|
318
|
+
expect([balance(makerTokenA), await t.account("Offer", second.offer)]).toEqual([600n, null]);
|
|
424
319
|
});
|
|
425
320
|
`,
|
|
426
321
|
}),
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `typesol/testing`: test a project's program in LiteSVM (an in-process Solana runtime) without the boilerplate.
|
|
3
|
+
*
|
|
4
|
+
* const t = await testProgram("counter", import.meta.url);
|
|
5
|
+
* const alice = await t.wallet();
|
|
6
|
+
* const counter = await t.pda("counter", alice);
|
|
7
|
+
* t.ok(await t.send(alice, t.ix("initialize", {}, { authority: alice, counter })));
|
|
8
|
+
* t.ok(await t.send(alice, t.ix("increment", { by: 5n }, { authority: alice, counter })));
|
|
9
|
+
* expect((await t.account("Counter", counter))?.count).toBe(5n);
|
|
10
|
+
*
|
|
11
|
+
* Accounts are passed by name; their signer / writable flags and fixed addresses (system program, token programs…)
|
|
12
|
+
* come from the IDL, as Anchor's `.accounts({...})` does, and PDAs seeded by constants, arguments or other accounts'
|
|
13
|
+
* keys are derived when left out. Arguments take plain values: bigints or numbers for
|
|
14
|
+
* integers, address strings for Pubkeys, null for an empty Option. Decoded accounts and return data come back the
|
|
15
|
+
* same way (bigints, address strings).
|
|
16
|
+
*
|
|
17
|
+
* Needs `litesvm` and `@anchor-lang/core`, which `typesol init` projects have.
|
|
18
|
+
*/
|
|
19
|
+
import anchor, { type Idl } from "@anchor-lang/core";
|
|
20
|
+
import { type Address, type Instruction, type KeyPairSigner } from "@solana/kit";
|
|
21
|
+
import { FailedTransactionMetadata, LiteSVM, TransactionMetadata } from "litesvm";
|
|
22
|
+
declare const BorshCoder: typeof anchor.BorshCoder;
|
|
23
|
+
type BorshCoder = InstanceType<typeof BorshCoder>;
|
|
24
|
+
export type Result = TransactionMetadata | FailedTransactionMetadata;
|
|
25
|
+
/** An account for an instruction: an address, a keypair (which then signs), or null to leave an optional one out. */
|
|
26
|
+
export type AccountInput = Address | KeyPairSigner | null;
|
|
27
|
+
/**
|
|
28
|
+
* A PDA seed: text, an address (or keypair), raw bytes, or a bigint as a little-endian u64. A string that is a valid
|
|
29
|
+
* address is taken as one; pass `new TextEncoder().encode(s)` for text that happens to look like an address.
|
|
30
|
+
*/
|
|
31
|
+
export type SeedInput = string | Address | KeyPairSigner | Uint8Array | bigint;
|
|
32
|
+
export interface TestProgram {
|
|
33
|
+
svm: LiteSVM;
|
|
34
|
+
programId: Address;
|
|
35
|
+
idl: Idl;
|
|
36
|
+
coder: BorshCoder;
|
|
37
|
+
/** A new keypair funded with `sol` SOL (default 10). */
|
|
38
|
+
wallet(sol?: number): Promise<KeyPairSigner>;
|
|
39
|
+
/** The program's PDA for these seeds. */
|
|
40
|
+
pda(...seeds: SeedInput[]): Promise<Address>;
|
|
41
|
+
/** The PDA and its bump. */
|
|
42
|
+
pdaWithBump(...seeds: SeedInput[]): Promise<[Address, number]>;
|
|
43
|
+
/** An instruction of this program, accounts by name (`authority` or `authority`'s snake_case), args as plain values. */
|
|
44
|
+
ix(name: string, args?: Record<string, unknown>, accounts?: Record<string, AccountInput>, remaining?: {
|
|
45
|
+
address: Address;
|
|
46
|
+
writable?: boolean;
|
|
47
|
+
signer?: KeyPairSigner;
|
|
48
|
+
}[]): Instruction;
|
|
49
|
+
/** Send instructions in one transaction paid by `payer` (keypairs passed as accounts sign too). */
|
|
50
|
+
send(payer: KeyPairSigner, ...ixs: Instruction[]): Promise<Result>;
|
|
51
|
+
/** The successful result, or a thrown error with the program's logs. */
|
|
52
|
+
ok(res: Result): TransactionMetadata;
|
|
53
|
+
/** The custom error code of a failed transaction (6000+ for the program's error enums); throws if it succeeded. */
|
|
54
|
+
errorCode(res: Result): number;
|
|
55
|
+
/** An account of this program decoded by its IDL type, or null when it does not exist. */
|
|
56
|
+
account<T = Record<string, unknown>>(type: string, address: Address): Promise<T | null>;
|
|
57
|
+
/** A successful instruction's return data, decoded by its IDL `returns` type. */
|
|
58
|
+
returned<T = unknown>(instruction: string, res: Result): T;
|
|
59
|
+
/** Lamports of any account (0 when it does not exist). */
|
|
60
|
+
balance(address: Address): bigint;
|
|
61
|
+
}
|
|
62
|
+
/** Build `program` of the project around `from` (a file URL such as import.meta.url, or a directory) and load it. */
|
|
63
|
+
export declare function testProgram(program: string, from: string | URL): Promise<TestProgram>;
|
|
64
|
+
/** The same helpers for a program already loaded into `svm` at `programId` with this IDL. */
|
|
65
|
+
export declare function programHarness(svm: LiteSVM, programId: Address, idl: Idl): TestProgram;
|
|
66
|
+
export {};
|