@typesol/cli 0.13.0 → 0.14.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 +14 -0
- package/README.md +7 -6
- package/dist/compiler/emit/calls.js +1 -1
- package/dist/compiler/emit/function.js +1 -1
- package/package.json +1 -1
- package/runtime/typesol_rt.h +57 -8
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ 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.14.0 — 2026-10-27
|
|
9
|
+
|
|
10
|
+
PDAs are derived without the PDA syscalls, so instructions that create, check or search for PDAs use up to a few
|
|
11
|
+
thousand fewer compute units. No language, IDL or error-code changes. Not externally audited (see SECURITY.md).
|
|
12
|
+
|
|
13
|
+
### Performance
|
|
14
|
+
- PDAs are derived with the `sol_sha256` and `sol_curve_validate_point` syscalls instead of
|
|
15
|
+
`sol_try_find_program_address` / `sol_create_program_address` (1,500 CU per bump tried): the
|
|
16
|
+
same addresses, bumps and limits (16 seeds to check, 15 to search, 32 bytes each). `init` with seeds, `Seeds<…>`,
|
|
17
|
+
`create`, `signer` and `findProgramAddress` all use it. Benchmarks: counter.initialize 3,030 → 1,872 CU,
|
|
18
|
+
escrow.make 5,974 → 3,638; in the official examples escrow take_offer 14,519 → 7,117 and close_user 1,728 → 586.
|
|
19
|
+
Binaries grow 0.4–1.2 KB. New tests compare it with @solana/kit's derivation over random seeds, canonical bumps
|
|
20
|
+
below 255 and on-curve bumps. Needs the curve25519 syscalls (active on mainnet-beta since epoch 637).
|
|
21
|
+
|
|
8
22
|
## 0.13.0 — 2026-10-27
|
|
9
23
|
|
|
10
24
|
Every program in solana-developers/program-examples now ports to TypeSOL: PDA seeds from account fields, remaining
|
package/README.md
CHANGED
|
@@ -227,19 +227,20 @@ shows mean compute units per call (generated by `scripts/readme-bench.ts` from t
|
|
|
227
227
|
<!-- bench:table -->
|
|
228
228
|
| Instruction | TypeSOL | Pinocchio 0.10 | Native Rust | Anchor 1.0.2 |
|
|
229
229
|
| --- | ---: | ---: | ---: | ---: |
|
|
230
|
-
| counter.initialize | **
|
|
230
|
+
| counter.initialize | **1,872** | 3,186 | 4,601 | 6,573 |
|
|
231
231
|
| counter.increment | **110** | 113 | 857 | 2,323 |
|
|
232
|
-
| vault.open | **
|
|
232
|
+
| vault.open | **1,891** | 3,185 | 4,613 | 6,627 |
|
|
233
233
|
| vault.deposit | **1,314** | 1,377 | 2,832 | 4,355 |
|
|
234
234
|
| vault.withdraw | **315** | 370 | 1,176 | 3,628 |
|
|
235
235
|
| vault.shut | **161** | 167 | 950 | 2,024 |
|
|
236
|
-
| escrow.make | **
|
|
236
|
+
| escrow.make | **3,638** | 6,179 | 9,240 | 13,304 |
|
|
237
237
|
| escrow.take | **4,035** | 4,051 | 8,500 | 14,365 |
|
|
238
238
|
| escrow.refund | **2,716** | 2,769 | 5,695 | 8,449 |
|
|
239
|
-
| binary, counter / vault / escrow |
|
|
239
|
+
| binary, counter / vault / escrow | 13.2 / 16.5 / 24.0 KB | 19.6 / 22.9 / 29.4 KB | 27.9 / 35.0 / 46.5 KB | 145.2 / 158.9 / 190.7 KB |
|
|
240
240
|
<!-- /bench:table -->
|
|
241
241
|
|
|
242
|
-
`initialize`, `open` and `make` include the PDA bump search
|
|
242
|
+
`initialize`, `open` and `make` include the PDA bump search. TypeSOL hashes PDA candidates with the sha256 and
|
|
243
|
+
curve-check syscalls instead of the 1,500-CU PDA syscalls the others call, with the same results. The
|
|
243
244
|
escrow moves SPL tokens through CPIs (`take` makes three). Like Pinocchio, TypeSOL reads accounts in place and builds
|
|
244
245
|
CPI account records only for the accounts a call lists. It uses fewer compute units than Pinocchio on every
|
|
245
246
|
instruction here, while also refusing duplicate writable accounts as Anchor does. Fewer compute
|
|
@@ -275,7 +276,7 @@ Details are in [docs/testing.md](docs/testing.md).
|
|
|
275
276
|
|
|
276
277
|
## Status
|
|
277
278
|
|
|
278
|
-
**TypeSOL is pre-1.0 (0.
|
|
279
|
+
**TypeSOL is pre-1.0 (0.14).** Releases follow the organization's
|
|
279
280
|
[versioning policy](https://github.com/typesol/.github/blob/main/VERSIONING.md); [CHANGELOG.md](CHANGELOG.md) lists every
|
|
280
281
|
change.
|
|
281
282
|
- The compiler, runtime and tooling have been through internal security reviews and are covered by the tests above.
|
|
@@ -361,7 +361,7 @@ export class CallEmitter extends ExpressionEmitter {
|
|
|
361
361
|
const t = this.tmp("pda");
|
|
362
362
|
const b = this.tmp("bump");
|
|
363
363
|
this.line(`SolPubkey ${t}; uint8_t ${b};`);
|
|
364
|
-
this.line(`if (
|
|
364
|
+
this.line(`if (ts_find_pda(${seeds.name}, ${seeds.count}, ${program.c}, &${t}, &${b}) != SUCCESS) TS_FAIL(TS_ERR_SEEDS, "TypeSOL: no program address for these seeds");`);
|
|
365
365
|
return { c: `(&${t})`, type: PUBKEY };
|
|
366
366
|
}
|
|
367
367
|
case "metadataAddress": {
|
|
@@ -444,7 +444,7 @@ export class FunctionEmitter extends StatementEmitter {
|
|
|
444
444
|
else {
|
|
445
445
|
bumpDecls.push(` uint8_t sb_${a.name}; (void)sb_${a.name};`);
|
|
446
446
|
out.push(` { SolPubkey pda_${a.name};`);
|
|
447
|
-
out.push(` if (
|
|
447
|
+
out.push(` if (ts_find_pda(${seeds}, ${parts.length}, ${program}, &pda_${a.name}, &sb_${a.name}) != SUCCESS || !ts_key_eq(&pda_${a.name}, &a_${a.name}->key)) TS_FAIL(TS_ERR_SEEDS, "TypeSOL: ${a.name} is not the expected PDA"); }`);
|
|
448
448
|
this.bumps.set(a.name, `sb_${a.name}`);
|
|
449
449
|
}
|
|
450
450
|
}
|
package/package.json
CHANGED
package/runtime/typesol_rt.h
CHANGED
|
@@ -305,6 +305,55 @@ static inline __attribute__((always_inline)) uint64_t ts_invoke(TsParams *params
|
|
|
305
305
|
return sol_invoke_signed(ix, infos, ix->account_len, signers, nsigners);
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
+
/*
|
|
309
|
+
* ---- Program derived addresses ------------------------------------------------
|
|
310
|
+
* The same addresses sol_create_program_address / sol_try_find_program_address compute — sha256 of the seeds, the
|
|
311
|
+
* program id and "ProgramDerivedAddress", valid only off the ed25519 curve — from the sha256 and curve-validation
|
|
312
|
+
* syscalls, which cost a fraction of the 1500 compute units the PDA syscalls charge per attempt. The seed limits are the
|
|
313
|
+
* syscalls' (at most 16 seeds of at most 32 bytes, the bump included), and so is the result for every input.
|
|
314
|
+
*/
|
|
315
|
+
uint64_t sol_curve_validate_point(uint64_t curve_id, const uint8_t *point, uint8_t *result);
|
|
316
|
+
#define TS_MAX_PDA_SEEDS 16
|
|
317
|
+
|
|
318
|
+
/* The address of `seeds` (and `bump`, when given) under `program`; false when it lies on the curve (no PDA). */
|
|
319
|
+
static inline bool ts_pda_hash(const SolSignerSeed *seeds, int n, const uint8_t *bump, const SolPubkey *program, SolPubkey *out) {
|
|
320
|
+
SolBytes parts[TS_MAX_PDA_SEEDS + 2];
|
|
321
|
+
int k = 0;
|
|
322
|
+
for (int i = 0; i < n; i++) parts[k++] = (SolBytes){seeds[i].addr, seeds[i].len};
|
|
323
|
+
if (bump) parts[k++] = (SolBytes){bump, 1};
|
|
324
|
+
parts[k++] = (SolBytes){program->x, 32};
|
|
325
|
+
parts[k++] = (SolBytes){(const uint8_t *)"ProgramDerivedAddress", 21};
|
|
326
|
+
sol_sha256(parts, k, out->x);
|
|
327
|
+
uint8_t ignored;
|
|
328
|
+
return sol_curve_validate_point(0 /* CURVE25519_EDWARDS */, out->x, &ignored) != 0; /* 0: a valid curve point */
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
static inline bool ts_pda_seeds_ok(const SolSignerSeed *seeds, int n, int max) {
|
|
332
|
+
if (n > max) return false;
|
|
333
|
+
for (int i = 0; i < n; i++)
|
|
334
|
+
if (seeds[i].len > 32) return false;
|
|
335
|
+
return true;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/* sol_create_program_address: the bump is the last of `seeds`. */
|
|
339
|
+
static inline uint64_t ts_create_pda_address(const SolSignerSeed *seeds, int n, const SolPubkey *program, SolPubkey *out) {
|
|
340
|
+
if (!ts_pda_seeds_ok(seeds, n, TS_MAX_PDA_SEEDS) || !ts_pda_hash(seeds, n, NULL, program, out)) return 1; /* not SUCCESS: the PDA syscalls fail the same inputs */
|
|
341
|
+
return SUCCESS;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/* sol_try_find_program_address: the canonical (highest) bump. */
|
|
345
|
+
static inline uint64_t ts_find_pda(const SolSignerSeed *seeds, int n, const SolPubkey *program, SolPubkey *out, uint8_t *bump_out) {
|
|
346
|
+
if (!ts_pda_seeds_ok(seeds, n, TS_MAX_PDA_SEEDS - 1)) return 1; /* not SUCCESS: the PDA syscalls fail the same inputs */
|
|
347
|
+
for (int b = 255; b >= 0; b--) {
|
|
348
|
+
uint8_t bump = (uint8_t)b;
|
|
349
|
+
if (ts_pda_hash(seeds, n, &bump, program, out)) {
|
|
350
|
+
*bump_out = bump;
|
|
351
|
+
return SUCCESS;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return 1; /* not SUCCESS: the PDA syscalls fail the same inputs */
|
|
355
|
+
}
|
|
356
|
+
|
|
308
357
|
/* ---- Account checks ------------------------------------------------------ */
|
|
309
358
|
/* Copy between two places of the same type: distinct, or the very same place (then nothing to do). */
|
|
310
359
|
static inline void ts_copy(void *dst, const void *src, uint64_t n) {
|
|
@@ -461,7 +510,7 @@ static inline uint64_t ts_check_program_data(const TsAccount *a, const SolPubkey
|
|
|
461
510
|
SolSignerSeed seed = {(const uint8_t *)program_id, 32};
|
|
462
511
|
SolPubkey expected;
|
|
463
512
|
uint8_t bump;
|
|
464
|
-
if (
|
|
513
|
+
if (ts_find_pda(&seed, 1, &TS_BPF_UPGRADEABLE_LOADER_ID, &expected, &bump) != SUCCESS || !ts_key_eq(&expected, &a->key))
|
|
465
514
|
TS_FAIL(TS_ERR_ADDRESS, "TypeSOL: not this program's ProgramData account");
|
|
466
515
|
return SUCCESS;
|
|
467
516
|
}
|
|
@@ -471,7 +520,7 @@ static const uint8_t TS_EVENT_IX_TAG[8] = {0xe4, 0x45, 0xa5, 0x2e, 0x51, 0xcb, 0
|
|
|
471
520
|
static inline uint64_t ts_check_event_authority(const TsAccount *a, const SolPubkey *program_id, uint8_t *bump) {
|
|
472
521
|
SolSignerSeed seed = {(const uint8_t *)"__event_authority", 17};
|
|
473
522
|
SolPubkey expected;
|
|
474
|
-
if (
|
|
523
|
+
if (ts_find_pda(&seed, 1, program_id, &expected, bump) != SUCCESS || !ts_key_eq(&expected, &a->key))
|
|
475
524
|
TS_FAIL(TS_ERR_SEEDS, "TypeSOL: not this program's event authority");
|
|
476
525
|
return SUCCESS;
|
|
477
526
|
}
|
|
@@ -510,7 +559,7 @@ static inline uint64_t ts_init_zeroed(TsAccount *a, const SolPubkey *program_id,
|
|
|
510
559
|
static inline uint64_t ts_require_pda(const TsAccount *a, const SolSignerSeed *seeds, int n,
|
|
511
560
|
const SolPubkey *program_id) {
|
|
512
561
|
SolPubkey derived;
|
|
513
|
-
if (
|
|
562
|
+
if (ts_create_pda_address(seeds, n, program_id, &derived) != SUCCESS || !ts_key_eq(&derived, &a->key))
|
|
514
563
|
TS_FAIL(TS_ERR_SEEDS, "TypeSOL: PDA seeds do not match account");
|
|
515
564
|
return SUCCESS;
|
|
516
565
|
}
|
|
@@ -536,7 +585,7 @@ static inline uint64_t ts_create_pda(TsParams *params, TsAccount *payer, TsAccou
|
|
|
536
585
|
uint8_t *bump_out) {
|
|
537
586
|
SolPubkey derived;
|
|
538
587
|
uint8_t bump;
|
|
539
|
-
if (
|
|
588
|
+
if (ts_find_pda(seeds, n, params->program_id, &derived, &bump) != SUCCESS ||
|
|
540
589
|
!ts_key_eq(&derived, &acct->key))
|
|
541
590
|
TS_FAIL(TS_ERR_SEEDS, "TypeSOL: PDA seeds do not match account");
|
|
542
591
|
if (acct->data_len != 0 || !ts_key_eq(&acct->owner, &TS_SYSTEM_PROGRAM_ID))
|
|
@@ -627,7 +676,7 @@ static inline uint64_t ts_create_pda_if_needed(TsParams *params, TsAccount *paye
|
|
|
627
676
|
if (ts_key_eq(&acct->owner, params->program_id)) {
|
|
628
677
|
SolPubkey derived;
|
|
629
678
|
uint8_t bump;
|
|
630
|
-
if (
|
|
679
|
+
if (ts_find_pda(seeds, n, params->program_id, &derived, &bump) != SUCCESS ||
|
|
631
680
|
!ts_key_eq(&derived, &acct->key))
|
|
632
681
|
TS_FAIL(TS_ERR_SEEDS, "TypeSOL: PDA seeds do not match account");
|
|
633
682
|
if (acct->data_len < space) TS_FAIL(TS_ERR_ACCOUNT_TOO_SMALL, "TypeSOL: account data too small");
|
|
@@ -1105,7 +1154,7 @@ static const SolPubkey TS_MPL_METADATA_ID = {{11,112,101,177,227,209,124,69,56,1
|
|
|
1105
1154
|
static inline uint64_t ts_ata_address(const SolPubkey *owner, const SolPubkey *mint, const SolPubkey *token_program, SolPubkey *out) {
|
|
1106
1155
|
SolSignerSeed seeds[3] = {{(const uint8_t *)owner, 32}, {(const uint8_t *)token_program, 32}, {(const uint8_t *)mint, 32}};
|
|
1107
1156
|
uint8_t bump;
|
|
1108
|
-
if (
|
|
1157
|
+
if (ts_find_pda(seeds, 3, &TS_ATA_PROGRAM_ID, out, &bump) != SUCCESS)
|
|
1109
1158
|
TS_FAIL(TS_ERR_SEEDS, "TypeSOL: cannot derive the associated token address");
|
|
1110
1159
|
return SUCCESS;
|
|
1111
1160
|
}
|
|
@@ -1155,7 +1204,7 @@ static inline uint64_t ts_t22_init_metadata(TsParams *params, const TsAccount *p
|
|
|
1155
1204
|
static inline uint64_t ts_metadata_address(const SolPubkey *mint, SolPubkey *out) {
|
|
1156
1205
|
SolSignerSeed seeds[3] = {{(const uint8_t *)"metadata", 8}, {(const uint8_t *)&TS_MPL_METADATA_ID, 32}, {(const uint8_t *)mint, 32}};
|
|
1157
1206
|
uint8_t bump;
|
|
1158
|
-
if (
|
|
1207
|
+
if (ts_find_pda(seeds, 3, &TS_MPL_METADATA_ID, out, &bump) != SUCCESS)
|
|
1159
1208
|
TS_FAIL(TS_ERR_SEEDS, "TypeSOL: cannot derive the metadata address");
|
|
1160
1209
|
return SUCCESS;
|
|
1161
1210
|
}
|
|
@@ -1225,7 +1274,7 @@ static inline uint64_t ts_update_extra_metas(TsParams *params, TsAccount *payer,
|
|
|
1225
1274
|
SolSignerSeed seeds[2] = {{(const uint8_t *)"extra-account-metas", 19}, {(const uint8_t *)mint, 32}};
|
|
1226
1275
|
SolPubkey pda;
|
|
1227
1276
|
uint8_t bump;
|
|
1228
|
-
if (
|
|
1277
|
+
if (ts_find_pda(seeds, 2, params->program_id, &pda, &bump) != SUCCESS || !ts_key_eq(&pda, &acct->key))
|
|
1229
1278
|
TS_FAIL(TS_ERR_SEEDS, "TypeSOL: not the mint's extra-account-metas PDA");
|
|
1230
1279
|
if (!ts_key_eq(&acct->owner, params->program_id)) TS_FAIL(TS_ERR_WRONG_OWNER, "TypeSOL: the extra-account-metas account is not this program's");
|
|
1231
1280
|
if (acct->data_len < 16 || ts_ld64(acct->data) != ts_ld64(TS_TRANSFER_HOOK_EXECUTE_DISC))
|