@molpha/sdk 0.2.0-dev-20261005112114 → 0.2.0-dev-20261005191253
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 +41 -1
- package/dist/{chunk-2X22JY7Q.js → chunk-H77BBZOL.js} +6 -3
- package/dist/chunk-H77BBZOL.js.map +1 -0
- package/dist/index.d.ts +204 -6
- package/dist/index.js +378 -48
- package/dist/index.js.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +1 -1
- package/dist/{wallet-LqCFWwV4.d.ts → wallet--IGHoCtG.d.ts} +1 -1
- package/idl/molpha.json +17 -0
- package/package.json +1 -1
- package/dist/chunk-2X22JY7Q.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { toPublicKey, addressBytes, toSolanaAddress, getAssociatedTokenAddressSync, TOKEN_PROGRAM_ADDRESS, SYSTEM_PROGRAM_ADDRESS, setComputeUnitLimit, addressFromBytes, findProgramAddressSync } from './chunk-
|
|
1
|
+
import { toPublicKey, addressBytes, toSolanaAddress, getAssociatedTokenAddressSync, TOKEN_PROGRAM_ADDRESS, SYSTEM_PROGRAM_ADDRESS, setComputeUnitLimit, setComputeUnitPrice, 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 } from '@anchor-lang/core';
|
|
7
|
+
import { AnchorProvider, Program, web3 } from '@anchor-lang/core';
|
|
8
8
|
import BN from 'bn.js';
|
|
9
9
|
import { ed25519 } from '@noble/curves/ed25519.js';
|
|
10
10
|
|
|
@@ -775,7 +775,16 @@ function parseGatewayInfo(data) {
|
|
|
775
775
|
if (programId !== void 0 && typeof programId !== "string") {
|
|
776
776
|
throw new Error("GET /v1/info programId must be a base58 string");
|
|
777
777
|
}
|
|
778
|
-
|
|
778
|
+
const info = { gatewayAuthority };
|
|
779
|
+
if (programId !== void 0) info.programId = programId;
|
|
780
|
+
const positive = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : void 0;
|
|
781
|
+
const tickMs = positive(record.tickMs);
|
|
782
|
+
if (tickMs !== void 0) info.tickMs = tickMs;
|
|
783
|
+
const roundTimeoutSeconds = positive(record.roundTimeoutSeconds);
|
|
784
|
+
if (roundTimeoutSeconds !== void 0) info.roundTimeoutSeconds = roundTimeoutSeconds;
|
|
785
|
+
const maxInflightRounds = positive(record.maxInflightRounds);
|
|
786
|
+
if (maxInflightRounds !== void 0) info.maxInflightRounds = maxInflightRounds;
|
|
787
|
+
return info;
|
|
779
788
|
}
|
|
780
789
|
|
|
781
790
|
// src/gateway/index.ts
|
|
@@ -786,6 +795,37 @@ function msUntilNextTick(nowMs, tickMs) {
|
|
|
786
795
|
return tickMs - nowMs % tickMs + 1;
|
|
787
796
|
}
|
|
788
797
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
798
|
+
var BACKOFF_BASE_MS = 250;
|
|
799
|
+
var BACKOFF_MAX_MS = 5e3;
|
|
800
|
+
var DEFAULT_ROUND_TIMEOUT_MS = 35e3;
|
|
801
|
+
var DEFAULT_MAX_RETRIES = 6;
|
|
802
|
+
function retryDelayMs(cause, failures, nowMs, tickMs, random = Math.random) {
|
|
803
|
+
const nextTick = msUntilNextTick(nowMs, tickMs);
|
|
804
|
+
switch (cause.kind) {
|
|
805
|
+
case "conflict":
|
|
806
|
+
return nextTick + random() * (tickMs / 2);
|
|
807
|
+
case "busy":
|
|
808
|
+
return Math.max(nextTick, cause.retryAfterMs ?? tickMs) + random() * tickMs;
|
|
809
|
+
case "error": {
|
|
810
|
+
const backoff = Math.min(BACKOFF_BASE_MS * 2 ** Math.max(failures - 1, 0), BACKOFF_MAX_MS);
|
|
811
|
+
return Math.max(nextTick, backoff / 2 + random() * (backoff / 2));
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
function mergeCause(a, b) {
|
|
816
|
+
if (a.kind === "busy" || b.kind === "busy") {
|
|
817
|
+
const waits = [a, b].flatMap((c) => c.kind === "busy" && c.retryAfterMs !== void 0 ? [c.retryAfterMs] : []);
|
|
818
|
+
return waits.length ? { kind: "busy", retryAfterMs: Math.max(...waits) } : { kind: "busy" };
|
|
819
|
+
}
|
|
820
|
+
if (a.kind === "conflict" || b.kind === "conflict") return { kind: "conflict" };
|
|
821
|
+
return { kind: "error" };
|
|
822
|
+
}
|
|
823
|
+
function retryAfterMs(res) {
|
|
824
|
+
const raw = res.headers.get("retry-after");
|
|
825
|
+
if (raw === null) return void 0;
|
|
826
|
+
const seconds = Number(raw);
|
|
827
|
+
return Number.isFinite(seconds) && seconds >= 0 ? Math.min(seconds, 60) * 1e3 : void 0;
|
|
828
|
+
}
|
|
789
829
|
var DEFAULT_GATEWAY_ENDPOINT = "https://dev-gateway.molpha.io/";
|
|
790
830
|
var GatewayError = class extends Error {
|
|
791
831
|
constructor(message, status) {
|
|
@@ -808,6 +848,9 @@ var MolphaGateway = class {
|
|
|
808
848
|
programIdBytes;
|
|
809
849
|
/** Gateway PDA per endpoint URL; resolved once per client lifetime. */
|
|
810
850
|
gatewayPdas = /* @__PURE__ */ new Map();
|
|
851
|
+
/** Advertised timing per endpoint URL, read lazily on the first retry. */
|
|
852
|
+
advertisedInfo = /* @__PURE__ */ new Map();
|
|
853
|
+
random;
|
|
811
854
|
constructor(endpoints, getRegistrySelectionConfig = async () => {
|
|
812
855
|
throw new Error(
|
|
813
856
|
"MolphaGateway requires getRegistrySelectionConfig to request signed data \u2014 pass the current on-chain registry version, redundancy buffer and node count (e.g. () => solana.getRegistrySelectionConfig())"
|
|
@@ -828,6 +871,11 @@ var MolphaGateway = class {
|
|
|
828
871
|
this.allowUnverifiedNodeKeysForPrivateApi = options.allowUnverifiedNodeKeysForPrivateApi ?? false;
|
|
829
872
|
this.programId = options.programId ?? MOLPHA_PROGRAM_ID;
|
|
830
873
|
this.programIdBytes = addressToBytes(this.programId);
|
|
874
|
+
this.random = options.random ?? Math.random;
|
|
875
|
+
}
|
|
876
|
+
/** The configured gateway base URLs, in failover order. */
|
|
877
|
+
endpointUrls() {
|
|
878
|
+
return this.endpoints.map((e) => e.url);
|
|
831
879
|
}
|
|
832
880
|
/** Tries endpoints in order; returns the first node list it can fetch. */
|
|
833
881
|
async getNodes() {
|
|
@@ -931,8 +979,8 @@ var MolphaGateway = class {
|
|
|
931
979
|
encrypt,
|
|
932
980
|
sourcePayment,
|
|
933
981
|
maxAge = 60,
|
|
934
|
-
maxRetries =
|
|
935
|
-
timeoutMs =
|
|
982
|
+
maxRetries = DEFAULT_MAX_RETRIES,
|
|
983
|
+
timeoutMs = DEFAULT_ROUND_TIMEOUT_MS
|
|
936
984
|
} = opts;
|
|
937
985
|
const signaturesRequired = assertSignaturesRequired(opts.signaturesRequired);
|
|
938
986
|
const verifyNodeKeys = opts.verifyNodeKeys ?? this.verifyNodeKeys;
|
|
@@ -954,17 +1002,19 @@ var MolphaGateway = class {
|
|
|
954
1002
|
}
|
|
955
1003
|
const consumerAuthority = opts.consumerAuthority ?? this.defaultConsumerAuthority ?? subscriptionOwner;
|
|
956
1004
|
const authSigner = signer ?? this.defaultSigner;
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
throw new Error("Round context resolved neither nodeCount nor nodes");
|
|
967
|
-
|
|
1005
|
+
let round = await this.resolveContext(opts.context, encrypt !== void 0);
|
|
1006
|
+
let { registryVersion, redundancyBuffer } = round;
|
|
1007
|
+
const nodeCountOf = (r) => {
|
|
1008
|
+
if (r.nodeCount !== void 0 && r.nodes !== void 0 && r.nodes.length !== r.nodeCount) {
|
|
1009
|
+
throw new Error(
|
|
1010
|
+
`Gateway node list has ${r.nodes.length} nodes but registry ${r.registryVersion} has node_count ${r.nodeCount} \u2014 refresh the round context`
|
|
1011
|
+
);
|
|
1012
|
+
}
|
|
1013
|
+
const n = r.nodeCount ?? r.nodes?.length;
|
|
1014
|
+
if (n === void 0) throw new Error("Round context resolved neither nodeCount nor nodes");
|
|
1015
|
+
return n;
|
|
1016
|
+
};
|
|
1017
|
+
let nodeCount = nodeCountOf(round);
|
|
968
1018
|
let terms = null;
|
|
969
1019
|
let authorizations = 0;
|
|
970
1020
|
let requoted = false;
|
|
@@ -987,11 +1037,19 @@ var MolphaGateway = class {
|
|
|
987
1037
|
}
|
|
988
1038
|
}
|
|
989
1039
|
let lastError;
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
1040
|
+
let tickMs = opts.tickMs;
|
|
1041
|
+
let groupSize = effectiveSelectionSize(signaturesRequired, redundancyBuffer, nodeCount);
|
|
1042
|
+
let registryIndexes = Array.from({ length: nodeCount }, (_, index) => index);
|
|
1043
|
+
let cause = { kind: "error" };
|
|
1044
|
+
let refreshedOnce = false;
|
|
1045
|
+
let retryNow = false;
|
|
993
1046
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
994
|
-
if (attempt > 0
|
|
1047
|
+
if (attempt > 0 && !retryNow) {
|
|
1048
|
+
tickMs ??= await this.advertisedTickMs(timeoutMs) ?? DEFAULT_TICK_MS;
|
|
1049
|
+
await sleep(retryDelayMs(cause, attempt, Date.now(), tickMs, this.random));
|
|
1050
|
+
}
|
|
1051
|
+
retryNow = false;
|
|
1052
|
+
cause = { kind: "error" };
|
|
995
1053
|
let encKeyBundle;
|
|
996
1054
|
if (encrypt) {
|
|
997
1055
|
const recipients = selectedNodesForPrivateApiEncryption(round.nodes ?? [], registryIndexes);
|
|
@@ -1025,6 +1083,7 @@ var MolphaGateway = class {
|
|
|
1025
1083
|
if (encKeyBundle) baseBody.encKeyBundle = encKeyBundle;
|
|
1026
1084
|
if (sourcePayments) baseBody.sourcePayments = sourcePayments;
|
|
1027
1085
|
let requote = false;
|
|
1086
|
+
let restartForRefresh = false;
|
|
1028
1087
|
for (const endpoint of this.endpoints) {
|
|
1029
1088
|
try {
|
|
1030
1089
|
let authSig = ZERO_AUTH_SIG;
|
|
@@ -1098,17 +1157,27 @@ var MolphaGateway = class {
|
|
|
1098
1157
|
break;
|
|
1099
1158
|
}
|
|
1100
1159
|
const errorDetail = !res.ok ? await parseGatewayErrorDetail(res) : void 0;
|
|
1101
|
-
if (res.status === 400
|
|
1160
|
+
if (res.status === 400 && !refreshedOnce && !restartForRefresh && /registryVersion.*current version/i.test(errorDetail ?? "")) {
|
|
1161
|
+
restartForRefresh = true;
|
|
1162
|
+
break;
|
|
1163
|
+
}
|
|
1164
|
+
if (res.status === 400 || res.status === 401 || res.status === 403) {
|
|
1102
1165
|
throw new GatewayError(
|
|
1103
1166
|
formatGatewayErrorMessage("Gateway rejected request", res.status, errorDetail),
|
|
1104
1167
|
res.status
|
|
1105
1168
|
);
|
|
1106
1169
|
}
|
|
1107
|
-
if (res.status === 503) {
|
|
1170
|
+
if (res.status === 503 || res.status === 429) {
|
|
1108
1171
|
lastError = new GatewayError(
|
|
1109
|
-
formatGatewayErrorMessage(
|
|
1110
|
-
|
|
1172
|
+
formatGatewayErrorMessage(
|
|
1173
|
+
res.status === 429 ? "Gateway rate limited" : "Gateway unavailable",
|
|
1174
|
+
res.status,
|
|
1175
|
+
errorDetail
|
|
1176
|
+
),
|
|
1177
|
+
res.status
|
|
1111
1178
|
);
|
|
1179
|
+
const wait = retryAfterMs(res);
|
|
1180
|
+
cause = mergeCause(cause, wait === void 0 ? { kind: "busy" } : { kind: "busy", retryAfterMs: wait });
|
|
1112
1181
|
continue;
|
|
1113
1182
|
}
|
|
1114
1183
|
if (!res.ok) {
|
|
@@ -1116,6 +1185,7 @@ var MolphaGateway = class {
|
|
|
1116
1185
|
formatGatewayErrorMessage("Gateway error", res.status, errorDetail),
|
|
1117
1186
|
res.status
|
|
1118
1187
|
);
|
|
1188
|
+
if (res.status === 409) cause = mergeCause(cause, { kind: "conflict" });
|
|
1119
1189
|
continue;
|
|
1120
1190
|
}
|
|
1121
1191
|
const json = await res.json();
|
|
@@ -1129,12 +1199,23 @@ var MolphaGateway = class {
|
|
|
1129
1199
|
lastError = new Error(`Gateway returned status: ${json.status}`);
|
|
1130
1200
|
} catch (err) {
|
|
1131
1201
|
if (err instanceof UpstreamPaymentRequiredError) throw err;
|
|
1132
|
-
if (err instanceof GatewayError && (err.status === 400 || err.status === 401 || err.status === 402)) {
|
|
1202
|
+
if (err instanceof GatewayError && (err.status === 400 || err.status === 401 || err.status === 402 || err.status === 403)) {
|
|
1133
1203
|
throw err;
|
|
1134
1204
|
}
|
|
1135
1205
|
lastError = err;
|
|
1136
1206
|
}
|
|
1137
1207
|
}
|
|
1208
|
+
if (restartForRefresh) {
|
|
1209
|
+
round = await this.resolveContext(void 0, encrypt !== void 0);
|
|
1210
|
+
({ registryVersion, redundancyBuffer } = round);
|
|
1211
|
+
nodeCount = nodeCountOf(round);
|
|
1212
|
+
groupSize = effectiveSelectionSize(signaturesRequired, redundancyBuffer, nodeCount);
|
|
1213
|
+
registryIndexes = Array.from({ length: nodeCount }, (_, index) => index);
|
|
1214
|
+
refreshedOnce = true;
|
|
1215
|
+
retryNow = true;
|
|
1216
|
+
attempt--;
|
|
1217
|
+
continue;
|
|
1218
|
+
}
|
|
1138
1219
|
if (requote) continue;
|
|
1139
1220
|
}
|
|
1140
1221
|
throw lastError instanceof Error ? lastError : new Error("Gateway round failed after retries");
|
|
@@ -1163,6 +1244,27 @@ var MolphaGateway = class {
|
|
|
1163
1244
|
if (registry.nodeCount !== void 0) return registry;
|
|
1164
1245
|
return { ...registry, nodes: await this.getNodes() };
|
|
1165
1246
|
}
|
|
1247
|
+
/**
|
|
1248
|
+
* The gateway's advertised tick grid, read once per endpoint and only when a retry needs it. Any
|
|
1249
|
+
* failure (an old gateway without `/v1/info`, a timeout) means "not advertised", so the caller
|
|
1250
|
+
* falls back to the default; a failure is not cached.
|
|
1251
|
+
*/
|
|
1252
|
+
async advertisedTickMs(timeoutMs) {
|
|
1253
|
+
for (const endpoint of this.endpoints) {
|
|
1254
|
+
let pending = this.advertisedInfo.get(endpoint.url);
|
|
1255
|
+
if (!pending) {
|
|
1256
|
+
pending = this.fetchGatewayInfo(endpoint, Math.min(timeoutMs, 3e3)).catch(() => void 0);
|
|
1257
|
+
this.advertisedInfo.set(endpoint.url, pending);
|
|
1258
|
+
}
|
|
1259
|
+
const info = await pending;
|
|
1260
|
+
if (info === void 0) {
|
|
1261
|
+
this.advertisedInfo.delete(endpoint.url);
|
|
1262
|
+
continue;
|
|
1263
|
+
}
|
|
1264
|
+
if (info.tickMs !== void 0) return info.tickMs;
|
|
1265
|
+
}
|
|
1266
|
+
return void 0;
|
|
1267
|
+
}
|
|
1166
1268
|
/** Gateway PDA bytes for an endpoint, cached per URL. A failed lookup is not cached. */
|
|
1167
1269
|
resolveGatewayPda(endpoint, timeoutMs) {
|
|
1168
1270
|
let pending = this.gatewayPdas.get(endpoint.url);
|
|
@@ -6475,6 +6577,11 @@ var molpha_default = {
|
|
|
6475
6577
|
code: 6100,
|
|
6476
6578
|
name: "InvalidRentReceiver",
|
|
6477
6579
|
msg: "rent receiver must be the account that paid for the closed account"
|
|
6580
|
+
},
|
|
6581
|
+
{
|
|
6582
|
+
code: 6101,
|
|
6583
|
+
name: "InvalidInitialTau",
|
|
6584
|
+
msg: "initial_tau is below the minimum ticket threshold"
|
|
6478
6585
|
}
|
|
6479
6586
|
],
|
|
6480
6587
|
types: [
|
|
@@ -7514,6 +7621,18 @@ var molpha_default = {
|
|
|
7514
7621
|
{
|
|
7515
7622
|
name: "protocol_fee_bps",
|
|
7516
7623
|
type: "u16"
|
|
7624
|
+
},
|
|
7625
|
+
{
|
|
7626
|
+
name: "initial_tau",
|
|
7627
|
+
docs: [
|
|
7628
|
+
"Ticket threshold of the two genesis epochs: a round is a ticket when the 64-bit prefix of its",
|
|
7629
|
+
"ticket hash is below it, so the sampling probability is `initial_tau / 2^64`. Size it as",
|
|
7630
|
+
"`2^64 * target_tickets_per_epoch / expected_rounds_per_epoch`. `u64::MAX` samples every round",
|
|
7631
|
+
"(every round then costs a ticket transaction until the controller, which moves the",
|
|
7632
|
+
"threshold at most 8x per epoch with a two-epoch lag, brings it down). Must be at least",
|
|
7633
|
+
"`TAU_MIN`."
|
|
7634
|
+
],
|
|
7635
|
+
type: "u64"
|
|
7517
7636
|
}
|
|
7518
7637
|
]
|
|
7519
7638
|
}
|
|
@@ -9347,7 +9466,17 @@ function planIdFromVariant(variant) {
|
|
|
9347
9466
|
}
|
|
9348
9467
|
|
|
9349
9468
|
// src/solana/client.ts
|
|
9350
|
-
var
|
|
9469
|
+
var MAX_COMPUTE_UNIT_LIMIT = 14e5;
|
|
9470
|
+
function estimateSubmitComputeUnits(signerCount) {
|
|
9471
|
+
if (!Number.isInteger(signerCount) || signerCount < 1) return MAX_COMPUTE_UNIT_LIMIT;
|
|
9472
|
+
const measured = 43e3 + 9200 * signerCount;
|
|
9473
|
+
return Math.min(Math.round(measured * 1.15) + 1e4, MAX_COMPUTE_UNIT_LIMIT);
|
|
9474
|
+
}
|
|
9475
|
+
var REGISTRY_CACHE_MS = 3e4;
|
|
9476
|
+
var KEY_CACHE_LIMIT = 8192;
|
|
9477
|
+
var PRIORITY_FEE_CACHE_MS = 5e3;
|
|
9478
|
+
var AUTO_FEE_PERCENTILE = 0.75;
|
|
9479
|
+
var AUTO_FEE_CAP_MICRO_LAMPORTS = 1e6;
|
|
9351
9480
|
var MolphaSolanaClient = class _MolphaSolanaClient {
|
|
9352
9481
|
constructor(program, provider, programId) {
|
|
9353
9482
|
this.program = program;
|
|
@@ -9357,6 +9486,13 @@ var MolphaSolanaClient = class _MolphaSolanaClient {
|
|
|
9357
9486
|
program;
|
|
9358
9487
|
provider;
|
|
9359
9488
|
programId;
|
|
9489
|
+
// Registry reads and signer keys are shared by every submit that names the same registry
|
|
9490
|
+
// version, so a fleet of feeds reads them once. Promises are cached, so concurrent submits
|
|
9491
|
+
// coalesce; a failed read is dropped and retried by the next call.
|
|
9492
|
+
registryCache = /* @__PURE__ */ new Map();
|
|
9493
|
+
nodeKeyCache = /* @__PURE__ */ new Map();
|
|
9494
|
+
coalitionCache = /* @__PURE__ */ new Map();
|
|
9495
|
+
priorityFeeCache;
|
|
9360
9496
|
static create(opts) {
|
|
9361
9497
|
const programId = toSolanaAddress(opts.programId ?? MOLPHA_PROGRAM_ADDRESS);
|
|
9362
9498
|
const provider = new AnchorProvider(opts.connection, opts.wallet, {
|
|
@@ -9515,12 +9651,16 @@ var MolphaSolanaClient = class _MolphaSolanaClient {
|
|
|
9515
9651
|
const { payload, signature: schnorr } = attestation;
|
|
9516
9652
|
const sourceId = toFixedBytes(payload.sourceId, 32, "sourceId");
|
|
9517
9653
|
const submitter = this.wallet;
|
|
9518
|
-
const registry = await this.
|
|
9654
|
+
const registry = await this.fetchRegistryCached(payload.registryVersion);
|
|
9519
9655
|
const remaining = resolveRemainingAccounts(schnorr.signersBitmap, registry);
|
|
9520
9656
|
assertSignerCount(remaining.length, payload.signaturesRequired, registry);
|
|
9521
|
-
const coalitionKey = opts?.coalitionKey ?? await this.computeSignerCoalitionKey(remaining, registry);
|
|
9657
|
+
const coalitionKey = opts?.coalitionKey ?? await this.computeSignerCoalitionKey(remaining, registry, schnorr.signersBitmap);
|
|
9522
9658
|
const feed = feedPda(sourceId, payload.signaturesRequired, submitter, this.programId);
|
|
9523
|
-
const
|
|
9659
|
+
const preInstructions = [
|
|
9660
|
+
setComputeUnitLimit(opts?.computeUnitLimit ?? estimateSubmitComputeUnits(remaining.length))
|
|
9661
|
+
];
|
|
9662
|
+
const price = await this.resolvePriorityFee(opts?.priorityFeeMicroLamports, feed);
|
|
9663
|
+
if (price > 0) preInstructions.push(setComputeUnitPrice(price));
|
|
9524
9664
|
const signature = await this.methods.submitAttestation(
|
|
9525
9665
|
buildSubmitAttestationArgs(attestation, coalitionKey, opts?.rawValue)
|
|
9526
9666
|
).accountsPartial({
|
|
@@ -9529,26 +9669,80 @@ var MolphaSolanaClient = class _MolphaSolanaClient {
|
|
|
9529
9669
|
feed,
|
|
9530
9670
|
protocolConfig: protocolConfigPda(this.programId),
|
|
9531
9671
|
systemProgram: SYSTEM_PROGRAM_ADDRESS
|
|
9532
|
-
}).remainingAccounts(remaining).preInstructions(
|
|
9672
|
+
}).remainingAccounts(remaining).preInstructions(preInstructions).rpc();
|
|
9533
9673
|
return { signature, feed };
|
|
9534
9674
|
}
|
|
9535
|
-
/**
|
|
9536
|
-
|
|
9537
|
-
const
|
|
9538
|
-
|
|
9539
|
-
);
|
|
9540
|
-
const
|
|
9541
|
-
|
|
9542
|
-
|
|
9543
|
-
|
|
9544
|
-
|
|
9675
|
+
/** The registry for `version`, reused for {@link REGISTRY_CACHE_MS}. */
|
|
9676
|
+
fetchRegistryCached(version) {
|
|
9677
|
+
const now = Date.now();
|
|
9678
|
+
const hit = this.registryCache.get(version);
|
|
9679
|
+
if (hit && now - hit.at < REGISTRY_CACHE_MS) return hit.value;
|
|
9680
|
+
const value = this.fetchRegistry(version);
|
|
9681
|
+
this.registryCache.set(version, { at: now, value });
|
|
9682
|
+
value.catch(() => {
|
|
9683
|
+
if (this.registryCache.get(version)?.value === value) this.registryCache.delete(version);
|
|
9684
|
+
});
|
|
9685
|
+
return value;
|
|
9686
|
+
}
|
|
9687
|
+
async resolvePriorityFee(option, feed) {
|
|
9688
|
+
if (option === void 0) return 0;
|
|
9689
|
+
if (option !== "auto") {
|
|
9690
|
+
if (!Number.isFinite(option) || option < 0) {
|
|
9691
|
+
throw new Error('priorityFeeMicroLamports must be a non-negative number or "auto"');
|
|
9545
9692
|
}
|
|
9546
|
-
return
|
|
9547
|
-
|
|
9548
|
-
|
|
9549
|
-
|
|
9693
|
+
return Math.floor(option);
|
|
9694
|
+
}
|
|
9695
|
+
const now = Date.now();
|
|
9696
|
+
if (!this.priorityFeeCache || now - this.priorityFeeCache.at >= PRIORITY_FEE_CACHE_MS) {
|
|
9697
|
+
const value = (async () => {
|
|
9698
|
+
try {
|
|
9699
|
+
const recent = await this.provider.connection.getRecentPrioritizationFees({
|
|
9700
|
+
lockedWritableAccounts: [new web3.PublicKey(feed)]
|
|
9701
|
+
});
|
|
9702
|
+
const fees = recent.map((r) => r.prioritizationFee).sort((a, b) => a - b);
|
|
9703
|
+
if (fees.length === 0) return 0;
|
|
9704
|
+
const fee = fees[Math.min(fees.length - 1, Math.floor(fees.length * AUTO_FEE_PERCENTILE))];
|
|
9705
|
+
return Math.min(fee, AUTO_FEE_CAP_MICRO_LAMPORTS);
|
|
9706
|
+
} catch {
|
|
9707
|
+
return 0;
|
|
9708
|
+
}
|
|
9709
|
+
})();
|
|
9710
|
+
this.priorityFeeCache = { at: now, value };
|
|
9711
|
+
}
|
|
9712
|
+
return this.priorityFeeCache.value;
|
|
9713
|
+
}
|
|
9714
|
+
/** Sum of the signers' keys, read from their on-chain `Node` accounts (one batched fetch). */
|
|
9715
|
+
computeSignerCoalitionKey(remaining, registry, signersBitmap) {
|
|
9716
|
+
const setKey = `${registry.version}:${signersBitmap}`;
|
|
9717
|
+
const known = this.coalitionCache.get(setKey);
|
|
9718
|
+
if (known) return known;
|
|
9719
|
+
if (this.coalitionCache.size >= KEY_CACHE_LIMIT) this.coalitionCache.clear();
|
|
9720
|
+
const pending = this.sumSignerKeys(remaining, registry);
|
|
9721
|
+
this.coalitionCache.set(setKey, pending);
|
|
9722
|
+
pending.catch(() => {
|
|
9723
|
+
if (this.coalitionCache.get(setKey) === pending) this.coalitionCache.delete(setKey);
|
|
9550
9724
|
});
|
|
9551
|
-
return
|
|
9725
|
+
return pending;
|
|
9726
|
+
}
|
|
9727
|
+
async sumSignerKeys(remaining, registry) {
|
|
9728
|
+
const addresses = remaining.map((meta) => meta.pubkey);
|
|
9729
|
+
const missing = addresses.filter((a) => !this.nodeKeyCache.has(a.toBase58()));
|
|
9730
|
+
if (missing.length > 0) {
|
|
9731
|
+
const accounts = await this.accounts.node.fetchMultiple(missing);
|
|
9732
|
+
if (this.nodeKeyCache.size + missing.length > KEY_CACHE_LIMIT) this.nodeKeyCache.clear();
|
|
9733
|
+
accounts.forEach((account, i) => {
|
|
9734
|
+
if (!account) {
|
|
9735
|
+
throw new Error(
|
|
9736
|
+
`Node account ${missing[i].toBase58()} (registry ${registry.version}) does not exist`
|
|
9737
|
+
);
|
|
9738
|
+
}
|
|
9739
|
+
this.nodeKeyCache.set(missing[i].toBase58(), {
|
|
9740
|
+
x: nodeCoordinate(account, "secp256k1PubkeyX", "secp256k1_pubkey_x"),
|
|
9741
|
+
y: nodeCoordinate(account, "secp256k1PubkeyY", "secp256k1_pubkey_y")
|
|
9742
|
+
});
|
|
9743
|
+
});
|
|
9744
|
+
}
|
|
9745
|
+
return computeCoalitionKey(addresses.map((a) => this.nodeKeyCache.get(a.toBase58())));
|
|
9552
9746
|
}
|
|
9553
9747
|
/**
|
|
9554
9748
|
* Read the feed written by `submitter` (default: this wallet) for
|
|
@@ -10242,7 +10436,139 @@ function parseStarknetVerifyResult(result) {
|
|
|
10242
10436
|
return { success, code, reason: verifyCodeName(code) ?? "UNKNOWN" };
|
|
10243
10437
|
}
|
|
10244
10438
|
|
|
10439
|
+
// src/bulk.ts
|
|
10440
|
+
var DEFAULT_CONCURRENCY = 32;
|
|
10441
|
+
var MAX_DEFAULT_CONCURRENCY = 64;
|
|
10442
|
+
var DEFAULT_SUBMIT_CONCURRENCY = 8;
|
|
10443
|
+
var BACKOFF_COOLDOWN_MS = 1e3;
|
|
10444
|
+
var AdaptiveLimiter = class {
|
|
10445
|
+
constructor(initial, max, now = Date.now) {
|
|
10446
|
+
this.max = max;
|
|
10447
|
+
this.now = now;
|
|
10448
|
+
this.max = Math.max(1, max);
|
|
10449
|
+
this.limit_ = Math.min(Math.max(1, Math.floor(initial)), this.max);
|
|
10450
|
+
}
|
|
10451
|
+
max;
|
|
10452
|
+
now;
|
|
10453
|
+
limit_;
|
|
10454
|
+
inFlight = 0;
|
|
10455
|
+
successes = 0;
|
|
10456
|
+
lastCut = -Infinity;
|
|
10457
|
+
waiters = [];
|
|
10458
|
+
get limit() {
|
|
10459
|
+
return this.limit_;
|
|
10460
|
+
}
|
|
10461
|
+
async acquire() {
|
|
10462
|
+
if (this.inFlight < this.limit_) {
|
|
10463
|
+
this.inFlight++;
|
|
10464
|
+
return;
|
|
10465
|
+
}
|
|
10466
|
+
await new Promise((resolve) => this.waiters.push(resolve));
|
|
10467
|
+
}
|
|
10468
|
+
/** Release a slot. `busy` reports that the gateway answered at capacity. */
|
|
10469
|
+
release(outcome) {
|
|
10470
|
+
this.inFlight--;
|
|
10471
|
+
if (outcome === "busy") {
|
|
10472
|
+
this.successes = 0;
|
|
10473
|
+
const t = this.now();
|
|
10474
|
+
if (t - this.lastCut >= BACKOFF_COOLDOWN_MS) {
|
|
10475
|
+
this.limit_ = Math.max(1, Math.floor(this.limit_ / 2));
|
|
10476
|
+
this.lastCut = t;
|
|
10477
|
+
}
|
|
10478
|
+
} else if (outcome === "ok") {
|
|
10479
|
+
if (++this.successes >= this.limit_ && this.limit_ < this.max) {
|
|
10480
|
+
this.limit_++;
|
|
10481
|
+
this.successes = 0;
|
|
10482
|
+
}
|
|
10483
|
+
}
|
|
10484
|
+
while (this.waiters.length > 0 && this.inFlight < this.limit_) {
|
|
10485
|
+
this.inFlight++;
|
|
10486
|
+
this.waiters.shift()();
|
|
10487
|
+
}
|
|
10488
|
+
}
|
|
10489
|
+
};
|
|
10490
|
+
function spreadOffsetMs(sourceIdHex, spreadMs) {
|
|
10491
|
+
if (spreadMs <= 0) return 0;
|
|
10492
|
+
return Number.parseInt(sourceIdHex.slice(0, 8), 16) % spreadMs;
|
|
10493
|
+
}
|
|
10494
|
+
var isBusy = (err) => err instanceof GatewayError && (err.status === 503 || err.status === 429);
|
|
10495
|
+
async function requestMany(sdk, feeds, opts = {}) {
|
|
10496
|
+
const results = new Array(feeds.length);
|
|
10497
|
+
if (feeds.length === 0) return results;
|
|
10498
|
+
const advertised = await sdk.gateway.fetchGatewayInfo(sdkFirstEndpoint(sdk)).catch(() => void 0);
|
|
10499
|
+
const ceiling = opts.concurrency ?? (advertised?.maxInflightRounds ? Math.min(Math.max(1, Math.floor(advertised.maxInflightRounds / 2)), MAX_DEFAULT_CONCURRENCY) : DEFAULT_CONCURRENCY);
|
|
10500
|
+
const requests = new AdaptiveLimiter(ceiling, ceiling);
|
|
10501
|
+
const submits = new AdaptiveLimiter(
|
|
10502
|
+
opts.submitConcurrency ?? DEFAULT_SUBMIT_CONCURRENCY,
|
|
10503
|
+
opts.submitConcurrency ?? DEFAULT_SUBMIT_CONCURRENCY
|
|
10504
|
+
);
|
|
10505
|
+
const context = await sdk.gateway.prepareContext();
|
|
10506
|
+
const submit = opts.submit ?? true;
|
|
10507
|
+
const started = Date.now();
|
|
10508
|
+
const one = async (index) => {
|
|
10509
|
+
const feed = feeds[index];
|
|
10510
|
+
const result = { index, ok: false, ...feed.label !== void 0 ? { label: feed.label } : {} };
|
|
10511
|
+
results[index] = result;
|
|
10512
|
+
try {
|
|
10513
|
+
const wait = spreadOffsetMs(deriveSourceIdString(feed.apiConfig), opts.spreadMs ?? 0) - (Date.now() - started);
|
|
10514
|
+
if (wait > 0) await new Promise((resolve) => setTimeout(resolve, wait));
|
|
10515
|
+
if (opts.signal?.aborted) {
|
|
10516
|
+
result.error = new Error("aborted before the feed was started");
|
|
10517
|
+
return;
|
|
10518
|
+
}
|
|
10519
|
+
await requests.acquire();
|
|
10520
|
+
let outcome = "other";
|
|
10521
|
+
try {
|
|
10522
|
+
result.attestation = await sdk.gateway.requestSignedData({
|
|
10523
|
+
...opts.request,
|
|
10524
|
+
apiConfig: feed.apiConfig,
|
|
10525
|
+
signaturesRequired: feed.signaturesRequired,
|
|
10526
|
+
context
|
|
10527
|
+
});
|
|
10528
|
+
result.ok = true;
|
|
10529
|
+
outcome = "ok";
|
|
10530
|
+
} catch (err) {
|
|
10531
|
+
result.error = err;
|
|
10532
|
+
outcome = isBusy(err) ? "busy" : "other";
|
|
10533
|
+
} finally {
|
|
10534
|
+
requests.release(outcome);
|
|
10535
|
+
}
|
|
10536
|
+
if (result.ok && submit && result.attestation) {
|
|
10537
|
+
await submits.acquire();
|
|
10538
|
+
try {
|
|
10539
|
+
const sent = await sdk.solana.submitAttestation(result.attestation);
|
|
10540
|
+
result.signature = sent.signature;
|
|
10541
|
+
result.feedAddress = sent.feed;
|
|
10542
|
+
} catch (err) {
|
|
10543
|
+
result.submitError = err;
|
|
10544
|
+
} finally {
|
|
10545
|
+
submits.release("other");
|
|
10546
|
+
}
|
|
10547
|
+
}
|
|
10548
|
+
} finally {
|
|
10549
|
+
opts.onResult?.(result);
|
|
10550
|
+
}
|
|
10551
|
+
};
|
|
10552
|
+
await Promise.all(feeds.map((_, index) => one(index)));
|
|
10553
|
+
return results;
|
|
10554
|
+
}
|
|
10555
|
+
function sdkFirstEndpoint(sdk) {
|
|
10556
|
+
return sdk.gateway.endpointUrls()[0];
|
|
10557
|
+
}
|
|
10558
|
+
|
|
10245
10559
|
// src/index.ts
|
|
10560
|
+
var SubmitFailedError = class extends Error {
|
|
10561
|
+
constructor(result, cause) {
|
|
10562
|
+
super(
|
|
10563
|
+
`Gateway round succeeded but submitting the attestation failed: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
10564
|
+
);
|
|
10565
|
+
this.result = result;
|
|
10566
|
+
this.cause = cause;
|
|
10567
|
+
this.name = "SubmitFailedError";
|
|
10568
|
+
}
|
|
10569
|
+
result;
|
|
10570
|
+
cause;
|
|
10571
|
+
};
|
|
10246
10572
|
var MolphaSDK = class {
|
|
10247
10573
|
gateway;
|
|
10248
10574
|
solana;
|
|
@@ -10272,11 +10598,15 @@ var MolphaSDK = class {
|
|
|
10272
10598
|
*/
|
|
10273
10599
|
async requestAndSubmit(opts) {
|
|
10274
10600
|
const result = await this.gateway.requestSignedData(opts);
|
|
10275
|
-
|
|
10276
|
-
|
|
10601
|
+
try {
|
|
10602
|
+
const { signature, feed } = await this.solana.submitAttestation(result);
|
|
10603
|
+
return { result, signature, feed };
|
|
10604
|
+
} catch (cause) {
|
|
10605
|
+
throw new SubmitFailedError(result, cause);
|
|
10606
|
+
}
|
|
10277
10607
|
}
|
|
10278
10608
|
};
|
|
10279
10609
|
|
|
10280
|
-
export { AggregationConfigError, DEFAULT_GATEWAY_ENDPOINT, EIP712_DOMAIN_TYPEHASH, GatewayError, INT256_MAX, INT256_MIN, MESSAGE_PREFIX, MIN_TOLERANCE_SIGNATURES, MOLPHA_IDL, MOLPHA_PROGRAM_ADDRESS, MOLPHA_PROGRAM_ID, MOLPHA_VERIFIER_ABI, MOLPHA_VERIFIER_ADDRESS, MOLPHA_VERIFIER_STARKNET_ADDRESSES, MOLPHA_VERIFIER_STARKNET_SEPOLIA, MolphaGateway, MolphaSDK, MolphaSolanaClient, PlanType, REQUEST_AUTH_DOMAIN, SELECTION_WINDOW_MS, 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, evmAddressFromPrivateKey, feedPda, formatInt256Decimal, gatewayPda, gatewaySignerFromWallet, getMolphaStarknetVerifierAddress, hashRequestAuth, hexToBytes, msUntilNextTick, nodePda, normalizeEndpoint, normalizeSecp256k1PublicKeyHex, parseEvmVerifyResult, parseGatewayInfo, parseStarknetVerifyResult, parseUpstreamQuote, planIdFromVariant, planPda, planVariant, probeSource, protocolConfigPda, registryPda, registryStatePda, resolveRemainingAccounts, secp256k1PublicKeyFromCoordinates, selectedIndices, signSourcePayments, signerFromKeypair, signersBitmapToDecimal, signersBitmapToStarknetUint256, signersBitmapToUint256, subscriptionPda, timestampAgeSeconds, timestampSeconds, toChecksumAddress, toFixedBytes, toFixedHex, transferWithAuthorizationDigest, transferWithAuthorizationHash, u256beFromBigInt, u32be, u32le, u64be, u64le, u8, utf8, validateSuppliedTerms, verifyCodeName };
|
|
10610
|
+
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_IDL, MOLPHA_PROGRAM_ADDRESS, MOLPHA_PROGRAM_ID, 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, msUntilNextTick, 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 };
|
|
10281
10611
|
//# sourceMappingURL=index.js.map
|
|
10282
10612
|
//# sourceMappingURL=index.js.map
|