@typesol/cli 0.16.0 → 0.17.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 +46 -0
- package/README.md +3 -3
- package/dist/clientgen.js +37 -0
- package/dist/compiler/analyze.d.ts +7 -1
- package/dist/compiler/analyze.js +258 -30
- package/dist/compiler/emit/base.d.ts +7 -0
- package/dist/compiler/emit/base.js +61 -1
- package/dist/compiler/emit/borsh.d.ts +13 -0
- package/dist/compiler/emit/borsh.js +232 -0
- package/dist/compiler/emit/calls.js +43 -15
- package/dist/compiler/emit/cpi.d.ts +32 -8
- package/dist/compiler/emit/cpi.js +522 -20
- package/dist/compiler/emit/expressions.js +19 -2
- package/dist/compiler/emit/function.js +34 -11
- package/dist/compiler/emit/program.js +24 -6
- package/dist/compiler/emit/shared.d.ts +12 -0
- package/dist/compiler/emit/shared.js +19 -2
- package/dist/compiler/emit/statements.js +11 -3
- package/dist/compiler/emit/storage.d.ts +4 -0
- package/dist/compiler/emit/storage.js +60 -12
- package/dist/compiler/idl.js +33 -4
- package/dist/compiler/model.d.ts +41 -2
- package/dist/compiler/model.js +8 -2
- package/dist/portgen.js +60 -4
- package/docs/errors.md +3 -2
- package/docs/language.md +10 -6
- package/docs/migrate-from-anchor.md +14 -5
- package/docs/test-your-program.md +1 -1
- package/lib/typesol.d.ts +259 -3
- package/package.json +1 -1
- package/runtime/typesol_rt.h +32 -7
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,52 @@ until 1.0, a minor version may change the language, the IDL layout or error code
|
|
|
5
5
|
|
|
6
6
|
## Unreleased
|
|
7
7
|
|
|
8
|
+
## 0.17.0 — 2026-10-28
|
|
9
|
+
|
|
10
|
+
Anchor's account bytes on request (`@layout borsh`, so a TypeSOL program can take over an Anchor program's accounts
|
|
11
|
+
without a migration), lists with a fixed capacity anywhere in account data (`FixedVec`), arrow functions and generic
|
|
12
|
+
helpers, Metaplex collection verification, Core plugins and Bubblegum V2, and a faster UTF-8 check. Existing programs
|
|
13
|
+
keep their layouts and error codes (3004 is new, raised only by `@layout borsh` accounts). Not externally audited (see
|
|
14
|
+
SECURITY.md).
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- `/** @layout borsh */` on an account struct stores it with Anchor's bytes (Borsh String / Vec / Option for
|
|
18
|
+
`FixedString` / `FixedVec` / `Option`), so a TypeSOL program can take over an Anchor program's accounts without a
|
|
19
|
+
migration; its IDL lists Anchor's own types. An instruction unpacks the account into the fixed layout when it starts
|
|
20
|
+
and packs it back when it returns (3003 for bytes that are not a valid value, 3004 when the account is too small).
|
|
21
|
+
Checked byte for byte against an Anchor twin, including switching programs in the middle of a sequence.
|
|
22
|
+
`typesol port` marks Anchor account structs that need it.
|
|
23
|
+
- `FixedVec<T, N>`: a list with room for N elements (u32 length + N slots, like `FixedString<N>`) as any account field,
|
|
24
|
+
several per struct and in nested structs, with `get` / `set` / `push` / `pop` / `clear` / `.length` / `for … of`;
|
|
25
|
+
`push` on a full one fails with 5104, `fixedVec([…])` writes a whole one. The IDL describes each as a
|
|
26
|
+
`FixedVec<N>_<T>` struct `{ len, items }`; the generated client reads and writes them as arrays. The
|
|
27
|
+
basics/favorites port uses one.
|
|
28
|
+
- Bubblegum V2: `createTreeV2`, `mintV2` (optionally into a Metaplex Core collection), `transferV2` and `burnV2`, which
|
|
29
|
+
take the leaf (root, data and creator hashes, nonce, index) and pass its Merkle proof on from the remaining accounts;
|
|
30
|
+
the Core `BubblegumV2` collection plugin. Checked against Bubblegum, mpl-account-compression, mpl-noop and Core
|
|
31
|
+
(`pnpm fetch:programs` now fetches the mpl-account-compression pair too).
|
|
32
|
+
- Metaplex Core plugins: a literal plugin list in `createAsset` / `createCollection` (Royalties, freeze / burn / transfer /
|
|
33
|
+
update delegates and their permanent forms, Attributes, Edition, AddBlocker, ImmutableMetadata, each with an optional
|
|
34
|
+
authority), and `addPlugin`, `updatePlugin`, `removePlugin` with their collection forms, checked against the real Core
|
|
35
|
+
program (a frozen asset cannot move until it is thawed).
|
|
36
|
+
- Arrow functions: `const f = (x: u64): u64 => …` (and `const f = function (…) {…}`) at the top level or inside a
|
|
37
|
+
function is a helper. Local ones capture nothing (a closure over the enclosing function's variables is refused with
|
|
38
|
+
the name it reads); functions are called, not passed around.
|
|
39
|
+
- Generic helpers: `function max<T extends bigint>(a: T, b: T): T` compiles once per set of types it is called with,
|
|
40
|
+
inferred from the arguments or written out (`max<u128>(x, 1n)`); type parameters stand for integers, booleans,
|
|
41
|
+
Pubkeys or enums.
|
|
42
|
+
- `x as T` (and `satisfies`): names a type the value already has, never a conversion; mainly `return (a + b) as T`
|
|
43
|
+
in a generic helper.
|
|
44
|
+
- Metaplex collections: `createV3`'s `isCollection: true` (CollectionDetails::V1), `createMasterEditionV3(…, maxSupply | null)`,
|
|
45
|
+
`verifyCollection(…)` (Verify with CollectionV1) and `masterEditionAddress(mint)`. The tokens/nft-operations port now
|
|
46
|
+
uses them instead of hand-encoded `invoke`s, checked against the real Token Metadata program.
|
|
47
|
+
|
|
48
|
+
### Changed
|
|
49
|
+
- IDLs no longer list `FixedString*` / `FixedOption_*` / `FixedVec*` helper types that nothing in them refers to
|
|
50
|
+
(for example an `Option` used only as an instruction argument, which is Borsh there).
|
|
51
|
+
- UTF-8 checks of strings run eight ASCII bytes at a time: every official-example instruction with a string argument
|
|
52
|
+
gets cheaper (create_token_mint 40,919 → 39,828 CU, set_favorites 4,282 → 4,195).
|
|
53
|
+
|
|
8
54
|
## 0.16.0 — 2026-10-28
|
|
9
55
|
|
|
10
56
|
Everyday TypeScript that Anchor developers reach for (`for … of`, `??`, destructuring, `**`, structs returned from
|
package/README.md
CHANGED
|
@@ -191,8 +191,8 @@ Runtime failures point back to your source: `Error 6000 Unauthorized (counter.ts
|
|
|
191
191
|
| --- | --- |
|
|
192
192
|
| **Accounts** | `Signer`, `Account<T>`, `Mut<T>`, `Init<T>`, `InitIfNeeded<T>` (PDAs and associated token accounts), `Zeroed<T>` (up to 10 MiB), optional accounts (`A \| null`), token accounts and mints, programs, sysvars |
|
|
193
193
|
| **Constraints** | `HasOne`, `Seeds` (also from account fields and under another program; `pdaBump` like `ctx.bumps`), `CloseTo`, `Ata`, `Address`, `Owner`, `Executable`. They are checked before the body runs and recorded in the IDL |
|
|
194
|
-
| **Data** | `u8`…`u128`, `i8`…`i128`, `boolean`, `Pubkey`, `Hash`, nested structs, `FixedArray`, `FixedString`, `Option`, unit enums, data enums (discriminated unions), `Vec` that resizes its account |
|
|
195
|
-
| **Control flow** | `if`, `for`, `for … of`, `while`, `switch` (with narrowing on data enums), ternaries, `??`, destructuring, helper functions (returning structs too) across multiple files |
|
|
194
|
+
| **Data** | `u8`…`u128`, `i8`…`i128`, `boolean`, `Pubkey`, `Hash`, nested structs, `FixedArray`, `FixedString`, `Option`, unit enums, data enums (discriminated unions), `Vec` that resizes its account, `FixedVec` lists anywhere, and Anchor's own byte layout (`@layout borsh`) to take over existing Anchor accounts |
|
|
195
|
+
| **Control flow** | `if`, `for`, `for … of`, `while`, `switch` (with narrowing on data enums), ternaries, `??`, destructuring, helper functions (returning structs too, arrow functions, generics) across multiple files |
|
|
196
196
|
| **CPIs** | System, SPL Token, Token-2022 (including extension reads and metadata), ATA, Metaplex Token Metadata, Core, Bubblegum. Generic `invoke` with PDA signing, return data, and typed bindings generated from any Anchor IDL (`typesol cpi`) |
|
|
197
197
|
| **Events** | `emit` (log-based) and `emitCpi` (Anchor `emit_cpi!`, through a self-CPI) |
|
|
198
198
|
| **Crypto & oracles** | `sha256`, `keccak256`, Merkle proofs, Ed25519 and secp256k1 signature checks, Pyth pull oracle, Switchboard |
|
|
@@ -306,7 +306,7 @@ Details are in [docs/testing.md](docs/testing.md).
|
|
|
306
306
|
|
|
307
307
|
## Status
|
|
308
308
|
|
|
309
|
-
**TypeSOL is pre-1.0 (0.
|
|
309
|
+
**TypeSOL is pre-1.0 (0.17).** Releases follow the organization's
|
|
310
310
|
[versioning policy](https://github.com/typesol/.github/blob/main/VERSIONING.md); [CHANGELOG.md](CHANGELOG.md) lists every
|
|
311
311
|
change.
|
|
312
312
|
- The compiler, runtime and tooling have been through internal security reviews and are covered by the tests above.
|
package/dist/clientgen.js
CHANGED
|
@@ -43,6 +43,7 @@ export const TYPESOL_ERRORS = {
|
|
|
43
43
|
3000: "AccountDiscriminatorAlreadySet",
|
|
44
44
|
3002: "AccountDiscriminatorMismatch",
|
|
45
45
|
3003: "AccountDidNotDeserialize",
|
|
46
|
+
3004: "AccountDidNotSerialize",
|
|
46
47
|
3005: "AccountNotEnoughKeys",
|
|
47
48
|
3006: "AccountNotMutable",
|
|
48
49
|
3007: "AccountOwnedByWrongProgram",
|
|
@@ -383,6 +384,8 @@ export function generateClient(idl, opts = {}) {
|
|
|
383
384
|
const arrayLen = (t) => (typeof t === "object" && t.array ? Number(t.array[1]) : NaN);
|
|
384
385
|
if (/^FixedString\d+$/.test(n) && f[0].name === "len" && Number.isInteger(arrayLen(f[1].type)))
|
|
385
386
|
return { kind: "fstring", cap: arrayLen(f[1].type) };
|
|
387
|
+
if (/^FixedVec\d+_/.test(n) && f[0].name === "len" && f[1].name === "items" && Number.isInteger(arrayLen(f[1].type)))
|
|
388
|
+
return { kind: "fvec", elem: f[1].type.array[0], cap: arrayLen(f[1].type) };
|
|
386
389
|
if (n.startsWith("FixedOption_") && f[0].name === "is_some")
|
|
387
390
|
return { kind: "foption", inner: f[1].type };
|
|
388
391
|
if (n.startsWith("FixedEnum_") && f[0].name === "tag" && defs.has(n.slice("FixedEnum_".length)) && Number.isInteger(arrayLen(f[1].type)))
|
|
@@ -476,6 +479,8 @@ export function generateClient(idl, opts = {}) {
|
|
|
476
479
|
const s = special(n);
|
|
477
480
|
if (s?.kind === "fstring")
|
|
478
481
|
return "string";
|
|
482
|
+
if (s?.kind === "fvec")
|
|
483
|
+
return `Array<${tsType(s.elem, dir)}>`;
|
|
479
484
|
if (s?.kind === "foption")
|
|
480
485
|
return `${tsType(s.inner, dir)} | null`;
|
|
481
486
|
if (s?.kind === "fenum")
|
|
@@ -484,6 +489,7 @@ export function generateClient(idl, opts = {}) {
|
|
|
484
489
|
}
|
|
485
490
|
let needsFixedString = false;
|
|
486
491
|
let needsFixedOption = false;
|
|
492
|
+
let needsFixedVec = false;
|
|
487
493
|
let needsUnitEnum = false;
|
|
488
494
|
function codec(t) {
|
|
489
495
|
if (typeof t === "string") {
|
|
@@ -525,6 +531,11 @@ export function generateClient(idl, opts = {}) {
|
|
|
525
531
|
const s = special(n);
|
|
526
532
|
if (s?.kind === "fstring")
|
|
527
533
|
return ((needsFixedString = true), `fixedString(${s.cap})`);
|
|
534
|
+
if (s?.kind === "fvec") {
|
|
535
|
+
if (idlSize(s.elem) === undefined)
|
|
536
|
+
throw new ClientIdlError(`${n}: the elements have no fixed size`);
|
|
537
|
+
return ((needsFixedVec = true), `fixedVec(${codec(s.elem)}, ${idlSize(s.elem)}, ${s.cap})`);
|
|
538
|
+
}
|
|
528
539
|
if (s?.kind === "foption") {
|
|
529
540
|
const size = idlSize(s.inner);
|
|
530
541
|
if (size === undefined)
|
|
@@ -560,6 +571,10 @@ export function generateClient(idl, opts = {}) {
|
|
|
560
571
|
const sp = special(n);
|
|
561
572
|
if (sp?.kind === "fstring")
|
|
562
573
|
return 4 + sp.cap;
|
|
574
|
+
if (sp?.kind === "fvec") {
|
|
575
|
+
const e = idlSize(sp.elem);
|
|
576
|
+
return e === undefined ? undefined : 4 + e * sp.cap;
|
|
577
|
+
}
|
|
563
578
|
if (sp?.kind === "fenum")
|
|
564
579
|
return sp.size;
|
|
565
580
|
if (sp?.kind === "foption") {
|
|
@@ -982,6 +997,28 @@ export function generateClient(idl, opts = {}) {
|
|
|
982
997
|
` });`,
|
|
983
998
|
],
|
|
984
999
|
});
|
|
1000
|
+
if (needsFixedVec)
|
|
1001
|
+
helperDefs.push({
|
|
1002
|
+
name: "fixedVec",
|
|
1003
|
+
lines: [
|
|
1004
|
+
`/** TypeSOL FixedVec<T, N> in account data: u32 length, then room for N elements (unused ones zero). */`,
|
|
1005
|
+
`const fixedVec = <From, To extends From>(item: Codec<From, To>, size: number, cap: number): Codec<From[], To[]> =>`,
|
|
1006
|
+
` createCodec<From[], To[]>({`,
|
|
1007
|
+
` fixedSize: 4 + size * cap,`,
|
|
1008
|
+
` write: (value, bytes, offset) => {`,
|
|
1009
|
+
` if (value.length > cap) throw new Error(value.length + " elements; the field holds " + cap);`,
|
|
1010
|
+
` bytes.fill(0, offset, offset + 4 + size * cap);`,
|
|
1011
|
+
` getU32Codec().write(value.length, bytes, offset);`,
|
|
1012
|
+
` value.forEach((v, i) => item.write(v, bytes, offset + 4 + i * size));`,
|
|
1013
|
+
` return offset + 4 + size * cap;`,
|
|
1014
|
+
` },`,
|
|
1015
|
+
` read: (bytes, offset) => {`,
|
|
1016
|
+
` const n = Math.min(getU32Codec().read(bytes, offset)[0], cap);`,
|
|
1017
|
+
` return [Array.from({ length: n }, (_, i) => item.read(bytes, offset + 4 + i * size)[0]), offset + 4 + size * cap];`,
|
|
1018
|
+
` },`,
|
|
1019
|
+
` });`,
|
|
1020
|
+
],
|
|
1021
|
+
});
|
|
985
1022
|
if (needsUnitEnum)
|
|
986
1023
|
helperDefs.push({
|
|
987
1024
|
name: "unitEnum",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
2
|
import { Reporter } from "./diagnostics.js";
|
|
3
|
-
import { type DataEnumDef, type EnumDef, type ProgramModel, type ScalarType, type StructDef, type ValueType, isScalar } from "./model.js";
|
|
3
|
+
import { type DataEnumDef, type EnumDef, type GenericHelper, type Helper, type ProgramModel, type ScalarType, type StructDef, type ValueType, isScalar } from "./model.js";
|
|
4
4
|
export declare const LIB_PATH: string;
|
|
5
5
|
/** Why a name cannot be used as a C member, or undefined when it can. */
|
|
6
6
|
export declare function cNameProblem(name: string): string | undefined;
|
|
@@ -11,6 +11,8 @@ export declare const MAX_RETURN_DATA = 1024;
|
|
|
11
11
|
export declare const MAX_CPI_ALLOCATION: number;
|
|
12
12
|
export interface Analysis {
|
|
13
13
|
model: ProgramModel;
|
|
14
|
+
/** A generic helper's instance for these type arguments (analyzed once, then kept in `model.helpers`). */
|
|
15
|
+
instantiate(g: GenericHelper, typeArgs: ScalarType[]): Helper;
|
|
14
16
|
/** TypeScript's checker: used for narrowing (which variant a data-enum value is in a branch). */
|
|
15
17
|
checker: ts.TypeChecker;
|
|
16
18
|
sourceFile: ts.SourceFile;
|
|
@@ -23,6 +25,8 @@ export interface Analysis {
|
|
|
23
25
|
/** What type resolution needs: imports, unit enums, and where to record Option<T> uses. */
|
|
24
26
|
export interface TypeScope {
|
|
25
27
|
imports: Map<string, string>;
|
|
28
|
+
/** Inside a generic helper's instance: type parameter -> the type it stands for. */
|
|
29
|
+
typeArgs?: Map<string, ScalarType>;
|
|
26
30
|
enums: Map<string, EnumDef>;
|
|
27
31
|
dataEnums?: Map<string, DataEnumDef>;
|
|
28
32
|
options: Map<string, ScalarType | {
|
|
@@ -49,4 +53,6 @@ export declare function resolveOptionType(node: ts.TypeNode, scope: TypeScope):
|
|
|
49
53
|
}) | undefined;
|
|
50
54
|
/** Values a local, argument or helper parameter can hold. */
|
|
51
55
|
export declare function resolveValueType(node: ts.TypeNode, scope: TypeScope, allowVec?: boolean): ValueType | undefined;
|
|
56
|
+
/** The helper (or generic) a call's callee names: a local function by its declaration, others by name. */
|
|
57
|
+
export declare function calleeName(callee: ts.Identifier, decls: Map<ts.Node, string>, checker: ts.TypeChecker): string | undefined;
|
|
52
58
|
export { isScalar };
|