@typesol/cli 0.17.0 → 0.18.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.
@@ -30,6 +30,7 @@
30
30
  #define TS_ERR_TOKEN_OWNER 2015 /* ConstraintTokenOwner */
31
31
  #define TS_ERR_MINT_AUTHORITY 2016 /* ConstraintMintMintAuthority */
32
32
  #define TS_ERR_FREEZE_AUTHORITY 2017 /* ConstraintMintFreezeAuthority */
33
+ #define TS_ERR_MINT_DECIMALS 2018 /* ConstraintMintDecimals */
33
34
  #define TS_ERR_DUPLICATE_MUT 2040 /* ConstraintDuplicateMutableAccount */
34
35
  #define TS_ERR_ALREADY_INITIALIZED 3000 /* AccountDiscriminatorAlreadySet */
35
36
  #define TS_ERR_DISCRIMINATOR 3002 /* AccountDiscriminatorMismatch */
@@ -42,6 +43,7 @@
42
43
  #define TS_ERR_WRONG_PROGRAM 3008 /* InvalidProgramId */
43
44
  #define TS_ERR_NOT_EXECUTABLE 3009 /* InvalidProgramExecutable */
44
45
  #define TS_ERR_NOT_SIGNER 3010 /* AccountNotSigner */
46
+ #define TS_ERR_NOT_SYSTEM_OWNED 3011 /* AccountNotSystemOwned */
45
47
  #define TS_ERR_NOT_INITIALIZED 3012 /* AccountNotInitialized */
46
48
  #define TS_ERR_REALLOC 3016 /* AccountReallocExceedsLimit */
47
49
  #define TS_ERR_CAST 4102 /* InvalidNumericConversion */
@@ -634,6 +636,129 @@ static inline uint64_t ts_create_pda(TsParams *params, TsAccount *payer, TsAccou
634
636
  return SUCCESS;
635
637
  }
636
638
 
