@typesol/cli 0.24.0 → 0.25.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 -2
- package/README.md +10 -7
- package/dist/clientgen.js +36 -21
- package/dist/compiler/analyze.js +32 -2
- package/dist/compiler/emit/base.d.ts +10 -0
- package/dist/compiler/emit/base.js +8 -0
- package/dist/compiler/emit/calls.js +10 -10
- package/dist/compiler/emit/cpi.d.ts +10 -1
- package/dist/compiler/emit/cpi.js +182 -166
- package/dist/compiler/emit/function.d.ts +9 -0
- package/dist/compiler/emit/function.js +169 -139
- package/dist/compiler/emit/program.js +3 -0
- package/dist/compiler/emit/shared.d.ts +1 -0
- package/dist/compiler/emit/shared.js +1 -0
- package/dist/compiler/emit/statements.d.ts +6 -0
- package/dist/compiler/emit/statements.js +35 -5
- package/docs/architecture.md +26 -2
- package/docs/case-study-escrow.md +224 -0
- package/docs/errors.md +26 -1
- package/docs/language.md +12 -8
- package/docs/migrate-from-anchor.md +3 -1
- package/docs/runtime.md +59 -0
- package/lib/typesol.d.ts +35 -13
- package/package.json +1 -1
- package/runtime/typesol_rt.h +85 -14
package/runtime/typesol_rt.h
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
#define TS_ERR_INSTRUCTION_NOT_FOUND 101 /* InstructionFallbackNotFound */
|
|
19
19
|
#define TS_ERR_INSTRUCTION_DATA 102 /* InstructionDidNotDeserialize */
|
|
20
20
|
#define TS_ERR_NOT_WRITABLE 3006 /* AccountNotMutable */
|
|
21
|
+
#define TS_ERR_CONSTRAINT_MUT 2000 /* ConstraintMut */
|
|
21
22
|
#define TS_ERR_CONSTRAINT 2001 /* ConstraintHasOne */
|
|
22
23
|
#define TS_ERR_NOT_RENT_EXEMPT 2005 /* ConstraintRentExempt */
|
|
23
24
|
#define TS_ERR_CONSTRAINT_SIGNER 2002 /* ConstraintSigner */
|
|
@@ -31,6 +32,8 @@
|
|
|
31
32
|
#define TS_ERR_MINT_AUTHORITY 2016 /* ConstraintMintMintAuthority */
|
|
32
33
|
#define TS_ERR_FREEZE_AUTHORITY 2017 /* ConstraintMintFreezeAuthority */
|
|
33
34
|
#define TS_ERR_MINT_DECIMALS 2018 /* ConstraintMintDecimals */
|
|
35
|
+
#define TS_ERR_TOKEN_TOKEN_PROGRAM 2021 /* ConstraintTokenTokenProgram */
|
|
36
|
+
#define TS_ERR_MINT_TOKEN_PROGRAM 2022 /* ConstraintMintTokenProgram */
|
|
34
37
|
#define TS_ERR_DUPLICATE_MUT 2040 /* ConstraintDuplicateMutableAccount */
|
|
35
38
|
#define TS_ERR_ALREADY_INITIALIZED 3000 /* AccountDiscriminatorAlreadySet */
|
|
36
39
|
#define TS_ERR_DISCRIMINATOR 3002 /* AccountDiscriminatorMismatch */
|
|
@@ -62,11 +65,12 @@
|
|
|
62
65
|
#define TS_ERR_NO_RETURN 5114
|
|
63
66
|
#define TS_ERR_ACCOUNT_MISSING 5115
|
|
64
67
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
/* Every failure logs through this one sol_log_ call: each syscall call site costs a 16-byte relocation in the binary. */
|
|
69
|
+
static __attribute__((noinline)) uint64_t ts_fail(uint64_t code, const char *msg, uint64_t len) {
|
|
70
|
+
sol_log_(msg, len);
|
|
71
|
+
return code;
|
|
72
|
+
}
|
|
73
|
+
#define TS_FAIL(code, msg) return ts_fail((code), (msg), sol_strlen(msg))
|
|
70
74
|
|
|
71
75
|
#define TS_TRY(expr) \
|
|
72
76
|
do { \
|
|
@@ -274,6 +278,7 @@ typedef struct {
|
|
|
274
278
|
const SolPubkey *program_id;
|
|
275
279
|
TsAccount *ka[TS_MAX_ACCOUNTS]; /* ka_num entries */
|
|
276
280
|
SolAccountInfo infos[TS_MAX_ACCOUNTS]; /* scratch for CPI account lists (ts_invoke) */
|
|
281
|
+
uint64_t loaded_mut; /* bit i: ka[i] loaded with loadAccountMut (zeroed by the entrypoint of a program that uses it) */
|
|
277
282
|
} TsParams;
|
|
278
283
|
|
|
279
284
|
/*
|
|
@@ -409,6 +414,12 @@ static inline uint64_t ts_check_writable(const TsAccount *a) {
|
|
|
409
414
|
if (!a->is_writable) TS_FAIL(TS_ERR_NOT_WRITABLE, "TypeSOL: account must be writable");
|
|
410
415
|
return SUCCESS;
|
|
411
416
|
}
|
|
417
|
+
/* A Mut<…> parameter passed read-only: Anchor's `#[account(mut)]` (ConstraintMut). ts_check_writable is for accounts
|
|
418
|
+
the program makes writable at runtime (remaining accounts, CPI metas). */
|
|
419
|
+
static inline uint64_t ts_check_mut(const TsAccount *a) {
|
|
420
|
+
if (!a->is_writable) TS_FAIL(TS_ERR_CONSTRAINT_MUT, "TypeSOL: account must be writable (mut)");
|
|
421
|
+
return SUCCESS;
|
|
422
|
+
}
|
|
412
423
|
|
|
413
424
|
static inline uint64_t ts_check_program_account(const TsAccount *a, const SolPubkey *program_id,
|
|
414
425
|
const uint8_t disc[8], uint64_t size) {
|
|
@@ -427,7 +438,7 @@ static inline uint64_t ts_check_vec_len(const TsAccount *a, uint64_t fixed, uint
|
|
|
427
438
|
return SUCCESS;
|
|
428
439
|
}
|
|
429
440
|
|
|
430
|
-
/* No other account in the instruction may be the same account (
|
|
441
|
+
/* No other account in the instruction may be the same account (remaining accounts written in place). */
|
|
431
442
|
static inline uint64_t ts_check_unaliased(const TsParams *params, const TsAccount *a) {
|
|
432
443
|
/* The loader passes each account once and marks repeats as duplicates, which share its record. */
|
|
433
444
|
uint64_t seen = 0;
|
|
@@ -436,6 +447,18 @@ static inline uint64_t ts_check_unaliased(const TsParams *params, const TsAccoun
|
|
|
436
447
|
return SUCCESS;
|
|
437
448
|
}
|
|
438
449
|
|
|
450
|
+
/* loadAccountMut: unaliased, and not already loaded mutably in this instruction. Two loads of the same slot would be
|
|
451
|
+
two writable views of one account, as two slots naming it would (read both, write both: the classic drain). */
|
|
452
|
+
static inline uint64_t ts_claim_mut(TsParams *params, const TsAccount *a) {
|
|
453
|
+
uint64_t seen = 0, at = 0;
|
|
454
|
+
for (uint64_t i = 0; i < params->ka_num; i++)
|
|
455
|
+
if (params->ka[i] == a) seen++, at = i;
|
|
456
|
+
if (seen > 1) TS_FAIL(TS_ERR_DUPLICATE_MUT, "TypeSOL: account passed more than once");
|
|
457
|
+
if (params->loaded_mut >> at & 1) TS_FAIL(TS_ERR_DUPLICATE_MUT, "TypeSOL: account already loaded with loadAccountMut");
|
|
458
|
+
params->loaded_mut |= (uint64_t)1 << at;
|
|
459
|
+
return SUCCESS;
|
|
460
|
+
}
|
|
461
|
+
|
|
439
462
|
static inline uint64_t ts_check_address(const TsAccount *a, const SolPubkey *expected) {
|
|
440
463
|
if (!ts_key_eq(&a->key, expected)) TS_FAIL(TS_ERR_WRONG_PROGRAM, "TypeSOL: unexpected program address");
|
|
441
464
|
return SUCCESS;
|
|
@@ -503,6 +526,49 @@ static inline void ts_write_le(uint8_t *p, uint64_t v, int n) { ts_memcpy(p, &v,
|
|
|
503
526
|
uint64_t sol_get_rent_sysvar(void *ret);
|
|
504
527
|
uint64_t sol_get_clock_sysvar(void *ret);
|
|
505
528
|
|
|
529
|
+
/* The general case, in integers: exactly (uint64_t)((double)per_year * threshold) as soft-float computes it (the
|
|
530
|
+
conversion and the product rounded to nearest, ties to even; NaN, negative and < 1 give 0), or UINT64_MAX when that
|
|
531
|
+
product is >= 2^64 (no double below 2^64 converts to UINT64_MAX: the largest is 2^64 - 2048). No soft-float routine
|
|
532
|
+
is linked, which keeps about 1.7 KB out of every program that creates accounts. */
|
|
533
|
+
static __attribute__((noinline, minsize)) uint64_t ts_rent_slow(uint64_t per_year, uint64_t tbits) {
|
|
534
|
+
uint64_t e = (tbits >> 52) & 0x7ff, f = tbits & 0xfffffffffffffULL;
|
|
535
|
+
if (e == 0x7ff) return !f && per_year && !(tbits >> 63) ? UINT64_MAX : 0; /* +inf overflows; NaN, inf * 0, -inf: 0 */
|
|
536
|
+
if ((tbits >> 63) || !per_year || (!e && !f)) return 0; /* a negative or zero product */
|
|
537
|
+
/* Round v * 2^ex (with a sticky bit for bits already dropped) to 53 bits, nearest-even, twice: per_year to a double,
|
|
538
|
+
then the exact product of the two significands. */
|
|
539
|
+
uint64_t v = per_year, sticky = 0, mt = e ? f | (1ULL << 52) : f;
|
|
540
|
+
int64_t ex = (e ? (int64_t)e : 1) - 1075;
|
|
541
|
+
for (int pass = 0; pass < 2; pass++) {
|
|
542
|
+
int top = 63;
|
|
543
|
+
while (!(v >> top)) top--;
|
|
544
|
+
if (top > 52) {
|
|
545
|
+
int sh = top - 52;
|
|
546
|
+
uint64_t rem = v & ((1ULL << sh) - 1), half = 1ULL << (sh - 1);
|
|
547
|
+
v >>= sh;
|
|
548
|
+
ex += sh;
|
|
549
|
+
if (rem > half || (rem == half && (sticky || (v & 1)))) v++;
|
|
550
|
+
if (v >> 53) v >>= 1, ex++;
|
|
551
|
+
}
|
|
552
|
+
if (pass) break;
|
|
553
|
+
/* v (< 2^53) * mt (< 2^53) as hi:lo, folded to 64 bits with the dropped low bits as the sticky bit. */
|
|
554
|
+
uint64_t a0 = v & 0xffffffff, a1 = v >> 32, b0 = mt & 0xffffffff, b1 = mt >> 32;
|
|
555
|
+
uint64_t lo = a0 * b0, mid = a0 * b1 + a1 * b0, hi = a1 * b1;
|
|
556
|
+
uint64_t lo2 = lo + (mid << 32);
|
|
557
|
+
hi += (mid >> 32) + (lo2 < lo);
|
|
558
|
+
lo = lo2;
|
|
559
|
+
int s = 0;
|
|
560
|
+
while (hi >> s) s++;
|
|
561
|
+
v = s ? (hi << (64 - s)) | (lo >> s) : lo;
|
|
562
|
+
sticky = s ? (lo & ((1ULL << s) - 1)) != 0 : 0;
|
|
563
|
+
ex += s;
|
|
564
|
+
}
|
|
565
|
+
int top = 63;
|
|
566
|
+
while (!(v >> top)) top--;
|
|
567
|
+
if (top + ex >= 64) return UINT64_MAX;
|
|
568
|
+
if (ex >= 0) return v << ex;
|
|
569
|
+
return ex > -64 ? v >> -ex : 0;
|
|
570
|
+
}
|
|
571
|
+
|
|
506
572
|
/* The minimum balance of `data_len` bytes under `rent`, the Rent sysvar
|
|
507
573
|
{ lamports_per_byte_year: u64, exemption_threshold: f64, burn_percent: u8 }. */
|
|
508
574
|
static inline uint64_t ts_minimum_balance_of(const uint64_t rent[3], uint64_t data_len, uint64_t *out) {
|
|
@@ -520,11 +586,9 @@ static inline uint64_t ts_minimum_balance_of(const uint64_t rent[3], uint64_t da
|
|
|
520
586
|
return SUCCESS;
|
|
521
587
|
}
|
|
522
588
|
}
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
if (lamports >= 18446744073709551615.0) TS_FAIL(TS_ERR_OVERFLOW, "TypeSOL: arithmetic overflow");
|
|
527
|
-
*out = (uint64_t)lamports;
|
|
589
|
+
uint64_t lamports = ts_rent_slow(per_year, rent[1]);
|
|
590
|
+
if (lamports == UINT64_MAX) TS_FAIL(TS_ERR_OVERFLOW, "TypeSOL: arithmetic overflow");
|
|
591
|
+
*out = lamports;
|
|
528
592
|
return SUCCESS;
|
|
529
593
|
}
|
|
530
594
|
|
|
@@ -1424,7 +1488,8 @@ static inline int64_t ts_le_i64(const uint8_t *p) {
|
|
|
1424
1488
|
}
|
|
1425
1489
|
|
|
1426
1490
|
/* Like get_price_no_older_than: fully verified, matching feed id, publish_time + max_age >= now. */
|
|
1427
|
-
|
|
1491
|
+
/* expo: the exponent the caller scales by; checked when check_expo (a feed's exponent can change). */
|
|
1492
|
+
static inline uint64_t ts_pyth_price(const TsAccount *a, const SolPubkey *feed, uint64_t max_age, bool check_expo, int32_t expo, int64_t *out) {
|
|
1428
1493
|
uint64_t m;
|
|
1429
1494
|
bool full;
|
|
1430
1495
|
TS_TRY(ts_pyth_message(a, &m, &full));
|
|
@@ -1436,6 +1501,11 @@ static inline uint64_t ts_pyth_price(const TsAccount *a, const SolPubkey *feed,
|
|
|
1436
1501
|
int64_t deadline;
|
|
1437
1502
|
if (__builtin_add_overflow(published, (int64_t)(max_age > INT64_MAX ? INT64_MAX : max_age), &deadline)) deadline = INT64_MAX;
|
|
1438
1503
|
if (deadline < now) TS_FAIL(TS_ERR_ORACLE, "TypeSOL: Pyth price is too old");
|
|
1504
|
+
if (check_expo) {
|
|
1505
|
+
int32_t e;
|
|
1506
|
+
ts_memcpy(&e, a->data + m + 48, 4);
|
|
1507
|
+
if (e != expo) TS_FAIL(TS_ERR_ORACLE, "TypeSOL: Pyth exponent is not the expected one");
|
|
1508
|
+
}
|
|
1439
1509
|
*out = ts_le_i64(a->data + m + 32);
|
|
1440
1510
|
return SUCCESS;
|
|
1441
1511
|
}
|
|
@@ -1461,9 +1531,10 @@ static inline uint64_t ts_check_switchboard(const TsAccount *a, bool devnet) {
|
|
|
1461
1531
|
* The feed's current result (an 18-decimal fixed-point i128) scaled to `decimals`, if it has samples
|
|
1462
1532
|
* and was produced within `max_stale_slots` slots of now.
|
|
1463
1533
|
*/
|
|
1464
|
-
static inline uint64_t ts_switchboard_value(const TsAccount *a, uint64_t max_stale_slots, uint8_t decimals, int64_t *out) {
|
|
1534
|
+
static inline uint64_t ts_switchboard_value(const TsAccount *a, uint64_t max_stale_slots, uint8_t decimals, uint8_t min_samples, int64_t *out) {
|
|
1465
1535
|
if (decimals > 18) TS_FAIL(TS_ERR_ORACLE, "TypeSOL: at most 18 decimals");
|
|
1466
|
-
if (a->data[TS_SB_RESULT_SAMPLES] == 0
|
|
1536
|
+
if (a->data[TS_SB_RESULT_SAMPLES] == 0 || a->data[TS_SB_RESULT_SAMPLES] < min_samples)
|
|
1537
|
+
TS_FAIL(TS_ERR_ORACLE, "TypeSOL: Switchboard feed has too few samples");
|
|
1467
1538
|
uint64_t clock[5] = {0};
|
|
1468
1539
|
if (sol_get_clock_sysvar(clock) != SUCCESS) TS_FAIL(TS_ERR_SYSVAR, "TypeSOL: clock sysvar unavailable");
|
|
1469
1540
|
uint64_t slot = (uint64_t)ts_le_i64(a->data + TS_SB_RESULT_SLOT);
|