@typesol/cli 0.22.0 → 0.24.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 +82 -0
- package/README.md +13 -12
- package/SECURITY.md +1 -1
- package/dist/clientgen.js +190 -41
- package/dist/compiler/analyze.js +195 -25
- package/dist/compiler/emit/base.d.ts +28 -5
- package/dist/compiler/emit/base.js +81 -15
- package/dist/compiler/emit/calls.d.ts +18 -1
- package/dist/compiler/emit/calls.js +159 -7
- package/dist/compiler/emit/cpi.d.ts +23 -0
- package/dist/compiler/emit/cpi.js +274 -5
- package/dist/compiler/emit/expressions.d.ts +9 -1
- package/dist/compiler/emit/expressions.js +25 -12
- package/dist/compiler/emit/function.js +8 -4
- package/dist/compiler/emit/program.js +9 -2
- package/dist/compiler/emit/shared.d.ts +43 -3
- package/dist/compiler/emit/shared.js +43 -0
- package/dist/compiler/emit/statements.d.ts +39 -2
- package/dist/compiler/emit/statements.js +346 -87
- package/dist/compiler/emit/storage.d.ts +6 -2
- package/dist/compiler/emit/storage.js +75 -45
- package/dist/compiler/model.d.ts +26 -1
- package/dist/idlpublish.d.ts +6 -1
- package/dist/idlpublish.js +47 -5
- package/dist/portgen.js +1 -1
- package/dist/svmrpc.d.ts +7 -1
- package/dist/svmrpc.js +133 -19
- package/dist/testing.js +23 -3
- package/docs/cli.md +2 -2
- package/docs/client.md +13 -10
- package/docs/errors.md +9 -3
- package/docs/language.md +21 -20
- package/docs/mainnet.md +1 -1
- package/docs/migrate-from-anchor.md +8 -9
- package/docs/test-your-program.md +6 -2
- package/docs/testing.md +1 -1
- package/docs/troubleshooting.md +3 -2
- package/lib/typesol.d.ts +127 -20
- package/package.json +1 -1
- package/runtime/typesol_rt.h +73 -18
package/lib/typesol.d.ts
CHANGED
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
* Integer types are all `bigint` to TypeScript. The compiler tracks the declared
|
|
9
9
|
* width itself, rejects `number`, and checks every arithmetic operation for
|
|
10
10
|
* overflow at runtime.
|
|
11
|
+
*
|
|
12
|
+
* Integer operators: + - * / % are checked (overflow and division by zero abort). & | ^ work on equal types;
|
|
13
|
+
* << aborts when bits would be lost or the amount is not below the width; >> is arithmetic on signed types;
|
|
14
|
+
* ~ flips the bits within the type's width.
|
|
11
15
|
*/
|
|
12
16
|
|
|
13
17
|
export type u8 = bigint;
|
|
@@ -27,10 +31,6 @@ export interface Pubkey {
|
|
|
27
31
|
equals(other: Pubkey): boolean;
|
|
28
32
|
}
|
|
29
33
|
|
|
30
|
-
/**
|
|
31
|
-
* A fixed-length array stored inline in account data, e.g. `scores: FixedArray<u64, 8>`.
|
|
32
|
-
* Indexes are bounds-checked; out of range aborts the instruction.
|
|
33
|
-
*/
|
|
34
34
|
/**
|
|
35
35
|
* Callback methods of Vec, FixedArray and FixedVec. Each expands into a loop over the elements (as `list.get(i)` gives
|
|
36
36
|
* them: structs by reference) with the arrow written at the call; it may read and write the function's variables.
|
|
@@ -50,8 +50,22 @@ export interface ListMethods<T> {
|
|
|
50
50
|
includes(value: T): boolean;
|
|
51
51
|
/** The index of the first element equal to `value`, or -1. */
|
|
52
52
|
indexOf(value: T): i64;
|
|
53
|
+
/**
|
|
54
|
+
* The elements `fn` maps them to (integers, booleans, Pubkeys or enums). Use it right away (another list method,
|
|
55
|
+
* `.length`, `for … of`: one loop, nothing stored) or store it (`const ys = xs.map(…)`: a FixedVec on the stack, as
|
|
56
|
+
* big as `xs`; from a `Vec`, annotate it: `const ys: FixedVec<u64, 16> = …`). Stored with a `FixedVec<Struct, N>` or
|
|
57
|
+
* `FixedVec<FixedString<N>, M>` annotation it gives structs (`(o) => ({ qty: o.qty })`) or text. A stored list holds
|
|
58
|
+
* at most 2 KiB; one element more than it holds aborts (5104).
|
|
59
|
+
*/
|
|
60
|
+
map<U>(fn: (x: T, index: u64) => U): FixedVec<U, never>;
|
|
61
|
+
/** The elements `fn` holds for, used or stored like `map`'s; callbacks after it get the index among those. */
|
|
62
|
+
filter(fn: (x: T, index: u64) => boolean): FixedVec<T, never>;
|
|
53
63
|
}
|
|
54
64
|
|
|
65
|
+
/**
|
|
66
|
+
* A fixed-length array stored inline in account data, e.g. `scores: FixedArray<u64, 8>`.
|
|
67
|
+
* Indexes are bounds-checked; out of range aborts the instruction.
|
|
68
|
+
*/
|
|
55
69
|
export interface FixedArray<T, N extends number> extends ListMethods<T> {
|
|
56
70
|
readonly length: u64;
|
|
57
71
|
get(index: bigint): T;
|
|
@@ -74,15 +88,17 @@ export type FixedString<N extends number> = string & { readonly __capacity?: N }
|
|
|
74
88
|
export declare function byteLength(value: string): u64;
|
|
75
89
|
|
|
76
90
|
/**
|
|
77
|
-
* An optional
|
|
91
|
+
* An optional value: `best: Option<u64>` (same as `u64 | null`), also of a struct (`home: Option<Point>`). Compare with `=== null` / `!== null`;
|
|
78
92
|
* TypeScript narrows it, and using an empty option where a value is needed aborts (5108).
|
|
79
93
|
* In account data it takes a fixed 1 + size(T) bytes.
|
|
80
94
|
*/
|
|
81
95
|
export type Option<T> = T | null;
|
|
82
96
|
|
|
83
97
|
/**
|
|
84
|
-
* A growable list. As
|
|
85
|
-
*
|
|
98
|
+
* A growable list. As an account struct field it is stored Borsh-style (u32 length + elements) and the account is
|
|
99
|
+
* resized as it grows. An account may have any number of them, declared anywhere: they are stored after the
|
|
100
|
+
* fixed-size fields, in the order declared (in a `@layout borsh` struct, a Vec must be the last field). As an
|
|
101
|
+
* instruction argument it is a read-only view.
|
|
86
102
|
*/
|
|
87
103
|
export interface Vec<T> extends ListMethods<T> {
|
|
88
104
|
readonly length: u64;
|
|
@@ -100,8 +116,8 @@ export interface Vec<T> extends ListMethods<T> {
|
|
|
100
116
|
|
|
101
117
|
/**
|
|
102
118
|
* A list stored inline with room for N elements: a u32 length, then N slots (Anchor `Vec<T>` with `#[max_len(N)]`, but
|
|
103
|
-
* always 4 + N × size(T) bytes). Unlike `Vec<T>`
|
|
104
|
-
* `push` fails (5104) when it is full. Write a whole one with `fixedVec([a, b])`.
|
|
119
|
+
* always 4 + N × size(T) bytes). Unlike `Vec<T>` its size is fixed: it keeps its place among the fields, can be in
|
|
120
|
+
* nested structs, and `push` takes no payer; `push` fails (5104) when it is full. Write a whole one with `fixedVec([a, b])`.
|
|
105
121
|
*/
|
|
106
122
|
export interface FixedVec<T, N extends number> extends ListMethods<T> {
|
|
107
123
|
readonly length: u64;
|
|
@@ -603,11 +619,6 @@ export interface SelfProgram extends AccountMeta {
|
|
|
603
619
|
*/
|
|
604
620
|
export declare function emitCpi<T>(event: T, eventAuthority: EventAuthority, self: SelfProgram): void;
|
|
605
621
|
|
|
606
|
-
/**
|
|
607
|
-
* Metaplex Core (CoREEN…), address checked. Assets and collections without plugins; `null` leaves an optional
|
|
608
|
-
* account out. Accounts that sign (the new asset/collection, authorities) are keypair Signers, or PDAs of this
|
|
609
|
-
* program covered by `signerSeeds`.
|
|
610
|
-
*/
|
|
611
622
|
/**
|
|
612
623
|
* Who manages a Metaplex Core plugin: `"owner"`, `"updateAuthority"`, `"none"` (nobody: fixed), or an address. Left out,
|
|
613
624
|
* the plugin type's default (the owner for owner-managed plugins, the update authority for the others).
|
|
@@ -642,7 +653,49 @@ export type CorePlugin = (
|
|
|
642
653
|
| { kind: "PermanentFreezeExecute"; frozen: boolean }
|
|
643
654
|
) & { authority?: CorePluginAuthority };
|
|
644
655
|
export type CorePluginKind = CorePlugin["kind"] | "Groups";
|
|
656
|
+
/** When a Core lifecycle event reaches an external plugin, and what the plugin may do about it. */
|
|
657
|
+
export interface CoreLifecycleCheck {
|
|
658
|
+
event: "create" | "transfer" | "burn" | "update" | "execute";
|
|
659
|
+
canListen?: boolean;
|
|
660
|
+
canApprove?: boolean;
|
|
661
|
+
canReject?: boolean;
|
|
662
|
+
}
|
|
663
|
+
/** An account Core passes to an external plugin: one it already has, or an address. */
|
|
664
|
+
export type CoreExtraAccount =
|
|
665
|
+
| { kind: "program" | "collection" | "owner" | "recipient" | "asset"; isSigner?: boolean; isWritable?: boolean }
|
|
666
|
+
| { kind: "address"; address: Pubkey; isSigner?: boolean; isWritable?: boolean };
|
|
667
|
+
/**
|
|
668
|
+
* A Metaplex Core external plugin adapter, written as an object literal: data a third party writes (`AppData`), an
|
|
669
|
+
* account that approves or rejects lifecycle events (`Oracle`), or a program called on them (`LifecycleHook`); the
|
|
670
|
+
* `Linked` forms go on a collection, for each of its assets. `authority` manages the adapter itself.
|
|
671
|
+
*/
|
|
672
|
+
export type CoreExternalPlugin = (
|
|
673
|
+
| { kind: "AppData" | "LinkedAppData"; dataAuthority: CorePluginAuthority; schema?: "binary" | "json" | "msgpack" }
|
|
674
|
+
| {
|
|
675
|
+
kind: "Oracle";
|
|
676
|
+
baseAddress: Pubkey;
|
|
677
|
+
checks: CoreLifecycleCheck[];
|
|
678
|
+
baseAddressConfig?: CoreExtraAccount;
|
|
679
|
+
resultsOffset?: "none" | "anchor" | u64;
|
|
680
|
+
}
|
|
681
|
+
| {
|
|
682
|
+
kind: "LifecycleHook" | "LinkedLifecycleHook";
|
|
683
|
+
hookedProgram: Pubkey;
|
|
684
|
+
checks: CoreLifecycleCheck[];
|
|
685
|
+
extraAccounts?: CoreExtraAccount[];
|
|
686
|
+
dataAuthority?: CorePluginAuthority;
|
|
687
|
+
schema?: "binary" | "json" | "msgpack";
|
|
688
|
+
}
|
|
689
|
+
) & { authority?: CorePluginAuthority };
|
|
690
|
+
/** Which external plugin: an AppData by its data authority, an Oracle by its base address, a LifecycleHook by its program. */
|
|
691
|
+
export type CoreExternalPluginKey =
|
|
692
|
+
{ kind: "AppData" | "LinkedAppData"; dataAuthority: CorePluginAuthority } | { kind: "Oracle" | "LifecycleHook" | "LinkedLifecycleHook"; address: Pubkey };
|
|
645
693
|
|
|
694
|
+
/**
|
|
695
|
+
* Metaplex Core (CoREEN…), address checked. Assets and collections, with plugins and external plugin adapters; `null`
|
|
696
|
+
* leaves an optional account out. Accounts that sign (the new asset/collection, authorities) are keypair Signers, or
|
|
697
|
+
* PDAs of this program covered by `signerSeeds`.
|
|
698
|
+
*/
|
|
646
699
|
export interface MplCoreProgram extends AccountMeta {
|
|
647
700
|
readonly __mpl_core: void;
|
|
648
701
|
/**
|
|
@@ -745,6 +798,61 @@ export interface MplCoreProgram extends AccountMeta {
|
|
|
745
798
|
kind: CorePluginKind,
|
|
746
799
|
signerSeeds?: Seed[] | SignerSeeds,
|
|
747
800
|
): void;
|
|
801
|
+
/** AddExternalPluginAdapterV1, signed by the update authority as `authority` (`null`: the payer). */
|
|
802
|
+
addExternalPlugin(
|
|
803
|
+
asset: AccountMeta,
|
|
804
|
+
collection: AccountMeta | null,
|
|
805
|
+
payer: Mut<Signer>,
|
|
806
|
+
authority: AccountMeta | null,
|
|
807
|
+
systemProgram: SystemProgram,
|
|
808
|
+
plugin: CoreExternalPlugin,
|
|
809
|
+
signerSeeds?: Seed[] | SignerSeeds,
|
|
810
|
+
): void;
|
|
811
|
+
removeExternalPlugin(
|
|
812
|
+
asset: AccountMeta,
|
|
813
|
+
collection: AccountMeta | null,
|
|
814
|
+
payer: Mut<Signer>,
|
|
815
|
+
authority: AccountMeta | null,
|
|
816
|
+
systemProgram: SystemProgram,
|
|
817
|
+
key: CoreExternalPluginKey,
|
|
818
|
+
signerSeeds?: Seed[] | SignerSeeds,
|
|
819
|
+
): void;
|
|
820
|
+
/** WriteExternalPluginAdapterDataV1, signed by the plugin's data authority: `data` replaces what it holds (`null` clears it). */
|
|
821
|
+
writeExternalPluginData(
|
|
822
|
+
asset: AccountMeta,
|
|
823
|
+
collection: AccountMeta | null,
|
|
824
|
+
payer: Mut<Signer>,
|
|
825
|
+
authority: AccountMeta | null,
|
|
826
|
+
systemProgram: SystemProgram,
|
|
827
|
+
key: CoreExternalPluginKey,
|
|
828
|
+
data: string | Vec<u8> | null,
|
|
829
|
+
signerSeeds?: Seed[] | SignerSeeds,
|
|
830
|
+
): void;
|
|
831
|
+
addCollectionExternalPlugin(
|
|
832
|
+
collection: AccountMeta,
|
|
833
|
+
payer: Mut<Signer>,
|
|
834
|
+
authority: AccountMeta | null,
|
|
835
|
+
systemProgram: SystemProgram,
|
|
836
|
+
plugin: CoreExternalPlugin,
|
|
837
|
+
signerSeeds?: Seed[] | SignerSeeds,
|
|
838
|
+
): void;
|
|
839
|
+
removeCollectionExternalPlugin(
|
|
840
|
+
collection: AccountMeta,
|
|
841
|
+
payer: Mut<Signer>,
|
|
842
|
+
authority: AccountMeta | null,
|
|
843
|
+
systemProgram: SystemProgram,
|
|
844
|
+
key: CoreExternalPluginKey,
|
|
845
|
+
signerSeeds?: Seed[] | SignerSeeds,
|
|
846
|
+
): void;
|
|
847
|
+
writeCollectionExternalPluginData(
|
|
848
|
+
collection: AccountMeta,
|
|
849
|
+
payer: Mut<Signer>,
|
|
850
|
+
authority: AccountMeta | null,
|
|
851
|
+
systemProgram: SystemProgram,
|
|
852
|
+
key: CoreExternalPluginKey,
|
|
853
|
+
data: string | Vec<u8> | null,
|
|
854
|
+
signerSeeds?: Seed[] | SignerSeeds,
|
|
855
|
+
): void;
|
|
748
856
|
/** TransferV1, signed by the owner (or a delegate) as `authority` (`null`: the payer). */
|
|
749
857
|
transferAsset(
|
|
750
858
|
asset: AccountMeta,
|
|
@@ -1429,7 +1537,6 @@ export declare function invoke(
|
|
|
1429
1537
|
signerSeeds?: Seed[] | SignerSeeds,
|
|
1430
1538
|
): void;
|
|
1431
1539
|
|
|
1432
|
-
/** Abort with the given error unless `condition` holds. Use a string enum member or a string literal. */
|
|
1433
1540
|
/**
|
|
1434
1541
|
* Anchor's own error codes (anchor-lang `ErrorCode`): `require(condition, AnchorError.AccountDidNotDeserialize)` fails
|
|
1435
1542
|
* with that code, as `require!(condition, ErrorCode::AccountDidNotDeserialize)` does. Not listed in the IDL's errors.
|
|
@@ -1513,6 +1620,11 @@ export declare enum AnchorError {
|
|
|
1513
1620
|
InvalidNumericConversion = 4102,
|
|
1514
1621
|
Deprecated = 5000,
|
|
1515
1622
|
}
|
|
1623
|
+
/**
|
|
1624
|
+
* Abort with the given error unless `condition` holds. The error is a member of an exported string enum, any
|
|
1625
|
+
* `AnchorError` member, or a string literal: each distinct literal becomes an error named `RequireFailed<N>` with code
|
|
1626
|
+
* 6000 + N, numbered after the program's error enums and listed in the IDL.
|
|
1627
|
+
*/
|
|
1516
1628
|
export declare function require(condition: boolean, error: string | AnchorError): asserts condition;
|
|
1517
1629
|
|
|
1518
1630
|
/** Write a string, integer or pubkey to the program log. */
|
|
@@ -1554,11 +1666,6 @@ export declare function i16(value: bigint): i16;
|
|
|
1554
1666
|
export declare function i32(value: bigint): i32;
|
|
1555
1667
|
export declare function i128(value: bigint): i128;
|
|
1556
1668
|
|
|
1557
|
-
/**
|
|
1558
|
-
* Integer operators: + - * / % are checked (overflow and division by zero abort). & | ^ work on equal types;
|
|
1559
|
-
* << aborts when bits would be lost or the amount is not below the width; >> is arithmetic on signed types;
|
|
1560
|
-
* ~ flips the bits within the type's width.
|
|
1561
|
-
*/
|
|
1562
1669
|
/** floor(a * b / c) with a double-width intermediate (all u64, or all u128); aborts if c is 0 or the result does not fit. */
|
|
1563
1670
|
export declare function mulDiv(a: u64, b: u64, c: u64): u64;
|
|
1564
1671
|
/** ceil(a * b / c), like mulDiv. */
|
package/package.json
CHANGED
package/runtime/typesol_rt.h
CHANGED
|
@@ -104,6 +104,25 @@ static inline __attribute__((always_inline)) void ts_memcpy(void *dst, const voi
|
|
|
104
104
|
for (; n; n--) *d++ = *s++;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
/* Copy that may overlap (a growable Vec's followers moving when it grows or shrinks): forward or backward as needed. */
|
|
108
|
+
static inline void ts_memmove(void *dst, const void *src, uint64_t n) {
|
|
109
|
+
uint8_t *d = (uint8_t *)dst;
|
|
110
|
+
const uint8_t *s = (const uint8_t *)src;
|
|
111
|
+
if (d == s || n == 0) return;
|
|
112
|
+
if (d < s) {
|
|
113
|
+
ts_memcpy(d, s, n); /* forward: each 8-byte load happens before the store that could overlap it */
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
d += n;
|
|
117
|
+
s += n;
|
|
118
|
+
for (; n >= 8; n -= 8) {
|
|
119
|
+
d -= 8;
|
|
120
|
+
s -= 8;
|
|
121
|
+
ts_st64(d, ts_ld64(s));
|
|
122
|
+
}
|
|
123
|
+
while (n--) *--d = *--s;
|
|
124
|
+
}
|
|
125
|
+
|
|
107
126
|
static inline __attribute__((always_inline)) void ts_memzero(void *dst, uint64_t n) {
|
|
108
127
|
uint8_t *d = (uint8_t *)dst;
|
|
109
128
|
for (; n >= 8; n -= 8, d += 8) ts_st64(d, 0);
|
|
@@ -350,9 +369,19 @@ static inline uint64_t ts_create_pda_address(const SolSignerSeed *seeds, int n,
|
|
|
350
369
|
/* sol_try_find_program_address: the canonical (highest) bump. */
|
|
351
370
|
static inline uint64_t ts_find_pda(const SolSignerSeed *seeds, int n, const SolPubkey *program, SolPubkey *out, uint8_t *bump_out) {
|
|
352
371
|
if (!ts_pda_seeds_ok(seeds, n, TS_MAX_PDA_SEEDS - 1)) return 1; /* not SUCCESS: the PDA syscalls fail the same inputs */
|
|
372
|
+
/* The seed list is built once; each attempt only changes the bump byte it points at. */
|
|
373
|
+
SolBytes parts[TS_MAX_PDA_SEEDS + 2];
|
|
374
|
+
int k = 0;
|
|
375
|
+
for (int i = 0; i < n; i++) parts[k++] = (SolBytes){seeds[i].addr, seeds[i].len};
|
|
376
|
+
uint8_t bump = 255;
|
|
377
|
+
parts[k++] = (SolBytes){&bump, 1};
|
|
378
|
+
parts[k++] = (SolBytes){program->x, 32};
|
|
379
|
+
parts[k++] = (SolBytes){(const uint8_t *)"ProgramDerivedAddress", 21};
|
|
353
380
|
for (int b = 255; b >= 0; b--) {
|
|
354
|
-
|
|
355
|
-
|
|
381
|
+
bump = (uint8_t)b;
|
|
382
|
+
sol_sha256(parts, k, out->x);
|
|
383
|
+
uint8_t ignored;
|
|
384
|
+
if (sol_curve_validate_point(0 /* CURVE25519_EDWARDS */, out->x, &ignored) != 0) {
|
|
356
385
|
*bump_out = bump;
|
|
357
386
|
return SUCCESS;
|
|
358
387
|
}
|
|
@@ -474,10 +503,9 @@ static inline void ts_write_le(uint8_t *p, uint64_t v, int n) { ts_memcpy(p, &v,
|
|
|
474
503
|
uint64_t sol_get_rent_sysvar(void *ret);
|
|
475
504
|
uint64_t sol_get_clock_sysvar(void *ret);
|
|
476
505
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
if (sol_get_rent_sysvar(rent) != SUCCESS) TS_FAIL(TS_ERR_SYSVAR, "TypeSOL: rent sysvar unavailable");
|
|
506
|
+
/* The minimum balance of `data_len` bytes under `rent`, the Rent sysvar
|
|
507
|
+
{ lamports_per_byte_year: u64, exemption_threshold: f64, burn_percent: u8 }. */
|
|
508
|
+
static inline uint64_t ts_minimum_balance_of(const uint64_t rent[3], uint64_t data_len, uint64_t *out) {
|
|
481
509
|
uint64_t bytes, per_year;
|
|
482
510
|
if (ts_add_overflow_u64(data_len, 128, &bytes) || ts_mul_overflow_u64(bytes, rent[0], &per_year))
|
|
483
511
|
TS_FAIL(TS_ERR_OVERFLOW, "TypeSOL: arithmetic overflow");
|
|
@@ -500,6 +528,18 @@ static inline uint64_t ts_minimum_balance(uint64_t data_len, uint64_t *out) {
|
|
|
500
528
|
return SUCCESS;
|
|
501
529
|
}
|
|
502
530
|
|
|
531
|
+
static inline uint64_t ts_rent(uint64_t rent[3]) {
|
|
532
|
+
rent[0] = rent[1] = rent[2] = 0;
|
|
533
|
+
if (sol_get_rent_sysvar(rent) != SUCCESS) TS_FAIL(TS_ERR_SYSVAR, "TypeSOL: rent sysvar unavailable");
|
|
534
|
+
return SUCCESS;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
static inline uint64_t ts_minimum_balance(uint64_t data_len, uint64_t *out) {
|
|
538
|
+
uint64_t rent[3];
|
|
539
|
+
TS_TRY(ts_rent(rent));
|
|
540
|
+
return ts_minimum_balance_of(rent, data_len, out);
|
|
541
|
+
}
|
|
542
|
+
|
|
503
543
|
/* Clock sysvar word `i`: 0 slot, 2 epoch (u64). */
|
|
504
544
|
static inline uint64_t ts_clock_u64(int i, uint64_t *out) {
|
|
505
545
|
uint64_t clock[5] = {0};
|
|
@@ -772,9 +812,10 @@ static inline uint64_t ts_resize(TsParams *params, TsAccount *payer, TsAccount *
|
|
|
772
812
|
if (new_len > old_len) {
|
|
773
813
|
/* The payer always funds the extra rent, even when the account already holds enough lamports: those may be
|
|
774
814
|
deposits, which must not become rent (and shrinking never pays anything back, see ts_realloc). */
|
|
775
|
-
uint64_t old_rent, new_rent;
|
|
776
|
-
TS_TRY(
|
|
777
|
-
TS_TRY(
|
|
815
|
+
uint64_t rent[3], old_rent, new_rent;
|
|
816
|
+
TS_TRY(ts_rent(rent)); /* read once: the sysvar cannot change within the instruction */
|
|
817
|
+
TS_TRY(ts_minimum_balance_of(rent, old_len, &old_rent));
|
|
818
|
+
TS_TRY(ts_minimum_balance_of(rent, new_len, &new_rent));
|
|
778
819
|
if (new_rent > old_rent) {
|
|
779
820
|
if (payer == NULL) TS_FAIL(TS_ERR_REALLOC, "TypeSOL: growing an account needs a payer");
|
|
780
821
|
TS_TRY(ts_system_transfer(params, payer, acct, new_rent - old_rent));
|
|
@@ -1321,18 +1362,32 @@ static inline uint64_t ts_str_i128(__int128 v, TS_Str *out) {
|
|
|
1321
1362
|
return ts_str_u128(mag, v < 0, out);
|
|
1322
1363
|
}
|
|
1323
1364
|
|
|
1365
|
+
/* "00" … "99": two digits per division. */
|
|
1366
|
+
static const char TS_DIGIT_PAIRS[201] =
|
|
1367
|
+
"00010203040506070809101112131415161718192021222324252627282930313233343536373839"
|
|
1368
|
+
"40414243444546474849505152535455565758596061626364656667686970717273747576777879"
|
|
1369
|
+
"8081828384858687888990919293949596979899";
|
|
1324
1370
|
static inline uint64_t ts_str_u64(uint64_t v, bool negative, TS_Str *out) {
|
|
1371
|
+
/* Written from the end, two digits at a time, then copied out in one go. */
|
|
1325
1372
|
uint8_t tmp[21];
|
|
1326
|
-
int
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1373
|
+
int i = 21;
|
|
1374
|
+
while (v >= 100) {
|
|
1375
|
+
uint64_t q = v / 100, r = v - q * 100;
|
|
1376
|
+
tmp[--i] = (uint8_t)TS_DIGIT_PAIRS[2 * r + 1];
|
|
1377
|
+
tmp[--i] = (uint8_t)TS_DIGIT_PAIRS[2 * r];
|
|
1378
|
+
v = q;
|
|
1379
|
+
}
|
|
1380
|
+
if (v >= 10) {
|
|
1381
|
+
tmp[--i] = (uint8_t)TS_DIGIT_PAIRS[2 * v + 1];
|
|
1382
|
+
tmp[--i] = (uint8_t)TS_DIGIT_PAIRS[2 * v];
|
|
1383
|
+
} else
|
|
1384
|
+
tmp[--i] = (uint8_t)('0' + v);
|
|
1385
|
+
if (negative) tmp[--i] = '-';
|
|
1386
|
+
uint64_t n = (uint64_t)(21 - i);
|
|
1332
1387
|
uint8_t *p;
|
|
1333
|
-
TS_TRY(ts_alloc(
|
|
1334
|
-
|
|
1335
|
-
*out = ts_str(p,
|
|
1388
|
+
TS_TRY(ts_alloc(n, &p));
|
|
1389
|
+
ts_memcpy(p, tmp + i, n);
|
|
1390
|
+
*out = ts_str(p, n);
|
|
1336
1391
|
return SUCCESS;
|
|
1337
1392
|
}
|
|
1338
1393
|
static inline uint64_t ts_str_i64(int64_t v, TS_Str *out) {
|