639
+ /*
640
+ * Create `acct` with `space` bytes owned by `owner` (a token program), funded by `payer`: a PDA of this program when
641
+ * `n` > 0 (seeds without the bump; a pre-funded address is topped up, allocated and assigned), otherwise a new keypair
642
+ * account that signed the transaction. Writes the bump (0 for a keypair).
643
+ */
644
+ static inline uint64_t ts_create_owned(TsParams *params, TsAccount *payer, TsAccount *acct, SolSignerSeed *seeds, int n,
645
+ const SolPubkey *owner, uint64_t space, uint8_t *bump_out) {
646
+ uint8_t bump = 0;
647
+ if (n > 0) {
648
+ SolPubkey derived;
649
+ if (ts_find_pda(seeds, n, params->program_id, &derived, &bump) != SUCCESS || !ts_key_eq(&derived, &acct->key))
650
+ TS_FAIL(TS_ERR_SEEDS, "TypeSOL: PDA seeds do not match account");
651
+ seeds[n].addr = ≎
652
+ seeds[n].len = 1;
653
+ } else if (!acct->is_signer) {
654
+ TS_FAIL(TS_ERR_NOT_SIGNER, "TypeSOL: a new keypair account must sign");
655
+ }
656
+ if (acct->data_len != 0 || !ts_key_eq(&acct->owner, &TS_SYSTEM_PROGRAM_ID))
657
+ TS_FAIL(TS_ERR_ALREADY_INITIALIZED, "TypeSOL: account already exists");
658
+ uint64_t lamports;
659
+ TS_TRY(ts_minimum_balance(space, &lamports));
660
+ SolSignerSeeds signer = {seeds, (uint64_t)(n + 1)};
661
+ SolSignerSeeds *signers = n > 0 ? &signer : NULL;
662
+ int nsigners = n > 0 ? 1 : 0;
663
+ if (acct->lamports == 0) {
664
+ uint8_t data[52];
665
+ uint32_t tag = 0; /* CreateAccount */
666
+ ts_memcpy(data, &tag, 4);
667
+ ts_memcpy(data + 4, &lamports, 8);
668
+ ts_memcpy(data + 12, &space, 8);
669
+ ts_memcpy(data + 20, owner, 32);
670
+ SolAccountMeta metas[2] = {{&payer->key, true, true}, {&acct->key, true, true}};
671
+ SolInstruction ix = {(SolPubkey *)&TS_SYSTEM_PROGRAM_ID, metas, 2, data, sizeof(data)};
672
+ TS_TRY(ts_invoke(params, &ix, signers, nsigners));
673
+ } else {
674
+ if (acct->lamports < lamports) TS_TRY(ts_system_transfer(params, payer, acct, lamports - acct->lamports));
675
+ uint8_t alloc[12];
676
+ uint32_t alloc_tag = 8; /* Allocate */
677
+ ts_memcpy(alloc, &alloc_tag, 4);
678
+ ts_memcpy(alloc + 4, &space, 8);
679
+ SolAccountMeta one[1] = {{&acct->key, true, true}};
680
+ SolInstruction alloc_ix = {(SolPubkey *)&TS_SYSTEM_PROGRAM_ID, one, 1, alloc, sizeof(alloc)};
681
+ TS_TRY(ts_invoke(params, &alloc_ix, signers, nsigners));
682
+ uint8_t assign[36];
683
+ uint32_t assign_tag = 1; /* Assign */
684
+ ts_memcpy(assign, &assign_tag, 4);
685
+ ts_memcpy(assign + 4, owner, 32);
686
+ SolInstruction assign_ix = {(SolPubkey *)&TS_SYSTEM_PROGRAM_ID, one, 1, assign, sizeof(assign)};
687
+ TS_TRY(ts_invoke(params, &assign_ix, signers, nsigners));
688
+ }
689
+ *bump_out = bump;
690
+ return SUCCESS;
691
+ }
692
+
693
+ /* InitializeMint2: decimals, mint authority, optional freeze authority. */
694
+ static inline uint64_t ts_token_init_mint(TsParams *params, TsAccount *tp, TsAccount *mint, uint8_t decimals,
695
+ const SolPubkey *authority, const SolPubkey *freeze) {
696
+ uint8_t data[67];
697
+ data[0] = 20;
698
+ data[1] = decimals;
699
+ ts_memcpy(data + 2, authority, 32);
700
+ data[34] = freeze ? 1 : 0;
701
+ if (freeze) ts_memcpy(data + 35, freeze, 32);
702
+ SolAccountMeta metas[1] = {{&mint->key, true, false}};
703
+ SolInstruction ix = {(SolPubkey *)&tp->key, metas, 1, data, freeze ? 67 : 35};
704
+ return ts_invoke(params, &ix, NULL, 0);
705
+ }
706
+
707
+ /* Bytes a token account of `mint` needs: 165 under SPL Token, Token-2022's GetAccountDataSize answer otherwise
708
+ (a mint's extensions can add account extensions). */
709
+ static inline uint64_t ts_token_account_space(TsParams *params, TsAccount *tp, TsAccount *mint, uint64_t *space) {
710
+ if (ts_key_eq(&tp->key, &TS_TOKEN_PROGRAM_ID)) {
711
+ *space = TS_TOKEN_ACCOUNT_LEN;
712
+ return SUCCESS;
713
+ }
714
+ uint8_t data[1] = {21}; /* GetAccountDataSize */
715
+ SolAccountMeta metas[1] = {{&mint->key, false, false}};
716
+ SolInstruction ix = {(SolPubkey *)&tp->key, metas, 1, data, 1};
717
+ TS_TRY(ts_invoke(params, &ix, NULL, 0));
718
+ SolPubkey from;
719
+ uint64_t size = 0;
720
+ if (sol_get_return_data((uint8_t *)&size, 8, &from) != 8 || !ts_key_eq(&from, &tp->key))
721
+ TS_FAIL(TS_ERR_TOKEN_ACCOUNT, "TypeSOL: the token program did not report an account size");
722
+ *space = size;
723
+ return SUCCESS;
724
+ }
725
+
726
+ /* InitializeAccount3: `acct` holds `mint` tokens for `owner`. */
727
+ static inline uint64_t ts_token_init_account(TsParams *params, TsAccount *tp, TsAccount *acct, TsAccount *mint, const SolPubkey *owner) {
728
+ uint8_t data[33];
729
+ data[0] = 18;
730
+ ts_memcpy(data + 1, owner, 32);
731
+ SolAccountMeta metas[2] = {{&acct->key, true, false}, {&mint->key, false, false}};
732
+ SolInstruction ix = {(SolPubkey *)&tp->key, metas, 2, data, sizeof(data)};
733
+ return ts_invoke(params, &ix, NULL, 0);
734
+ }
735
+
736
+ /* An existing account `init_if_needed` found: the PDA it must be (when seeds are given) and owned by token program `tp`. */
737
+ static inline uint64_t ts_token_existing(TsParams *params, TsAccount *acct, TsAccount *tp, SolSignerSeed *seeds, int n,
738
+ uint8_t *bump_out) {
739
+ *bump_out = 0;
740
+ if (n > 0) {
741
+ SolPubkey derived;
742
+ if (ts_find_pda(seeds, n, params->program_id, &derived, bump_out) != SUCCESS || !ts_key_eq(&derived, &acct->key))
743
+ TS_FAIL(TS_ERR_SEEDS, "TypeSOL: PDA seeds do not match account");
744
+ }
745
+ if (!ts_key_eq(&acct->owner, &tp->key)) TS_FAIL(TS_ERR_WRONG_OWNER, "TypeSOL: account not owned by the token program");
746
+ return SUCCESS;
747
+ }
748
+
749
+ /* An existing mint must have these decimals and authorities (Anchor's mint:: constraints on init_if_needed). */
750
+ static inline uint64_t ts_check_mint_is(const TsAccount *mint, uint8_t decimals, const SolPubkey *authority, const SolPubkey *freeze) {
751
+ const uint8_t *d = mint->data;
752
+ uint32_t tag;
753
+ ts_memcpy(&tag, d, 4);
754
+ if (tag != 1 || !ts_key_eq((const SolPubkey *)(d + 4), authority)) TS_FAIL(TS_ERR_MINT_AUTHORITY, "TypeSOL: mint authority does not match");
755
+ ts_memcpy(&tag, d + 46, 4);
756
+ if (freeze ? (tag != 1 || !ts_key_eq((const SolPubkey *)(d + 50), freeze)) : tag != 0)
757
+ TS_FAIL(TS_ERR_FREEZE_AUTHORITY, "TypeSOL: freeze authority does not match");
758
+ if (d[44] != decimals) TS_FAIL(TS_ERR_MINT_DECIMALS, "TypeSOL: mint decimals do not match");
759
+ return SUCCESS;
760
+ }
761
+
637
762
  /*
638
763
  * Resize a program-owned account to `new_len` bytes (realloc). Growth is limited to 10 KiB past the
639
764
  * size the account had when the instruction started; new bytes are zeroed. When growing, `payer`
@@ -975,6 +1100,44 @@ static inline uint64_t ts_alloc(uint64_t n, uint8_t **out) {
975
1100
  return SUCCESS;
976
1101
  }
977
1102
 
1103
+ /* Base58 text of a 32-byte value (a Pubkey or Hash in a template), on the heap. The value is eight big-endian u32
1104
+ limbs divided by 58^5 at a time, so each pass gives five digits with one 64-bit division per limb. */
1105
+ static uint64_t ts_base58_32(const uint8_t *in, TS_Str *out) {
1106
+ static const char digits[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
1107
+ const uint64_t D = 656356768ULL; /* 58^5 */
1108
+ uint32_t limb[8];
1109
+ for (int i = 0; i < 8; i++)
1110
+ limb[i] = (uint32_t)in[4 * i] << 24 | (uint32_t)in[4 * i + 1] << 16 | (uint32_t)in[4 * i + 2] << 8 | (uint32_t)in[4 * i + 3];
1111
+ uint8_t rev[50]; /* base-58 digits, least significant first; 32 bytes need at most 44 */
1112
+ uint64_t len = 0;
1113
+ int start = 0;
1114
+ while (start < 8 && limb[start] == 0) start++;
1115
+ while (start < 8) {
1116
+ uint64_t rem = 0;
1117
+ for (int i = start; i < 8; i++) {
1118
+ uint64_t cur = (rem << 32) | limb[i];
1119
+ uint64_t q = cur / D;
1120
+ rem = cur - q * D;
1121
+ limb[i] = (uint32_t)q;
1122
+ }
1123
+ while (start < 8 && limb[start] == 0) start++;
1124
+ for (int k = 0; k < 5; k++) {
1125
+ uint64_t q = rem / 58;
1126
+ rev[len++] = (uint8_t)(rem - q * 58);
1127
+ rem = q;
1128
+ }
1129
+ }
1130
+ while (len > 0 && rev[len - 1] == 0) len--;
1131
+ uint64_t zeros = 0;
1132
+ while (zeros < 32 && in[zeros] == 0) zeros++;
1133
+ uint8_t *p;
1134
+ TS_TRY(ts_alloc(zeros + len, &p));
1135
+ for (uint64_t i = 0; i < zeros; i++) p[i] = '1';
1136
+ for (uint64_t j = 0; j < len; j++) p[zeros + j] = (uint8_t)digits[rev[len - 1 - j]];
1137
+ *out = ts_str(p, zeros + len);
1138
+ return SUCCESS;
1139
+ }
1140
+
978
1141
  static inline uint64_t ts_str_concat(TS_Str a, TS_Str b, TS_Str *out) {
979
1142
  uint8_t *p;
980
1143
  TS_TRY(ts_alloc(a.len + b.len, &p));
@@ -1005,6 +1168,41 @@ static inline uint64_t ts_mul_div(uint64_t a, uint64_t b, uint64_t c, bool ceil,
1005
1168
  return SUCCESS;
1006
1169
  }
1007
1170
 
1171
+ /* floor or ceil of a * b / c for u128 through a 256-bit product; fails on c == 0 or a result above u128. */
1172
+ static uint64_t ts_mul_div_u128(unsigned __int128 a, unsigned __int128 b, unsigned __int128 c, bool ceil, unsigned __int128 *out) {
1173
+ if (c == 0) TS_FAIL(TS_ERR_DIV_ZERO, "TypeSOL: division by zero");
1174
+ const unsigned __int128 M = (unsigned __int128)UINT64_MAX;
1175
+ unsigned __int128 a0 = a & M, a1 = a >> 64, b0 = b & M, b1 = b >> 64;
1176
+ unsigned __int128 p00 = a0 * b0, p01 = a0 * b1, p10 = a1 * b0, p11 = a1 * b1;
1177
+ unsigned __int128 mid = (p00 >> 64) + (p01 & M) + (p10 & M);
1178
+ unsigned __int128 lo = (p00 & M) | (mid << 64);
1179
+ unsigned __int128 hi = p11 + (p01 >> 64) + (p10 >> 64) + (mid >> 64);
1180
+ unsigned __int128 q, r;
1181
+ if (hi == 0) {
1182
+ q = lo / c;
1183
+ r = lo % c;
1184
+ } else {
1185
+ /* The quotient fits in 128 bits only when hi < c; then long division of hi:lo by c, one bit at a time. */
1186
+ if (hi >= c) TS_FAIL(TS_ERR_OVERFLOW, "TypeSOL: arithmetic overflow");
1187
+ q = 0;
1188
+ r = hi;
1189
+ for (int i = 127; i >= 0; i--) {
1190
+ bool carry = (r >> 127) != 0;
1191
+ r = (r << 1) | ((lo >> i) & 1);
1192
+ if (carry || r >= c) {
1193
+ r -= c;
1194
+ q |= (unsigned __int128)1 << i;
1195
+ }
1196
+ }
1197
+ }
1198
+ if (ceil && r != 0) {
1199
+ if (q == ~(unsigned __int128)0) TS_FAIL(TS_ERR_OVERFLOW, "TypeSOL: arithmetic overflow");
1200
+ q += 1;
1201
+ }
1202
+ *out = q;
1203
+ return SUCCESS;
1204
+ }
1205
+
1008
1206
  /* Integer square roots (floor), by Newton's method from an over-estimate. */
1009
1207
  static inline uint64_t ts_isqrt_u64(uint64_t x) {
1010
1208
  if (x < 2) return x;