@sovereign-sdk/web3 0.13.0 → 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 +116 -7
- package/dist/index.d.ts +116 -7
- package/dist/index.js +419 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +419 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -1
package/dist/index.mjs
CHANGED
|
@@ -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
|
|
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
|
-
|
|
446
|
-
|
|
447
|
-
|
|
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] =
|
|
476
|
+
preamble[offset] = pubkeys.length;
|
|
458
477
|
offset += 1;
|
|
459
|
-
|
|
460
|
-
|
|
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(
|
|
568
|
-
|
|
569
|
-
|
|
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
|
*
|
|
@@ -682,6 +986,13 @@ var SolanaSignableRollup = class {
|
|
|
682
986
|
async chainHash() {
|
|
683
987
|
return this.inner.chainHash();
|
|
684
988
|
}
|
|
989
|
+
/**
|
|
990
|
+
* Pre-fetches the rollup schema, populating the serializer and chain hash caches.
|
|
991
|
+
* Call this during initialization to avoid the latency of lazy-loading on the first transaction.
|
|
992
|
+
*/
|
|
993
|
+
hydrate() {
|
|
994
|
+
return this.inner.hydrate();
|
|
995
|
+
}
|
|
685
996
|
async healthcheck(timeout) {
|
|
686
997
|
return this.inner.healthcheck(timeout);
|
|
687
998
|
}
|
|
@@ -757,6 +1068,102 @@ var SolanaSignableRollup = class {
|
|
|
757
1068
|
buffer.set(message.signature, offset);
|
|
758
1069
|
return buffer;
|
|
759
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
|
+
}
|
|
760
1167
|
};
|
|
761
1168
|
async function createSolanaSignableRollup(rollupConfig, solanaEndpoint = "/sequencer/accept-solana-offchain-tx") {
|
|
762
1169
|
const standardRollup = await createStandardRollup(rollupConfig);
|
|
@@ -776,7 +1183,6 @@ export {
|
|
|
776
1183
|
StandardRollup,
|
|
777
1184
|
VersionMismatchError,
|
|
778
1185
|
addressFromPublicKey,
|
|
779
|
-
createSolanaPreamble,
|
|
780
1186
|
createSolanaSignableRollup,
|
|
781
1187
|
createStandardRollup,
|
|
782
1188
|
standardTypeBuilder
|