@towns-labs/relayer 3.3.1 → 3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/README.md +1 -1
- package/dist/index.js +451 -82
- package/dist/index.js.map +4 -4
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -12198,7 +12198,7 @@ var cors = /* @__PURE__ */ __name((options) => {
|
|
|
12198
12198
|
}, "cors2");
|
|
12199
12199
|
}, "cors");
|
|
12200
12200
|
|
|
12201
|
-
// ../
|
|
12201
|
+
// ../contracts/dist/addresses.json
|
|
12202
12202
|
var addresses_default = {
|
|
12203
12203
|
dev: {
|
|
12204
12204
|
"84532": {
|
|
@@ -12292,7 +12292,7 @@ var addresses_default = {
|
|
|
12292
12292
|
}
|
|
12293
12293
|
};
|
|
12294
12294
|
|
|
12295
|
-
// ../
|
|
12295
|
+
// ../contracts/dist/index.js
|
|
12296
12296
|
var requiredAddressKeys = [
|
|
12297
12297
|
"orchestrator",
|
|
12298
12298
|
"simpleFunder",
|
|
@@ -21741,7 +21741,7 @@ function errorDetails(error48) {
|
|
|
21741
21741
|
__name(errorDetails, "errorDetails");
|
|
21742
21742
|
var logger = createLogger("eip7702-relayer");
|
|
21743
21743
|
|
|
21744
|
-
// ../
|
|
21744
|
+
// ../contracts/dist/abis/index.js
|
|
21745
21745
|
var orchestratorAbi = [
|
|
21746
21746
|
{ type: "receive", stateMutability: "payable" },
|
|
21747
21747
|
{
|
|
@@ -44780,6 +44780,76 @@ function isPendingTransactionIdUniqueConstraintError(errorMessage) {
|
|
|
44780
44780
|
}
|
|
44781
44781
|
__name(isPendingTransactionIdUniqueConstraintError, "isPendingTransactionIdUniqueConstraintError");
|
|
44782
44782
|
|
|
44783
|
+
// src/durable-objects/signer-replacement-policy.ts
|
|
44784
|
+
function ceilMultiplyByBps(value, bps) {
|
|
44785
|
+
if (bps < 0) {
|
|
44786
|
+
throw new Error("bumpBps must be non-negative");
|
|
44787
|
+
}
|
|
44788
|
+
const multiplier = BigInt(1e4 + bps);
|
|
44789
|
+
const numerator = value * multiplier;
|
|
44790
|
+
return (numerator + 9999n) / 10000n;
|
|
44791
|
+
}
|
|
44792
|
+
__name(ceilMultiplyByBps, "ceilMultiplyByBps");
|
|
44793
|
+
function mapStoredTxStatusToPublicStatus(storedStatus) {
|
|
44794
|
+
if (storedStatus === "confirmed") return "confirmed";
|
|
44795
|
+
if (storedStatus === "failed" || storedStatus === "stuck" || storedStatus === "abandoned") {
|
|
44796
|
+
return "failed";
|
|
44797
|
+
}
|
|
44798
|
+
return "pending";
|
|
44799
|
+
}
|
|
44800
|
+
__name(mapStoredTxStatusToPublicStatus, "mapStoredTxStatusToPublicStatus");
|
|
44801
|
+
function shouldTriggerReplacementByFee(input) {
|
|
44802
|
+
return input.requiredMaxFeePerGas > input.currentMaxFeePerGas + input.staleThresholdPerGas;
|
|
44803
|
+
}
|
|
44804
|
+
__name(shouldTriggerReplacementByFee, "shouldTriggerReplacementByFee");
|
|
44805
|
+
function computeReplacementFees(input) {
|
|
44806
|
+
const bumpedMaxFee = ceilMultiplyByBps(input.currentMaxFeePerGas, input.bumpBps);
|
|
44807
|
+
const bumpedMaxPriorityFee = ceilMultiplyByBps(input.currentMaxPriorityFeePerGas, input.bumpBps);
|
|
44808
|
+
const maxFeePerGas = bumpedMaxFee > input.requiredMaxFeePerGas ? bumpedMaxFee : input.requiredMaxFeePerGas;
|
|
44809
|
+
const maxPriorityFeePerGas = bumpedMaxPriorityFee > input.requiredMaxPriorityFeePerGas ? bumpedMaxPriorityFee : input.requiredMaxPriorityFeePerGas;
|
|
44810
|
+
if (input.maxFeePerGasCap !== void 0 && (maxFeePerGas > input.maxFeePerGasCap || maxPriorityFeePerGas > input.maxFeePerGasCap)) {
|
|
44811
|
+
return null;
|
|
44812
|
+
}
|
|
44813
|
+
return {
|
|
44814
|
+
maxFeePerGas,
|
|
44815
|
+
maxPriorityFeePerGas
|
|
44816
|
+
};
|
|
44817
|
+
}
|
|
44818
|
+
__name(computeReplacementFees, "computeReplacementFees");
|
|
44819
|
+
function shouldApplyFinalization(input) {
|
|
44820
|
+
if (!input.eventTxHash) return true;
|
|
44821
|
+
if (input.status === "confirmed") return true;
|
|
44822
|
+
return input.activeTxHash.toLowerCase() === input.eventTxHash.toLowerCase();
|
|
44823
|
+
}
|
|
44824
|
+
__name(shouldApplyFinalization, "shouldApplyFinalization");
|
|
44825
|
+
function shouldAttemptReplacementNow(input) {
|
|
44826
|
+
if (input.maxAttempts <= 0) return false;
|
|
44827
|
+
if (input.attempts >= input.maxAttempts) return false;
|
|
44828
|
+
if (input.attempts === 0) return true;
|
|
44829
|
+
const exponent = input.attempts - 1;
|
|
44830
|
+
const backoffMs = input.baseBackoffMs * Math.pow(2, exponent);
|
|
44831
|
+
return input.nowMs >= input.lastReplacementAtMs + backoffMs;
|
|
44832
|
+
}
|
|
44833
|
+
__name(shouldAttemptReplacementNow, "shouldAttemptReplacementNow");
|
|
44834
|
+
async function withCleanupOnError(operation, cleanup) {
|
|
44835
|
+
try {
|
|
44836
|
+
return await operation();
|
|
44837
|
+
} catch (error48) {
|
|
44838
|
+
await cleanup();
|
|
44839
|
+
throw error48;
|
|
44840
|
+
}
|
|
44841
|
+
}
|
|
44842
|
+
__name(withCleanupOnError, "withCleanupOnError");
|
|
44843
|
+
async function withRecoveryOnError(operation, recovery, fallback) {
|
|
44844
|
+
try {
|
|
44845
|
+
return await operation();
|
|
44846
|
+
} catch {
|
|
44847
|
+
await recovery();
|
|
44848
|
+
return fallback;
|
|
44849
|
+
}
|
|
44850
|
+
}
|
|
44851
|
+
__name(withRecoveryOnError, "withRecoveryOnError");
|
|
44852
|
+
|
|
44783
44853
|
// src/durable-objects/signer.do.ts
|
|
44784
44854
|
var DEFAULT_MAX_PENDING = 16;
|
|
44785
44855
|
var BALANCE_CHECK_INTERVAL_MS = 3e5;
|
|
@@ -44787,12 +44857,14 @@ var STALE_TX_THRESHOLD_MS = 5 * 60 * 1e3;
|
|
|
44787
44857
|
var DEFAULT_INTENT_EXPIRY_BUFFER_SECONDS = 30;
|
|
44788
44858
|
var DUPLICATE_TX_WAIT_MS = 2e3;
|
|
44789
44859
|
var DUPLICATE_TX_WAIT_POLL_MS = 100;
|
|
44790
|
-
|
|
44791
|
-
|
|
44792
|
-
|
|
44793
|
-
|
|
44794
|
-
|
|
44795
|
-
|
|
44860
|
+
var DEFAULT_REPLACEMENT_BUMP_BPS = 1250;
|
|
44861
|
+
var DEFAULT_REPLACEMENT_TRIGGER_WEI = 0n;
|
|
44862
|
+
var DEFAULT_REPLACEMENT_MAX_ATTEMPTS = 3;
|
|
44863
|
+
var DEFAULT_REPLACEMENT_BACKOFF_MS = 3e4;
|
|
44864
|
+
function mapStoredTxStatusToPublicStatus2(storedStatus) {
|
|
44865
|
+
return mapStoredTxStatusToPublicStatus(storedStatus);
|
|
44866
|
+
}
|
|
44867
|
+
__name(mapStoredTxStatusToPublicStatus2, "mapStoredTxStatusToPublicStatus");
|
|
44796
44868
|
function isIntentExpired(expiryTimestamp, bufferSeconds = DEFAULT_INTENT_EXPIRY_BUFFER_SECONDS) {
|
|
44797
44869
|
const expiry = typeof expiryTimestamp === "string" ? BigInt(expiryTimestamp) : expiryTimestamp;
|
|
44798
44870
|
const currentTime = BigInt(Math.floor(Date.now() / 1e3));
|
|
@@ -44851,8 +44923,8 @@ var SignerDO = class extends DurableObject {
|
|
|
44851
44923
|
if (request.method !== "POST") {
|
|
44852
44924
|
return new Response("Method not allowed", { status: 405 });
|
|
44853
44925
|
}
|
|
44854
|
-
const { txId, status } = await request.json();
|
|
44855
|
-
await this.handleFinalized(txId, status);
|
|
44926
|
+
const { txId, status, txHash } = await request.json();
|
|
44927
|
+
await this.handleFinalized(txId, status, txHash);
|
|
44856
44928
|
return Response.json({ ok: true });
|
|
44857
44929
|
}
|
|
44858
44930
|
case "/maintenance": {
|
|
@@ -44934,6 +45006,20 @@ var SignerDO = class extends DurableObject {
|
|
|
44934
45006
|
UPDATE schema_version SET version = 2 WHERE id = 1;
|
|
44935
45007
|
`);
|
|
44936
45008
|
}
|
|
45009
|
+
if (currentVersion < 3) {
|
|
45010
|
+
this.sql.exec(`
|
|
45011
|
+
ALTER TABLE pending_transactions ADD COLUMN tx_to TEXT;
|
|
45012
|
+
ALTER TABLE pending_transactions ADD COLUMN tx_data TEXT;
|
|
45013
|
+
ALTER TABLE pending_transactions ADD COLUMN tx_value TEXT;
|
|
45014
|
+
ALTER TABLE pending_transactions ADD COLUMN tx_authorization_list TEXT;
|
|
45015
|
+
ALTER TABLE pending_transactions ADD COLUMN max_fee_per_gas TEXT;
|
|
45016
|
+
ALTER TABLE pending_transactions ADD COLUMN max_priority_fee_per_gas TEXT;
|
|
45017
|
+
ALTER TABLE pending_transactions ADD COLUMN replacement_attempts INTEGER NOT NULL DEFAULT 0;
|
|
45018
|
+
ALTER TABLE pending_transactions ADD COLUMN last_replacement_at INTEGER NOT NULL DEFAULT 0;
|
|
45019
|
+
CREATE INDEX IF NOT EXISTS idx_pending_replacement ON pending_transactions(status, sent_at);
|
|
45020
|
+
UPDATE schema_version SET version = 3 WHERE id = 1;
|
|
45021
|
+
`);
|
|
45022
|
+
}
|
|
44937
45023
|
}
|
|
44938
45024
|
/**
|
|
44939
45025
|
* Self-initialize on first request
|
|
@@ -45097,7 +45183,7 @@ var SignerDO = class extends DurableObject {
|
|
|
45097
45183
|
this.sql.exec("DELETE FROM pending_transactions WHERE id = ?", txId);
|
|
45098
45184
|
}
|
|
45099
45185
|
const pendingMaxRows = this.sql.exec(
|
|
45100
|
-
"SELECT MAX(nonce) as max_nonce FROM pending_transactions WHERE status
|
|
45186
|
+
"SELECT MAX(nonce) as max_nonce FROM pending_transactions WHERE status IN ('pending', 'replacing')"
|
|
45101
45187
|
).toArray();
|
|
45102
45188
|
const pendingMax = pendingMaxRows[0]?.max_nonce;
|
|
45103
45189
|
const nextNonce = Math.max(onChainNonce, (pendingMax ?? -1) + 1);
|
|
@@ -45202,7 +45288,7 @@ var SignerDO = class extends DurableObject {
|
|
|
45202
45288
|
try {
|
|
45203
45289
|
const receipt = await this.getTransactionReceipt(txHash, chainId);
|
|
45204
45290
|
if (receipt) {
|
|
45205
|
-
if (status === "pending") {
|
|
45291
|
+
if (status === "pending" || status === "replacing") {
|
|
45206
45292
|
const finalStatus = receipt.status === "0x1" ? "confirmed" : "failed";
|
|
45207
45293
|
this.sql.exec(
|
|
45208
45294
|
"UPDATE pending_transactions SET status = ? WHERE id = ?",
|
|
@@ -45227,7 +45313,7 @@ var SignerDO = class extends DurableObject {
|
|
|
45227
45313
|
}
|
|
45228
45314
|
} catch {
|
|
45229
45315
|
}
|
|
45230
|
-
const fallbackStatus =
|
|
45316
|
+
const fallbackStatus = mapStoredTxStatusToPublicStatus2(status);
|
|
45231
45317
|
return {
|
|
45232
45318
|
txId,
|
|
45233
45319
|
txHash,
|
|
@@ -45333,7 +45419,9 @@ var SignerDO = class extends DurableObject {
|
|
|
45333
45419
|
}
|
|
45334
45420
|
const minBalance = this.parseBalance(this.env.MIN_SIGNER_BALANCE, 10000000000000000n);
|
|
45335
45421
|
const isPaused = !!state.paused || balanceWei < minBalance;
|
|
45336
|
-
const pendingCount = this.sql.exec(
|
|
45422
|
+
const pendingCount = this.sql.exec(
|
|
45423
|
+
"SELECT COUNT(*) as c FROM pending_transactions WHERE status IN ('pending', 'replacing')"
|
|
45424
|
+
).toArray()[0]?.c ?? 0;
|
|
45337
45425
|
const maxPending = parseInt(
|
|
45338
45426
|
this.env.MAX_PENDING_PER_SIGNER ?? String(DEFAULT_MAX_PENDING),
|
|
45339
45427
|
10
|
|
@@ -45372,7 +45460,7 @@ var SignerDO = class extends DurableObject {
|
|
|
45372
45460
|
return { error: "Signer is paused", code: "PAUSED" };
|
|
45373
45461
|
}
|
|
45374
45462
|
const pending = this.sql.exec(
|
|
45375
|
-
"SELECT COUNT(*) as c FROM pending_transactions WHERE status
|
|
45463
|
+
"SELECT COUNT(*) as c FROM pending_transactions WHERE status IN ('pending', 'replacing')"
|
|
45376
45464
|
).toArray()[0]?.c ?? 0;
|
|
45377
45465
|
if (pending >= maxPending) {
|
|
45378
45466
|
return {
|
|
@@ -45415,13 +45503,34 @@ var SignerDO = class extends DurableObject {
|
|
|
45415
45503
|
throw new SignerDOError(errorResult.error, errorResult.code ?? "BROADCAST_FAILED");
|
|
45416
45504
|
}
|
|
45417
45505
|
const successResult = result;
|
|
45506
|
+
const { txParams, initialFeeParams } = await withCleanupOnError(
|
|
45507
|
+
async () => {
|
|
45508
|
+
const preparedTxParams = await this.buildTxParams(
|
|
45509
|
+
tx,
|
|
45510
|
+
successResult.chainId,
|
|
45511
|
+
successResult.address
|
|
45512
|
+
);
|
|
45513
|
+
const recommendedFeeParams = await this.getRecommendedFeeParams(
|
|
45514
|
+
successResult.chainId
|
|
45515
|
+
);
|
|
45516
|
+
return {
|
|
45517
|
+
txParams: preparedTxParams,
|
|
45518
|
+
initialFeeParams: recommendedFeeParams
|
|
45519
|
+
};
|
|
45520
|
+
},
|
|
45521
|
+
async () => {
|
|
45522
|
+
await this.syncNonceFromChainAtomic(tx.id);
|
|
45523
|
+
}
|
|
45524
|
+
);
|
|
45418
45525
|
let txHash;
|
|
45526
|
+
let usedFeeParams = initialFeeParams;
|
|
45527
|
+
let usedNonce = successResult.nonce;
|
|
45419
45528
|
try {
|
|
45420
|
-
txHash = await this.
|
|
45421
|
-
|
|
45529
|
+
txHash = await this.signAndBroadcastPrepared(
|
|
45530
|
+
txParams,
|
|
45422
45531
|
successResult.nonce,
|
|
45423
|
-
successResult.
|
|
45424
|
-
|
|
45532
|
+
successResult.chainId,
|
|
45533
|
+
initialFeeParams
|
|
45425
45534
|
);
|
|
45426
45535
|
} catch (error48) {
|
|
45427
45536
|
const errorMessage = getErrorMessage(error48);
|
|
@@ -45448,12 +45557,15 @@ var SignerDO = class extends DurableObject {
|
|
|
45448
45557
|
`[SignerDO] Retrying with synced nonce: ${retryNonce} (synced base: ${syncedNonce})`
|
|
45449
45558
|
);
|
|
45450
45559
|
try {
|
|
45451
|
-
|
|
45452
|
-
|
|
45560
|
+
const retryFeeParams = await this.getRecommendedFeeParams(successResult.chainId);
|
|
45561
|
+
txHash = await this.signAndBroadcastPrepared(
|
|
45562
|
+
txParams,
|
|
45453
45563
|
retryNonce,
|
|
45454
|
-
successResult.
|
|
45455
|
-
|
|
45564
|
+
successResult.chainId,
|
|
45565
|
+
retryFeeParams
|
|
45456
45566
|
);
|
|
45567
|
+
usedFeeParams = retryFeeParams;
|
|
45568
|
+
usedNonce = retryNonce;
|
|
45457
45569
|
} catch (retryError) {
|
|
45458
45570
|
await this.syncNonceFromChainAtomic(tx.id);
|
|
45459
45571
|
throw retryError;
|
|
@@ -45463,7 +45575,22 @@ var SignerDO = class extends DurableObject {
|
|
|
45463
45575
|
throw error48;
|
|
45464
45576
|
}
|
|
45465
45577
|
}
|
|
45466
|
-
this.sql.exec(
|
|
45578
|
+
this.sql.exec(
|
|
45579
|
+
`
|
|
45580
|
+
UPDATE pending_transactions
|
|
45581
|
+
SET tx_hash = ?, tx_to = ?, tx_data = ?, tx_value = ?, tx_authorization_list = ?,
|
|
45582
|
+
max_fee_per_gas = ?, max_priority_fee_per_gas = ?
|
|
45583
|
+
WHERE id = ?
|
|
45584
|
+
`,
|
|
45585
|
+
txHash,
|
|
45586
|
+
txParams.to,
|
|
45587
|
+
txParams.data,
|
|
45588
|
+
txParams.value.toString(),
|
|
45589
|
+
this.serializeAuthorizationList(txParams.authorizationList),
|
|
45590
|
+
usedFeeParams.maxFeePerGas.toString(),
|
|
45591
|
+
usedFeeParams.maxPriorityFeePerGas.toString(),
|
|
45592
|
+
tx.id
|
|
45593
|
+
);
|
|
45467
45594
|
const signerName = this.getSignerName();
|
|
45468
45595
|
if (!signerName) {
|
|
45469
45596
|
throw new SignerDOError(
|
|
@@ -45471,18 +45598,10 @@ var SignerDO = class extends DurableObject {
|
|
|
45471
45598
|
"NOT_INITIALIZED"
|
|
45472
45599
|
);
|
|
45473
45600
|
}
|
|
45474
|
-
await this.
|
|
45475
|
-
type: "monitor",
|
|
45476
|
-
txId: tx.id,
|
|
45477
|
-
txHash,
|
|
45478
|
-
signerName,
|
|
45479
|
-
chainId: successResult.chainId,
|
|
45480
|
-
attempt: 0
|
|
45481
|
-
});
|
|
45482
|
-
this.sql.exec("UPDATE pending_transactions SET queued = 1 WHERE id = ?", tx.id);
|
|
45601
|
+
await this.enqueueMonitorJob(tx.id, txHash, signerName, successResult.chainId);
|
|
45483
45602
|
return {
|
|
45484
45603
|
txHash,
|
|
45485
|
-
nonce:
|
|
45604
|
+
nonce: usedNonce,
|
|
45486
45605
|
signer: successResult.address,
|
|
45487
45606
|
signerName
|
|
45488
45607
|
};
|
|
@@ -45550,16 +45669,76 @@ var SignerDO = class extends DurableObject {
|
|
|
45550
45669
|
}
|
|
45551
45670
|
return "";
|
|
45552
45671
|
}
|
|
45553
|
-
|
|
45554
|
-
|
|
45555
|
-
|
|
45556
|
-
|
|
45557
|
-
|
|
45672
|
+
async enqueueMonitorJob(txId, txHash, signerName, chainId) {
|
|
45673
|
+
try {
|
|
45674
|
+
await this.env.MONITOR_QUEUE.send({
|
|
45675
|
+
type: "monitor",
|
|
45676
|
+
txId,
|
|
45677
|
+
txHash,
|
|
45678
|
+
signerName,
|
|
45679
|
+
chainId,
|
|
45680
|
+
attempt: 0
|
|
45681
|
+
});
|
|
45682
|
+
this.sql.exec("UPDATE pending_transactions SET queued = 1 WHERE id = ?", txId);
|
|
45683
|
+
return true;
|
|
45684
|
+
} catch {
|
|
45685
|
+
this.sql.exec("UPDATE pending_transactions SET queued = 0 WHERE id = ?", txId);
|
|
45686
|
+
return false;
|
|
45687
|
+
}
|
|
45688
|
+
}
|
|
45689
|
+
parseOptionalBigInt(value) {
|
|
45690
|
+
if (!value) return void 0;
|
|
45691
|
+
const trimmed = value.trim();
|
|
45692
|
+
if (!trimmed) return void 0;
|
|
45693
|
+
return BigInt(trimmed);
|
|
45694
|
+
}
|
|
45695
|
+
serializeAuthorizationList(authorizationList) {
|
|
45696
|
+
if (!authorizationList || authorizationList.length === 0) return null;
|
|
45697
|
+
return JSON.stringify(
|
|
45698
|
+
authorizationList,
|
|
45699
|
+
(_key, value) => typeof value === "bigint" ? value.toString() : value
|
|
45700
|
+
);
|
|
45701
|
+
}
|
|
45702
|
+
deserializeAuthorizationList(value) {
|
|
45703
|
+
if (!value) return void 0;
|
|
45704
|
+
const parsed = JSON.parse(value);
|
|
45705
|
+
return parsed.map((item) => {
|
|
45706
|
+
const copy = { ...item };
|
|
45707
|
+
if (typeof copy.chainId === "string") copy.chainId = BigInt(copy.chainId);
|
|
45708
|
+
if (typeof copy.nonce === "string") copy.nonce = BigInt(copy.nonce);
|
|
45709
|
+
return copy;
|
|
45710
|
+
});
|
|
45711
|
+
}
|
|
45712
|
+
getRecommendedFeeParams(chainId) {
|
|
45713
|
+
const { publicClient } = this.ensureClients(chainId);
|
|
45714
|
+
return publicClient.estimateFeesPerGas({
|
|
45715
|
+
type: "eip1559",
|
|
45716
|
+
chain: publicClient.chain
|
|
45717
|
+
});
|
|
45718
|
+
}
|
|
45719
|
+
parseReplacementConfig() {
|
|
45720
|
+
return {
|
|
45721
|
+
bumpBps: parseInt(
|
|
45722
|
+
this.env.REPLACEMENT_BUMP_BPS ?? String(DEFAULT_REPLACEMENT_BUMP_BPS),
|
|
45723
|
+
10
|
|
45724
|
+
),
|
|
45725
|
+
triggerThresholdWei: this.parseOptionalBigInt(this.env.REPLACEMENT_TRIGGER_THRESHOLD_WEI) ?? DEFAULT_REPLACEMENT_TRIGGER_WEI,
|
|
45726
|
+
maxAttempts: parseInt(
|
|
45727
|
+
this.env.REPLACEMENT_MAX_ATTEMPTS ?? String(DEFAULT_REPLACEMENT_MAX_ATTEMPTS),
|
|
45728
|
+
10
|
|
45729
|
+
),
|
|
45730
|
+
baseBackoffMs: parseInt(
|
|
45731
|
+
this.env.REPLACEMENT_BACKOFF_BASE_MS ?? String(DEFAULT_REPLACEMENT_BACKOFF_MS),
|
|
45732
|
+
10
|
|
45733
|
+
),
|
|
45734
|
+
maxFeeCapWei: this.parseOptionalBigInt(this.env.REPLACEMENT_MAX_FEE_PER_GAS_WEI)
|
|
45735
|
+
};
|
|
45736
|
+
}
|
|
45737
|
+
async buildTxParams(tx, chainId, signerAddress) {
|
|
45558
45738
|
const contracts = getContractAddresses(
|
|
45559
45739
|
this.env,
|
|
45560
45740
|
chainId
|
|
45561
45741
|
);
|
|
45562
|
-
let txParams;
|
|
45563
45742
|
switch (tx.type) {
|
|
45564
45743
|
case "create-account": {
|
|
45565
45744
|
if (tx.preCall && tx.preCall.executionData !== "0x") {
|
|
@@ -45569,23 +45748,23 @@ var SignerDO = class extends DurableObject {
|
|
|
45569
45748
|
nonce: BigInt(tx.preCall.nonce),
|
|
45570
45749
|
signature: tx.preCall.signature
|
|
45571
45750
|
};
|
|
45572
|
-
|
|
45751
|
+
return {
|
|
45573
45752
|
to: contracts.orchestrator,
|
|
45574
45753
|
data: encodeFunctionData({
|
|
45575
45754
|
abi: orchestratorAbi,
|
|
45576
45755
|
functionName: "executePreCalls",
|
|
45577
45756
|
args: [tx.accountAddress, [signedCall]]
|
|
45578
45757
|
}),
|
|
45579
|
-
|
|
45580
|
-
};
|
|
45581
|
-
} else {
|
|
45582
|
-
txParams = {
|
|
45583
|
-
to: tx.accountAddress,
|
|
45584
|
-
data: "0x",
|
|
45758
|
+
value: 0n,
|
|
45585
45759
|
authorizationList: [tx.authorization]
|
|
45586
45760
|
};
|
|
45587
45761
|
}
|
|
45588
|
-
|
|
45762
|
+
return {
|
|
45763
|
+
to: tx.accountAddress,
|
|
45764
|
+
data: "0x",
|
|
45765
|
+
value: 0n,
|
|
45766
|
+
authorizationList: [tx.authorization]
|
|
45767
|
+
};
|
|
45589
45768
|
}
|
|
45590
45769
|
case "execute-intent": {
|
|
45591
45770
|
const bufferSeconds = parseInt(
|
|
@@ -45600,18 +45779,18 @@ var SignerDO = class extends DurableObject {
|
|
|
45600
45779
|
}
|
|
45601
45780
|
const intentWithRecipient = {
|
|
45602
45781
|
...tx.intent,
|
|
45603
|
-
paymentRecipient: getPaymentRecipient(this.env.FEE_RECIPIENT,
|
|
45782
|
+
paymentRecipient: getPaymentRecipient(this.env.FEE_RECIPIENT, signerAddress)
|
|
45604
45783
|
};
|
|
45605
45784
|
const encodedIntent = this.encodeIntentToBytes(intentWithRecipient);
|
|
45606
|
-
|
|
45785
|
+
return {
|
|
45607
45786
|
to: contracts.orchestrator,
|
|
45608
45787
|
data: encodeFunctionData({
|
|
45609
45788
|
abi: orchestratorAbi,
|
|
45610
45789
|
functionName: "execute",
|
|
45611
45790
|
args: [encodedIntent]
|
|
45612
|
-
})
|
|
45791
|
+
}),
|
|
45792
|
+
value: 0n
|
|
45613
45793
|
};
|
|
45614
|
-
break;
|
|
45615
45794
|
}
|
|
45616
45795
|
case "batch-execute-intent": {
|
|
45617
45796
|
const batchBufferSeconds = parseInt(
|
|
@@ -45628,35 +45807,34 @@ var SignerDO = class extends DurableObject {
|
|
|
45628
45807
|
}
|
|
45629
45808
|
const intentsWithRecipient = tx.intents.map((intent) => ({
|
|
45630
45809
|
...intent,
|
|
45631
|
-
paymentRecipient: getPaymentRecipient(this.env.FEE_RECIPIENT,
|
|
45810
|
+
paymentRecipient: getPaymentRecipient(this.env.FEE_RECIPIENT, signerAddress)
|
|
45632
45811
|
}));
|
|
45633
45812
|
const encodedIntents = intentsWithRecipient.map(
|
|
45634
45813
|
(intent) => this.encodeIntentToBytes(intent)
|
|
45635
45814
|
);
|
|
45636
|
-
|
|
45815
|
+
return {
|
|
45637
45816
|
to: contracts.orchestrator,
|
|
45638
45817
|
data: encodeFunctionData({
|
|
45639
45818
|
abi: orchestratorAbi,
|
|
45640
45819
|
functionName: "execute",
|
|
45641
45820
|
args: [encodedIntents]
|
|
45642
|
-
})
|
|
45821
|
+
}),
|
|
45822
|
+
value: 0n
|
|
45643
45823
|
};
|
|
45644
|
-
break;
|
|
45645
45824
|
}
|
|
45646
|
-
default:
|
|
45647
|
-
throw new SignerDOError(
|
|
45648
|
-
`Unknown transaction type: ${tx.type}`,
|
|
45649
|
-
"BROADCAST_FAILED"
|
|
45650
|
-
);
|
|
45651
45825
|
}
|
|
45826
|
+
}
|
|
45827
|
+
async signAndBroadcastPrepared(txParams, nonce, chainId, feeParams) {
|
|
45828
|
+
const { walletClient, account } = this.ensureClients(chainId);
|
|
45652
45829
|
try {
|
|
45653
|
-
|
|
45830
|
+
return await walletClient.sendTransaction({
|
|
45654
45831
|
...txParams,
|
|
45655
45832
|
nonce,
|
|
45656
45833
|
account,
|
|
45657
|
-
chain: { id: chainId }
|
|
45834
|
+
chain: { id: chainId },
|
|
45835
|
+
maxFeePerGas: feeParams.maxFeePerGas,
|
|
45836
|
+
maxPriorityFeePerGas: feeParams.maxPriorityFeePerGas
|
|
45658
45837
|
});
|
|
45659
|
-
return txHash;
|
|
45660
45838
|
} catch (error48) {
|
|
45661
45839
|
const message = getErrorMessage(error48);
|
|
45662
45840
|
throw new SignerDOError(
|
|
@@ -45745,9 +45923,187 @@ var SignerDO = class extends DurableObject {
|
|
|
45745
45923
|
/**
|
|
45746
45924
|
* Handle transaction finalization (called by queue consumer)
|
|
45747
45925
|
*/
|
|
45748
|
-
async handleFinalized(txId, status) {
|
|
45926
|
+
async handleFinalized(txId, status, txHash) {
|
|
45927
|
+
if (txHash) {
|
|
45928
|
+
const rows = this.sql.exec("SELECT tx_hash FROM pending_transactions WHERE id = ?", txId).toArray();
|
|
45929
|
+
if (rows.length === 0) return;
|
|
45930
|
+
const activeTxHash = rows[0].tx_hash ?? "";
|
|
45931
|
+
if (!shouldApplyFinalization({ activeTxHash, eventTxHash: txHash, status })) {
|
|
45932
|
+
return;
|
|
45933
|
+
}
|
|
45934
|
+
}
|
|
45749
45935
|
this.sql.exec("UPDATE pending_transactions SET status = ? WHERE id = ?", status, txId);
|
|
45750
45936
|
}
|
|
45937
|
+
async tryReplaceStaleTransaction(txId, chainId, signerName) {
|
|
45938
|
+
const config2 = this.parseReplacementConfig();
|
|
45939
|
+
const nowMs = Date.now();
|
|
45940
|
+
const claimedRows = this.sql.exec(
|
|
45941
|
+
`
|
|
45942
|
+
UPDATE pending_transactions
|
|
45943
|
+
SET status = 'replacing'
|
|
45944
|
+
WHERE id = ? AND status = 'pending'
|
|
45945
|
+
RETURNING
|
|
45946
|
+
nonce,
|
|
45947
|
+
tx_to,
|
|
45948
|
+
tx_data,
|
|
45949
|
+
tx_value,
|
|
45950
|
+
tx_authorization_list,
|
|
45951
|
+
max_fee_per_gas,
|
|
45952
|
+
max_priority_fee_per_gas,
|
|
45953
|
+
replacement_attempts,
|
|
45954
|
+
last_replacement_at
|
|
45955
|
+
`,
|
|
45956
|
+
txId
|
|
45957
|
+
).toArray();
|
|
45958
|
+
if (claimedRows.length === 0) return "skipped";
|
|
45959
|
+
return withRecoveryOnError(
|
|
45960
|
+
async () => {
|
|
45961
|
+
const claimed = claimedRows[0];
|
|
45962
|
+
const attempts = claimed.replacement_attempts ?? 0;
|
|
45963
|
+
const lastReplacementAtMs = claimed.last_replacement_at ?? 0;
|
|
45964
|
+
if (attempts >= config2.maxAttempts) {
|
|
45965
|
+
this.sql.exec(
|
|
45966
|
+
"UPDATE pending_transactions SET status = ? WHERE id = ?",
|
|
45967
|
+
"abandoned",
|
|
45968
|
+
txId
|
|
45969
|
+
);
|
|
45970
|
+
return "abandoned";
|
|
45971
|
+
}
|
|
45972
|
+
if (!shouldAttemptReplacementNow({
|
|
45973
|
+
nowMs,
|
|
45974
|
+
attempts,
|
|
45975
|
+
maxAttempts: config2.maxAttempts,
|
|
45976
|
+
lastReplacementAtMs,
|
|
45977
|
+
baseBackoffMs: config2.baseBackoffMs
|
|
45978
|
+
})) {
|
|
45979
|
+
this.sql.exec(
|
|
45980
|
+
"UPDATE pending_transactions SET status = ? WHERE id = ?",
|
|
45981
|
+
"pending",
|
|
45982
|
+
txId
|
|
45983
|
+
);
|
|
45984
|
+
return "skipped";
|
|
45985
|
+
}
|
|
45986
|
+
const currentMaxFee = this.parseOptionalBigInt(
|
|
45987
|
+
claimed.max_fee_per_gas
|
|
45988
|
+
);
|
|
45989
|
+
const currentMaxPriority = this.parseOptionalBigInt(
|
|
45990
|
+
claimed.max_priority_fee_per_gas
|
|
45991
|
+
);
|
|
45992
|
+
const txTo = claimed.tx_to ?? "";
|
|
45993
|
+
const txData = claimed.tx_data ?? "";
|
|
45994
|
+
const txValue = this.parseOptionalBigInt(claimed.tx_value);
|
|
45995
|
+
if (!currentMaxFee || !currentMaxPriority || !txTo || !txData || txValue === void 0) {
|
|
45996
|
+
this.sql.exec(
|
|
45997
|
+
"UPDATE pending_transactions SET status = ? WHERE id = ?",
|
|
45998
|
+
"stuck",
|
|
45999
|
+
txId
|
|
46000
|
+
);
|
|
46001
|
+
return "skipped";
|
|
46002
|
+
}
|
|
46003
|
+
const requiredFees = await this.getRecommendedFeeParams(chainId);
|
|
46004
|
+
const triggerReplacement = shouldTriggerReplacementByFee({
|
|
46005
|
+
requiredMaxFeePerGas: requiredFees.maxFeePerGas,
|
|
46006
|
+
currentMaxFeePerGas: currentMaxFee,
|
|
46007
|
+
staleThresholdPerGas: config2.triggerThresholdWei
|
|
46008
|
+
});
|
|
46009
|
+
if (!triggerReplacement) {
|
|
46010
|
+
this.sql.exec(
|
|
46011
|
+
"UPDATE pending_transactions SET status = ? WHERE id = ?",
|
|
46012
|
+
"pending",
|
|
46013
|
+
txId
|
|
46014
|
+
);
|
|
46015
|
+
return "skipped";
|
|
46016
|
+
}
|
|
46017
|
+
const nextFeeParams = computeReplacementFees({
|
|
46018
|
+
currentMaxFeePerGas: currentMaxFee,
|
|
46019
|
+
currentMaxPriorityFeePerGas: currentMaxPriority,
|
|
46020
|
+
requiredMaxFeePerGas: requiredFees.maxFeePerGas,
|
|
46021
|
+
requiredMaxPriorityFeePerGas: requiredFees.maxPriorityFeePerGas,
|
|
46022
|
+
bumpBps: config2.bumpBps,
|
|
46023
|
+
maxFeePerGasCap: config2.maxFeeCapWei
|
|
46024
|
+
});
|
|
46025
|
+
const nextAttempts = attempts + 1;
|
|
46026
|
+
if (!nextFeeParams) {
|
|
46027
|
+
this.sql.exec(
|
|
46028
|
+
`
|
|
46029
|
+
UPDATE pending_transactions
|
|
46030
|
+
SET status = 'abandoned',
|
|
46031
|
+
replacement_attempts = ?,
|
|
46032
|
+
last_replacement_at = ?
|
|
46033
|
+
WHERE id = ?
|
|
46034
|
+
`,
|
|
46035
|
+
nextAttempts,
|
|
46036
|
+
nowMs,
|
|
46037
|
+
txId
|
|
46038
|
+
);
|
|
46039
|
+
return "abandoned";
|
|
46040
|
+
}
|
|
46041
|
+
const txParams = {
|
|
46042
|
+
to: txTo,
|
|
46043
|
+
data: txData,
|
|
46044
|
+
value: txValue,
|
|
46045
|
+
authorizationList: this.deserializeAuthorizationList(
|
|
46046
|
+
claimed.tx_authorization_list
|
|
46047
|
+
)
|
|
46048
|
+
};
|
|
46049
|
+
try {
|
|
46050
|
+
const nonce = claimed.nonce;
|
|
46051
|
+
const replacementHash = await this.signAndBroadcastPrepared(
|
|
46052
|
+
txParams,
|
|
46053
|
+
nonce,
|
|
46054
|
+
chainId,
|
|
46055
|
+
nextFeeParams
|
|
46056
|
+
);
|
|
46057
|
+
this.sql.exec(
|
|
46058
|
+
`
|
|
46059
|
+
UPDATE pending_transactions
|
|
46060
|
+
SET tx_hash = ?,
|
|
46061
|
+
status = 'pending',
|
|
46062
|
+
queued = 0,
|
|
46063
|
+
max_fee_per_gas = ?,
|
|
46064
|
+
max_priority_fee_per_gas = ?,
|
|
46065
|
+
replacement_attempts = ?,
|
|
46066
|
+
last_replacement_at = ?,
|
|
46067
|
+
sent_at = ?
|
|
46068
|
+
WHERE id = ?
|
|
46069
|
+
`,
|
|
46070
|
+
replacementHash,
|
|
46071
|
+
nextFeeParams.maxFeePerGas.toString(),
|
|
46072
|
+
nextFeeParams.maxPriorityFeePerGas.toString(),
|
|
46073
|
+
nextAttempts,
|
|
46074
|
+
nowMs,
|
|
46075
|
+
nowMs,
|
|
46076
|
+
txId
|
|
46077
|
+
);
|
|
46078
|
+
await this.enqueueMonitorJob(txId, replacementHash, signerName, chainId);
|
|
46079
|
+
return "replaced";
|
|
46080
|
+
} catch {
|
|
46081
|
+
const terminal = nextAttempts >= config2.maxAttempts;
|
|
46082
|
+
this.sql.exec(
|
|
46083
|
+
`
|
|
46084
|
+
UPDATE pending_transactions
|
|
46085
|
+
SET status = ?,
|
|
46086
|
+
replacement_attempts = ?,
|
|
46087
|
+
last_replacement_at = ?
|
|
46088
|
+
WHERE id = ?
|
|
46089
|
+
`,
|
|
46090
|
+
terminal ? "abandoned" : "pending",
|
|
46091
|
+
nextAttempts,
|
|
46092
|
+
nowMs,
|
|
46093
|
+
txId
|
|
46094
|
+
);
|
|
46095
|
+
return terminal ? "abandoned" : "skipped";
|
|
46096
|
+
}
|
|
46097
|
+
},
|
|
46098
|
+
async () => {
|
|
46099
|
+
this.sql.exec(
|
|
46100
|
+
"UPDATE pending_transactions SET status = 'pending' WHERE id = ? AND status = 'replacing'",
|
|
46101
|
+
txId
|
|
46102
|
+
);
|
|
46103
|
+
},
|
|
46104
|
+
"skipped"
|
|
46105
|
+
);
|
|
46106
|
+
}
|
|
45751
46107
|
/**
|
|
45752
46108
|
* Maintenance: clean up stale transactions, check balance
|
|
45753
46109
|
*/
|
|
@@ -45783,11 +46139,25 @@ var SignerDO = class extends DurableObject {
|
|
|
45783
46139
|
if (newStatus === "confirmed") confirmed++;
|
|
45784
46140
|
else failed++;
|
|
45785
46141
|
} else {
|
|
45786
|
-
this.
|
|
45787
|
-
|
|
45788
|
-
|
|
46142
|
+
const signerName2 = this.getSignerName();
|
|
46143
|
+
if (!signerName2) {
|
|
46144
|
+
this.sql.exec(
|
|
46145
|
+
"UPDATE pending_transactions SET status = 'stuck' WHERE id = ?",
|
|
46146
|
+
tx.id
|
|
46147
|
+
);
|
|
46148
|
+
stuck++;
|
|
46149
|
+
continue;
|
|
46150
|
+
}
|
|
46151
|
+
const replacementResult = await this.tryReplaceStaleTransaction(
|
|
46152
|
+
tx.id,
|
|
46153
|
+
chainId,
|
|
46154
|
+
signerName2
|
|
45789
46155
|
);
|
|
45790
|
-
|
|
46156
|
+
if (replacementResult === "abandoned") {
|
|
46157
|
+
failed++;
|
|
46158
|
+
} else if (replacementResult === "skipped") {
|
|
46159
|
+
stuck++;
|
|
46160
|
+
}
|
|
45791
46161
|
}
|
|
45792
46162
|
}
|
|
45793
46163
|
let requeued = 0;
|
|
@@ -45800,18 +46170,14 @@ var SignerDO = class extends DurableObject {
|
|
|
45800
46170
|
`
|
|
45801
46171
|
).toArray();
|
|
45802
46172
|
for (const tx of orphaned) {
|
|
45803
|
-
|
|
45804
|
-
|
|
45805
|
-
|
|
45806
|
-
|
|
45807
|
-
|
|
45808
|
-
|
|
45809
|
-
|
|
45810
|
-
attempt: 0
|
|
45811
|
-
});
|
|
45812
|
-
this.sql.exec("UPDATE pending_transactions SET queued = 1 WHERE id = ?", tx.id);
|
|
46173
|
+
const queued = await this.enqueueMonitorJob(
|
|
46174
|
+
tx.id,
|
|
46175
|
+
tx.tx_hash,
|
|
46176
|
+
signerName,
|
|
46177
|
+
chainId
|
|
46178
|
+
);
|
|
46179
|
+
if (queued) {
|
|
45813
46180
|
requeued++;
|
|
45814
|
-
} catch {
|
|
45815
46181
|
}
|
|
45816
46182
|
}
|
|
45817
46183
|
}
|
|
@@ -45861,7 +46227,9 @@ var SignerDO = class extends DurableObject {
|
|
|
45861
46227
|
async getStatus() {
|
|
45862
46228
|
await this.ensureInitialized();
|
|
45863
46229
|
const stateRows = this.sql.exec("SELECT * FROM signer_state WHERE id = 1").toArray();
|
|
45864
|
-
const pendingCount = this.sql.exec(
|
|
46230
|
+
const pendingCount = this.sql.exec(
|
|
46231
|
+
"SELECT COUNT(*) as c FROM pending_transactions WHERE status IN ('pending', 'replacing')"
|
|
46232
|
+
).toArray()[0]?.c ?? 0;
|
|
45865
46233
|
const recentTransactions = this.sql.exec("SELECT * FROM pending_transactions ORDER BY sent_at DESC LIMIT 10").toArray();
|
|
45866
46234
|
return {
|
|
45867
46235
|
state: stateRows.length > 0 ? stateRows[0] : null,
|
|
@@ -47229,6 +47597,7 @@ async function handleMonitorJob(msg, env) {
|
|
|
47229
47597
|
headers: { "Content-Type": "application/json" },
|
|
47230
47598
|
body: JSON.stringify({
|
|
47231
47599
|
txId,
|
|
47600
|
+
txHash,
|
|
47232
47601
|
status: receipt.status === "0x1" ? "confirmed" : "failed"
|
|
47233
47602
|
})
|
|
47234
47603
|
});
|