@molpha/sdk 0.2.0-dev-20261008064918 → 0.2.0-dev-20261010184043
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/README.md +54 -10
- package/dist/index.d.ts +121 -40
- package/dist/index.js +226 -115
- package/dist/index.js.map +1 -1
- package/idl/molpha.json +27 -42
- package/idl/profiles/dev/molpha.json +27 -42
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { toPublicKey, addressBytes,
|
|
1
|
+
import { toPublicKey, addressBytes, setComputeUnitLimit, setComputeUnitPrice, SYSTEM_PROGRAM_ADDRESS, toSolanaAddress, getAssociatedTokenAddressSync, TOKEN_PROGRAM_ADDRESS, addressFromBytes, findProgramAddressSync } from './chunk-H77BBZOL.js';
|
|
2
2
|
import { getAddressEncoder, address, getProgramDerivedAddress } from '@solana/kit';
|
|
3
3
|
import { keccak_256 } from '@noble/hashes/sha3.js';
|
|
4
4
|
import { utf8ToBytes, hexToBytes as hexToBytes$1, concatBytes as concatBytes$1, bytesToHex as bytesToHex$1, randomBytes } from '@noble/hashes/utils.js';
|
|
5
5
|
import { secp256k1 } from '@noble/curves/secp256k1.js';
|
|
6
6
|
import { gcm } from '@noble/ciphers/aes.js';
|
|
7
|
-
import { AnchorProvider, Program
|
|
7
|
+
import { web3, AnchorProvider, Program } from '@anchor-lang/core';
|
|
8
8
|
import BN from 'bn.js';
|
|
9
9
|
import { ed25519 } from '@noble/curves/ed25519.js';
|
|
10
10
|
|
|
@@ -401,6 +401,17 @@ function deriveSelectionBitmap(sourceId, registryVersion, nodeCount, signaturesR
|
|
|
401
401
|
const size = effectiveSelectionSize(signaturesRequired, redundancy, nodeCount);
|
|
402
402
|
return deriveGroupBitmap(seed, nodeCount, size);
|
|
403
403
|
}
|
|
404
|
+
|
|
405
|
+
// src/core/timestamp.ts
|
|
406
|
+
var ROUND_TICK_MS = 100;
|
|
407
|
+
function timestampSeconds(timestamp) {
|
|
408
|
+
const ms = typeof timestamp === "bigint" ? timestamp : BigInt(timestamp);
|
|
409
|
+
if (ms < 0n) throw new RangeError("timestamp must not be negative");
|
|
410
|
+
return Number(ms / 1000n);
|
|
411
|
+
}
|
|
412
|
+
function timestampAgeSeconds(timestamp, nowSeconds) {
|
|
413
|
+
return Math.max(0, nowSeconds - timestampSeconds(timestamp));
|
|
414
|
+
}
|
|
404
415
|
var REQUEST_AUTH_DOMAIN = utf8("MOLPHA_REQAUTH_V1");
|
|
405
416
|
function encodeRequestAuth(fields) {
|
|
406
417
|
if (!Number.isInteger(fields.signaturesRequired) || fields.signaturesRequired < 1 || fields.signaturesRequired > 255) {
|
|
@@ -808,8 +819,6 @@ function parseGatewayInfo(data) {
|
|
|
808
819
|
const info = { gatewayAuthority };
|
|
809
820
|
if (programId !== void 0) info.programId = programId;
|
|
810
821
|
const positive = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : void 0;
|
|
811
|
-
const tickMs = positive(record.tickMs);
|
|
812
|
-
if (tickMs !== void 0) info.tickMs = tickMs;
|
|
813
822
|
const roundTimeoutSeconds = positive(record.roundTimeoutSeconds);
|
|
814
823
|
if (roundTimeoutSeconds !== void 0) info.roundTimeoutSeconds = roundTimeoutSeconds;
|
|
815
824
|
const maxInflightRounds = positive(record.maxInflightRounds);
|
|
@@ -820,26 +829,21 @@ function parseGatewayInfo(data) {
|
|
|
820
829
|
// src/gateway/index.ts
|
|
821
830
|
var ZERO_AUTH_SIG = new Uint8Array(64);
|
|
822
831
|
var authTimestampNow = () => Math.floor(Date.now() / 1e3);
|
|
823
|
-
var
|
|
824
|
-
|
|
825
|
-
return tickMs - nowMs % tickMs + 1;
|
|
826
|
-
}
|
|
827
|
-
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
832
|
+
var timerSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
833
|
+
var CONFLICT_JITTER_MS = 20;
|
|
828
834
|
var BACKOFF_BASE_MS = 250;
|
|
829
835
|
var BACKOFF_MAX_MS = 5e3;
|
|
830
836
|
var DEFAULT_ROUND_TIMEOUT_MS = 35e3;
|
|
831
837
|
var DEFAULT_MAX_RETRIES = 6;
|
|
832
|
-
function retryDelayMs(cause, failures,
|
|
833
|
-
const
|
|
838
|
+
function retryDelayMs(cause, failures, random = Math.random) {
|
|
839
|
+
const backoff = Math.min(BACKOFF_BASE_MS * 2 ** Math.max(failures - 1, 0), BACKOFF_MAX_MS);
|
|
834
840
|
switch (cause.kind) {
|
|
835
841
|
case "conflict":
|
|
836
|
-
return
|
|
842
|
+
return ROUND_TICK_MS + random() * CONFLICT_JITTER_MS;
|
|
837
843
|
case "busy":
|
|
838
|
-
return Math.max(
|
|
839
|
-
case "error":
|
|
840
|
-
|
|
841
|
-
return Math.max(nextTick, backoff / 2 + random() * (backoff / 2));
|
|
842
|
-
}
|
|
844
|
+
return Math.max(cause.retryAfterMs ?? 0, backoff) + random() * backoff;
|
|
845
|
+
case "error":
|
|
846
|
+
return backoff / 2 + random() * (backoff / 2);
|
|
843
847
|
}
|
|
844
848
|
}
|
|
845
849
|
function mergeCause(a, b) {
|
|
@@ -877,9 +881,8 @@ var MolphaGateway = class {
|
|
|
877
881
|
programIdBytes;
|
|
878
882
|
/** Gateway PDA per endpoint URL; resolved once per client lifetime. */
|
|
879
883
|
gatewayPdas = /* @__PURE__ */ new Map();
|
|
880
|
-
/** Advertised timing per endpoint URL, read lazily on the first retry. */
|
|
881
|
-
advertisedInfo = /* @__PURE__ */ new Map();
|
|
882
884
|
random;
|
|
885
|
+
sleep;
|
|
883
886
|
constructor(endpoints, getRegistrySelectionConfig = async () => {
|
|
884
887
|
throw new Error(
|
|
885
888
|
"MolphaGateway requires getRegistrySelectionConfig to request signed data \u2014 pass the current on-chain registry version, redundancy buffer and node count (e.g. () => solana.getRegistrySelectionConfig())"
|
|
@@ -901,6 +904,7 @@ var MolphaGateway = class {
|
|
|
901
904
|
this.programId = options.programId ?? MOLPHA_PROGRAM_ID;
|
|
902
905
|
this.programIdBytes = addressToBytes(this.programId);
|
|
903
906
|
this.random = options.random ?? Math.random;
|
|
907
|
+
this.sleep = options.sleep ?? timerSleep;
|
|
904
908
|
}
|
|
905
909
|
/** The configured gateway base URLs, in failover order. */
|
|
906
910
|
endpointUrls() {
|
|
@@ -976,10 +980,13 @@ var MolphaGateway = class {
|
|
|
976
980
|
* failover. The body is POSTed to each endpoint in order until one `completed`s.
|
|
977
981
|
*
|
|
978
982
|
* The round's `sourceId` is derived from `apiConfig`. The request carries no round timestamp:
|
|
979
|
-
* the gateway
|
|
980
|
-
*
|
|
981
|
-
* timestamp and the signers' bitmap
|
|
982
|
-
*
|
|
983
|
+
* the gateway stamps the round with its own clock floored to the round tick (`timestamp`, unix
|
|
984
|
+
* milliseconds, a multiple of {@link ROUND_TICK_MS}), and the committee follows from it. The
|
|
985
|
+
* result carries the assigned timestamp and the signers' bitmap; the timestamp is taken as the
|
|
986
|
+
* gateway gave it. Requests for the same source, quorum and registry version inside one tick
|
|
987
|
+
* share one round. One consumer gets one round per tick per source and quorum: a second request
|
|
988
|
+
* in the tick is refused (HTTP 409) and retried one tick later. A retry is stamped anew, so it
|
|
989
|
+
* is a new round that spends another unit of round quota.
|
|
983
990
|
*
|
|
984
991
|
* The request is authorized by a `RequestAuth` signature (`authSig`) over the program id, the
|
|
985
992
|
* endpoint's Gateway PDA, the source id, the quorum and `authTimestamp` (unix seconds, read
|
|
@@ -1066,7 +1073,6 @@ var MolphaGateway = class {
|
|
|
1066
1073
|
}
|
|
1067
1074
|
}
|
|
1068
1075
|
let lastError;
|
|
1069
|
-
let tickMs = opts.tickMs;
|
|
1070
1076
|
let groupSize = effectiveSelectionSize(signaturesRequired, redundancyBuffer, nodeCount);
|
|
1071
1077
|
let registryIndexes = Array.from({ length: nodeCount }, (_, index) => index);
|
|
1072
1078
|
let cause = { kind: "error" };
|
|
@@ -1074,8 +1080,7 @@ var MolphaGateway = class {
|
|
|
1074
1080
|
let retryNow = false;
|
|
1075
1081
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
1076
1082
|
if (attempt > 0 && !retryNow) {
|
|
1077
|
-
|
|
1078
|
-
await sleep(retryDelayMs(cause, attempt, Date.now(), tickMs, this.random));
|
|
1083
|
+
await this.sleep(retryDelayMs(cause, attempt, this.random));
|
|
1079
1084
|
}
|
|
1080
1085
|
retryNow = false;
|
|
1081
1086
|
cause = { kind: "error" };
|
|
@@ -1273,27 +1278,6 @@ var MolphaGateway = class {
|
|
|
1273
1278
|
if (registry.nodeCount !== void 0) return registry;
|
|
1274
1279
|
return { ...registry, nodes: await this.getNodes() };
|
|
1275
1280
|
}
|
|
1276
|
-
/**
|
|
1277
|
-
* The gateway's advertised tick grid, read once per endpoint and only when a retry needs it. Any
|
|
1278
|
-
* failure (an old gateway without `/v1/info`, a timeout) means "not advertised", so the caller
|
|
1279
|
-
* falls back to the default; a failure is not cached.
|
|
1280
|
-
*/
|
|
1281
|
-
async advertisedTickMs(timeoutMs) {
|
|
1282
|
-
for (const endpoint of this.endpoints) {
|
|
1283
|
-
let pending = this.advertisedInfo.get(endpoint.url);
|
|
1284
|
-
if (!pending) {
|
|
1285
|
-
pending = this.fetchGatewayInfo(endpoint, Math.min(timeoutMs, 3e3)).catch(() => void 0);
|
|
1286
|
-
this.advertisedInfo.set(endpoint.url, pending);
|
|
1287
|
-
}
|
|
1288
|
-
const info = await pending;
|
|
1289
|
-
if (info === void 0) {
|
|
1290
|
-
this.advertisedInfo.delete(endpoint.url);
|
|
1291
|
-
continue;
|
|
1292
|
-
}
|
|
1293
|
-
if (info.tickMs !== void 0) return info.tickMs;
|
|
1294
|
-
}
|
|
1295
|
-
return void 0;
|
|
1296
|
-
}
|
|
1297
1281
|
/** Gateway PDA bytes for an endpoint, cached per URL. A failed lookup is not cached. */
|
|
1298
1282
|
resolveGatewayPda(endpoint, timeoutMs) {
|
|
1299
1283
|
let pending = this.gatewayPdas.get(endpoint.url);
|
|
@@ -1630,7 +1614,7 @@ var molpha_default = {
|
|
|
1630
1614
|
name: "molpha",
|
|
1631
1615
|
version: "0.1.0",
|
|
1632
1616
|
spec: "0.1.0",
|
|
1633
|
-
description: "Molpha Oracle Solana Program
|
|
1617
|
+
description: "Molpha Oracle Solana Program"
|
|
1634
1618
|
},
|
|
1635
1619
|
instructions: [
|
|
1636
1620
|
{
|
|
@@ -3089,6 +3073,16 @@ var molpha_default = {
|
|
|
3089
3073
|
writable: true,
|
|
3090
3074
|
signer: true
|
|
3091
3075
|
},
|
|
3076
|
+
{
|
|
3077
|
+
name: "program",
|
|
3078
|
+
docs: [
|
|
3079
|
+
"This program. Its account names the ProgramData account holding the upgrade authority."
|
|
3080
|
+
],
|
|
3081
|
+
address: "chivcFQgxzwkpLvW41PV431HQ4dYpaW3povQH3AdpQt"
|
|
3082
|
+
},
|
|
3083
|
+
{
|
|
3084
|
+
name: "program_data"
|
|
3085
|
+
},
|
|
3092
3086
|
{
|
|
3093
3087
|
name: "token_program",
|
|
3094
3088
|
address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
|
|
@@ -5736,19 +5730,6 @@ var molpha_default = {
|
|
|
5736
5730
|
84
|
|
5737
5731
|
]
|
|
5738
5732
|
},
|
|
5739
|
-
{
|
|
5740
|
-
name: "DelegateStatusChanged",
|
|
5741
|
-
discriminator: [
|
|
5742
|
-
188,
|
|
5743
|
-
202,
|
|
5744
|
-
137,
|
|
5745
|
-
41,
|
|
5746
|
-
54,
|
|
5747
|
-
213,
|
|
5748
|
-
14,
|
|
5749
|
-
92
|
|
5750
|
-
]
|
|
5751
|
-
},
|
|
5752
5733
|
{
|
|
5753
5734
|
name: "EpochClosed",
|
|
5754
5735
|
discriminator: [
|
|
@@ -6611,6 +6592,16 @@ var molpha_default = {
|
|
|
6611
6592
|
code: 6101,
|
|
6612
6593
|
name: "InvalidInitialTau",
|
|
6613
6594
|
msg: "initial_tau is below the minimum ticket threshold"
|
|
6595
|
+
},
|
|
6596
|
+
{
|
|
6597
|
+
code: 6102,
|
|
6598
|
+
name: "InvalidProgramData",
|
|
6599
|
+
msg: "program data account is not this program's"
|
|
6600
|
+
},
|
|
6601
|
+
{
|
|
6602
|
+
code: 6103,
|
|
6603
|
+
name: "NotUpgradeAuthority",
|
|
6604
|
+
msg: "initialize must be signed by the program's upgrade authority"
|
|
6614
6605
|
}
|
|
6615
6606
|
],
|
|
6616
6607
|
types: [
|
|
@@ -6693,6 +6684,9 @@ var molpha_default = {
|
|
|
6693
6684
|
},
|
|
6694
6685
|
{
|
|
6695
6686
|
name: "timestamp",
|
|
6687
|
+
docs: [
|
|
6688
|
+
"Gateway-assigned round time in unix **milliseconds**."
|
|
6689
|
+
],
|
|
6696
6690
|
type: "u64"
|
|
6697
6691
|
}
|
|
6698
6692
|
]
|
|
@@ -6908,34 +6902,6 @@ var molpha_default = {
|
|
|
6908
6902
|
]
|
|
6909
6903
|
}
|
|
6910
6904
|
},
|
|
6911
|
-
{
|
|
6912
|
-
name: "DelegateStatusChanged",
|
|
6913
|
-
type: {
|
|
6914
|
-
kind: "struct",
|
|
6915
|
-
fields: [
|
|
6916
|
-
{
|
|
6917
|
-
name: "delegate",
|
|
6918
|
-
type: "pubkey"
|
|
6919
|
-
},
|
|
6920
|
-
{
|
|
6921
|
-
name: "owner",
|
|
6922
|
-
type: "pubkey"
|
|
6923
|
-
},
|
|
6924
|
-
{
|
|
6925
|
-
name: "old_status",
|
|
6926
|
-
type: "u8"
|
|
6927
|
-
},
|
|
6928
|
-
{
|
|
6929
|
-
name: "new_status",
|
|
6930
|
-
type: "u8"
|
|
6931
|
-
},
|
|
6932
|
-
{
|
|
6933
|
-
name: "updated_at",
|
|
6934
|
-
type: "i64"
|
|
6935
|
-
}
|
|
6936
|
-
]
|
|
6937
|
-
}
|
|
6938
|
-
},
|
|
6939
6905
|
{
|
|
6940
6906
|
name: "EpochClosed",
|
|
6941
6907
|
type: {
|
|
@@ -7344,6 +7310,9 @@ var molpha_default = {
|
|
|
7344
7310
|
},
|
|
7345
7311
|
{
|
|
7346
7312
|
name: "timestamp",
|
|
7313
|
+
docs: [
|
|
7314
|
+
"Unix milliseconds. Updates are strictly ordered on it, so two rounds in one second order."
|
|
7315
|
+
],
|
|
7347
7316
|
type: "u64"
|
|
7348
7317
|
},
|
|
7349
7318
|
{
|
|
@@ -9501,11 +9470,85 @@ function estimateSubmitComputeUnits(signerCount) {
|
|
|
9501
9470
|
const measured = 43e3 + 9200 * signerCount;
|
|
9502
9471
|
return Math.min(Math.round(measured * 1.15) + 1e4, MAX_COMPUTE_UNIT_LIMIT);
|
|
9503
9472
|
}
|
|
9473
|
+
function estimateSubmitBatchComputeUnits(signerCounts) {
|
|
9474
|
+
if (signerCounts.length === 0 || signerCounts.some((n) => !Number.isInteger(n) || n < 1)) {
|
|
9475
|
+
return MAX_COMPUTE_UNIT_LIMIT;
|
|
9476
|
+
}
|
|
9477
|
+
const measured = signerCounts.reduce((sum, n) => sum + 43e3 + 9200 * n, 0);
|
|
9478
|
+
return Math.min(Math.round(measured * 1.15) + 1e4, MAX_COMPUTE_UNIT_LIMIT);
|
|
9479
|
+
}
|
|
9480
|
+
var SUBMIT_INSTRUCTION_DATA_BYTES = 234;
|
|
9481
|
+
var PLACEHOLDER_BLOCKHASH = "11111111111111111111111111111111";
|
|
9482
|
+
var shortVecBytes = (n) => n < 128 ? 1 : n < 16384 ? 2 : 3;
|
|
9483
|
+
function transactionSize(payer, instructions) {
|
|
9484
|
+
const tx = new web3.Transaction();
|
|
9485
|
+
tx.feePayer = payer;
|
|
9486
|
+
tx.recentBlockhash = PLACEHOLDER_BLOCKHASH;
|
|
9487
|
+
tx.add(...instructions);
|
|
9488
|
+
const message = tx.compileMessage();
|
|
9489
|
+
const keys = message.accountKeys.length;
|
|
9490
|
+
let size = 1 + 64 + 3 + shortVecBytes(keys) + 32 * keys + 32 + shortVecBytes(message.instructions.length);
|
|
9491
|
+
message.instructions.forEach((compiled, i) => {
|
|
9492
|
+
const dataBytes = instructions[i].data.length;
|
|
9493
|
+
size += 1 + shortVecBytes(compiled.accounts.length) + compiled.accounts.length + shortVecBytes(dataBytes) + dataBytes;
|
|
9494
|
+
});
|
|
9495
|
+
return size;
|
|
9496
|
+
}
|
|
9497
|
+
function estimateSubmitTransactionSize(count, signerCount, opts = {}) {
|
|
9498
|
+
const key = () => web3.Keypair.generate().publicKey;
|
|
9499
|
+
const [payer, registry, feed, protocolConfig, program] = [key(), key(), key(), key(), key()];
|
|
9500
|
+
const distinct = Math.max(signerCount, opts.distinctSigners ?? signerCount);
|
|
9501
|
+
const nodes = Array.from({ length: distinct }, key);
|
|
9502
|
+
const instructions = [setComputeUnitLimit(1)];
|
|
9503
|
+
if (opts.priorityFee) instructions.push(setComputeUnitPrice(1));
|
|
9504
|
+
for (let i = 0; i < count; i++) {
|
|
9505
|
+
instructions.push(
|
|
9506
|
+
new web3.TransactionInstruction({
|
|
9507
|
+
programId: program,
|
|
9508
|
+
keys: [
|
|
9509
|
+
{ pubkey: payer, isSigner: true, isWritable: true },
|
|
9510
|
+
{ pubkey: registry, isSigner: false, isWritable: false },
|
|
9511
|
+
{ pubkey: feed, isSigner: false, isWritable: true },
|
|
9512
|
+
{ pubkey: protocolConfig, isSigner: false, isWritable: false },
|
|
9513
|
+
{ pubkey: new web3.PublicKey(SYSTEM_PROGRAM_ADDRESS), isSigner: false, isWritable: false },
|
|
9514
|
+
...Array.from({ length: signerCount }, (_, j) => ({
|
|
9515
|
+
pubkey: nodes[(i * signerCount + j) % distinct],
|
|
9516
|
+
isSigner: false,
|
|
9517
|
+
isWritable: false
|
|
9518
|
+
}))
|
|
9519
|
+
],
|
|
9520
|
+
data: Buffer.alloc(SUBMIT_INSTRUCTION_DATA_BYTES)
|
|
9521
|
+
})
|
|
9522
|
+
);
|
|
9523
|
+
}
|
|
9524
|
+
return transactionSize(payer, instructions);
|
|
9525
|
+
}
|
|
9526
|
+
function maxAttestationsPerTransaction(signerCount, opts = {}) {
|
|
9527
|
+
let count = 0;
|
|
9528
|
+
while (estimateSubmitTransactionSize(count + 1, signerCount, opts) <= web3.PACKET_DATA_SIZE) count++;
|
|
9529
|
+
return count;
|
|
9530
|
+
}
|
|
9504
9531
|
var REGISTRY_CACHE_MS = 3e4;
|
|
9505
9532
|
var KEY_CACHE_LIMIT = 8192;
|
|
9506
9533
|
var PRIORITY_FEE_CACHE_MS = 5e3;
|
|
9507
9534
|
var AUTO_FEE_PERCENTILE = 0.75;
|
|
9508
9535
|
var AUTO_FEE_CAP_MICRO_LAMPORTS = 1e6;
|
|
9536
|
+
var BatchTooLargeError = class extends RangeError {
|
|
9537
|
+
constructor(fits, size, limit, count) {
|
|
9538
|
+
super(
|
|
9539
|
+
`${count} attestations need a ${size} byte transaction, over the ${limit} byte limit; ${fits} fit`
|
|
9540
|
+
);
|
|
9541
|
+
this.fits = fits;
|
|
9542
|
+
this.size = size;
|
|
9543
|
+
this.limit = limit;
|
|
9544
|
+
this.count = count;
|
|
9545
|
+
this.name = "BatchTooLargeError";
|
|
9546
|
+
}
|
|
9547
|
+
fits;
|
|
9548
|
+
size;
|
|
9549
|
+
limit;
|
|
9550
|
+
count;
|
|
9551
|
+
};
|
|
9509
9552
|
var MolphaSolanaClient = class _MolphaSolanaClient {
|
|
9510
9553
|
constructor(program, provider, programId) {
|
|
9511
9554
|
this.program = program;
|
|
@@ -9677,6 +9720,87 @@ var MolphaSolanaClient = class _MolphaSolanaClient {
|
|
|
9677
9720
|
* `keccak256(rawValue)`; {@link buildSubmitAttestationArgs} validates that before send.
|
|
9678
9721
|
*/
|
|
9679
9722
|
async submitAttestation(attestation, opts) {
|
|
9723
|
+
const { args, accounts, remaining, feed } = await this.prepareSubmit(attestation, opts);
|
|
9724
|
+
const preInstructions = [
|
|
9725
|
+
setComputeUnitLimit(opts?.computeUnitLimit ?? estimateSubmitComputeUnits(remaining.length))
|
|
9726
|
+
];
|
|
9727
|
+
const price = await this.resolvePriorityFee(opts?.priorityFeeMicroLamports, feed);
|
|
9728
|
+
if (price > 0) preInstructions.push(setComputeUnitPrice(price));
|
|
9729
|
+
const signature = await this.methods.submitAttestation(args).accountsPartial(accounts).remainingAccounts(remaining).preInstructions(preInstructions).rpc();
|
|
9730
|
+
return { signature, feed };
|
|
9731
|
+
}
|
|
9732
|
+
/**
|
|
9733
|
+
* Submit several attestations of one feed in one transaction.
|
|
9734
|
+
*
|
|
9735
|
+
* Each becomes its own `submit_attestation` instruction, in timestamp order, and instructions run
|
|
9736
|
+
* in the order they are listed: all of them are applied, oldest first, whatever order other
|
|
9737
|
+
* transactions reach the leader in. That is what separate submits cannot promise. Transactions sent
|
|
9738
|
+
* milliseconds apart for one feed are overtaken by a newer one often enough (about a third at 250 ms
|
|
9739
|
+
* spacing in a devnet run) that the older one fails with `FeedNotNewer`; a batch makes the spacing
|
|
9740
|
+
* between transactions several times longer and spends one fee and one signature for them all.
|
|
9741
|
+
*
|
|
9742
|
+
* The transaction is atomic: if one instruction fails (typically because a newer attestation
|
|
9743
|
+
* of the feed landed first, so every instruction is stale) none of them applies. Batch only what
|
|
9744
|
+
* is due together, and keep batches of one feed in the order they were made.
|
|
9745
|
+
*
|
|
9746
|
+
* All attestations must be for one feed (the same `sourceId` and `signaturesRequired`) with
|
|
9747
|
+
* distinct timestamps. They need not share a registry version or a signer set. Values longer than
|
|
9748
|
+
* 32 bytes (`rawValue`) are not supported here: submit those with {@link submitAttestation}.
|
|
9749
|
+
*
|
|
9750
|
+
* @throws {BatchTooLargeError} when they do not fit in one transaction (about three do with
|
|
9751
|
+
* three signers; see {@link maxAttestationsPerTransaction}). Nothing is sent.
|
|
9752
|
+
*/
|
|
9753
|
+
async submitAttestations(attestations, opts) {
|
|
9754
|
+
if (attestations.length === 0) {
|
|
9755
|
+
throw new RangeError("submitAttestations needs at least one attestation");
|
|
9756
|
+
}
|
|
9757
|
+
const sorted = [...attestations].sort((a, b) => a.payload.timestamp - b.payload.timestamp);
|
|
9758
|
+
const feedKey = (a) => `${bytesToHex(toFixedBytes(a.payload.sourceId, 32, "sourceId"))}:${a.payload.signaturesRequired}`;
|
|
9759
|
+
const first = feedKey(sorted[0]);
|
|
9760
|
+
sorted.forEach((a, i) => {
|
|
9761
|
+
if (feedKey(a) !== first) {
|
|
9762
|
+
throw new RangeError(
|
|
9763
|
+
"submitAttestations: every attestation must be for the same feed (sourceId and signaturesRequired)"
|
|
9764
|
+
);
|
|
9765
|
+
}
|
|
9766
|
+
if (i > 0 && a.payload.timestamp === sorted[i - 1].payload.timestamp) {
|
|
9767
|
+
throw new RangeError(
|
|
9768
|
+
`submitAttestations: two attestations have timestamp ${a.payload.timestamp}; the second would be refused as not newer`
|
|
9769
|
+
);
|
|
9770
|
+
}
|
|
9771
|
+
});
|
|
9772
|
+
if (sorted.length === 1) return { ...await this.submitAttestation(sorted[0], opts), count: 1 };
|
|
9773
|
+
const prepared = await Promise.all(sorted.map((a) => this.prepareSubmit(a)));
|
|
9774
|
+
const instructions = await Promise.all(
|
|
9775
|
+
prepared.map(
|
|
9776
|
+
(p) => this.methods.submitAttestation(p.args).accountsPartial(p.accounts).remainingAccounts(p.remaining).instruction()
|
|
9777
|
+
)
|
|
9778
|
+
);
|
|
9779
|
+
const feed = prepared[0].feed;
|
|
9780
|
+
const budget = [
|
|
9781
|
+
setComputeUnitLimit(
|
|
9782
|
+
opts?.computeUnitLimit ?? estimateSubmitBatchComputeUnits(prepared.map((p) => p.remaining.length))
|
|
9783
|
+
)
|
|
9784
|
+
];
|
|
9785
|
+
const price = await this.resolvePriorityFee(opts?.priorityFeeMicroLamports, feed);
|
|
9786
|
+
if (price > 0) budget.push(setComputeUnitPrice(price));
|
|
9787
|
+
const payer = this.provider.wallet.publicKey;
|
|
9788
|
+
const size = transactionSize(payer, [...budget, ...instructions]);
|
|
9789
|
+
if (size > web3.PACKET_DATA_SIZE) {
|
|
9790
|
+
let fits = 0;
|
|
9791
|
+
while (fits < instructions.length && transactionSize(payer, [...budget, ...instructions.slice(0, fits + 1)]) <= web3.PACKET_DATA_SIZE) {
|
|
9792
|
+
fits++;
|
|
9793
|
+
}
|
|
9794
|
+
throw new BatchTooLargeError(fits, size, web3.PACKET_DATA_SIZE, instructions.length);
|
|
9795
|
+
}
|
|
9796
|
+
const signature = await this.provider.sendAndConfirm(new web3.Transaction().add(...budget, ...instructions));
|
|
9797
|
+
return { signature, feed, count: instructions.length };
|
|
9798
|
+
}
|
|
9799
|
+
/**
|
|
9800
|
+
* Everything one `submit_attestation` instruction needs: the arguments, the named accounts, the
|
|
9801
|
+
* signer `Node` accounts (ascending bit order) and the feed address.
|
|
9802
|
+
*/
|
|
9803
|
+
async prepareSubmit(attestation, opts) {
|
|
9680
9804
|
const { payload, signature: schnorr } = attestation;
|
|
9681
9805
|
const sourceId = toFixedBytes(payload.sourceId, 32, "sourceId");
|
|
9682
9806
|
const submitter = this.wallet;
|
|
@@ -9685,21 +9809,18 @@ var MolphaSolanaClient = class _MolphaSolanaClient {
|
|
|
9685
9809
|
assertSignerCount(remaining.length, payload.signaturesRequired, registry);
|
|
9686
9810
|
const coalitionKey = opts?.coalitionKey ?? await this.computeSignerCoalitionKey(remaining, registry, schnorr.signersBitmap);
|
|
9687
9811
|
const feed = feedPda(sourceId, payload.signaturesRequired, submitter, this.programId);
|
|
9688
|
-
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
|
|
9692
|
-
|
|
9693
|
-
|
|
9694
|
-
|
|
9695
|
-
|
|
9696
|
-
|
|
9697
|
-
|
|
9698
|
-
feed
|
|
9699
|
-
|
|
9700
|
-
systemProgram: SYSTEM_PROGRAM_ADDRESS
|
|
9701
|
-
}).remainingAccounts(remaining).preInstructions(preInstructions).rpc();
|
|
9702
|
-
return { signature, feed };
|
|
9812
|
+
return {
|
|
9813
|
+
args: buildSubmitAttestationArgs(attestation, coalitionKey, opts?.rawValue),
|
|
9814
|
+
accounts: {
|
|
9815
|
+
submitter,
|
|
9816
|
+
registry: registryPda(payload.registryVersion, this.programId),
|
|
9817
|
+
feed,
|
|
9818
|
+
protocolConfig: protocolConfigPda(this.programId),
|
|
9819
|
+
systemProgram: SYSTEM_PROGRAM_ADDRESS
|
|
9820
|
+
},
|
|
9821
|
+
remaining,
|
|
9822
|
+
feed
|
|
9823
|
+
};
|
|
9703
9824
|
}
|
|
9704
9825
|
/** The registry for `version`, reused for {@link REGISTRY_CACHE_MS}. */
|
|
9705
9826
|
fetchRegistryCached(version) {
|
|
@@ -10024,16 +10145,6 @@ function attestationMessageHashFromAttestation(attestation) {
|
|
|
10024
10145
|
});
|
|
10025
10146
|
}
|
|
10026
10147
|
|
|
10027
|
-
// src/core/timestamp.ts
|
|
10028
|
-
function timestampSeconds(timestamp) {
|
|
10029
|
-
const ms = typeof timestamp === "bigint" ? timestamp : BigInt(timestamp);
|
|
10030
|
-
if (ms < 0n) throw new RangeError("timestamp must not be negative");
|
|
10031
|
-
return Number(ms / 1000n);
|
|
10032
|
-
}
|
|
10033
|
-
function timestampAgeSeconds(timestamp, nowSeconds) {
|
|
10034
|
-
return Math.max(0, nowSeconds - timestampSeconds(timestamp));
|
|
10035
|
-
}
|
|
10036
|
-
|
|
10037
10148
|
// src/core/verifyCodes.ts
|
|
10038
10149
|
var VERIFY_CODES = {
|
|
10039
10150
|
OK: 0,
|
|
@@ -10632,6 +10743,6 @@ var MolphaSDK = class {
|
|
|
10632
10743
|
}
|
|
10633
10744
|
};
|
|
10634
10745
|
|
|
10635
|
-
export { AdaptiveLimiter, AggregationConfigError, DEFAULT_GATEWAY_ENDPOINT, DEFAULT_MAX_RETRIES, DEFAULT_ROUND_TIMEOUT_MS, EIP712_DOMAIN_TYPEHASH, GatewayError, INT256_MAX, INT256_MIN, MESSAGE_PREFIX, MIN_TOLERANCE_SIGNATURES, MOLPHA_DEV_VERIFIERS_PROVISIONAL, MOLPHA_IDL, MOLPHA_PROGRAM_ADDRESS, MOLPHA_PROGRAM_ID, MOLPHA_SDK_PROFILE, MOLPHA_VERIFIER_ABI, MOLPHA_VERIFIER_ADDRESS, MOLPHA_VERIFIER_STARKNET_ADDRESSES, MOLPHA_VERIFIER_STARKNET_SEPOLIA, MolphaGateway, MolphaSDK, MolphaSolanaClient, PlanType, REQUEST_AUTH_DOMAIN, SELECTION_WINDOW_MS, SubmitFailedError, TRANSFER_WITH_AUTHORIZATION_TYPEHASH, UpstreamPaymentRequiredError, VERIFY_CODES, addressToBytes, assertAggregationQuorum, attestationMessageHash, attestationMessageHashFromAttestation, base64ToBytes, bigIntFromBytesBe, bitmapBitSet, bitmapToIndices, buildEvmVerifierArgs, buildStarknetVerifierArgs, buildSubmitAttestationArgs, bytesToBase64, bytesToHex, bytesToHex0x, canonicalizeAPIConfig, canonicalizeAggregation, commitmentAddressToStarknetFelt, computeCoalitionKey, concatBytes, createEvmSignerFromPrivateKey, decodeInt256, deriveGatewayPda, deriveGatewayPdaAddress, deriveGroupBitmap, deriveSelectionBitmap, deriveSelectionSeed, deriveSourceId, deriveSourceIdString, domainSeparator, effectiveSelectionSize, eligibleSetSize, encodeEvmVerifyCalldata, encodeInt256, encodeInt256Decimal, encodeRequestAuth, encodeStarknetVerifyCalldata, ensureLength, estimateSubmitComputeUnits, evmAddressFromPrivateKey, feedPda, formatInt256Decimal, gatewayPda, gatewaySignerFromWallet, getMolphaStarknetVerifierAddress, hashRequestAuth, hexToBytes,
|
|
10746
|
+
export { AdaptiveLimiter, AggregationConfigError, BatchTooLargeError, DEFAULT_GATEWAY_ENDPOINT, DEFAULT_MAX_RETRIES, DEFAULT_ROUND_TIMEOUT_MS, EIP712_DOMAIN_TYPEHASH, GatewayError, INT256_MAX, INT256_MIN, MESSAGE_PREFIX, MIN_TOLERANCE_SIGNATURES, MOLPHA_DEV_VERIFIERS_PROVISIONAL, MOLPHA_IDL, MOLPHA_PROGRAM_ADDRESS, MOLPHA_PROGRAM_ID, MOLPHA_SDK_PROFILE, MOLPHA_VERIFIER_ABI, MOLPHA_VERIFIER_ADDRESS, MOLPHA_VERIFIER_STARKNET_ADDRESSES, MOLPHA_VERIFIER_STARKNET_SEPOLIA, MolphaGateway, MolphaSDK, MolphaSolanaClient, PlanType, REQUEST_AUTH_DOMAIN, ROUND_TICK_MS, SELECTION_WINDOW_MS, SUBMIT_INSTRUCTION_DATA_BYTES, SubmitFailedError, TRANSFER_WITH_AUTHORIZATION_TYPEHASH, UpstreamPaymentRequiredError, VERIFY_CODES, addressToBytes, assertAggregationQuorum, attestationMessageHash, attestationMessageHashFromAttestation, base64ToBytes, bigIntFromBytesBe, bitmapBitSet, bitmapToIndices, buildEvmVerifierArgs, buildStarknetVerifierArgs, buildSubmitAttestationArgs, bytesToBase64, bytesToHex, bytesToHex0x, canonicalizeAPIConfig, canonicalizeAggregation, commitmentAddressToStarknetFelt, computeCoalitionKey, concatBytes, createEvmSignerFromPrivateKey, decodeInt256, deriveGatewayPda, deriveGatewayPdaAddress, deriveGroupBitmap, deriveSelectionBitmap, deriveSelectionSeed, deriveSourceId, deriveSourceIdString, domainSeparator, effectiveSelectionSize, eligibleSetSize, encodeEvmVerifyCalldata, encodeInt256, encodeInt256Decimal, encodeRequestAuth, encodeStarknetVerifyCalldata, ensureLength, estimateSubmitBatchComputeUnits, estimateSubmitComputeUnits, estimateSubmitTransactionSize, evmAddressFromPrivateKey, feedPda, formatInt256Decimal, gatewayPda, gatewaySignerFromWallet, getMolphaStarknetVerifierAddress, hashRequestAuth, hexToBytes, maxAttestationsPerTransaction, nodePda, normalizeEndpoint, normalizeSecp256k1PublicKeyHex, parseEvmVerifyResult, parseGatewayInfo, parseStarknetVerifyResult, parseUpstreamQuote, planIdFromVariant, planPda, planVariant, probeSource, protocolConfigPda, registryPda, registryStatePda, requestMany, resolveRemainingAccounts, retryDelayMs, secp256k1PublicKeyFromCoordinates, selectedIndices, signSourcePayments, signerFromKeypair, signersBitmapToDecimal, signersBitmapToStarknetUint256, signersBitmapToUint256, spreadOffsetMs, subscriptionPda, timestampAgeSeconds, timestampSeconds, toChecksumAddress, toFixedBytes, toFixedHex, transferWithAuthorizationDigest, transferWithAuthorizationHash, u256beFromBigInt, u32be, u32le, u64be, u64le, u8, utf8, validateSuppliedTerms, verifyCodeName };
|
|
10636
10747
|
//# sourceMappingURL=index.js.map
|
|
10637
10748
|
//# sourceMappingURL=index.js.map
|