@sovereign-sdk/web3 0.13.1 → 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/dist/index.d.mts +111 -7
- package/dist/index.d.ts +111 -7
- package/dist/index.js +412 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +412 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -40,7 +40,6 @@ __export(src_exports, {
|
|
|
40
40
|
StandardRollup: () => StandardRollup,
|
|
41
41
|
VersionMismatchError: () => VersionMismatchError,
|
|
42
42
|
addressFromPublicKey: () => addressFromPublicKey,
|
|
43
|
-
createSolanaPreamble: () => createSolanaPreamble,
|
|
44
43
|
createSolanaSignableRollup: () => createSolanaSignableRollup,
|
|
45
44
|
createStandardRollup: () => createStandardRollup,
|
|
46
45
|
standardTypeBuilder: () => standardTypeBuilder
|
|
@@ -479,21 +478,40 @@ async function createStandardRollup(rollupConfig, typeBuilderOverrides) {
|
|
|
479
478
|
|
|
480
479
|
// src/rollup/solana-signable-rollup.ts
|
|
481
480
|
var import_signers = require("@sovereign-sdk/signers");
|
|
481
|
+
var import_utils4 = require("@sovereign-sdk/utils");
|
|
482
|
+
var import_bs58 = __toESM(require("bs58"));
|
|
482
483
|
var import_js_base642 = require("js-base64");
|
|
484
|
+
var MULTISIG_SIMPLE_DISCRIMINATOR = 128;
|
|
483
485
|
var VEC_LENGTH_PREFIX_SIZE = 4;
|
|
484
486
|
var CHAIN_HASH_SIZE = 32;
|
|
485
487
|
var PUBKEY_SIZE = 32;
|
|
486
488
|
var SIGNATURE_SIZE = 64;
|
|
487
|
-
var
|
|
489
|
+
var MULTISIG_PREAMBLE_FIXED_LENGTH = 53;
|
|
490
|
+
var MAX_MULTISIG_SIGNERS = 21;
|
|
488
491
|
var SIGNING_DOMAIN = new Uint8Array([
|
|
489
492
|
255,
|
|
490
493
|
...new TextEncoder().encode("solana offchain")
|
|
491
494
|
]);
|
|
492
495
|
var HEADER_VERSION = 0;
|
|
493
496
|
var MESSAGE_FORMAT = 0;
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
+
function compareByteArrays(left, right) {
|
|
498
|
+
const minLength = Math.min(left.length, right.length);
|
|
499
|
+
for (let i = 0; i < minLength; i++) {
|
|
500
|
+
if (left[i] !== right[i]) {
|
|
501
|
+
return left[i] - right[i];
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return left.length - right.length;
|
|
505
|
+
}
|
|
506
|
+
function createSolanaPreamble(pubkeys, chainHash, messageLength) {
|
|
507
|
+
if (pubkeys.length < 1 || pubkeys.length > MAX_MULTISIG_SIGNERS) {
|
|
508
|
+
throw new Error(
|
|
509
|
+
`Invalid signer count: expected 1-${MAX_MULTISIG_SIGNERS} signers, got ${pubkeys.length}`
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
const preamble = new Uint8Array(
|
|
513
|
+
MULTISIG_PREAMBLE_FIXED_LENGTH + pubkeys.length * PUBKEY_SIZE
|
|
514
|
+
);
|
|
497
515
|
let offset = 0;
|
|
498
516
|
preamble.set(SIGNING_DOMAIN, offset);
|
|
499
517
|
offset += 16;
|
|
@@ -503,10 +521,17 @@ function createSolanaPreamble(pubkey, chainHash, messageLength) {
|
|
|
503
521
|
offset += 32;
|
|
504
522
|
preamble[offset] = MESSAGE_FORMAT;
|
|
505
523
|
offset += 1;
|
|
506
|
-
preamble[offset] =
|
|
524
|
+
preamble[offset] = pubkeys.length;
|
|
507
525
|
offset += 1;
|
|
508
|
-
|
|
509
|
-
|
|
526
|
+
for (const pubkey of pubkeys) {
|
|
527
|
+
if (pubkey.length !== PUBKEY_SIZE) {
|
|
528
|
+
throw new Error(
|
|
529
|
+
`Invalid public key length: expected ${PUBKEY_SIZE} bytes, got ${pubkey.length}`
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
preamble.set(pubkey, offset);
|
|
533
|
+
offset += PUBKEY_SIZE;
|
|
534
|
+
}
|
|
510
535
|
new DataView(preamble.buffer, offset).setUint16(0, messageLength, true);
|
|
511
536
|
return preamble;
|
|
512
537
|
}
|
|
@@ -550,6 +575,13 @@ var SolanaSignableRollup = class {
|
|
|
550
575
|
const serializedMessage = this.serializeSolanaSpecMessage(solanaMessage);
|
|
551
576
|
return this.submitSerializedMessage(serializedMessage);
|
|
552
577
|
}
|
|
578
|
+
/**
|
|
579
|
+
* Submits a Solana spec-compliant multisig message to the rollup.
|
|
580
|
+
*/
|
|
581
|
+
async submitSolanaSpecMultisigMessage(solanaMessage) {
|
|
582
|
+
const serializedMessage = this.serializeSolanaSpecMultisigMessage(solanaMessage);
|
|
583
|
+
return this.submitSerializedMessage(serializedMessage);
|
|
584
|
+
}
|
|
553
585
|
/**
|
|
554
586
|
* Helper to build an unsigned transaction using the standard type builder.
|
|
555
587
|
*/
|
|
@@ -572,6 +604,45 @@ var SolanaSignableRollup = class {
|
|
|
572
604
|
});
|
|
573
605
|
return { response, transaction };
|
|
574
606
|
}
|
|
607
|
+
/**
|
|
608
|
+
* Concatenates a Solana preamble with the message bytes it signs.
|
|
609
|
+
*/
|
|
610
|
+
combinePreambleAndMessage(preamble, message) {
|
|
611
|
+
const signedMessage = new Uint8Array(preamble.length + message.length);
|
|
612
|
+
signedMessage.set(preamble, 0);
|
|
613
|
+
signedMessage.set(message, preamble.length);
|
|
614
|
+
return signedMessage;
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Validates and canonicalizes the multisig pubkey list.
|
|
618
|
+
* The canonical order is lexicographic by raw pubkey bytes.
|
|
619
|
+
*/
|
|
620
|
+
canonicalizeMultisigPubkeys(multisigPubkeys) {
|
|
621
|
+
if (!multisigPubkeys) {
|
|
622
|
+
throw new Error(
|
|
623
|
+
"multisigPubkeys is required for Solana multisig transactions"
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
if (multisigPubkeys.length < 2 || multisigPubkeys.length > MAX_MULTISIG_SIGNERS) {
|
|
627
|
+
throw new Error(
|
|
628
|
+
`Invalid multisig signer count: expected 2-${MAX_MULTISIG_SIGNERS} signers, got ${multisigPubkeys.length}`
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
const seenPubkeys = /* @__PURE__ */ new Set();
|
|
632
|
+
for (const pubkey of multisigPubkeys) {
|
|
633
|
+
if (pubkey.length !== PUBKEY_SIZE) {
|
|
634
|
+
throw new Error(
|
|
635
|
+
`Invalid public key length: expected ${PUBKEY_SIZE} bytes, got ${pubkey.length}`
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
const pubkeyHex = (0, import_utils4.bytesToHex)(pubkey);
|
|
639
|
+
if (seenPubkeys.has(pubkeyHex)) {
|
|
640
|
+
throw new Error(`Duplicate multisig public key provided: ${pubkeyHex}`);
|
|
641
|
+
}
|
|
642
|
+
seenPubkeys.add(pubkeyHex);
|
|
643
|
+
}
|
|
644
|
+
return [...multisigPubkeys].sort(compareByteArrays);
|
|
645
|
+
}
|
|
575
646
|
/**
|
|
576
647
|
* Helper to create and serialize a SolanaOffchainUnsignedTransaction to JSON bytes.
|
|
577
648
|
*/
|
|
@@ -613,12 +684,15 @@ var SolanaSignableRollup = class {
|
|
|
613
684
|
const jsonBytes = await this.createSolanaJsonBytes(unsignedTx);
|
|
614
685
|
const pubkey = await signer.publicKey();
|
|
615
686
|
const chainHash = await this.inner.chainHash();
|
|
616
|
-
const preamble = createSolanaPreamble(
|
|
617
|
-
|
|
618
|
-
|
|
687
|
+
const preamble = createSolanaPreamble(
|
|
688
|
+
[pubkey],
|
|
689
|
+
chainHash,
|
|
690
|
+
jsonBytes.length
|
|
691
|
+
);
|
|
692
|
+
const signedMessageWithPreamble = this.combinePreambleAndMessage(
|
|
693
|
+
preamble,
|
|
694
|
+
jsonBytes
|
|
619
695
|
);
|
|
620
|
-
signedMessageWithPreamble.set(preamble, 0);
|
|
621
|
-
signedMessageWithPreamble.set(jsonBytes, preamble.length);
|
|
622
696
|
const signature = await signer.sign(signedMessageWithPreamble);
|
|
623
697
|
const solanaMessage = {
|
|
624
698
|
signed_message_with_preamble: signedMessageWithPreamble,
|
|
@@ -692,6 +766,235 @@ var SolanaSignableRollup = class {
|
|
|
692
766
|
throw new Error(`Unsupported authenticator: ${authenticator}`);
|
|
693
767
|
}
|
|
694
768
|
}
|
|
769
|
+
/**
|
|
770
|
+
* Creates V1 JSON bytes for a multisig transaction, including multisig_id and version.
|
|
771
|
+
* The resulting JSON is what each signer signs directly (no discriminator prefix).
|
|
772
|
+
*/
|
|
773
|
+
async createMultisigJsonBytes(unsignedTx, multisigAddress) {
|
|
774
|
+
const serializer = await this.inner.serializer();
|
|
775
|
+
const schema = serializer.schema;
|
|
776
|
+
const chainName = schema.chain_data.chain_name || "";
|
|
777
|
+
const solanaUnsignedTx = {
|
|
778
|
+
runtime_call: unsignedTx.runtime_call,
|
|
779
|
+
uniqueness: unsignedTx.uniqueness,
|
|
780
|
+
details: unsignedTx.details,
|
|
781
|
+
chain_name: chainName,
|
|
782
|
+
// Hardcoded to base58 encoding, only correct for rollups using Base58Address as their
|
|
783
|
+
// primary address type. Will be replaced with rollup-aware address formatting once the
|
|
784
|
+
// SDK supports flexible address encoding (see #2673).
|
|
785
|
+
multisig_id: import_bs58.default.encode(multisigAddress),
|
|
786
|
+
version: 1
|
|
787
|
+
};
|
|
788
|
+
return new TextEncoder().encode(JSON.stringify(solanaUnsignedTx));
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Creates the preamble+JSON bytes signed by every signer in a spec-compliant multisig flow.
|
|
792
|
+
*/
|
|
793
|
+
async createSpecCompliantMultisigSignedMessage(unsignedTx, multisigAddress, multisigPubkeys) {
|
|
794
|
+
const jsonBytes = await this.createMultisigJsonBytes(
|
|
795
|
+
unsignedTx,
|
|
796
|
+
multisigAddress
|
|
797
|
+
);
|
|
798
|
+
const chainHash = await this.inner.chainHash();
|
|
799
|
+
const preamble = createSolanaPreamble(
|
|
800
|
+
multisigPubkeys,
|
|
801
|
+
chainHash,
|
|
802
|
+
jsonBytes.length
|
|
803
|
+
);
|
|
804
|
+
return this.combinePreambleAndMessage(preamble, jsonBytes);
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Signs an unsigned transaction using Solana offchain simple multisig signing.
|
|
808
|
+
*/
|
|
809
|
+
async signForSolanaSimpleMultisig(unsignedTx, signer, multisigAddress, multisigPubkeys) {
|
|
810
|
+
const pubkey = await signer.publicKey();
|
|
811
|
+
const signerPubkeyHex = (0, import_utils4.bytesToHex)(pubkey);
|
|
812
|
+
const multisigPubkeyHexes = multisigPubkeys.map(import_utils4.bytesToHex);
|
|
813
|
+
if (!multisigPubkeyHexes.includes(signerPubkeyHex)) {
|
|
814
|
+
throw new Error(
|
|
815
|
+
`Signer public key ${signerPubkeyHex} is not present in multisigPubkeys`
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
const jsonBytes = await this.createMultisigJsonBytes(
|
|
819
|
+
unsignedTx,
|
|
820
|
+
multisigAddress
|
|
821
|
+
);
|
|
822
|
+
const signature = await signer.sign(jsonBytes);
|
|
823
|
+
return this.typeBuilder.transaction({
|
|
824
|
+
unsignedTx,
|
|
825
|
+
sender: pubkey,
|
|
826
|
+
signature,
|
|
827
|
+
rollup: this.inner
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
/**
|
|
831
|
+
* Signs an unsigned transaction using the spec-compliant multisig preamble.
|
|
832
|
+
*/
|
|
833
|
+
async signForSolanaSpecMultisig(unsignedTx, signer, multisigAddress, multisigPubkeys) {
|
|
834
|
+
const pubkey = await signer.publicKey();
|
|
835
|
+
const signerPubkeyHex = (0, import_utils4.bytesToHex)(pubkey);
|
|
836
|
+
const multisigPubkeyHexes = multisigPubkeys.map(import_utils4.bytesToHex);
|
|
837
|
+
if (!multisigPubkeyHexes.includes(signerPubkeyHex)) {
|
|
838
|
+
throw new Error(
|
|
839
|
+
`Signer public key ${signerPubkeyHex} is not present in multisigPubkeys`
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
const signedMessageWithPreamble = await this.createSpecCompliantMultisigSignedMessage(
|
|
843
|
+
unsignedTx,
|
|
844
|
+
multisigAddress,
|
|
845
|
+
multisigPubkeys
|
|
846
|
+
);
|
|
847
|
+
const signature = await signer.sign(signedMessageWithPreamble);
|
|
848
|
+
return this.typeBuilder.transaction({
|
|
849
|
+
unsignedTx,
|
|
850
|
+
sender: pubkey,
|
|
851
|
+
signature,
|
|
852
|
+
rollup: this.inner
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Signs an unsigned transaction for use in a Solana offchain multisig.
|
|
857
|
+
*
|
|
858
|
+
* `authenticator: "standard"` delegates directly to the wrapped standard rollup.
|
|
859
|
+
* `authenticator: "solanaSimple"` signs the V1 JSON payload directly.
|
|
860
|
+
* `authenticator: "solana"` signs the spec-compliant preamble+JSON bytes.
|
|
861
|
+
* Solana multisig authenticators treat `multisigPubkeys` as an unordered set and
|
|
862
|
+
* canonicalize it before signing.
|
|
863
|
+
*/
|
|
864
|
+
async signTransactionForMultisig(unsignedTx, params) {
|
|
865
|
+
switch (params.authenticator) {
|
|
866
|
+
case "standard":
|
|
867
|
+
return this.inner.signTransaction(unsignedTx, params.signer);
|
|
868
|
+
case "solanaSimple":
|
|
869
|
+
return this.signForSolanaSimpleMultisig(
|
|
870
|
+
unsignedTx,
|
|
871
|
+
params.signer,
|
|
872
|
+
params.multisigAddress,
|
|
873
|
+
this.canonicalizeMultisigPubkeys(params.multisigPubkeys)
|
|
874
|
+
);
|
|
875
|
+
case "solana":
|
|
876
|
+
return this.signForSolanaSpecMultisig(
|
|
877
|
+
unsignedTx,
|
|
878
|
+
params.signer,
|
|
879
|
+
params.multisigAddress,
|
|
880
|
+
this.canonicalizeMultisigPubkeys(params.multisigPubkeys)
|
|
881
|
+
);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* Builds a spec-compliant multisig envelope from a V1 transaction and ordered multisig pubkeys.
|
|
886
|
+
*/
|
|
887
|
+
buildSpecCompliantMultisigEnvelope(tx, signedMessageWithPreamble, multisigPubkeys) {
|
|
888
|
+
const multisigPubkeyHexes = multisigPubkeys.map(import_utils4.bytesToHex);
|
|
889
|
+
const indexByPubkey = /* @__PURE__ */ new Map();
|
|
890
|
+
multisigPubkeyHexes.forEach((pubkeyHex, index) => {
|
|
891
|
+
indexByPubkey.set(pubkeyHex, index);
|
|
892
|
+
});
|
|
893
|
+
const accountedPubkeys = /* @__PURE__ */ new Set();
|
|
894
|
+
const signaturesByIndex = /* @__PURE__ */ new Map();
|
|
895
|
+
let signerBitfield = 0;
|
|
896
|
+
for (const signer of tx.signatures) {
|
|
897
|
+
const signerIndex = indexByPubkey.get(signer.pub_key);
|
|
898
|
+
if (signerIndex === void 0) {
|
|
899
|
+
throw new Error(
|
|
900
|
+
`Signed pubkey ${signer.pub_key} is not present in multisigPubkeys`
|
|
901
|
+
);
|
|
902
|
+
}
|
|
903
|
+
if (accountedPubkeys.has(signer.pub_key)) {
|
|
904
|
+
throw new Error(
|
|
905
|
+
`Duplicate signed pubkey in multisig transaction: ${signer.pub_key}`
|
|
906
|
+
);
|
|
907
|
+
}
|
|
908
|
+
accountedPubkeys.add(signer.pub_key);
|
|
909
|
+
signaturesByIndex.set(signerIndex, (0, import_utils4.hexToBytes)(signer.signature));
|
|
910
|
+
signerBitfield = (signerBitfield | 1 << signerIndex) >>> 0;
|
|
911
|
+
}
|
|
912
|
+
for (const unusedPubkey of tx.unused_pub_keys) {
|
|
913
|
+
if (!indexByPubkey.has(unusedPubkey)) {
|
|
914
|
+
throw new Error(
|
|
915
|
+
`Unused pubkey ${unusedPubkey} is not present in multisigPubkeys`
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
if (accountedPubkeys.has(unusedPubkey)) {
|
|
919
|
+
throw new Error(
|
|
920
|
+
`Duplicate pubkey in multisig transaction payload: ${unusedPubkey}`
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
accountedPubkeys.add(unusedPubkey);
|
|
924
|
+
}
|
|
925
|
+
if (accountedPubkeys.size !== multisigPubkeys.length) {
|
|
926
|
+
throw new Error(
|
|
927
|
+
"multisigPubkeys must contain every signer and unused pubkey exactly once"
|
|
928
|
+
);
|
|
929
|
+
}
|
|
930
|
+
const orderedSignatures = Array.from(signaturesByIndex.entries()).sort(([leftIndex], [rightIndex]) => leftIndex - rightIndex).map(([, signature]) => signature);
|
|
931
|
+
return {
|
|
932
|
+
signed_message_with_preamble: signedMessageWithPreamble,
|
|
933
|
+
signatures: orderedSignatures,
|
|
934
|
+
signer_bitfield: signerBitfield,
|
|
935
|
+
min_signers: tx.min_signers
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
/**
|
|
939
|
+
* Submits a multisig transaction using the Solana offchain simple multisig format.
|
|
940
|
+
*
|
|
941
|
+
* Accepts the V1 transaction produced by `MultisigTransaction.asTransaction()`.
|
|
942
|
+
* `authenticator: "standard"` delegates directly to the wrapped standard rollup.
|
|
943
|
+
* Solana authenticators rebuild the appropriate Solana multisig envelope and
|
|
944
|
+
* submit it to the Solana offchain endpoint.
|
|
945
|
+
*/
|
|
946
|
+
async submitMultisigTransaction(multisigTx, params) {
|
|
947
|
+
const { V1: tx } = multisigTx;
|
|
948
|
+
const unsignedTx = {
|
|
949
|
+
runtime_call: tx.runtime_call,
|
|
950
|
+
uniqueness: tx.uniqueness,
|
|
951
|
+
details: tx.details
|
|
952
|
+
};
|
|
953
|
+
switch (params.authenticator) {
|
|
954
|
+
case "standard":
|
|
955
|
+
return this.inner.submitTransaction(
|
|
956
|
+
multisigTx
|
|
957
|
+
);
|
|
958
|
+
case "solanaSimple": {
|
|
959
|
+
this.canonicalizeMultisigPubkeys(params.multisigPubkeys);
|
|
960
|
+
const jsonBytes = await this.createMultisigJsonBytes(
|
|
961
|
+
unsignedTx,
|
|
962
|
+
params.multisigAddress
|
|
963
|
+
);
|
|
964
|
+
const wireBytes = new Uint8Array(1 + jsonBytes.length);
|
|
965
|
+
wireBytes[0] = MULTISIG_SIMPLE_DISCRIMINATOR;
|
|
966
|
+
wireBytes.set(jsonBytes, 1);
|
|
967
|
+
const chainHash = await this.inner.chainHash();
|
|
968
|
+
const serialized = this.serializeSolanaMultisigMessage({
|
|
969
|
+
wire_bytes: wireBytes,
|
|
970
|
+
chain_hash: chainHash,
|
|
971
|
+
signatures: tx.signatures.map((s) => ({
|
|
972
|
+
signature: (0, import_utils4.hexToBytes)(s.signature),
|
|
973
|
+
pub_key: (0, import_utils4.hexToBytes)(s.pub_key)
|
|
974
|
+
})),
|
|
975
|
+
unused_pub_keys: tx.unused_pub_keys.map((pk) => (0, import_utils4.hexToBytes)(pk)),
|
|
976
|
+
min_signers: tx.min_signers
|
|
977
|
+
});
|
|
978
|
+
return this.submitSerializedMessage(serialized);
|
|
979
|
+
}
|
|
980
|
+
case "solana": {
|
|
981
|
+
const multisigPubkeys = this.canonicalizeMultisigPubkeys(
|
|
982
|
+
params.multisigPubkeys
|
|
983
|
+
);
|
|
984
|
+
const signedMessageWithPreamble = await this.createSpecCompliantMultisigSignedMessage(
|
|
985
|
+
unsignedTx,
|
|
986
|
+
params.multisigAddress,
|
|
987
|
+
multisigPubkeys
|
|
988
|
+
);
|
|
989
|
+
const message = this.buildSpecCompliantMultisigEnvelope(
|
|
990
|
+
tx,
|
|
991
|
+
signedMessageWithPreamble,
|
|
992
|
+
multisigPubkeys
|
|
993
|
+
);
|
|
994
|
+
return this.submitSolanaSpecMultisigMessage(message);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
}
|
|
695
998
|
/**
|
|
696
999
|
* Submits a transaction with the specified authenticator.
|
|
697
1000
|
*
|
|
@@ -813,6 +1116,102 @@ var SolanaSignableRollup = class {
|
|
|
813
1116
|
buffer.set(message.signature, offset);
|
|
814
1117
|
return buffer;
|
|
815
1118
|
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Serializes a SolanaOffchainSimpleMultisigMessage using borsh encoding.
|
|
1121
|
+
* Layout matches the Rust struct:
|
|
1122
|
+
* [u32 LE: wire_bytes.len][wire_bytes]
|
|
1123
|
+
* [32 bytes: chain_hash]
|
|
1124
|
+
* [u32 LE: signatures.len]
|
|
1125
|
+
* for each: [64 bytes: signature][32 bytes: pub_key]
|
|
1126
|
+
* [u32 LE: unused_pub_keys.len]
|
|
1127
|
+
* for each: [32 bytes: pub_key]
|
|
1128
|
+
* [u8: min_signers]
|
|
1129
|
+
*/
|
|
1130
|
+
serializeSolanaMultisigMessage(message) {
|
|
1131
|
+
if (message.chain_hash.length !== CHAIN_HASH_SIZE) {
|
|
1132
|
+
throw new Error(
|
|
1133
|
+
`Invalid chain hash length: expected ${CHAIN_HASH_SIZE} bytes, got ${message.chain_hash.length}`
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1136
|
+
const sigCount = message.signatures.length;
|
|
1137
|
+
const unusedCount = message.unused_pub_keys.length;
|
|
1138
|
+
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;
|
|
1139
|
+
const buffer = new Uint8Array(totalSize);
|
|
1140
|
+
const view = new DataView(buffer.buffer);
|
|
1141
|
+
let offset = 0;
|
|
1142
|
+
view.setUint32(offset, message.wire_bytes.length, true);
|
|
1143
|
+
offset += VEC_LENGTH_PREFIX_SIZE;
|
|
1144
|
+
buffer.set(message.wire_bytes, offset);
|
|
1145
|
+
offset += message.wire_bytes.length;
|
|
1146
|
+
buffer.set(message.chain_hash, offset);
|
|
1147
|
+
offset += CHAIN_HASH_SIZE;
|
|
1148
|
+
view.setUint32(offset, sigCount, true);
|
|
1149
|
+
offset += VEC_LENGTH_PREFIX_SIZE;
|
|
1150
|
+
for (const sig of message.signatures) {
|
|
1151
|
+
if (sig.signature.length !== SIGNATURE_SIZE) {
|
|
1152
|
+
throw new Error(
|
|
1153
|
+
`Invalid signature length: expected ${SIGNATURE_SIZE} bytes, got ${sig.signature.length}`
|
|
1154
|
+
);
|
|
1155
|
+
}
|
|
1156
|
+
if (sig.pub_key.length !== PUBKEY_SIZE) {
|
|
1157
|
+
throw new Error(
|
|
1158
|
+
`Invalid public key length: expected ${PUBKEY_SIZE} bytes, got ${sig.pub_key.length}`
|
|
1159
|
+
);
|
|
1160
|
+
}
|
|
1161
|
+
buffer.set(sig.signature, offset);
|
|
1162
|
+
offset += SIGNATURE_SIZE;
|
|
1163
|
+
buffer.set(sig.pub_key, offset);
|
|
1164
|
+
offset += PUBKEY_SIZE;
|
|
1165
|
+
}
|
|
1166
|
+
view.setUint32(offset, unusedCount, true);
|
|
1167
|
+
offset += VEC_LENGTH_PREFIX_SIZE;
|
|
1168
|
+
for (const pk of message.unused_pub_keys) {
|
|
1169
|
+
if (pk.length !== PUBKEY_SIZE) {
|
|
1170
|
+
throw new Error(
|
|
1171
|
+
`Invalid public key length: expected ${PUBKEY_SIZE} bytes, got ${pk.length}`
|
|
1172
|
+
);
|
|
1173
|
+
}
|
|
1174
|
+
buffer.set(pk, offset);
|
|
1175
|
+
offset += PUBKEY_SIZE;
|
|
1176
|
+
}
|
|
1177
|
+
buffer[offset] = message.min_signers;
|
|
1178
|
+
return buffer;
|
|
1179
|
+
}
|
|
1180
|
+
/**
|
|
1181
|
+
* Serializes a SolanaOffchainSpecCompliantMultisigMessage using borsh encoding.
|
|
1182
|
+
* Layout matches the Rust struct:
|
|
1183
|
+
* [u32 LE: signed_message_with_preamble.len][signed_message_with_preamble]
|
|
1184
|
+
* [u32 LE: signatures.len]
|
|
1185
|
+
* for each: [64 bytes: signature]
|
|
1186
|
+
* [u32 LE: signer_bitfield]
|
|
1187
|
+
* [u8: min_signers]
|
|
1188
|
+
*/
|
|
1189
|
+
serializeSolanaSpecMultisigMessage(message) {
|
|
1190
|
+
const sigCount = message.signatures.length;
|
|
1191
|
+
const totalSize = VEC_LENGTH_PREFIX_SIZE + message.signed_message_with_preamble.length + VEC_LENGTH_PREFIX_SIZE + sigCount * SIGNATURE_SIZE + 4 + 1;
|
|
1192
|
+
const buffer = new Uint8Array(totalSize);
|
|
1193
|
+
const view = new DataView(buffer.buffer);
|
|
1194
|
+
let offset = 0;
|
|
1195
|
+
view.setUint32(offset, message.signed_message_with_preamble.length, true);
|
|
1196
|
+
offset += VEC_LENGTH_PREFIX_SIZE;
|
|
1197
|
+
buffer.set(message.signed_message_with_preamble, offset);
|
|
1198
|
+
offset += message.signed_message_with_preamble.length;
|
|
1199
|
+
view.setUint32(offset, sigCount, true);
|
|
1200
|
+
offset += VEC_LENGTH_PREFIX_SIZE;
|
|
1201
|
+
for (const signature of message.signatures) {
|
|
1202
|
+
if (signature.length !== SIGNATURE_SIZE) {
|
|
1203
|
+
throw new Error(
|
|
1204
|
+
`Invalid signature length: expected ${SIGNATURE_SIZE} bytes, got ${signature.length}`
|
|
1205
|
+
);
|
|
1206
|
+
}
|
|
1207
|
+
buffer.set(signature, offset);
|
|
1208
|
+
offset += SIGNATURE_SIZE;
|
|
1209
|
+
}
|
|
1210
|
+
view.setUint32(offset, message.signer_bitfield >>> 0, true);
|
|
1211
|
+
offset += 4;
|
|
1212
|
+
buffer[offset] = message.min_signers;
|
|
1213
|
+
return buffer;
|
|
1214
|
+
}
|
|
816
1215
|
};
|
|
817
1216
|
async function createSolanaSignableRollup(rollupConfig, solanaEndpoint = "/sequencer/accept-solana-offchain-tx") {
|
|
818
1217
|
const standardRollup = await createStandardRollup(rollupConfig);
|
|
@@ -833,7 +1232,6 @@ var SovereignClient3 = __toESM(require("@sovereign-sdk/client"));
|
|
|
833
1232
|
StandardRollup,
|
|
834
1233
|
VersionMismatchError,
|
|
835
1234
|
addressFromPublicKey,
|
|
836
|
-
createSolanaPreamble,
|
|
837
1235
|
createSolanaSignableRollup,
|
|
838
1236
|
createStandardRollup,
|
|
839
1237
|
standardTypeBuilder
|