@sovereign-sdk/web3 0.13.1 → 0.14.1

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/dist/index.mjs CHANGED
@@ -121,7 +121,7 @@ var Rollup = class {
121
121
  * Retrieve dedup information about the provided address.
122
122
  *
123
123
  * @param publicKey - The public key to dedup.
124
- * TODO: How to add param for generation explicitly
124
+ * TODO: How to request a specific uniqueness strategy explicitly
125
125
  */
126
126
  async dedup(publicKey) {
127
127
  const credentialId = bytesToHex(publicKey);
@@ -430,21 +430,40 @@ async function createStandardRollup(rollupConfig, typeBuilderOverrides) {
430
430
 
431
431
  // src/rollup/solana-signable-rollup.ts
432
432
  import { isLedgerSolanaSigner } from "@sovereign-sdk/signers";
433
+ import { bytesToHex as bytesToHex3, hexToBytes as hexToBytes2 } from "@sovereign-sdk/utils";
434
+ import bs58 from "bs58";
433
435
  import { Base64 as Base642 } from "js-base64";
436
+ var MULTISIG_SIMPLE_DISCRIMINATOR = 128;
434
437
  var VEC_LENGTH_PREFIX_SIZE = 4;
435
438
  var CHAIN_HASH_SIZE = 32;
436
439
  var PUBKEY_SIZE = 32;
437
440
  var SIGNATURE_SIZE = 64;
438
- var PREAMBLE_LENGTH = 85;
441
+ var MULTISIG_PREAMBLE_FIXED_LENGTH = 53;
442
+ var MAX_MULTISIG_SIGNERS = 21;
439
443
  var SIGNING_DOMAIN = new Uint8Array([
440
444
  255,
441
445
  ...new TextEncoder().encode("solana offchain")
442
446
  ]);
443
447
  var HEADER_VERSION = 0;
444
448
  var MESSAGE_FORMAT = 0;
445
- var SINGLE_SIGNER_COUNT = 1;
446
- function createSolanaPreamble(pubkey, chainHash, messageLength) {
447
- const preamble = new Uint8Array(PREAMBLE_LENGTH);
449
+ function compareByteArrays(left, right) {
450
+ const minLength = Math.min(left.length, right.length);
451
+ for (let i = 0; i < minLength; i++) {
452
+ if (left[i] !== right[i]) {
453
+ return left[i] - right[i];
454
+ }
455
+ }
456
+ return left.length - right.length;
457
+ }
458
+ function createSolanaPreamble(pubkeys, chainHash, messageLength) {
459
+ if (pubkeys.length < 1 || pubkeys.length > MAX_MULTISIG_SIGNERS) {
460
+ throw new Error(
461
+ `Invalid signer count: expected 1-${MAX_MULTISIG_SIGNERS} signers, got ${pubkeys.length}`
462
+ );
463
+ }
464
+ const preamble = new Uint8Array(
465
+ MULTISIG_PREAMBLE_FIXED_LENGTH + pubkeys.length * PUBKEY_SIZE
466
+ );
448
467
  let offset = 0;
449
468
  preamble.set(SIGNING_DOMAIN, offset);
450
469
  offset += 16;
@@ -454,10 +473,17 @@ function createSolanaPreamble(pubkey, chainHash, messageLength) {
454
473
  offset += 32;
455
474
  preamble[offset] = MESSAGE_FORMAT;
456
475
  offset += 1;
457
- preamble[offset] = SINGLE_SIGNER_COUNT;
476
+ preamble[offset] = pubkeys.length;
458
477
  offset += 1;
459
- preamble.set(pubkey, offset);
460
- offset += 32;
478
+ for (const pubkey of pubkeys) {
479
+ if (pubkey.length !== PUBKEY_SIZE) {
480
+ throw new Error(
481
+ `Invalid public key length: expected ${PUBKEY_SIZE} bytes, got ${pubkey.length}`
482
+ );
483
+ }
484
+ preamble.set(pubkey, offset);
485
+ offset += PUBKEY_SIZE;
486
+ }
461
487
  new DataView(preamble.buffer, offset).setUint16(0, messageLength, true);
462
488
  return preamble;
463
489
  }
@@ -501,6 +527,13 @@ var SolanaSignableRollup = class {
501
527
  const serializedMessage = this.serializeSolanaSpecMessage(solanaMessage);
502
528
  return this.submitSerializedMessage(serializedMessage);
503
529
  }
530
+ /**
531
+ * Submits a Solana spec-compliant multisig message to the rollup.
532
+ */
533
+ async submitSolanaSpecMultisigMessage(solanaMessage) {
534
+ const serializedMessage = this.serializeSolanaSpecMultisigMessage(solanaMessage);
535
+ return this.submitSerializedMessage(serializedMessage);
536
+ }
504
537
  /**
505
538
  * Helper to build an unsigned transaction using the standard type builder.
506
539
  */
@@ -523,6 +556,45 @@ var SolanaSignableRollup = class {
523
556
  });
524
557
  return { response, transaction };
525
558
  }
559
+ /**
560
+ * Concatenates a Solana preamble with the message bytes it signs.
561
+ */
562
+ combinePreambleAndMessage(preamble, message) {
563
+ const signedMessage = new Uint8Array(preamble.length + message.length);
564
+ signedMessage.set(preamble, 0);
565
+ signedMessage.set(message, preamble.length);
566
+ return signedMessage;
567
+ }
568
+ /**
569
+ * Validates and canonicalizes the multisig pubkey list.
570
+ * The canonical order is lexicographic by raw pubkey bytes.
571
+ */
572
+ canonicalizeMultisigPubkeys(multisigPubkeys) {
573
+ if (!multisigPubkeys) {
574
+ throw new Error(
575
+ "multisigPubkeys is required for Solana multisig transactions"
576
+ );
577
+ }
578
+ if (multisigPubkeys.length < 2 || multisigPubkeys.length > MAX_MULTISIG_SIGNERS) {
579
+ throw new Error(
580
+ `Invalid multisig signer count: expected 2-${MAX_MULTISIG_SIGNERS} signers, got ${multisigPubkeys.length}`
581
+ );
582
+ }
583
+ const seenPubkeys = /* @__PURE__ */ new Set();
584
+ for (const pubkey of multisigPubkeys) {
585
+ if (pubkey.length !== PUBKEY_SIZE) {
586
+ throw new Error(
587
+ `Invalid public key length: expected ${PUBKEY_SIZE} bytes, got ${pubkey.length}`
588
+ );
589
+ }
590
+ const pubkeyHex = bytesToHex3(pubkey);
591
+ if (seenPubkeys.has(pubkeyHex)) {
592
+ throw new Error(`Duplicate multisig public key provided: ${pubkeyHex}`);
593
+ }
594
+ seenPubkeys.add(pubkeyHex);
595
+ }
596
+ return [...multisigPubkeys].sort(compareByteArrays);
597
+ }
526
598
  /**
527
599
  * Helper to create and serialize a SolanaOffchainUnsignedTransaction to JSON bytes.
528
600
  */
@@ -564,12 +636,15 @@ var SolanaSignableRollup = class {
564
636
  const jsonBytes = await this.createSolanaJsonBytes(unsignedTx);
565
637
  const pubkey = await signer.publicKey();
566
638
  const chainHash = await this.inner.chainHash();
567
- const preamble = createSolanaPreamble(pubkey, chainHash, jsonBytes.length);
568
- const signedMessageWithPreamble = new Uint8Array(
569
- preamble.length + jsonBytes.length
639
+ const preamble = createSolanaPreamble(
640
+ [pubkey],
641
+ chainHash,
642
+ jsonBytes.length
643
+ );
644
+ const signedMessageWithPreamble = this.combinePreambleAndMessage(
645
+ preamble,
646
+ jsonBytes
570
647
  );
571
- signedMessageWithPreamble.set(preamble, 0);
572
- signedMessageWithPreamble.set(jsonBytes, preamble.length);
573
648
  const signature = await signer.sign(signedMessageWithPreamble);
574
649
  const solanaMessage = {
575
650
  signed_message_with_preamble: signedMessageWithPreamble,
@@ -643,6 +718,235 @@ var SolanaSignableRollup = class {
643
718
  throw new Error(`Unsupported authenticator: ${authenticator}`);
644
719
  }
645
720
  }
721
+ /**
722
+ * Creates V1 JSON bytes for a multisig transaction, including multisig_id and version.
723
+ * The resulting JSON is what each signer signs directly (no discriminator prefix).
724
+ */
725
+ async createMultisigJsonBytes(unsignedTx, multisigAddress) {
726
+ const serializer = await this.inner.serializer();
727
+ const schema = serializer.schema;
728
+ const chainName = schema.chain_data.chain_name || "";
729
+ const solanaUnsignedTx = {
730
+ runtime_call: unsignedTx.runtime_call,
731
+ uniqueness: unsignedTx.uniqueness,
732
+ details: unsignedTx.details,
733
+ chain_name: chainName,
734
+ // Hardcoded to base58 encoding, only correct for rollups using Base58Address as their
735
+ // primary address type. Will be replaced with rollup-aware address formatting once the
736
+ // SDK supports flexible address encoding (see #2673).
737
+ multisig_id: bs58.encode(multisigAddress),
738
+ version: 1
739
+ };
740
+ return new TextEncoder().encode(JSON.stringify(solanaUnsignedTx));
741
+ }
742
+ /**
743
+ * Creates the preamble+JSON bytes signed by every signer in a spec-compliant multisig flow.
744
+ */
745
+ async createSpecCompliantMultisigSignedMessage(unsignedTx, multisigAddress, multisigPubkeys) {
746
+ const jsonBytes = await this.createMultisigJsonBytes(
747
+ unsignedTx,
748
+ multisigAddress
749
+ );
750
+ const chainHash = await this.inner.chainHash();
751
+ const preamble = createSolanaPreamble(
752
+ multisigPubkeys,
753
+ chainHash,
754
+ jsonBytes.length
755
+ );
756
+ return this.combinePreambleAndMessage(preamble, jsonBytes);
757
+ }
758
+ /**
759
+ * Signs an unsigned transaction using Solana offchain simple multisig signing.
760
+ */
761
+ async signForSolanaSimpleMultisig(unsignedTx, signer, multisigAddress, multisigPubkeys) {
762
+ const pubkey = await signer.publicKey();
763
+ const signerPubkeyHex = bytesToHex3(pubkey);
764
+ const multisigPubkeyHexes = multisigPubkeys.map(bytesToHex3);
765
+ if (!multisigPubkeyHexes.includes(signerPubkeyHex)) {
766
+ throw new Error(
767
+ `Signer public key ${signerPubkeyHex} is not present in multisigPubkeys`
768
+ );
769
+ }
770
+ const jsonBytes = await this.createMultisigJsonBytes(
771
+ unsignedTx,
772
+ multisigAddress
773
+ );
774
+ const signature = await signer.sign(jsonBytes);
775
+ return this.typeBuilder.transaction({
776
+ unsignedTx,
777
+ sender: pubkey,
778
+ signature,
779
+ rollup: this.inner
780
+ });
781
+ }
782
+ /**
783
+ * Signs an unsigned transaction using the spec-compliant multisig preamble.
784
+ */
785
+ async signForSolanaSpecMultisig(unsignedTx, signer, multisigAddress, multisigPubkeys) {
786
+ const pubkey = await signer.publicKey();
787
+ const signerPubkeyHex = bytesToHex3(pubkey);
788
+ const multisigPubkeyHexes = multisigPubkeys.map(bytesToHex3);
789
+ if (!multisigPubkeyHexes.includes(signerPubkeyHex)) {
790
+ throw new Error(
791
+ `Signer public key ${signerPubkeyHex} is not present in multisigPubkeys`
792
+ );
793
+ }
794
+ const signedMessageWithPreamble = await this.createSpecCompliantMultisigSignedMessage(
795
+ unsignedTx,
796
+ multisigAddress,
797
+ multisigPubkeys
798
+ );
799
+ const signature = await signer.sign(signedMessageWithPreamble);
800
+ return this.typeBuilder.transaction({
801
+ unsignedTx,
802
+ sender: pubkey,
803
+ signature,
804
+ rollup: this.inner
805
+ });
806
+ }
807
+ /**
808
+ * Signs an unsigned transaction for use in a Solana offchain multisig.
809
+ *
810
+ * `authenticator: "standard"` delegates directly to the wrapped standard rollup.
811
+ * `authenticator: "solanaSimple"` signs the V1 JSON payload directly.
812
+ * `authenticator: "solana"` signs the spec-compliant preamble+JSON bytes.
813
+ * Solana multisig authenticators treat `multisigPubkeys` as an unordered set and
814
+ * canonicalize it before signing.
815
+ */
816
+ async signTransactionForMultisig(unsignedTx, params) {
817
+ switch (params.authenticator) {
818
+ case "standard":
819
+ return this.inner.signTransaction(unsignedTx, params.signer);
820
+ case "solanaSimple":
821
+ return this.signForSolanaSimpleMultisig(
822
+ unsignedTx,
823
+ params.signer,
824
+ params.multisigAddress,
825
+ this.canonicalizeMultisigPubkeys(params.multisigPubkeys)
826
+ );
827
+ case "solana":
828
+ return this.signForSolanaSpecMultisig(
829
+ unsignedTx,
830
+ params.signer,
831
+ params.multisigAddress,
832
+ this.canonicalizeMultisigPubkeys(params.multisigPubkeys)
833
+ );
834
+ }
835
+ }
836
+ /**
837
+ * Builds a spec-compliant multisig envelope from a V1 transaction and ordered multisig pubkeys.
838
+ */
839
+ buildSpecCompliantMultisigEnvelope(tx, signedMessageWithPreamble, multisigPubkeys) {
840
+ const multisigPubkeyHexes = multisigPubkeys.map(bytesToHex3);
841
+ const indexByPubkey = /* @__PURE__ */ new Map();
842
+ multisigPubkeyHexes.forEach((pubkeyHex, index) => {
843
+ indexByPubkey.set(pubkeyHex, index);
844
+ });
845
+ const accountedPubkeys = /* @__PURE__ */ new Set();
846
+ const signaturesByIndex = /* @__PURE__ */ new Map();
847
+ let signerBitfield = 0;
848
+ for (const signer of tx.signatures) {
849
+ const signerIndex = indexByPubkey.get(signer.pub_key);
850
+ if (signerIndex === void 0) {
851
+ throw new Error(
852
+ `Signed pubkey ${signer.pub_key} is not present in multisigPubkeys`
853
+ );
854
+ }
855
+ if (accountedPubkeys.has(signer.pub_key)) {
856
+ throw new Error(
857
+ `Duplicate signed pubkey in multisig transaction: ${signer.pub_key}`
858
+ );
859
+ }
860
+ accountedPubkeys.add(signer.pub_key);
861
+ signaturesByIndex.set(signerIndex, hexToBytes2(signer.signature));
862
+ signerBitfield = (signerBitfield | 1 << signerIndex) >>> 0;
863
+ }
864
+ for (const unusedPubkey of tx.unused_pub_keys) {
865
+ if (!indexByPubkey.has(unusedPubkey)) {
866
+ throw new Error(
867
+ `Unused pubkey ${unusedPubkey} is not present in multisigPubkeys`
868
+ );
869
+ }
870
+ if (accountedPubkeys.has(unusedPubkey)) {
871
+ throw new Error(
872
+ `Duplicate pubkey in multisig transaction payload: ${unusedPubkey}`
873
+ );
874
+ }
875
+ accountedPubkeys.add(unusedPubkey);
876
+ }
877
+ if (accountedPubkeys.size !== multisigPubkeys.length) {
878
+ throw new Error(
879
+ "multisigPubkeys must contain every signer and unused pubkey exactly once"
880
+ );
881
+ }
882
+ const orderedSignatures = Array.from(signaturesByIndex.entries()).sort(([leftIndex], [rightIndex]) => leftIndex - rightIndex).map(([, signature]) => signature);
883
+ return {
884
+ signed_message_with_preamble: signedMessageWithPreamble,
885
+ signatures: orderedSignatures,
886
+ signer_bitfield: signerBitfield,
887
+ min_signers: tx.min_signers
888
+ };
889
+ }
890
+ /**
891
+ * Submits a multisig transaction using the Solana offchain simple multisig format.
892
+ *
893
+ * Accepts the V1 transaction produced by `MultisigTransaction.asTransaction()`.
894
+ * `authenticator: "standard"` delegates directly to the wrapped standard rollup.
895
+ * Solana authenticators rebuild the appropriate Solana multisig envelope and
896
+ * submit it to the Solana offchain endpoint.
897
+ */
898
+ async submitMultisigTransaction(multisigTx, params) {
899
+ const { V1: tx } = multisigTx;
900
+ const unsignedTx = {
901
+ runtime_call: tx.runtime_call,
902
+ uniqueness: tx.uniqueness,
903
+ details: tx.details
904
+ };
905
+ switch (params.authenticator) {
906
+ case "standard":
907
+ return this.inner.submitTransaction(
908
+ multisigTx
909
+ );
910
+ case "solanaSimple": {
911
+ this.canonicalizeMultisigPubkeys(params.multisigPubkeys);
912
+ const jsonBytes = await this.createMultisigJsonBytes(
913
+ unsignedTx,
914
+ params.multisigAddress
915
+ );
916
+ const wireBytes = new Uint8Array(1 + jsonBytes.length);
917
+ wireBytes[0] = MULTISIG_SIMPLE_DISCRIMINATOR;
918
+ wireBytes.set(jsonBytes, 1);
919
+ const chainHash = await this.inner.chainHash();
920
+ const serialized = this.serializeSolanaMultisigMessage({
921
+ wire_bytes: wireBytes,
922
+ chain_hash: chainHash,
923
+ signatures: tx.signatures.map((s) => ({
924
+ signature: hexToBytes2(s.signature),
925
+ pub_key: hexToBytes2(s.pub_key)
926
+ })),
927
+ unused_pub_keys: tx.unused_pub_keys.map((pk) => hexToBytes2(pk)),
928
+ min_signers: tx.min_signers
929
+ });
930
+ return this.submitSerializedMessage(serialized);
931
+ }
932
+ case "solana": {
933
+ const multisigPubkeys = this.canonicalizeMultisigPubkeys(
934
+ params.multisigPubkeys
935
+ );
936
+ const signedMessageWithPreamble = await this.createSpecCompliantMultisigSignedMessage(
937
+ unsignedTx,
938
+ params.multisigAddress,
939
+ multisigPubkeys
940
+ );
941
+ const message = this.buildSpecCompliantMultisigEnvelope(
942
+ tx,
943
+ signedMessageWithPreamble,
944
+ multisigPubkeys
945
+ );
946
+ return this.submitSolanaSpecMultisigMessage(message);
947
+ }
948
+ }
949
+ }
646
950
  /**
647
951
  * Submits a transaction with the specified authenticator.
648
952
  *
@@ -764,6 +1068,102 @@ var SolanaSignableRollup = class {
764
1068
  buffer.set(message.signature, offset);
765
1069
  return buffer;
766
1070
  }
1071
+ /**
1072
+ * Serializes a SolanaOffchainSimpleMultisigMessage using borsh encoding.
1073
+ * Layout matches the Rust struct:
1074
+ * [u32 LE: wire_bytes.len][wire_bytes]
1075
+ * [32 bytes: chain_hash]
1076
+ * [u32 LE: signatures.len]
1077
+ * for each: [64 bytes: signature][32 bytes: pub_key]
1078
+ * [u32 LE: unused_pub_keys.len]
1079
+ * for each: [32 bytes: pub_key]
1080
+ * [u8: min_signers]
1081
+ */
1082
+ serializeSolanaMultisigMessage(message) {
1083
+ if (message.chain_hash.length !== CHAIN_HASH_SIZE) {
1084
+ throw new Error(
1085
+ `Invalid chain hash length: expected ${CHAIN_HASH_SIZE} bytes, got ${message.chain_hash.length}`
1086
+ );
1087
+ }
1088
+ const sigCount = message.signatures.length;
1089
+ const unusedCount = message.unused_pub_keys.length;
1090
+ const totalSize = VEC_LENGTH_PREFIX_SIZE + message.wire_bytes.length + CHAIN_HASH_SIZE + VEC_LENGTH_PREFIX_SIZE + sigCount * (SIGNATURE_SIZE + PUBKEY_SIZE) + VEC_LENGTH_PREFIX_SIZE + unusedCount * PUBKEY_SIZE + 1;
1091
+ const buffer = new Uint8Array(totalSize);
1092
+ const view = new DataView(buffer.buffer);
1093
+ let offset = 0;
1094
+ view.setUint32(offset, message.wire_bytes.length, true);
1095
+ offset += VEC_LENGTH_PREFIX_SIZE;
1096
+ buffer.set(message.wire_bytes, offset);
1097
+ offset += message.wire_bytes.length;
1098
+ buffer.set(message.chain_hash, offset);
1099
+ offset += CHAIN_HASH_SIZE;
1100
+ view.setUint32(offset, sigCount, true);
1101
+ offset += VEC_LENGTH_PREFIX_SIZE;
1102
+ for (const sig of message.signatures) {
1103
+ if (sig.signature.length !== SIGNATURE_SIZE) {
1104
+ throw new Error(
1105
+ `Invalid signature length: expected ${SIGNATURE_SIZE} bytes, got ${sig.signature.length}`
1106
+ );
1107
+ }
1108
+ if (sig.pub_key.length !== PUBKEY_SIZE) {
1109
+ throw new Error(
1110
+ `Invalid public key length: expected ${PUBKEY_SIZE} bytes, got ${sig.pub_key.length}`
1111
+ );
1112
+ }
1113
+ buffer.set(sig.signature, offset);
1114
+ offset += SIGNATURE_SIZE;
1115
+ buffer.set(sig.pub_key, offset);
1116
+ offset += PUBKEY_SIZE;
1117
+ }
1118
+ view.setUint32(offset, unusedCount, true);
1119
+ offset += VEC_LENGTH_PREFIX_SIZE;
1120
+ for (const pk of message.unused_pub_keys) {
1121
+ if (pk.length !== PUBKEY_SIZE) {
1122
+ throw new Error(
1123
+ `Invalid public key length: expected ${PUBKEY_SIZE} bytes, got ${pk.length}`
1124
+ );
1125
+ }
1126
+ buffer.set(pk, offset);
1127
+ offset += PUBKEY_SIZE;
1128
+ }
1129
+ buffer[offset] = message.min_signers;
1130
+ return buffer;
1131
+ }
1132
+ /**
1133
+ * Serializes a SolanaOffchainSpecCompliantMultisigMessage using borsh encoding.
1134
+ * Layout matches the Rust struct:
1135
+ * [u32 LE: signed_message_with_preamble.len][signed_message_with_preamble]
1136
+ * [u32 LE: signatures.len]
1137
+ * for each: [64 bytes: signature]
1138
+ * [u32 LE: signer_bitfield]
1139
+ * [u8: min_signers]
1140
+ */
1141
+ serializeSolanaSpecMultisigMessage(message) {
1142
+ const sigCount = message.signatures.length;
1143
+ const totalSize = VEC_LENGTH_PREFIX_SIZE + message.signed_message_with_preamble.length + VEC_LENGTH_PREFIX_SIZE + sigCount * SIGNATURE_SIZE + 4 + 1;
1144
+ const buffer = new Uint8Array(totalSize);
1145
+ const view = new DataView(buffer.buffer);
1146
+ let offset = 0;
1147
+ view.setUint32(offset, message.signed_message_with_preamble.length, true);
1148
+ offset += VEC_LENGTH_PREFIX_SIZE;
1149
+ buffer.set(message.signed_message_with_preamble, offset);
1150
+ offset += message.signed_message_with_preamble.length;
1151
+ view.setUint32(offset, sigCount, true);
1152
+ offset += VEC_LENGTH_PREFIX_SIZE;
1153
+ for (const signature of message.signatures) {
1154
+ if (signature.length !== SIGNATURE_SIZE) {
1155
+ throw new Error(
1156
+ `Invalid signature length: expected ${SIGNATURE_SIZE} bytes, got ${signature.length}`
1157
+ );
1158
+ }
1159
+ buffer.set(signature, offset);
1160
+ offset += SIGNATURE_SIZE;
1161
+ }
1162
+ view.setUint32(offset, message.signer_bitfield >>> 0, true);
1163
+ offset += 4;
1164
+ buffer[offset] = message.min_signers;
1165
+ return buffer;
1166
+ }
767
1167
  };
768
1168
  async function createSolanaSignableRollup(rollupConfig, solanaEndpoint = "/sequencer/accept-solana-offchain-tx") {
769
1169
  const standardRollup = await createStandardRollup(rollupConfig);
@@ -783,7 +1183,6 @@ export {
783
1183
  StandardRollup,
784
1184
  VersionMismatchError,
785
1185
  addressFromPublicKey,
786
- createSolanaPreamble,
787
1186
  createSolanaSignableRollup,
788
1187
  createStandardRollup,
789
1188
  standardTypeBuilder