@xyo-network/chain-bridge 1.23.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/node/BridgeActor.d.ts +1 -0
- package/dist/node/BridgeActor.d.ts.map +1 -1
- package/dist/node/index.mjs +2056 -1407
- package/dist/node/index.mjs.map +7 -1
- package/dist/node/server/routes/bridge/routeDefinitions/getRouteDefinitions.d.ts.map +1 -1
- package/dist/node/server/routes/bridge/routeDefinitions/pathParams/ChainIdPathParam.d.ts +2 -1
- package/dist/node/server/routes/bridge/routeDefinitions/pathParams/ChainIdPathParam.d.ts.map +1 -1
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeConfig.d.ts +0 -22
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeConfig.d.ts.map +1 -1
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeFromRemoteEstimate.d.ts +4 -0
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeFromRemoteEstimate.d.ts.map +1 -0
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeFromRemoteRetry.d.ts +10 -0
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeFromRemoteRetry.d.ts.map +1 -0
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeFromRemoteRetryFailed.d.ts +9 -0
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeFromRemoteRetryFailed.d.ts.map +1 -0
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeFromRemoteStatus.d.ts +0 -81
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeFromRemoteStatus.d.ts.map +1 -1
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeFromRemoteStatusByTx.d.ts +28 -0
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeFromRemoteStatusByTx.d.ts.map +1 -0
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeToRemote.d.ts +0 -81
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeToRemote.d.ts.map +1 -1
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeToRemoteEstimate.d.ts +0 -72
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeToRemoteEstimate.d.ts.map +1 -1
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeToRemoteMaxEstimate.d.ts +0 -72
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeToRemoteMaxEstimate.d.ts.map +1 -1
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeToRemoteRetry.d.ts +10 -0
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeToRemoteRetry.d.ts.map +1 -0
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeToRemoteRetryFailed.d.ts +9 -0
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeToRemoteRetryFailed.d.ts.map +1 -0
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeToRemoteStatus.d.ts +0 -81
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeToRemoteStatus.d.ts.map +1 -1
- package/dist/node/server/routes/bridge/routeDefinitions/routes/index.d.ts +6 -0
- package/dist/node/server/routes/bridge/routeDefinitions/routes/index.d.ts.map +1 -1
- package/dist/node/server/server.d.ts +3 -1
- package/dist/node/server/server.d.ts.map +1 -1
- package/dist/node/services/BridgeFulfillmentState.d.ts +22 -13
- package/dist/node/services/BridgeFulfillmentState.d.ts.map +1 -1
- package/dist/node/services/IBridgeServiceCollection.d.ts +12 -1
- package/dist/node/services/IBridgeServiceCollection.d.ts.map +1 -1
- package/dist/node/services/evm/getFeeStructure.d.ts +1 -1
- package/dist/node/services/evm/getFeeStructure.d.ts.map +1 -1
- package/dist/node/services/evm/getRemoteConfirmationDepth.d.ts.map +1 -1
- package/dist/node/services/evm/index.d.ts +1 -0
- package/dist/node/services/evm/index.d.ts.map +1 -1
- package/dist/node/services/evm/resolveDeployBlock.d.ts +28 -0
- package/dist/node/services/evm/resolveDeployBlock.d.ts.map +1 -0
- package/dist/node/services/getServices.d.ts +3 -1
- package/dist/node/services/getServices.d.ts.map +1 -1
- package/dist/node/services/queue/flows/createEthToXl1BridgeJob/createEthToXl1BridgeJob.d.ts +1 -1
- package/dist/node/services/queue/flows/createEthToXl1BridgeJob/getJobIdForEthToXl1BridgeJob.d.ts +2 -13
- package/dist/node/services/queue/flows/createEthToXl1BridgeJob/getJobIdForEthToXl1BridgeJob.d.ts.map +1 -1
- package/dist/node/services/queue/index.d.ts +2 -0
- package/dist/node/services/queue/index.d.ts.map +1 -1
- package/dist/node/services/queue/retryFailedJobs.d.ts +39 -0
- package/dist/node/services/queue/retryFailedJobs.d.ts.map +1 -0
- package/dist/node/services/queue/retrySingleFailedJob.d.ts +29 -0
- package/dist/node/services/queue/retrySingleFailedJob.d.ts.map +1 -0
- package/dist/node/services/queue/scanner/EvmBridgeCursor.d.ts +1 -0
- package/dist/node/services/queue/scanner/EvmBridgeCursor.d.ts.map +1 -1
- package/dist/node/services/queue/scanner/EvmBridgeScanner.d.ts +1 -0
- package/dist/node/services/queue/scanner/EvmBridgeScanner.d.ts.map +1 -1
- package/dist/node/services/queue/scanner/EvmBridgeScannerRunner.d.ts +1 -2
- package/dist/node/services/queue/scanner/EvmBridgeScannerRunner.d.ts.map +1 -1
- package/dist/node/services/queue/scanner/buildEvmBridgeScannerRunner.d.ts +0 -3
- package/dist/node/services/queue/scanner/buildEvmBridgeScannerRunner.d.ts.map +1 -1
- package/dist/node/services/queue/workers/EthEventVerification.d.ts.map +1 -1
- package/dist/node/services/queue/workers/EthToXl1BridgeParent.d.ts.map +1 -1
- package/dist/node/services/queue/workers/EthTransactionMonitor.d.ts.map +1 -1
- package/dist/node/services/queue/workers/EthTransactionPreparation.d.ts.map +1 -1
- package/dist/node/services/queue/workers/EthTransactionSubmission.d.ts.map +1 -1
- package/dist/node/services/queue/workers/EthTransactionSubmissionStorage.d.ts.map +1 -1
- package/dist/node/services/queue/workers/Xl1ReserveTxFulfillment.d.ts.map +1 -1
- package/dist/node/services/queue/workers/Xl1ToEthBridgeParent.d.ts.map +1 -1
- package/dist/node/services/queue/workers/Xl1TransactionMonitor.d.ts.map +1 -1
- package/dist/node/services/queue/workers/Xl1TransactionPreparation.d.ts.map +1 -1
- package/dist/node/services/queue/workers/Xl1TransactionSubmission.d.ts.map +1 -1
- package/dist/node/services/queue/workers/Xl1TransactionSubmissionStorage.d.ts.map +1 -1
- package/dist/node/services/queue/workers/util/buildAcceptedSnapshot.d.ts +47 -0
- package/dist/node/services/queue/workers/util/buildAcceptedSnapshot.d.ts.map +1 -0
- package/dist/node/services/queue/workers/util/buildEthToXl1ReserveTx.d.ts +22 -6
- package/dist/node/services/queue/workers/util/buildEthToXl1ReserveTx.d.ts.map +1 -1
- package/dist/node/services/queue/workers/util/index.d.ts +1 -0
- package/dist/node/services/queue/workers/util/index.d.ts.map +1 -1
- package/dist/node/services/queue/workers/util/resolveEvmBlockTagAtDepth.d.ts +22 -7
- package/dist/node/services/queue/workers/util/resolveEvmBlockTagAtDepth.d.ts.map +1 -1
- package/dist/node/services/queue/workers/util/verifyEthBridgeEvent.d.ts +7 -4
- package/dist/node/services/queue/workers/util/verifyEthBridgeEvent.d.ts.map +1 -1
- package/dist/node/services/util/generateBridgeEstimate.d.ts.map +1 -1
- package/dist/node/services/util/index.d.ts +0 -5
- package/dist/node/services/util/index.d.ts.map +1 -1
- package/dist/node/services/validation/index.d.ts +1 -0
- package/dist/node/services/validation/index.d.ts.map +1 -1
- package/dist/node/services/validation/validateAmountMeetsMinBridgeAmount.d.ts +20 -0
- package/dist/node/services/validation/validateAmountMeetsMinBridgeAmount.d.ts.map +1 -0
- package/dist/node/services/validation/validateBridgeEstimate.d.ts.map +1 -1
- package/dist/node/services/validation/validateSufficientXl1ReserveBalance.d.ts +15 -10
- package/dist/node/services/validation/validateSufficientXl1ReserveBalance.d.ts.map +1 -1
- package/dist/node/telemetry/bucketFailureReason.d.ts +17 -0
- package/dist/node/telemetry/bucketFailureReason.d.ts.map +1 -0
- package/dist/node/telemetry/createBalanceMonitor.d.ts +5 -0
- package/dist/node/telemetry/createBalanceMonitor.d.ts.map +1 -1
- package/dist/node/telemetry/createBridgeFlowMetrics.d.ts +76 -0
- package/dist/node/telemetry/createBridgeFlowMetrics.d.ts.map +1 -0
- package/dist/node/telemetry/createInFlightPoller.d.ts +29 -0
- package/dist/node/telemetry/createInFlightPoller.d.ts.map +1 -0
- package/dist/node/telemetry/createQueueMetrics.d.ts +4 -0
- package/dist/node/telemetry/createQueueMetrics.d.ts.map +1 -1
- package/dist/node/telemetry/index.d.ts +3 -0
- package/dist/node/telemetry/index.d.ts.map +1 -1
- package/package.json +98 -97
- package/dist/node/services/util/BridgeFees.d.ts +0 -13
- package/dist/node/services/util/BridgeFees.d.ts.map +0 -1
- package/dist/node/services/util/bridgeFeesAsBigInt.d.ts +0 -7
- package/dist/node/services/util/bridgeFeesAsBigInt.d.ts.map +0 -1
- package/dist/node/services/util/calculateBridgeFees.d.ts +0 -6
- package/dist/node/services/util/calculateBridgeFees.d.ts.map +0 -1
- package/dist/node/services/util/calculateMaxBridgeAmount.d.ts +0 -11
- package/dist/node/services/util/calculateMaxBridgeAmount.d.ts.map +0 -1
- package/dist/node/services/util/createBridgeTransfer.d.ts +0 -14
- package/dist/node/services/util/createBridgeTransfer.d.ts.map +0 -1
package/dist/node/index.mjs
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
|
-
var
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
5
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
6
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
7
|
+
if (decorator = decorators[i])
|
|
8
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
9
|
+
if (kind && result) __defProp(target, key, result);
|
|
10
|
+
return result;
|
|
11
|
+
};
|
|
12
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
3
13
|
|
|
4
14
|
// src/BridgeActor.ts
|
|
5
15
|
import { creatable } from "@xylabs/sdk-js";
|
|
@@ -16,7 +26,7 @@ import { isDefined } from "@xylabs/sdk-js";
|
|
|
16
26
|
import { Redis } from "ioredis";
|
|
17
27
|
var connection;
|
|
18
28
|
var maxRetriesPerRequest = null;
|
|
19
|
-
var getConnection =
|
|
29
|
+
var getConnection = (config) => {
|
|
20
30
|
if (isDefined(connection)) return connection;
|
|
21
31
|
const { redisHost: host, redisPort: port } = config;
|
|
22
32
|
connection = new Redis({
|
|
@@ -25,7 +35,7 @@ var getConnection = /* @__PURE__ */ __name((config) => {
|
|
|
25
35
|
maxRetriesPerRequest
|
|
26
36
|
});
|
|
27
37
|
return connection;
|
|
28
|
-
}
|
|
38
|
+
};
|
|
29
39
|
|
|
30
40
|
// src/services/queue/flowProducer.ts
|
|
31
41
|
import { isDefined as isDefined2 } from "@xylabs/sdk-js";
|
|
@@ -36,7 +46,7 @@ var prefix = "xl1-bridge";
|
|
|
36
46
|
|
|
37
47
|
// src/services/queue/flowProducer.ts
|
|
38
48
|
var flowProducer;
|
|
39
|
-
var getFlowProducer =
|
|
49
|
+
var getFlowProducer = (connection2, telemetry2) => {
|
|
40
50
|
if (isDefined2(flowProducer)) return flowProducer;
|
|
41
51
|
flowProducer = new FlowProducer({
|
|
42
52
|
connection: connection2,
|
|
@@ -44,355 +54,392 @@ var getFlowProducer = /* @__PURE__ */ __name((connection2, telemetry2) => {
|
|
|
44
54
|
prefix
|
|
45
55
|
});
|
|
46
56
|
return flowProducer;
|
|
47
|
-
}
|
|
57
|
+
};
|
|
48
58
|
|
|
49
59
|
// src/services/queue/workers/EthEventVerification.ts
|
|
50
|
-
import {
|
|
60
|
+
import {
|
|
61
|
+
assertEx as assertEx7,
|
|
62
|
+
isNull,
|
|
63
|
+
spanAsync as spanAsync4
|
|
64
|
+
} from "@xylabs/sdk-js";
|
|
51
65
|
import { UnrecoverableError, Worker } from "bullmq";
|
|
52
66
|
|
|
53
|
-
// src/
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const nonce = `evm:${evmChainId.toLowerCase()}:${evmContractAddress.toLowerCase()}:${bridgeId.toString()}`;
|
|
61
|
-
const transfer = createTransferPayload(bridgeAccount.address, {
|
|
62
|
-
[destAddress]: amount
|
|
63
|
-
});
|
|
64
|
-
const intent = new PayloadBuilder({
|
|
65
|
-
schema: BridgeIntentSchema
|
|
66
|
-
}).fields({
|
|
67
|
-
dest: xl1ChainId,
|
|
68
|
-
destAddress,
|
|
69
|
-
destAmount: amountHex,
|
|
70
|
-
destToken: xl1TokenAddress,
|
|
71
|
-
nonce,
|
|
72
|
-
src: evmChainId,
|
|
73
|
-
srcAddress: evmSrcAddress,
|
|
74
|
-
srcAmount: amountHex,
|
|
75
|
-
srcToken: evmTokenAddress
|
|
76
|
-
}).build();
|
|
77
|
-
const sourceObservation = new PayloadBuilder({
|
|
78
|
-
schema: BridgeSourceObservationSchema
|
|
79
|
-
}).fields({
|
|
80
|
-
dest: xl1ChainId,
|
|
81
|
-
destAddress,
|
|
82
|
-
destAmount: amountHex,
|
|
83
|
-
destToken: xl1TokenAddress,
|
|
84
|
-
src: evmChainId,
|
|
85
|
-
srcAddress: evmSrcAddress,
|
|
86
|
-
srcAmount: amountHex,
|
|
87
|
-
srcConfirmation: evmTxHash,
|
|
88
|
-
srcToken: evmTokenAddress
|
|
89
|
-
}).build();
|
|
90
|
-
return buildTransaction(xl1ChainId, [
|
|
91
|
-
transfer
|
|
92
|
-
], [
|
|
93
|
-
intent,
|
|
94
|
-
sourceObservation
|
|
95
|
-
], bridgeAccount, nbf, exp);
|
|
67
|
+
// src/telemetry/bucketFailureReason.ts
|
|
68
|
+
function bucketFailureReason(failedReason) {
|
|
69
|
+
if (/below_min_bridge_amount/i.test(failedReason)) return "below_min_bridge_amount";
|
|
70
|
+
if (/reserve below required|reserve_insufficient/i.test(failedReason)) return "reserve_insufficient";
|
|
71
|
+
if (/bridgestoremote slot empty|orphaned by reorg|non-canonical at/i.test(failedReason)) return "orphaned_at_depth";
|
|
72
|
+
if (/attempts? exhausted|attempt \d+ expired/i.test(failedReason)) return "attempts_exhausted";
|
|
73
|
+
return "other";
|
|
96
74
|
}
|
|
97
|
-
__name(buildEthToXl1ReserveTx, "buildEthToXl1ReserveTx");
|
|
98
|
-
|
|
99
|
-
// src/services/queue/workers/util/resolveEvmBlockTagAtDepth.ts
|
|
100
|
-
async function resolveEvmBlockTagAtDepth(provider, depth) {
|
|
101
|
-
if (depth === "finalized") return "finalized";
|
|
102
|
-
const head = await provider.getBlockNumber();
|
|
103
|
-
return Math.max(head - depth, 0);
|
|
104
|
-
}
|
|
105
|
-
__name(resolveEvmBlockTagAtDepth, "resolveEvmBlockTagAtDepth");
|
|
106
|
-
|
|
107
|
-
// src/services/queue/workers/util/submitEthTransaction.ts
|
|
108
|
-
import { assertEx, hexToBigInt, toEthAddress } from "@xylabs/sdk-js";
|
|
109
|
-
import { PayloadBuilder as PayloadBuilder2 } from "@xyo-network/sdk-js";
|
|
110
|
-
import { isBridgeIntent } from "@xyo-network/xl1-sdk";
|
|
111
|
-
var submitEthTransaction = /* @__PURE__ */ __name(async (tx, offChainPayloads, bridge, wallet) => {
|
|
112
|
-
const xl1Transaction = assertEx(tx[0], () => "No corresponding XL1 transaction found");
|
|
113
|
-
const allPayloads = [
|
|
114
|
-
...tx[1],
|
|
115
|
-
...offChainPayloads
|
|
116
|
-
];
|
|
117
|
-
const bridgeIntent = assertEx(allPayloads.find(isBridgeIntent), () => "No bridge intent found");
|
|
118
|
-
const srcAddress = toEthAddress(bridgeIntent.srcAddress);
|
|
119
|
-
const destAddress = toEthAddress(bridgeIntent.destAddress);
|
|
120
|
-
const amount = hexToBigInt(bridgeIntent.destAmount);
|
|
121
|
-
const nonce = hexToBigInt(await PayloadBuilder2.hash(xl1Transaction));
|
|
122
|
-
const bridgeTx = await bridge.connect(wallet).bridgeFromRemote(srcAddress, destAddress, amount, nonce);
|
|
123
|
-
const receipt = await bridgeTx.wait(1);
|
|
124
|
-
return receipt?.hash;
|
|
125
|
-
}, "submitEthTransaction");
|
|
126
|
-
|
|
127
|
-
// src/services/queue/workers/util/submitXl1Transaction.ts
|
|
128
|
-
var submitXl1Transaction = /* @__PURE__ */ __name(async (preparedTx, offChain = [], gateway) => {
|
|
129
|
-
const result = await gateway.addTransactionToChain(preparedTx, offChain);
|
|
130
|
-
return result;
|
|
131
|
-
}, "submitXl1Transaction");
|
|
132
75
|
|
|
133
|
-
// src/
|
|
134
|
-
import {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
76
|
+
// src/telemetry/createBalanceMonitor.ts
|
|
77
|
+
import { spanAsync } from "@xylabs/sdk-js";
|
|
78
|
+
var DEFAULT_INTERVAL_MS = 6e4;
|
|
79
|
+
var WEI_PER_GWEI = 10n ** 9n;
|
|
80
|
+
var TOKEN_DECIMALS = 10n ** 18n;
|
|
81
|
+
function createBalanceMonitor(config) {
|
|
82
|
+
const {
|
|
83
|
+
account,
|
|
84
|
+
bridge,
|
|
85
|
+
bridgeableToken,
|
|
86
|
+
gateway,
|
|
87
|
+
meter,
|
|
88
|
+
provider,
|
|
89
|
+
remoteChainId,
|
|
90
|
+
wallet,
|
|
91
|
+
intervalMs = DEFAULT_INTERVAL_MS
|
|
92
|
+
} = config;
|
|
93
|
+
let timer;
|
|
94
|
+
const ethWalletGasBalance = meter.createGauge(
|
|
95
|
+
"bridge_eth_wallet_gas_balance_gwei",
|
|
96
|
+
{ description: "ETH balance of the bridge runner wallet (in gwei)", unit: "gwei" }
|
|
97
|
+
);
|
|
98
|
+
const liquidityTokenBalance = meter.createGauge(
|
|
99
|
+
"bridge_eth_liquidity_token_balance",
|
|
100
|
+
{ description: "ERC-20 token balance of the liquidity source (in whole tokens)", unit: "tokens" }
|
|
101
|
+
);
|
|
102
|
+
const liquidityTokenAllowance = meter.createGauge(
|
|
103
|
+
"bridge_eth_liquidity_token_allowance",
|
|
104
|
+
{ description: "ERC-20 token allowance from liquidity source to bridge contract (in whole tokens)", unit: "tokens" }
|
|
105
|
+
);
|
|
106
|
+
const xl1AccountBalance = meter.createGauge(
|
|
107
|
+
"bridge_xl1_account_balance",
|
|
108
|
+
{ description: "XL1 native balance of the bridge reserve account (in whole XL1)", unit: "xl1" }
|
|
109
|
+
);
|
|
110
|
+
const walletAddress = wallet.address;
|
|
111
|
+
async function poll() {
|
|
112
|
+
await spanAsync("bridge:balance-monitor:poll", async () => {
|
|
113
|
+
try {
|
|
114
|
+
const balance = await provider.getBalance(walletAddress);
|
|
115
|
+
ethWalletGasBalance.record(Number(balance / WEI_PER_GWEI), { address: walletAddress, remoteChainId });
|
|
116
|
+
} catch (err) {
|
|
117
|
+
console.error("[BalanceMonitor] Failed to read ETH wallet gas balance:", err);
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
const liquiditySourceAddress = await bridge.liquiditySource();
|
|
121
|
+
const balance = await bridgeableToken.balanceOf(liquiditySourceAddress);
|
|
122
|
+
liquidityTokenBalance.record(Number(balance / TOKEN_DECIMALS), { address: liquiditySourceAddress, remoteChainId });
|
|
123
|
+
} catch (err) {
|
|
124
|
+
console.error("[BalanceMonitor] Failed to read liquidity source token balance:", err);
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
const liquiditySourceAddress = await bridge.liquiditySource();
|
|
128
|
+
const bridgeAddress = await bridge.getAddress();
|
|
129
|
+
const allowance = await bridgeableToken.allowance(liquiditySourceAddress, bridgeAddress);
|
|
130
|
+
liquidityTokenAllowance.record(Number(allowance / TOKEN_DECIMALS), { address: liquiditySourceAddress, remoteChainId });
|
|
131
|
+
} catch (err) {
|
|
132
|
+
console.error("[BalanceMonitor] Failed to read liquidity source token allowance:", err);
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const viewer = gateway.connection.viewer;
|
|
136
|
+
if (viewer) {
|
|
137
|
+
const balance = await viewer.account.balance.accountBalance(account.address);
|
|
138
|
+
xl1AccountBalance.record(Number(balance / TOKEN_DECIMALS), { address: account.address.toString(), remoteChainId });
|
|
139
|
+
}
|
|
140
|
+
} catch (err) {
|
|
141
|
+
console.error("[BalanceMonitor] Failed to read XL1 account balance:", err);
|
|
142
|
+
}
|
|
143
|
+
}, { timeBudgetLimit: 2e3 });
|
|
149
144
|
}
|
|
150
145
|
return {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
146
|
+
start() {
|
|
147
|
+
void poll();
|
|
148
|
+
timer = setInterval(() => void poll(), intervalMs);
|
|
149
|
+
},
|
|
150
|
+
stop() {
|
|
151
|
+
if (timer) {
|
|
152
|
+
clearInterval(timer);
|
|
153
|
+
timer = void 0;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
156
|
};
|
|
157
157
|
}
|
|
158
|
-
__name(verifyEthBridgeEvent, "verifyEthBridgeEvent");
|
|
159
158
|
|
|
160
|
-
// src/
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const { bridge, bridgeFulfillmentMap, provider, remoteConfirmationDepth } = svc;
|
|
166
|
-
const worker = new Worker(queueName, async (job) => {
|
|
167
|
-
const { bridgeId, identity } = job.data;
|
|
168
|
-
const id = BigInt(bridgeId);
|
|
169
|
-
const existing = await bridgeFulfillmentMap.get(identity);
|
|
170
|
-
if (existing?.canonical) {
|
|
171
|
-
await job.log(`[${identity}] canonical already set; verification skipped`);
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
const state = existing ?? {
|
|
175
|
-
identity,
|
|
176
|
-
previousAttempts: []
|
|
159
|
+
// src/telemetry/createBridgeFlowMetrics.ts
|
|
160
|
+
function createBridgeFlowMetrics(config) {
|
|
161
|
+
const { meter, remoteChainId } = config;
|
|
162
|
+
if (meter === void 0) {
|
|
163
|
+
const noop = () => {
|
|
177
164
|
};
|
|
178
|
-
if (state.failed) state.failed = void 0;
|
|
179
|
-
await job.log(`[${identity}] verifying BridgedToRemote(${id}) at depth ${remoteConfirmationDepth}`);
|
|
180
|
-
const data = await verifyEthBridgeEvent(id, bridge, provider, remoteConfirmationDepth);
|
|
181
|
-
if (isNull(data)) {
|
|
182
|
-
state.failed = {
|
|
183
|
-
at: Date.now(),
|
|
184
|
-
reason: "non-canonical at confirmation depth \u2014 orphaned by reorg or never reached canonical state"
|
|
185
|
-
};
|
|
186
|
-
await bridgeFulfillmentMap.set(identity, state);
|
|
187
|
-
await job.log(`[${identity}] non-canonical at depth \u2014 marked failed`);
|
|
188
|
-
throw new UnrecoverableError(`[${identity}] bridgesToRemote slot empty at confirmation depth ${remoteConfirmationDepth}`);
|
|
189
|
-
}
|
|
190
|
-
state.canonical = data;
|
|
191
|
-
await bridgeFulfillmentMap.set(identity, state);
|
|
192
|
-
await job.log(`[${identity}] verified: src=${data.srcAddress} dest=${data.destAddress} amount=${data.amount} evmTxHash=${data.evmTxHash}`);
|
|
193
|
-
}, {
|
|
194
|
-
connection: connection2,
|
|
195
|
-
telemetry: telemetry2,
|
|
196
|
-
prefix
|
|
197
|
-
});
|
|
198
|
-
worker.on("failed", (job, err) => {
|
|
199
|
-
console.error(`[${name}] Job ${job?.id} failed:`, err.message);
|
|
200
|
-
});
|
|
201
|
-
worker.on("error", (err) => {
|
|
202
|
-
console.error(`[${name}] Worker error:`, err);
|
|
203
|
-
});
|
|
204
|
-
}, "createWorker");
|
|
205
|
-
var EthEventVerification = {
|
|
206
|
-
createWorker,
|
|
207
|
-
name,
|
|
208
|
-
queueName
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
// src/services/queue/workers/EthToXl1BridgeParent.ts
|
|
212
|
-
import { Worker as Worker2 } from "bullmq";
|
|
213
|
-
var name2 = "Bridge Ethereum to XL1";
|
|
214
|
-
var queueName2 = "eth-to-xl1-bridge";
|
|
215
|
-
var createWorker2 = /* @__PURE__ */ __name((connection2, telemetry2) => {
|
|
216
|
-
const worker = new Worker2(queueName2, async (job) => {
|
|
217
|
-
await job.log(`[${job.name}] start`);
|
|
218
|
-
await job.log(`[${job.name}] done`);
|
|
219
|
-
return {};
|
|
220
|
-
}, {
|
|
221
|
-
connection: connection2,
|
|
222
|
-
telemetry: telemetry2,
|
|
223
|
-
prefix
|
|
224
|
-
});
|
|
225
|
-
worker.on("failed", (job, err) => {
|
|
226
|
-
console.error(`[${name2}] Job ${job?.id} failed:`, err.message);
|
|
227
|
-
});
|
|
228
|
-
worker.on("error", (err) => {
|
|
229
|
-
console.error(`[${name2}] Worker error:`, err);
|
|
230
|
-
});
|
|
231
|
-
}, "createWorker");
|
|
232
|
-
var EthToXl1BridgeParent = {
|
|
233
|
-
createWorker: createWorker2,
|
|
234
|
-
name: name2,
|
|
235
|
-
queueName: queueName2
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
// src/services/queue/workers/EthTransactionMonitor.ts
|
|
239
|
-
import { assertEx as assertEx3 } from "@xylabs/sdk-js";
|
|
240
|
-
import { PayloadBuilder as PayloadBuilder3 } from "@xyo-network/sdk-js";
|
|
241
|
-
import { Worker as Worker3 } from "bullmq";
|
|
242
|
-
var name3 = "Monitor Submitted ETH Transaction";
|
|
243
|
-
var queueName3 = "eth-tx-monitor";
|
|
244
|
-
var createWorker3 = /* @__PURE__ */ __name((connection2, telemetry2, services) => {
|
|
245
|
-
const provider = assertEx3(services?.provider, () => "provider service not provided");
|
|
246
|
-
const stateMap = assertEx3(services?.ethTxStateMap, () => "ethTxStateMap service not provided");
|
|
247
|
-
const worker = new Worker3(queueName3, async (job) => {
|
|
248
|
-
const { tx } = job.data;
|
|
249
|
-
const hash = await PayloadBuilder3.hash(tx[0]);
|
|
250
|
-
const state = assertEx3(await stateMap.get(hash), () => "State not found");
|
|
251
|
-
const submissionHash = assertEx3(state?.submissionHash, () => "submissionHash not found");
|
|
252
|
-
const receipt = assertEx3(await provider.getTransactionReceipt(submissionHash), () => "Transaction receipt not found");
|
|
253
|
-
await job.log(`[${hash}] confirmed ETH tx ${submissionHash} in block ${receipt.blockNumber}`);
|
|
254
|
-
const { blockHash, blockNumber } = receipt;
|
|
255
|
-
state.confirmationHash = blockHash;
|
|
256
|
-
await stateMap.set(hash, state);
|
|
257
165
|
return {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
166
|
+
recordAttemptExpiration: noop,
|
|
167
|
+
recordRetried: noop,
|
|
168
|
+
recordSuccess: noop,
|
|
169
|
+
recordTerminalFailure: noop
|
|
261
170
|
};
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
171
|
+
}
|
|
172
|
+
const completed = meter.createCounter(
|
|
173
|
+
"bridge_completed_total",
|
|
174
|
+
{ description: "Successful bridge settlements, per direction" }
|
|
175
|
+
);
|
|
176
|
+
const settleLatency = meter.createHistogram(
|
|
177
|
+
"bridge_settle_latency_seconds",
|
|
178
|
+
{ description: "Wall-clock seconds from acceptance to fulfillment, per direction", unit: "s" }
|
|
179
|
+
);
|
|
180
|
+
const feeRevenue = meter.createCounter(
|
|
181
|
+
"bridge_fee_revenue_total",
|
|
182
|
+
{ description: "Cumulative bridge fees collected (AttoXL1), per direction and token", unit: "AttoXL1" }
|
|
183
|
+
);
|
|
184
|
+
const attemptExpirations = meter.createCounter(
|
|
185
|
+
"bridge_attempt_expirations_total",
|
|
186
|
+
{ description: "Per-attempt failures that did not exhaust retries \u2014 chain or network friction" }
|
|
187
|
+
);
|
|
188
|
+
const terminalFailures = meter.createCounter(
|
|
189
|
+
"bridge_terminal_failures_total",
|
|
190
|
+
{ description: "Terminal failures (retries exhausted or UnrecoverableError), bucketed by reason" }
|
|
191
|
+
);
|
|
192
|
+
const retried = meter.createCounter(
|
|
193
|
+
"bridge_retried_total",
|
|
194
|
+
{ description: "Operator-initiated retries (failed \u2192 waiting), per direction and trigger" }
|
|
195
|
+
);
|
|
196
|
+
return {
|
|
197
|
+
recordSuccess({
|
|
198
|
+
direction,
|
|
199
|
+
feeAmount,
|
|
200
|
+
latencySeconds,
|
|
201
|
+
token
|
|
202
|
+
}) {
|
|
203
|
+
completed.add(1, { remoteChainId, direction });
|
|
204
|
+
if (latencySeconds !== void 0) {
|
|
205
|
+
settleLatency.record(latencySeconds, { remoteChainId, direction });
|
|
206
|
+
}
|
|
207
|
+
feeRevenue.add(Number(feeAmount), {
|
|
208
|
+
remoteChainId,
|
|
209
|
+
direction,
|
|
210
|
+
token
|
|
211
|
+
});
|
|
212
|
+
},
|
|
213
|
+
recordAttemptExpiration({ direction }) {
|
|
214
|
+
attemptExpirations.add(1, { remoteChainId, direction });
|
|
215
|
+
},
|
|
216
|
+
recordRetried({
|
|
217
|
+
count,
|
|
218
|
+
direction,
|
|
219
|
+
trigger
|
|
220
|
+
}) {
|
|
221
|
+
const delta = count ?? 1;
|
|
222
|
+
if (delta <= 0) return;
|
|
223
|
+
retried.add(delta, {
|
|
224
|
+
remoteChainId,
|
|
225
|
+
direction,
|
|
226
|
+
trigger
|
|
227
|
+
});
|
|
228
|
+
},
|
|
229
|
+
recordTerminalFailure({ direction, reason }) {
|
|
230
|
+
terminalFailures.add(1, {
|
|
231
|
+
remoteChainId,
|
|
232
|
+
direction,
|
|
233
|
+
reason
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
}
|
|
279
238
|
|
|
280
|
-
// src/
|
|
281
|
-
import {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
239
|
+
// src/telemetry/createInFlightPoller.ts
|
|
240
|
+
import { spanAsync as spanAsync2 } from "@xylabs/sdk-js";
|
|
241
|
+
var DEFAULT_INTERVAL_MS2 = 6e4;
|
|
242
|
+
function createInFlightPoller(config) {
|
|
243
|
+
const {
|
|
244
|
+
bridgeFulfillmentMap,
|
|
245
|
+
meter,
|
|
246
|
+
remoteChainId,
|
|
247
|
+
intervalMs = DEFAULT_INTERVAL_MS2
|
|
248
|
+
} = config;
|
|
249
|
+
let timer;
|
|
250
|
+
const inFlightGauge = meter.createGauge(
|
|
251
|
+
"bridge_in_flight",
|
|
252
|
+
{ description: "Bridges currently in-flight (canonical observed, fulfillment pending), per direction" }
|
|
253
|
+
);
|
|
254
|
+
const poll = async () => {
|
|
255
|
+
await spanAsync2("bridge:inflight:poll", async () => {
|
|
256
|
+
try {
|
|
257
|
+
let count = 0;
|
|
258
|
+
for await (const [, state] of bridgeFulfillmentMap) {
|
|
259
|
+
if (state.canonical !== void 0 && state.fulfilled === void 0) count++;
|
|
260
|
+
}
|
|
261
|
+
inFlightGauge.record(count, { remoteChainId, direction: "inbound" });
|
|
262
|
+
} catch (err) {
|
|
263
|
+
console.error("[InFlightPoller] Failed to poll bridgeFulfillmentMap:", err);
|
|
264
|
+
}
|
|
265
|
+
}, { timeBudgetLimit: 1e3 });
|
|
266
|
+
};
|
|
267
|
+
return {
|
|
268
|
+
poll,
|
|
269
|
+
start() {
|
|
270
|
+
void poll();
|
|
271
|
+
timer = setInterval(() => void poll(), intervalMs);
|
|
272
|
+
},
|
|
273
|
+
stop() {
|
|
274
|
+
if (timer) {
|
|
275
|
+
clearInterval(timer);
|
|
276
|
+
timer = void 0;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
}
|
|
286
281
|
|
|
287
|
-
// src/
|
|
288
|
-
import {
|
|
289
|
-
|
|
282
|
+
// src/telemetry/createQueueMetrics.ts
|
|
283
|
+
import { spanAsync as spanAsync3 } from "@xylabs/sdk-js";
|
|
284
|
+
var DEFAULT_INTERVAL_MS3 = 3e4;
|
|
285
|
+
function createQueueMetrics(config) {
|
|
286
|
+
const {
|
|
287
|
+
meter,
|
|
288
|
+
queues,
|
|
289
|
+
remoteChainId,
|
|
290
|
+
intervalMs = DEFAULT_INTERVAL_MS3
|
|
291
|
+
} = config;
|
|
292
|
+
let timer;
|
|
293
|
+
const waitingGauge = meter.createGauge(
|
|
294
|
+
"bridge_queue_waiting",
|
|
295
|
+
{ description: "Number of waiting jobs in the bridge queue" }
|
|
296
|
+
);
|
|
297
|
+
const activeGauge = meter.createGauge(
|
|
298
|
+
"bridge_queue_active",
|
|
299
|
+
{ description: "Number of active jobs in the bridge queue" }
|
|
300
|
+
);
|
|
301
|
+
const completedGauge = meter.createGauge(
|
|
302
|
+
"bridge_queue_completed",
|
|
303
|
+
{ description: "Number of completed jobs in the bridge queue" }
|
|
304
|
+
);
|
|
305
|
+
const failedGauge = meter.createGauge(
|
|
306
|
+
"bridge_queue_failed",
|
|
307
|
+
{ description: "Number of failed jobs in the bridge queue" }
|
|
308
|
+
);
|
|
309
|
+
const delayedGauge = meter.createGauge(
|
|
310
|
+
"bridge_queue_delayed",
|
|
311
|
+
{ description: "Number of delayed jobs in the bridge queue" }
|
|
312
|
+
);
|
|
313
|
+
async function poll() {
|
|
314
|
+
await spanAsync3("bridge:queue-metrics:poll", async () => {
|
|
315
|
+
for (const [name13, queue] of Object.entries(queues)) {
|
|
316
|
+
try {
|
|
317
|
+
const counts = await queue.getJobCounts("waiting", "active", "completed", "failed", "delayed");
|
|
318
|
+
const attrs = { queue_name: name13, remoteChainId };
|
|
319
|
+
waitingGauge.record(counts.waiting ?? 0, attrs);
|
|
320
|
+
activeGauge.record(counts.active ?? 0, attrs);
|
|
321
|
+
completedGauge.record(counts.completed ?? 0, attrs);
|
|
322
|
+
failedGauge.record(counts.failed ?? 0, attrs);
|
|
323
|
+
delayedGauge.record(counts.delayed ?? 0, attrs);
|
|
324
|
+
} catch (err) {
|
|
325
|
+
console.error(`[QueueMetrics] Failed to read job counts for queue ${name13}:`, err);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}, { timeBudgetLimit: 1e3 });
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
start() {
|
|
332
|
+
void poll();
|
|
333
|
+
timer = setInterval(() => void poll(), intervalMs);
|
|
334
|
+
},
|
|
335
|
+
stop() {
|
|
336
|
+
if (timer) {
|
|
337
|
+
clearInterval(timer);
|
|
338
|
+
timer = void 0;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
}
|
|
290
343
|
|
|
291
344
|
// src/services/evm/asChainId.ts
|
|
292
|
-
import { asHex
|
|
293
|
-
var asChainId =
|
|
294
|
-
const chainId =
|
|
345
|
+
import { asHex } from "@xylabs/sdk-js";
|
|
346
|
+
var asChainId = (value) => {
|
|
347
|
+
const chainId = asHex(value);
|
|
295
348
|
return chainId;
|
|
296
|
-
}
|
|
349
|
+
};
|
|
297
350
|
|
|
298
351
|
// src/services/evm/asToken.ts
|
|
299
352
|
import { asAddress } from "@xylabs/sdk-js";
|
|
300
|
-
var asToken =
|
|
353
|
+
var asToken = (value) => {
|
|
301
354
|
const token = asAddress(value);
|
|
302
355
|
return token;
|
|
303
|
-
}
|
|
356
|
+
};
|
|
304
357
|
|
|
305
358
|
// src/services/evm/getBridgeEscrowAddress.ts
|
|
306
|
-
import { asAddress as asAddress2, assertEx
|
|
307
|
-
var tryGetBridgeEscrowAddress =
|
|
359
|
+
import { asAddress as asAddress2, assertEx } from "@xylabs/sdk-js";
|
|
360
|
+
var tryGetBridgeEscrowAddress = (config) => {
|
|
308
361
|
const address = asAddress2(config.escrowAddress);
|
|
309
362
|
return address;
|
|
310
|
-
}
|
|
363
|
+
};
|
|
311
364
|
|
|
312
365
|
// src/services/evm/getBridgeFeesAddress.ts
|
|
313
|
-
import { asAddress as asAddress3, assertEx as
|
|
314
|
-
var tryGetBridgeFeesAddress =
|
|
366
|
+
import { asAddress as asAddress3, assertEx as assertEx2 } from "@xylabs/sdk-js";
|
|
367
|
+
var tryGetBridgeFeesAddress = (config) => {
|
|
315
368
|
const address = asAddress3(config.feesAddress);
|
|
316
369
|
return address;
|
|
317
|
-
}
|
|
370
|
+
};
|
|
318
371
|
|
|
319
372
|
// src/services/evm/getFeeStructure.ts
|
|
320
|
-
var getFeeStructure =
|
|
373
|
+
var getFeeStructure = (config) => {
|
|
321
374
|
const { feeFixed, feeRateBasisPoints } = config;
|
|
322
|
-
return {
|
|
323
|
-
|
|
324
|
-
feeRateBasisPoints
|
|
325
|
-
};
|
|
326
|
-
}, "getFeeStructure");
|
|
375
|
+
return { feeFixed, feeRateBasisPoints };
|
|
376
|
+
};
|
|
327
377
|
|
|
328
378
|
// src/services/evm/getMaxBridgeAmount.ts
|
|
329
|
-
var getMaxBridgeAmount =
|
|
379
|
+
var getMaxBridgeAmount = (config) => {
|
|
330
380
|
const { maxBridgeAmount } = config;
|
|
331
381
|
return maxBridgeAmount;
|
|
332
|
-
}
|
|
382
|
+
};
|
|
333
383
|
|
|
334
384
|
// src/services/evm/getMinBridgeAmount.ts
|
|
335
|
-
var getMinBridgeAmount =
|
|
385
|
+
var getMinBridgeAmount = (config) => {
|
|
336
386
|
const { minBridgeAmount } = config;
|
|
337
387
|
return minBridgeAmount;
|
|
338
|
-
}
|
|
388
|
+
};
|
|
339
389
|
|
|
340
390
|
// src/services/evm/getRemoteChainId.ts
|
|
341
|
-
import { assertEx as
|
|
342
|
-
var getRemoteChainId =
|
|
343
|
-
const remoteChainId =
|
|
391
|
+
import { assertEx as assertEx3 } from "@xylabs/sdk-js";
|
|
392
|
+
var getRemoteChainId = (config) => {
|
|
393
|
+
const remoteChainId = assertEx3(asChainId(config.remoteChainId), () => "Invalid remote chain ID in config");
|
|
344
394
|
return remoteChainId;
|
|
345
|
-
}
|
|
395
|
+
};
|
|
346
396
|
|
|
347
397
|
// src/services/evm/getRemoteTokenAddress.ts
|
|
348
|
-
import { assertEx as
|
|
349
|
-
var getRemoteTokenAddress =
|
|
398
|
+
import { assertEx as assertEx4 } from "@xylabs/sdk-js";
|
|
399
|
+
var getRemoteTokenAddress = (config) => {
|
|
350
400
|
const token = asToken(config.remoteTokenAddress);
|
|
351
|
-
return
|
|
352
|
-
}
|
|
401
|
+
return assertEx4(token, () => "Remote token address is not defined in bridge configuration");
|
|
402
|
+
};
|
|
353
403
|
|
|
354
404
|
// src/services/evm/getBridgeWalletAccount.ts
|
|
355
405
|
import { isDefined as isDefined3 } from "@xylabs/sdk-js";
|
|
356
406
|
import { resolveWalletForActor } from "@xyo-network/chain-orchestration";
|
|
357
407
|
var accountServiceSingleton;
|
|
358
|
-
var getBridgeWalletAccount =
|
|
408
|
+
var getBridgeWalletAccount = async (config) => {
|
|
359
409
|
if (accountServiceSingleton) return accountServiceSingleton;
|
|
360
410
|
const accountPath = typeof config.accountPath === "string" ? config.accountPath : void 0;
|
|
361
411
|
const account = await resolveWalletForActor(config.name, accountPath);
|
|
362
412
|
accountServiceSingleton = account;
|
|
363
413
|
return accountServiceSingleton;
|
|
364
|
-
}
|
|
414
|
+
};
|
|
365
415
|
|
|
366
416
|
// src/services/evm/getTransferAddresses.ts
|
|
367
|
-
var getTransferAddresses =
|
|
417
|
+
var getTransferAddresses = async (config) => {
|
|
368
418
|
const escrowAddress = tryGetBridgeEscrowAddress(config) ?? (await getBridgeWalletAccount(config)).address;
|
|
369
419
|
const feesAddress = tryGetBridgeFeesAddress(config) ?? (await getBridgeWalletAccount(config)).address;
|
|
370
|
-
return {
|
|
371
|
-
|
|
372
|
-
feesAddress
|
|
373
|
-
};
|
|
374
|
-
}, "getTransferAddresses");
|
|
420
|
+
return { escrowAddress, feesAddress };
|
|
421
|
+
};
|
|
375
422
|
|
|
376
423
|
// src/services/evm/getXl1ChainId.ts
|
|
377
|
-
import { assertEx as
|
|
378
|
-
var getXl1ChainId =
|
|
424
|
+
import { assertEx as assertEx5, isDefined as isDefined4 } from "@xylabs/sdk-js";
|
|
425
|
+
var getXl1ChainId = (config) => {
|
|
379
426
|
const xl1ChainId = config.xl1ChainId;
|
|
380
427
|
if (isDefined4(xl1ChainId)) {
|
|
381
|
-
return
|
|
428
|
+
return assertEx5(asChainId(xl1ChainId), () => "Invalid xl1ChainId in bridge config");
|
|
382
429
|
}
|
|
383
|
-
return
|
|
384
|
-
}
|
|
430
|
+
return assertEx5(asChainId(config.chain.id), () => "Invalid chain.id in config");
|
|
431
|
+
};
|
|
385
432
|
|
|
386
433
|
// src/services/evm/getXl1TokenAddress.ts
|
|
387
434
|
import { isDefined as isDefined5 } from "@xylabs/sdk-js";
|
|
388
|
-
var getXl1TokenAddress =
|
|
435
|
+
var getXl1TokenAddress = (config) => {
|
|
389
436
|
const token = asToken(config.xl1TokenAddress);
|
|
390
437
|
if (isDefined5(token)) return token;
|
|
391
438
|
return getXl1ChainId(config);
|
|
392
|
-
}
|
|
439
|
+
};
|
|
393
440
|
|
|
394
441
|
// src/services/evm/getBridgeSettings.ts
|
|
395
|
-
var getBridgeSettings =
|
|
442
|
+
var getBridgeSettings = async (config) => {
|
|
396
443
|
const { feeFixed, feeRateBasisPoints } = getFeeStructure(config);
|
|
397
444
|
const { feesAddress, escrowAddress } = await getTransferAddresses(config);
|
|
398
445
|
const maxBridgeAmount = getMaxBridgeAmount(config);
|
|
@@ -413,111 +460,479 @@ var getBridgeSettings = /* @__PURE__ */ __name(async (config) => {
|
|
|
413
460
|
xl1TokenAddress,
|
|
414
461
|
xl1ChainId
|
|
415
462
|
};
|
|
416
|
-
}
|
|
463
|
+
};
|
|
417
464
|
|
|
418
465
|
// src/services/evm/getRemoteConfirmationDepth.ts
|
|
419
|
-
import { isDefined as isDefined6 } from "@xylabs/sdk-js";
|
|
420
|
-
var HARDHAT_CHAIN_ID = "0x7a69";
|
|
421
|
-
var ETHEREUM_MAINNET_CHAIN_ID = "0x1";
|
|
466
|
+
import { isDefined as isDefined6, toHex } from "@xylabs/sdk-js";
|
|
467
|
+
var HARDHAT_CHAIN_ID = toHex("0x7a69");
|
|
468
|
+
var ETHEREUM_MAINNET_CHAIN_ID = toHex("0x1");
|
|
422
469
|
var FALLBACK_CONFIRMATION_DEPTH = 32;
|
|
423
|
-
var DEFAULT_CONFIRMATION_DEPTH_BY_CHAIN_ID =
|
|
424
|
-
[HARDHAT_CHAIN_ID
|
|
425
|
-
[ETHEREUM_MAINNET_CHAIN_ID
|
|
426
|
-
|
|
470
|
+
var DEFAULT_CONFIRMATION_DEPTH_BY_CHAIN_ID = /* @__PURE__ */ new Map([
|
|
471
|
+
[HARDHAT_CHAIN_ID, 0],
|
|
472
|
+
[ETHEREUM_MAINNET_CHAIN_ID, 32]
|
|
473
|
+
]);
|
|
427
474
|
function getRemoteConfirmationDepth(config) {
|
|
428
475
|
if (isDefined6(config.remoteConfirmationDepth)) return config.remoteConfirmationDepth;
|
|
429
|
-
const chainId = getRemoteChainId(config)
|
|
430
|
-
return DEFAULT_CONFIRMATION_DEPTH_BY_CHAIN_ID
|
|
476
|
+
const chainId = toHex(getRemoteChainId(config));
|
|
477
|
+
return DEFAULT_CONFIRMATION_DEPTH_BY_CHAIN_ID.get(chainId) ?? FALLBACK_CONFIRMATION_DEPTH;
|
|
431
478
|
}
|
|
432
|
-
__name(getRemoteConfirmationDepth, "getRemoteConfirmationDepth");
|
|
433
479
|
|
|
434
480
|
// src/services/evm/getScannerIntervalMs.ts
|
|
435
481
|
function getScannerIntervalMs(config) {
|
|
436
482
|
return config.scannerIntervalMs;
|
|
437
483
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
const
|
|
447
|
-
|
|
484
|
+
|
|
485
|
+
// src/services/evm/resolveDeployBlock.ts
|
|
486
|
+
import { getAddress } from "ethers";
|
|
487
|
+
async function contractExistsAt(provider, address, blockNumber) {
|
|
488
|
+
const code = await provider.getCode(getAddress(address), blockNumber);
|
|
489
|
+
return code !== "0x" && code.length > 2;
|
|
490
|
+
}
|
|
491
|
+
async function resolveDeployBlockByBinarySearch(provider, address) {
|
|
492
|
+
const head = await provider.getBlockNumber();
|
|
493
|
+
if (!await contractExistsAt(provider, address, head)) {
|
|
494
|
+
throw new Error(`resolveDeployBlockByBinarySearch: no contract code at ${address} at head=${head}`);
|
|
495
|
+
}
|
|
496
|
+
let low = 0;
|
|
497
|
+
let high = head;
|
|
498
|
+
while (low < high) {
|
|
499
|
+
const mid = Math.floor((low + high) / 2);
|
|
500
|
+
if (await contractExistsAt(provider, address, mid)) {
|
|
501
|
+
high = mid;
|
|
502
|
+
} else {
|
|
503
|
+
low = mid + 1;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
return low;
|
|
507
|
+
}
|
|
508
|
+
async function resolveDeployBlock(opts) {
|
|
509
|
+
const {
|
|
510
|
+
configuredDeployBlock,
|
|
511
|
+
contractAddress,
|
|
512
|
+
provider
|
|
513
|
+
} = opts;
|
|
514
|
+
if (configuredDeployBlock !== void 0 && configuredDeployBlock >= 0) return configuredDeployBlock;
|
|
515
|
+
return resolveDeployBlockByBinarySearch(provider, contractAddress);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// src/services/queue/workers/util/buildAcceptedSnapshot.ts
|
|
519
|
+
import { hexToBigInt, toHex as toHex2 } from "@xylabs/sdk-js";
|
|
520
|
+
import { buildEthToXl1BridgePayloads, calculateBridgeFees } from "@xyo-network/chain-bridge-shared";
|
|
521
|
+
import { PayloadBuilder } from "@xyo-network/sdk-js";
|
|
522
|
+
import {
|
|
523
|
+
BridgeDestinationObservationFieldsZod,
|
|
524
|
+
BridgeDestinationObservationSchema,
|
|
525
|
+
BridgeSourceObservationFieldsZod,
|
|
526
|
+
BridgeSourceObservationSchema
|
|
527
|
+
} from "@xyo-network/xl1-sdk";
|
|
528
|
+
function buildAcceptedSnapshot(options2) {
|
|
529
|
+
const {
|
|
530
|
+
amount,
|
|
531
|
+
bridgeAccountAddress,
|
|
532
|
+
bridgeId,
|
|
533
|
+
destAddress,
|
|
534
|
+
evmChainId,
|
|
535
|
+
evmContractAddress,
|
|
536
|
+
evmSrcAddress,
|
|
537
|
+
evmTokenAddress,
|
|
538
|
+
evmTxHash,
|
|
448
539
|
feeFixed,
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
540
|
+
feeRateBasisPoints,
|
|
541
|
+
feesAddress,
|
|
542
|
+
xl1ChainId,
|
|
543
|
+
xl1TokenAddress
|
|
544
|
+
} = options2;
|
|
545
|
+
const acceptedFees = calculateBridgeFees(toHex2(amount), { feeFixed, feeRateBasisPoints });
|
|
546
|
+
const feesAmount = hexToBigInt(acceptedFees.feeFixed) + hexToBigInt(acceptedFees.feeVariable);
|
|
547
|
+
const { intent: acceptedIntent } = buildEthToXl1BridgePayloads({
|
|
548
|
+
amount,
|
|
549
|
+
bridgeAccountAddress,
|
|
550
|
+
bridgeId,
|
|
551
|
+
destAddress,
|
|
552
|
+
evmChainId,
|
|
553
|
+
evmContractAddress,
|
|
554
|
+
evmSrcAddress,
|
|
555
|
+
evmTokenAddress,
|
|
556
|
+
feesAddress,
|
|
557
|
+
feesAmount,
|
|
558
|
+
xl1ChainId,
|
|
559
|
+
xl1TokenAddress
|
|
560
|
+
});
|
|
561
|
+
const sourceFields = BridgeSourceObservationFieldsZod.parse({
|
|
562
|
+
...acceptedIntent,
|
|
563
|
+
srcConfirmation: evmTxHash
|
|
564
|
+
});
|
|
565
|
+
const acceptedSourceObservation = new PayloadBuilder({ schema: BridgeSourceObservationSchema }).fields(sourceFields).build();
|
|
566
|
+
return {
|
|
567
|
+
acceptedFees,
|
|
568
|
+
acceptedIntent,
|
|
569
|
+
acceptedSourceObservation
|
|
477
570
|
};
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
571
|
+
}
|
|
572
|
+
function buildAcceptedDestinationObservation(intent, destConfirmation) {
|
|
573
|
+
const fields = BridgeDestinationObservationFieldsZod.parse({
|
|
574
|
+
...intent,
|
|
575
|
+
destConfirmation
|
|
576
|
+
});
|
|
577
|
+
return new PayloadBuilder({ schema: BridgeDestinationObservationSchema }).fields(fields).build();
|
|
578
|
+
}
|
|
481
579
|
|
|
482
|
-
// src/services/util/
|
|
483
|
-
import {
|
|
484
|
-
import {
|
|
485
|
-
import {
|
|
486
|
-
import {
|
|
487
|
-
|
|
488
|
-
const {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
580
|
+
// src/services/queue/workers/util/buildEthToXl1ReserveTx.ts
|
|
581
|
+
import { toHex as toHex3 } from "@xylabs/sdk-js";
|
|
582
|
+
import { buildEthToXl1BridgePayloads as buildEthToXl1BridgePayloads2 } from "@xyo-network/chain-bridge-shared";
|
|
583
|
+
import { PayloadBuilder as PayloadBuilder2 } from "@xyo-network/sdk-js";
|
|
584
|
+
import { BridgeSourceObservationSchema as BridgeSourceObservationSchema2, buildTransaction } from "@xyo-network/xl1-sdk";
|
|
585
|
+
async function buildEthToXl1ReserveTx(options2) {
|
|
586
|
+
const {
|
|
587
|
+
amount,
|
|
588
|
+
bridgeAccount,
|
|
589
|
+
bridgeId,
|
|
590
|
+
destAddress,
|
|
591
|
+
evmChainId,
|
|
592
|
+
evmContractAddress,
|
|
593
|
+
evmSrcAddress,
|
|
594
|
+
evmTokenAddress,
|
|
595
|
+
evmTxHash,
|
|
596
|
+
exp,
|
|
597
|
+
feesAddress,
|
|
598
|
+
feesAmount,
|
|
599
|
+
nbf,
|
|
600
|
+
xl1ChainId,
|
|
601
|
+
xl1TokenAddress
|
|
602
|
+
} = options2;
|
|
603
|
+
const { intent, transfer } = buildEthToXl1BridgePayloads2({
|
|
604
|
+
amount,
|
|
605
|
+
bridgeAccountAddress: bridgeAccount.address,
|
|
606
|
+
bridgeId,
|
|
607
|
+
destAddress,
|
|
608
|
+
evmChainId,
|
|
609
|
+
evmContractAddress,
|
|
610
|
+
evmSrcAddress,
|
|
611
|
+
evmTokenAddress,
|
|
612
|
+
feesAddress,
|
|
613
|
+
feesAmount,
|
|
614
|
+
xl1ChainId,
|
|
615
|
+
xl1TokenAddress
|
|
493
616
|
});
|
|
494
|
-
const
|
|
495
|
-
const
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
srcAddress: sender,
|
|
499
|
-
srcAmount,
|
|
500
|
-
srcToken: xl1TokenAddress,
|
|
501
|
-
// Destination
|
|
502
|
-
dest: remoteChainId,
|
|
617
|
+
const destAmountHex = toHex3(amount - feesAmount);
|
|
618
|
+
const srcAmountHex = toHex3(amount);
|
|
619
|
+
const sourceObservation = new PayloadBuilder2({ schema: BridgeSourceObservationSchema2 }).fields({
|
|
620
|
+
dest: xl1ChainId,
|
|
503
621
|
destAddress,
|
|
504
|
-
destAmount:
|
|
505
|
-
destToken:
|
|
506
|
-
|
|
622
|
+
destAmount: destAmountHex,
|
|
623
|
+
destToken: xl1TokenAddress,
|
|
624
|
+
src: evmChainId,
|
|
625
|
+
srcAddress: evmSrcAddress,
|
|
626
|
+
srcAmount: srcAmountHex,
|
|
627
|
+
srcConfirmation: evmTxHash,
|
|
628
|
+
srcToken: evmTokenAddress
|
|
629
|
+
}).build();
|
|
630
|
+
return buildTransaction(
|
|
631
|
+
xl1ChainId,
|
|
632
|
+
[transfer],
|
|
633
|
+
[intent, sourceObservation],
|
|
634
|
+
bridgeAccount,
|
|
635
|
+
nbf,
|
|
636
|
+
exp
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// src/services/queue/workers/util/resolveEvmBlockTagAtDepth.ts
|
|
641
|
+
async function resolveEvmBlockTagAtDepth(provider, depth, deployBlock) {
|
|
642
|
+
if (depth === "finalized") return "finalized";
|
|
643
|
+
if (depth === 0) return "latest";
|
|
644
|
+
const head = await provider.getBlockNumber();
|
|
645
|
+
return Math.max(head - depth, deployBlock);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// src/services/queue/workers/util/submitEthTransaction.ts
|
|
649
|
+
import {
|
|
650
|
+
assertEx as assertEx6,
|
|
651
|
+
hexToBigInt as hexToBigInt2,
|
|
652
|
+
toEthAddress
|
|
653
|
+
} from "@xylabs/sdk-js";
|
|
654
|
+
import { PayloadBuilder as PayloadBuilder3 } from "@xyo-network/sdk-js";
|
|
655
|
+
import { isBridgeIntent } from "@xyo-network/xl1-sdk";
|
|
656
|
+
var submitEthTransaction = async (tx, offChainPayloads, bridge, wallet) => {
|
|
657
|
+
const xl1Transaction = assertEx6(tx[0], () => "No corresponding XL1 transaction found");
|
|
658
|
+
const allPayloads = [...tx[1], ...offChainPayloads];
|
|
659
|
+
const bridgeIntent = assertEx6(allPayloads.find(isBridgeIntent), () => "No bridge intent found");
|
|
660
|
+
const srcAddress = toEthAddress(bridgeIntent.srcAddress);
|
|
661
|
+
const destAddress = toEthAddress(bridgeIntent.destAddress);
|
|
662
|
+
const amount = hexToBigInt2(bridgeIntent.destAmount);
|
|
663
|
+
const nonce = hexToBigInt2(await PayloadBuilder3.hash(xl1Transaction));
|
|
664
|
+
const bridgeTx = await bridge.connect(wallet).bridgeFromRemote(srcAddress, destAddress, amount, nonce);
|
|
665
|
+
const receipt = await bridgeTx.wait(1);
|
|
666
|
+
return receipt?.hash;
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
// src/services/queue/workers/util/submitXl1Transaction.ts
|
|
670
|
+
var submitXl1Transaction = async (preparedTx, offChain = [], gateway) => {
|
|
671
|
+
const result = await gateway.addTransactionToChain(preparedTx, offChain);
|
|
672
|
+
return result;
|
|
673
|
+
};
|
|
674
|
+
|
|
675
|
+
// src/services/queue/workers/util/verifyEthBridgeEvent.ts
|
|
676
|
+
import { asHex as asHex2, toAddress } from "@xylabs/sdk-js";
|
|
677
|
+
import { ZeroAddress } from "ethers";
|
|
678
|
+
async function verifyEthBridgeEvent(id, bridge, provider, confirmationDepth, deployBlock) {
|
|
679
|
+
const blockTag = await resolveEvmBlockTagAtDepth(provider, confirmationDepth, deployBlock);
|
|
680
|
+
const data = await bridge.bridgesToRemote(id, { blockTag });
|
|
681
|
+
if (data.srcAddress === ZeroAddress) {
|
|
682
|
+
return null;
|
|
683
|
+
}
|
|
684
|
+
const filter = bridge.filters.BridgedToRemote(id);
|
|
685
|
+
const events = await bridge.queryFilter(filter, deployBlock, blockTag);
|
|
686
|
+
const event = events[0];
|
|
687
|
+
if (event === void 0) {
|
|
688
|
+
return null;
|
|
689
|
+
}
|
|
690
|
+
return {
|
|
691
|
+
amount: data.amount,
|
|
692
|
+
destAddress: toAddress(data.destAddress),
|
|
693
|
+
destToken: toAddress(data.destToken),
|
|
694
|
+
evmTxHash: asHex2(event.transactionHash, true),
|
|
695
|
+
observedAt: Date.now(),
|
|
696
|
+
srcAddress: toAddress(data.srcAddress)
|
|
507
697
|
};
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// src/services/queue/workers/EthEventVerification.ts
|
|
701
|
+
var name = "Verify ETH Bridge Event at Depth";
|
|
702
|
+
var queueName = "eth-event-verify";
|
|
703
|
+
var createWorker = (connection2, telemetry2, services) => {
|
|
704
|
+
const svc = assertEx7(services, () => "services not provided");
|
|
705
|
+
const {
|
|
706
|
+
account,
|
|
707
|
+
bridge,
|
|
708
|
+
bridgeFlowMetrics,
|
|
709
|
+
bridgeFulfillmentMap,
|
|
710
|
+
config,
|
|
711
|
+
evmBridgeDeployBlock,
|
|
712
|
+
logger,
|
|
713
|
+
provider,
|
|
714
|
+
remoteConfirmationDepth
|
|
715
|
+
} = svc;
|
|
716
|
+
const worker = new Worker(
|
|
717
|
+
queueName,
|
|
718
|
+
async (job) => {
|
|
719
|
+
await spanAsync4("bridge:worker:eth-event-verify", async () => {
|
|
720
|
+
const { bridgeId, identity } = job.data;
|
|
721
|
+
const id = BigInt(bridgeId);
|
|
722
|
+
const existing = await bridgeFulfillmentMap.get(identity);
|
|
723
|
+
if (existing?.canonical) {
|
|
724
|
+
await job.log(`[${identity}] canonical already set; verification skipped`);
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
const state = existing ?? { identity, previousAttempts: [] };
|
|
728
|
+
await job.log(`[${identity}] verifying BridgedToRemote(${id}) at depth ${remoteConfirmationDepth}`);
|
|
729
|
+
const data = await verifyEthBridgeEvent(id, bridge, provider, remoteConfirmationDepth, evmBridgeDeployBlock);
|
|
730
|
+
if (isNull(data)) {
|
|
731
|
+
await job.log(`[${identity}] non-canonical at confirmation depth \u2014 orphaned by reorg or never reached canonical state`);
|
|
732
|
+
throw new UnrecoverableError(`[${identity}] bridgesToRemote slot empty at confirmation depth ${remoteConfirmationDepth}`);
|
|
733
|
+
}
|
|
734
|
+
state.canonical = data;
|
|
735
|
+
const { feeFixed, feeRateBasisPoints } = getFeeStructure(config);
|
|
736
|
+
const { feesAddress } = await getTransferAddresses(config);
|
|
737
|
+
const snapshot = buildAcceptedSnapshot({
|
|
738
|
+
amount: data.amount,
|
|
739
|
+
bridgeAccountAddress: account.address,
|
|
740
|
+
bridgeId: id,
|
|
741
|
+
destAddress: data.destAddress,
|
|
742
|
+
evmChainId: config.remoteChainId,
|
|
743
|
+
evmContractAddress: config.remoteBridgeContractAddress,
|
|
744
|
+
evmSrcAddress: data.srcAddress,
|
|
745
|
+
evmTokenAddress: data.destToken,
|
|
746
|
+
evmTxHash: data.evmTxHash,
|
|
747
|
+
feeFixed,
|
|
748
|
+
feeRateBasisPoints,
|
|
749
|
+
feesAddress,
|
|
750
|
+
xl1ChainId: getXl1ChainId(config),
|
|
751
|
+
xl1TokenAddress: getXl1TokenAddress(config)
|
|
752
|
+
});
|
|
753
|
+
state.acceptedIntent = snapshot.acceptedIntent;
|
|
754
|
+
state.acceptedFees = snapshot.acceptedFees;
|
|
755
|
+
state.acceptedSourceObservation = snapshot.acceptedSourceObservation;
|
|
756
|
+
await bridgeFulfillmentMap.set(identity, state);
|
|
757
|
+
await job.log(
|
|
758
|
+
`[${identity}] verified: src=${data.srcAddress} dest=${data.destAddress} amount=${data.amount} evmTxHash=${data.evmTxHash} snapshot: feeFixed=${snapshot.acceptedFees.feeFixed} feeVariable=${snapshot.acceptedFees.feeVariable}`
|
|
759
|
+
);
|
|
760
|
+
}, { logger, timeBudgetLimit: 5e3 });
|
|
761
|
+
},
|
|
762
|
+
{
|
|
763
|
+
connection: connection2,
|
|
764
|
+
telemetry: telemetry2,
|
|
765
|
+
prefix
|
|
766
|
+
}
|
|
767
|
+
);
|
|
768
|
+
worker.on("failed", (job, err) => {
|
|
769
|
+
logger.error(`[eth-verify] job ${job?.id} failed: ${err.message}`);
|
|
770
|
+
if (job === void 0) return;
|
|
771
|
+
const attemptsAllowed = job.opts.attempts ?? 1;
|
|
772
|
+
if (job.attemptsMade >= attemptsAllowed) {
|
|
773
|
+
bridgeFlowMetrics.recordTerminalFailure({ direction: "inbound", reason: bucketFailureReason(err.message) });
|
|
774
|
+
}
|
|
775
|
+
});
|
|
776
|
+
worker.on("error", (err) => {
|
|
777
|
+
logger.error(`[eth-verify] worker error: ${err.message}`);
|
|
778
|
+
});
|
|
779
|
+
};
|
|
780
|
+
var EthEventVerification = {
|
|
781
|
+
createWorker,
|
|
782
|
+
name,
|
|
783
|
+
queueName
|
|
784
|
+
};
|
|
785
|
+
|
|
786
|
+
// src/services/queue/workers/EthToXl1BridgeParent.ts
|
|
787
|
+
import { assertEx as assertEx8, spanAsync as spanAsync5 } from "@xylabs/sdk-js";
|
|
788
|
+
import { Worker as Worker2 } from "bullmq";
|
|
789
|
+
var name2 = "Bridge Ethereum to XL1";
|
|
790
|
+
var queueName2 = "eth-to-xl1-bridge";
|
|
791
|
+
var createWorker2 = (connection2, telemetry2, services) => {
|
|
792
|
+
const logger = assertEx8(services?.logger, () => "logger service not provided");
|
|
793
|
+
const worker = new Worker2(
|
|
794
|
+
queueName2,
|
|
795
|
+
async (job) => {
|
|
796
|
+
return await spanAsync5("bridge:worker:eth-to-xl1-bridge", async () => {
|
|
797
|
+
await job.log(`[${job.name}] start`);
|
|
798
|
+
await job.log(`[${job.name}] done`);
|
|
799
|
+
return {};
|
|
800
|
+
}, { logger, timeBudgetLimit: 5e3 });
|
|
801
|
+
},
|
|
802
|
+
{
|
|
803
|
+
connection: connection2,
|
|
804
|
+
telemetry: telemetry2,
|
|
805
|
+
prefix
|
|
806
|
+
}
|
|
807
|
+
);
|
|
808
|
+
worker.on("failed", (job, err) => {
|
|
809
|
+
logger.error(`[eth-to-xl1-parent] job ${job?.id} failed: ${err.message}`);
|
|
810
|
+
});
|
|
811
|
+
worker.on("error", (err) => {
|
|
812
|
+
logger.error(`[eth-to-xl1-parent] worker error: ${err.message}`);
|
|
813
|
+
});
|
|
814
|
+
};
|
|
815
|
+
var EthToXl1BridgeParent = {
|
|
816
|
+
createWorker: createWorker2,
|
|
817
|
+
name: name2,
|
|
818
|
+
queueName: queueName2
|
|
819
|
+
};
|
|
820
|
+
|
|
821
|
+
// src/services/queue/workers/EthTransactionMonitor.ts
|
|
822
|
+
import {
|
|
823
|
+
assertEx as assertEx9,
|
|
824
|
+
hexToBigInt as hexToBigInt3,
|
|
825
|
+
spanAsync as spanAsync6
|
|
826
|
+
} from "@xylabs/sdk-js";
|
|
827
|
+
import { calculateBridgeFees as calculateBridgeFees2 } from "@xyo-network/chain-bridge-shared";
|
|
828
|
+
import { PayloadBuilder as PayloadBuilder4 } from "@xyo-network/sdk-js";
|
|
829
|
+
import { isBridgeIntent as isBridgeIntent2 } from "@xyo-network/xl1-sdk";
|
|
830
|
+
import { Worker as Worker3 } from "bullmq";
|
|
831
|
+
var name3 = "Monitor Submitted ETH Transaction";
|
|
832
|
+
var queueName3 = "eth-tx-monitor";
|
|
833
|
+
var createWorker3 = (connection2, telemetry2, services) => {
|
|
834
|
+
const provider = assertEx9(services?.provider, () => "provider service not provided");
|
|
835
|
+
const stateMap = assertEx9(services?.ethTxStateMap, () => "ethTxStateMap service not provided");
|
|
836
|
+
const logger = assertEx9(services?.logger, () => "logger service not provided");
|
|
837
|
+
const config = assertEx9(services?.config, () => "config service not provided");
|
|
838
|
+
const bridgeFlowMetrics = assertEx9(services?.bridgeFlowMetrics, () => "bridgeFlowMetrics service not provided");
|
|
839
|
+
const worker = new Worker3(
|
|
840
|
+
queueName3,
|
|
841
|
+
async (job) => {
|
|
842
|
+
return await spanAsync6("bridge:worker:eth-tx-monitor", async () => {
|
|
843
|
+
const { tx } = job.data;
|
|
844
|
+
const hash = await PayloadBuilder4.hash(tx[0]);
|
|
845
|
+
const state = assertEx9(await stateMap.get(hash), () => "State not found");
|
|
846
|
+
const submissionHash = assertEx9(state?.submissionHash, () => "submissionHash not found");
|
|
847
|
+
const receipt = assertEx9(await provider.getTransactionReceipt(submissionHash), () => "Transaction receipt not found");
|
|
848
|
+
await job.log(`[${hash}] confirmed ETH tx ${submissionHash} in block ${receipt.blockNumber}`);
|
|
849
|
+
const { blockHash, blockNumber } = receipt;
|
|
850
|
+
state.confirmationHash = blockHash;
|
|
851
|
+
await stateMap.set(hash, state);
|
|
852
|
+
const bridgeIntent = tx[1].find(isBridgeIntent2);
|
|
853
|
+
const feeAmount = (() => {
|
|
854
|
+
if (bridgeIntent === void 0) return 0n;
|
|
855
|
+
const fees = calculateBridgeFees2(bridgeIntent.srcAmount, getFeeStructure(config));
|
|
856
|
+
return hexToBigInt3(fees.feeFixed) + hexToBigInt3(fees.feeVariable);
|
|
857
|
+
})();
|
|
858
|
+
const latencySeconds = (Date.now() - job.timestamp) / 1e3;
|
|
859
|
+
bridgeFlowMetrics.recordSuccess({
|
|
860
|
+
direction: "outbound",
|
|
861
|
+
feeAmount,
|
|
862
|
+
latencySeconds,
|
|
863
|
+
token: getXl1TokenAddress(config)
|
|
864
|
+
});
|
|
865
|
+
return {
|
|
866
|
+
blockHash,
|
|
867
|
+
blockNumber,
|
|
868
|
+
submissionHash
|
|
869
|
+
};
|
|
870
|
+
}, { logger, timeBudgetLimit: 2e3 });
|
|
871
|
+
},
|
|
872
|
+
{
|
|
873
|
+
connection: connection2,
|
|
874
|
+
telemetry: telemetry2,
|
|
875
|
+
prefix
|
|
876
|
+
}
|
|
877
|
+
);
|
|
878
|
+
worker.on("failed", (job, err) => {
|
|
879
|
+
logger.error(`[eth-monitor] job ${job?.id} failed: ${err.message}`);
|
|
880
|
+
if (job === void 0) return;
|
|
881
|
+
const attemptsAllowed = job.opts.attempts ?? 1;
|
|
882
|
+
if (job.attemptsMade < attemptsAllowed) {
|
|
883
|
+
bridgeFlowMetrics.recordAttemptExpiration({ direction: "outbound" });
|
|
884
|
+
} else {
|
|
885
|
+
bridgeFlowMetrics.recordTerminalFailure({ direction: "outbound", reason: bucketFailureReason(err.message) });
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
worker.on("error", (err) => {
|
|
889
|
+
logger.error(`[eth-monitor] worker error: ${err.message}`);
|
|
890
|
+
});
|
|
891
|
+
};
|
|
892
|
+
var EthTransactionMonitor = {
|
|
893
|
+
createWorker: createWorker3,
|
|
894
|
+
name: name3,
|
|
895
|
+
queueName: queueName3
|
|
896
|
+
};
|
|
897
|
+
|
|
898
|
+
// src/services/queue/workers/EthTransactionPreparation.ts
|
|
899
|
+
import {
|
|
900
|
+
assertEx as assertEx15,
|
|
901
|
+
hexToBigInt as hexToBigInt9,
|
|
902
|
+
spanAsync as spanAsync7
|
|
903
|
+
} from "@xylabs/sdk-js";
|
|
904
|
+
import { PayloadBuilder as PayloadBuilder7 } from "@xyo-network/sdk-js";
|
|
905
|
+
import { isBridgeIntent as isBridgeIntent5 } from "@xyo-network/xl1-sdk";
|
|
906
|
+
import { Worker as Worker4 } from "bullmq";
|
|
907
|
+
import { getAddress as getAddress2 } from "ethers";
|
|
908
|
+
|
|
909
|
+
// src/services/validation/validateAmountMeetsMinBridgeAmount.ts
|
|
910
|
+
import { hexToBigInt as hexToBigInt4 } from "@xylabs/sdk-js";
|
|
911
|
+
function validateAmountMeetsMinBridgeAmount({
|
|
912
|
+
amount,
|
|
913
|
+
minBridgeAmount
|
|
914
|
+
}) {
|
|
915
|
+
return amount >= hexToBigInt4(minBridgeAmount);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// src/services/validation/validateBridgeEstimateExact.ts
|
|
919
|
+
import { hexToBigInt as hexToBigInt5, isUndefined } from "@xylabs/sdk-js";
|
|
920
|
+
import { PayloadBuilder as PayloadBuilder5 } from "@xyo-network/sdk-js";
|
|
921
|
+
|
|
922
|
+
// src/services/util/generateBridgeEstimate.ts
|
|
923
|
+
import { generateBridgeEstimate as generateBridgeEstimateShared } from "@xyo-network/chain-bridge-shared";
|
|
924
|
+
var generateBridgeEstimate = async (srcAddress, srcAmount, destAddress, config, nonceOverride) => {
|
|
925
|
+
const settings = await getBridgeSettings(config);
|
|
926
|
+
return generateBridgeEstimateShared(srcAddress, srcAmount, destAddress, settings, nonceOverride);
|
|
927
|
+
};
|
|
517
928
|
|
|
518
929
|
// src/services/validation/validateBridgeEstimateExact.ts
|
|
519
|
-
var validateBridgeEstimateExact =
|
|
520
|
-
const {
|
|
930
|
+
var validateBridgeEstimateExact = async (intent, transfer, config) => {
|
|
931
|
+
const {
|
|
932
|
+
srcAddress,
|
|
933
|
+
srcAmount,
|
|
934
|
+
destAddress
|
|
935
|
+
} = intent;
|
|
521
936
|
if (hexToBigInt5(srcAmount) < hexToBigInt5(getMinBridgeAmount(config))) return false;
|
|
522
937
|
if (hexToBigInt5(srcAmount) > hexToBigInt5(getMaxBridgeAmount(config))) return false;
|
|
523
938
|
const [calculatedIntent, calculatedTransfer] = await generateBridgeEstimate(srcAddress, srcAmount, destAddress, config);
|
|
@@ -529,72 +944,65 @@ var validateBridgeEstimateExact = /* @__PURE__ */ __name(async (intent, transfer
|
|
|
529
944
|
const { epoch: actualTransferEpoch, ...actualTransferStatic } = transfer;
|
|
530
945
|
if (await PayloadBuilder5.dataHash(expectedTransferStatic) !== await PayloadBuilder5.dataHash(actualTransferStatic)) return false;
|
|
531
946
|
return true;
|
|
532
|
-
}
|
|
947
|
+
};
|
|
533
948
|
|
|
534
949
|
// src/services/validation/validateBridgeTransaction.ts
|
|
535
950
|
import { asAddress as asAddress4 } from "@xylabs/sdk-js";
|
|
536
|
-
import {
|
|
951
|
+
import {
|
|
952
|
+
addressesContains,
|
|
953
|
+
BoundWitnessValidator,
|
|
954
|
+
payloadHashesContainsAll,
|
|
955
|
+
payloadSchemasContainsAll
|
|
956
|
+
} from "@xyo-network/boundwitness-validator";
|
|
537
957
|
import { PayloadBuilder as PayloadBuilder6 } from "@xyo-network/sdk-js";
|
|
538
|
-
import { BridgeIntentSchema
|
|
539
|
-
var validateBridgeTransaction =
|
|
958
|
+
import { BridgeIntentSchema, TransferSchema } from "@xyo-network/xl1-sdk";
|
|
959
|
+
var validateBridgeTransaction = async (signedTxBw, intent, transfer, config) => {
|
|
540
960
|
const { srcAddress } = intent;
|
|
541
961
|
const chainId = getXl1ChainId(config);
|
|
542
962
|
if (signedTxBw.chain !== chainId) return false;
|
|
543
963
|
if (signedTxBw.payload_hashes.length != 2) return false;
|
|
544
|
-
if (!payloadSchemasContainsAll(signedTxBw, [
|
|
545
|
-
|
|
546
|
-
TransferSchema
|
|
547
|
-
])) return false;
|
|
548
|
-
const hashes = await PayloadBuilder6.hashes([
|
|
549
|
-
intent,
|
|
550
|
-
transfer
|
|
551
|
-
]);
|
|
964
|
+
if (!payloadSchemasContainsAll(signedTxBw, [BridgeIntentSchema, TransferSchema])) return false;
|
|
965
|
+
const hashes = await PayloadBuilder6.hashes([intent, transfer]);
|
|
552
966
|
if (!payloadHashesContainsAll(signedTxBw, hashes)) return false;
|
|
553
967
|
const errors = await new BoundWitnessValidator(signedTxBw).validate();
|
|
554
968
|
if (errors.length > 0) return false;
|
|
555
969
|
const sender = asAddress4(srcAddress, true);
|
|
556
970
|
if (!addressesContains(signedTxBw, sender)) return false;
|
|
557
971
|
return true;
|
|
558
|
-
}
|
|
972
|
+
};
|
|
559
973
|
|
|
560
974
|
// src/services/validation/validateSufficientLiquiditySourceAllowance.ts
|
|
561
|
-
import { assertEx as
|
|
562
|
-
import { isBridgeIntent as
|
|
563
|
-
var validateSufficientLiquiditySourceAllowance =
|
|
564
|
-
const allPayloads = [
|
|
565
|
-
|
|
566
|
-
...offChainPayloads
|
|
567
|
-
];
|
|
568
|
-
const bridgeIntent = assertEx9(allPayloads.find(isBridgeIntent2), () => "No bridge intent found");
|
|
975
|
+
import { assertEx as assertEx10, hexToBigInt as hexToBigInt6 } from "@xylabs/sdk-js";
|
|
976
|
+
import { isBridgeIntent as isBridgeIntent3 } from "@xyo-network/xl1-sdk";
|
|
977
|
+
var validateSufficientLiquiditySourceAllowance = async (tx, offChainPayloads, bridgeableToken, bridge, logger) => {
|
|
978
|
+
const allPayloads = [...tx[1], ...offChainPayloads];
|
|
979
|
+
const bridgeIntent = assertEx10(allPayloads.find(isBridgeIntent3), () => "No bridge intent found");
|
|
569
980
|
const amount = hexToBigInt6(bridgeIntent.destAmount);
|
|
570
981
|
const liquiditySourceAddress = await bridge.liquiditySource();
|
|
571
982
|
const bridgeAddress = await bridge.getAddress();
|
|
572
983
|
const remainingAllowance = await bridgeableToken.allowance(liquiditySourceAddress, bridgeAddress);
|
|
573
984
|
await logger?.log(`Remaining allowance: ${remainingAllowance.toString()}`);
|
|
574
985
|
return remainingAllowance >= amount;
|
|
575
|
-
}
|
|
986
|
+
};
|
|
576
987
|
|
|
577
988
|
// src/services/validation/validateSufficientLiquiditySourceBalance.ts
|
|
578
|
-
import { assertEx as
|
|
579
|
-
import { isBridgeIntent as
|
|
580
|
-
var validateSufficientLiquiditySourceBalance =
|
|
581
|
-
const allPayloads = [
|
|
582
|
-
|
|
583
|
-
...offChainPayloads
|
|
584
|
-
];
|
|
585
|
-
const bridgeIntent = assertEx10(allPayloads.find(isBridgeIntent3), () => "No bridge intent found");
|
|
989
|
+
import { assertEx as assertEx11, hexToBigInt as hexToBigInt7 } from "@xylabs/sdk-js";
|
|
990
|
+
import { isBridgeIntent as isBridgeIntent4 } from "@xyo-network/xl1-sdk";
|
|
991
|
+
var validateSufficientLiquiditySourceBalance = async (tx, offChainPayloads, bridgeableToken, bridge, logger) => {
|
|
992
|
+
const allPayloads = [...tx[1], ...offChainPayloads];
|
|
993
|
+
const bridgeIntent = assertEx11(allPayloads.find(isBridgeIntent4), () => "No bridge intent found");
|
|
586
994
|
const amount = hexToBigInt7(bridgeIntent.destAmount);
|
|
587
995
|
const liquiditySourceAddress = await bridge.liquiditySource();
|
|
588
996
|
const balance = await bridgeableToken.balanceOf(liquiditySourceAddress);
|
|
589
997
|
await logger?.log(`Remaining balance: ${balance.toString()}`);
|
|
590
998
|
return balance >= amount;
|
|
591
|
-
}
|
|
999
|
+
};
|
|
592
1000
|
|
|
593
1001
|
// src/services/validation/validateSufficientRunnerEthBalanceForGas.ts
|
|
594
|
-
import { assertEx as
|
|
1002
|
+
import { assertEx as assertEx12 } from "@xylabs/sdk-js";
|
|
595
1003
|
var DEFAULT_GAS_BUFFER_BPS = 2000n;
|
|
596
|
-
var validateSufficientRunnerEthBalanceForGas =
|
|
597
|
-
const provider =
|
|
1004
|
+
var validateSufficientRunnerEthBalanceForGas = async (preparedTx, wallet, logger, bufferBps = DEFAULT_GAS_BUFFER_BPS) => {
|
|
1005
|
+
const provider = assertEx12(wallet.provider, () => "Wallet provider is not defined");
|
|
598
1006
|
const feeData = await provider.getFeeData();
|
|
599
1007
|
const perGas = feeData.maxFeePerGas ?? feeData.gasPrice;
|
|
600
1008
|
if (perGas == null) {
|
|
@@ -610,91 +1018,112 @@ var validateSufficientRunnerEthBalanceForGas = /* @__PURE__ */ __name(async (pre
|
|
|
610
1018
|
const baseRequired = estGas * perGas + txValue;
|
|
611
1019
|
const required = baseRequired * (10000n + bufferBps) / 10000n;
|
|
612
1020
|
const balance = await provider.getBalance(await wallet.getAddress());
|
|
613
|
-
await logger?.log(
|
|
1021
|
+
await logger?.log(
|
|
1022
|
+
`[gas] runner=${await wallet.getAddress()} balance=${balance.toString()} estGas=${estGas.toString()} perGas=${perGas.toString()} value=${txValue.toString()} requiredWithBuffer=${required.toString()} bufferBps=${bufferBps.toString()}`
|
|
1023
|
+
);
|
|
614
1024
|
return balance >= required;
|
|
615
|
-
}
|
|
1025
|
+
};
|
|
616
1026
|
|
|
617
1027
|
// src/services/validation/validateSufficientXl1ReserveBalance.ts
|
|
618
|
-
import { assertEx as
|
|
619
|
-
|
|
620
|
-
|
|
1028
|
+
import { assertEx as assertEx13 } from "@xylabs/sdk-js";
|
|
1029
|
+
import { AttoXL1ConvertFactor } from "@xyo-network/xl1-sdk";
|
|
1030
|
+
var RESERVE_FEE_BUFFER = 1n * AttoXL1ConvertFactor.xl1;
|
|
1031
|
+
async function validateSufficientXl1ReserveBalance({
|
|
1032
|
+
amount,
|
|
1033
|
+
bridgeAccount,
|
|
1034
|
+
gateway,
|
|
1035
|
+
logger
|
|
1036
|
+
}) {
|
|
1037
|
+
const viewer = assertEx13(gateway.connection.viewer, () => "Gateway connection does not have a viewer");
|
|
621
1038
|
const balance = await viewer.account.balance.accountBalance(bridgeAccount.address);
|
|
622
|
-
|
|
623
|
-
|
|
1039
|
+
const required = amount + RESERVE_FEE_BUFFER;
|
|
1040
|
+
await logger?.log(
|
|
1041
|
+
`XL1 reserve account ${bridgeAccount.address} balance: ${balance.toString()}; required: ${required.toString()} (amount=${amount.toString()} + buffer=${RESERVE_FEE_BUFFER.toString()})`
|
|
1042
|
+
);
|
|
1043
|
+
return balance >= required;
|
|
624
1044
|
}
|
|
625
|
-
__name(validateSufficientXl1ReserveBalance, "validateSufficientXl1ReserveBalance");
|
|
626
1045
|
|
|
627
1046
|
// src/services/validation/validateSufficientXL1SourceAddressBalance.ts
|
|
628
|
-
import {
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
1047
|
+
import {
|
|
1048
|
+
asAddress as asAddress5,
|
|
1049
|
+
assertEx as assertEx14,
|
|
1050
|
+
hexToBigInt as hexToBigInt8
|
|
1051
|
+
} from "@xylabs/sdk-js";
|
|
1052
|
+
var validateSufficientXL1SourceAddressBalance = async (bridgeIntent, gateway, config, logger) => {
|
|
1053
|
+
const viewer = assertEx14(gateway.connection.viewer, () => "Gateway connection does not have a viewer");
|
|
1054
|
+
const {
|
|
1055
|
+
srcAddress,
|
|
1056
|
+
srcAmount,
|
|
1057
|
+
destAddress
|
|
1058
|
+
} = bridgeIntent;
|
|
632
1059
|
const srcAddressBranded = asAddress5(srcAddress, () => `Invalid source address in bridge intent: ${srcAddress}`);
|
|
633
1060
|
const [_, calculatedTransfer] = await generateBridgeEstimate(srcAddress, srcAmount, destAddress, config);
|
|
634
1061
|
const totalAmount = Object.values(calculatedTransfer.transfers).reduce((acc, transfer) => acc + hexToBigInt8(transfer), 0n);
|
|
635
1062
|
const accountBalance = await viewer.account.balance.accountBalance(srcAddressBranded);
|
|
636
1063
|
await logger?.log(`Account balance for ${srcAddressBranded}: ${accountBalance.toString()}`);
|
|
637
1064
|
return accountBalance >= totalAmount;
|
|
638
|
-
}
|
|
1065
|
+
};
|
|
639
1066
|
|
|
640
1067
|
// src/services/queue/workers/EthTransactionPreparation.ts
|
|
641
1068
|
var name4 = "Prepare ETH Transaction";
|
|
642
1069
|
var queueName4 = "eth-tx-prepare";
|
|
643
|
-
var createWorker4 =
|
|
644
|
-
const bridge =
|
|
645
|
-
const bridgeableToken =
|
|
646
|
-
const stateMap =
|
|
647
|
-
const wallet =
|
|
648
|
-
const
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
1070
|
+
var createWorker4 = (connection2, telemetry2, services) => {
|
|
1071
|
+
const bridge = assertEx15(services?.bridge, () => "bridge service not provided");
|
|
1072
|
+
const bridgeableToken = assertEx15(services?.bridgeableToken, () => "bridgeableToken service not provided");
|
|
1073
|
+
const stateMap = assertEx15(services?.ethTxStateMap, () => "ethTxStateMap service not provided");
|
|
1074
|
+
const wallet = assertEx15(services?.wallet, () => "wallet service not provided");
|
|
1075
|
+
const logger = assertEx15(services?.logger, () => "logger service not provided");
|
|
1076
|
+
const worker = new Worker4(
|
|
1077
|
+
queueName4,
|
|
1078
|
+
async (job) => {
|
|
1079
|
+
return await spanAsync7("bridge:worker:eth-tx-prepare", async () => {
|
|
1080
|
+
const { tx, offChainPayloads } = job.data;
|
|
1081
|
+
const hash = await PayloadBuilder7.hash(tx[0]);
|
|
1082
|
+
await job.log(`[${hash}] preparing ETH transaction`);
|
|
1083
|
+
await job.log(`[${hash}] validating liquiditySource has sufficient allowance`);
|
|
1084
|
+
if (!await validateSufficientLiquiditySourceAllowance(tx, offChainPayloads, bridgeableToken, bridge, job)) {
|
|
1085
|
+
throw new Error("Liquidity source does not have sufficient allowance for the bridge to execute the transaction");
|
|
1086
|
+
}
|
|
1087
|
+
await job.log(`[${hash}] validated liquiditySource has sufficient allowance`);
|
|
1088
|
+
await job.log(`[${hash}] validating liquiditySource has sufficient balance`);
|
|
1089
|
+
if (!await validateSufficientLiquiditySourceBalance(tx, offChainPayloads, bridgeableToken, bridge, job)) {
|
|
1090
|
+
throw new Error("Liquidity source does not have sufficient balance for the bridge to execute the transaction");
|
|
1091
|
+
}
|
|
1092
|
+
await job.log(`[${hash}] validated liquiditySource has sufficient balance`);
|
|
1093
|
+
await job.log(`[${hash}] building ETH transaction`);
|
|
1094
|
+
const allPayloads = [...tx[1], ...offChainPayloads];
|
|
1095
|
+
const bridgeIntent = assertEx15(allPayloads.find(isBridgeIntent5), () => "No bridge intent found");
|
|
1096
|
+
const amount = hexToBigInt9(bridgeIntent.destAmount);
|
|
1097
|
+
const srcAddress = getAddress2(bridgeIntent.srcAddress);
|
|
1098
|
+
const destAddress = getAddress2(bridgeIntent.destAddress);
|
|
1099
|
+
const nonce = hexToBigInt9(await PayloadBuilder7.hash(tx[0]));
|
|
1100
|
+
const preparedTx = await bridge.getFunction("bridgeFromRemote").populateTransaction(srcAddress, destAddress, amount, nonce);
|
|
1101
|
+
await job.log(`[${hash}] built ETH transaction`);
|
|
1102
|
+
await job.log(`[${hash}] validating tx runner has sufficient ETH for gas`);
|
|
1103
|
+
if (!await validateSufficientRunnerEthBalanceForGas(preparedTx, wallet, job)) {
|
|
1104
|
+
throw new Error("Transaction runner does not have sufficient ETH to cover estimated gas (with buffer)");
|
|
1105
|
+
}
|
|
1106
|
+
await job.log(`[${hash}] validated tx runner has sufficient ETH for gas`);
|
|
1107
|
+
await job.log(`[${hash}] storing ETH preparedTx`);
|
|
1108
|
+
await stateMap.set(hash, { preparedTx });
|
|
1109
|
+
await job.log(`[${hash}] stored ETH preparedTx`);
|
|
1110
|
+
await job.log(`[${hash}] prepared ETH transaction`);
|
|
1111
|
+
return {};
|
|
1112
|
+
}, { logger, timeBudgetLimit: 1e3 });
|
|
1113
|
+
},
|
|
1114
|
+
{
|
|
1115
|
+
connection: connection2,
|
|
1116
|
+
telemetry: telemetry2,
|
|
1117
|
+
prefix
|
|
677
1118
|
}
|
|
678
|
-
|
|
679
|
-
await job.log(`[${hash}] storing ETH preparedTx`);
|
|
680
|
-
await stateMap.set(hash, {
|
|
681
|
-
preparedTx
|
|
682
|
-
});
|
|
683
|
-
await job.log(`[${hash}] stored ETH preparedTx`);
|
|
684
|
-
await job.log(`[${hash}] prepared ETH transaction`);
|
|
685
|
-
return {};
|
|
686
|
-
}, {
|
|
687
|
-
connection: connection2,
|
|
688
|
-
telemetry: telemetry2,
|
|
689
|
-
prefix
|
|
690
|
-
});
|
|
1119
|
+
);
|
|
691
1120
|
worker.on("failed", (job, err) => {
|
|
692
|
-
|
|
1121
|
+
logger.error(`[eth-prep] job ${job?.id} failed: ${err.message}`);
|
|
693
1122
|
});
|
|
694
1123
|
worker.on("error", (err) => {
|
|
695
|
-
|
|
1124
|
+
logger.error(`[eth-prep] worker error: ${err.message}`);
|
|
696
1125
|
});
|
|
697
|
-
}
|
|
1126
|
+
};
|
|
698
1127
|
var EthTransactionPreparation = {
|
|
699
1128
|
createWorker: createWorker4,
|
|
700
1129
|
name: name4,
|
|
@@ -702,45 +1131,52 @@ var EthTransactionPreparation = {
|
|
|
702
1131
|
};
|
|
703
1132
|
|
|
704
1133
|
// src/services/queue/workers/EthTransactionSubmission.ts
|
|
705
|
-
import {
|
|
1134
|
+
import {
|
|
1135
|
+
assertEx as assertEx16,
|
|
1136
|
+
isDefined as isDefined7,
|
|
1137
|
+
spanAsync as spanAsync8
|
|
1138
|
+
} from "@xylabs/sdk-js";
|
|
706
1139
|
import { PayloadBuilder as PayloadBuilder8 } from "@xyo-network/sdk-js";
|
|
707
1140
|
import { Worker as Worker5 } from "bullmq";
|
|
708
1141
|
var name5 = "Submit ETH Transaction";
|
|
709
1142
|
var queueName5 = "eth-tx-submit";
|
|
710
|
-
var createWorker5 =
|
|
711
|
-
const bridge =
|
|
712
|
-
const wallet =
|
|
713
|
-
const stateMap =
|
|
714
|
-
const
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
submissionHash: existingSubmissionHash
|
|
723
|
-
|
|
1143
|
+
var createWorker5 = (connection2, telemetry2, services) => {
|
|
1144
|
+
const bridge = assertEx16(services?.bridge, () => "bridge service not provided");
|
|
1145
|
+
const wallet = assertEx16(services?.wallet, () => "wallet service not provided");
|
|
1146
|
+
const stateMap = assertEx16(services?.ethTxStateMap, () => "ethTxStateMap service not provided");
|
|
1147
|
+
const logger = assertEx16(services?.logger, () => "logger service not provided");
|
|
1148
|
+
const worker = new Worker5(
|
|
1149
|
+
queueName5,
|
|
1150
|
+
async (job) => {
|
|
1151
|
+
return await spanAsync8("bridge:worker:eth-tx-submit", async () => {
|
|
1152
|
+
const { tx, offChainPayloads } = job.data;
|
|
1153
|
+
const hash = await PayloadBuilder8.hash(tx[0]);
|
|
1154
|
+
const state = assertEx16(await stateMap.get(hash), () => `[${hash}] state not found`);
|
|
1155
|
+
const { submissionHash: existingSubmissionHash } = state;
|
|
1156
|
+
if (isDefined7(existingSubmissionHash)) {
|
|
1157
|
+
await job.log(`[${hash}] Tx already submitted with submission response hash ${existingSubmissionHash}`);
|
|
1158
|
+
return { submissionHash: existingSubmissionHash };
|
|
1159
|
+
}
|
|
1160
|
+
await job.log(`[${hash}] Submitting ETH tx`);
|
|
1161
|
+
const submissionHash = assertEx16(await submitEthTransaction(tx, offChainPayloads, bridge, wallet), () => `[${hash}] submissionHash not found in receipt`);
|
|
1162
|
+
await job.log(`[${hash}] Submitted ETH tx and received submission response hash ${submissionHash}`);
|
|
1163
|
+
return { submissionHash };
|
|
1164
|
+
}, { logger, timeBudgetLimit: 5e3 });
|
|
1165
|
+
},
|
|
1166
|
+
{
|
|
1167
|
+
connection: connection2,
|
|
1168
|
+
telemetry: telemetry2,
|
|
1169
|
+
concurrency: 1,
|
|
1170
|
+
prefix
|
|
724
1171
|
}
|
|
725
|
-
|
|
726
|
-
const submissionHash = assertEx15(await submitEthTransaction(tx, offChainPayloads, bridge, wallet), () => `[${hash}] submissionHash not found in receipt`);
|
|
727
|
-
await job.log(`[${hash}] Submitted ETH tx and received submission response hash ${submissionHash}`);
|
|
728
|
-
return {
|
|
729
|
-
submissionHash
|
|
730
|
-
};
|
|
731
|
-
}, {
|
|
732
|
-
connection: connection2,
|
|
733
|
-
telemetry: telemetry2,
|
|
734
|
-
concurrency: 1,
|
|
735
|
-
prefix
|
|
736
|
-
});
|
|
1172
|
+
);
|
|
737
1173
|
worker.on("failed", (job, err) => {
|
|
738
|
-
|
|
1174
|
+
logger.error(`[eth-submit] job ${job?.id} failed: ${err.message}`);
|
|
739
1175
|
});
|
|
740
1176
|
worker.on("error", (err) => {
|
|
741
|
-
|
|
1177
|
+
logger.error(`[eth-submit] worker error: ${err.message}`);
|
|
742
1178
|
});
|
|
743
|
-
}
|
|
1179
|
+
};
|
|
744
1180
|
var EthTransactionSubmission = {
|
|
745
1181
|
createWorker: createWorker5,
|
|
746
1182
|
name: name5,
|
|
@@ -748,48 +1184,55 @@ var EthTransactionSubmission = {
|
|
|
748
1184
|
};
|
|
749
1185
|
|
|
750
1186
|
// src/services/queue/workers/EthTransactionSubmissionStorage.ts
|
|
751
|
-
import {
|
|
1187
|
+
import {
|
|
1188
|
+
assertEx as assertEx17,
|
|
1189
|
+
isDefined as isDefined8,
|
|
1190
|
+
spanAsync as spanAsync9
|
|
1191
|
+
} from "@xylabs/sdk-js";
|
|
752
1192
|
import { PayloadBuilder as PayloadBuilder9 } from "@xyo-network/sdk-js";
|
|
753
1193
|
import { Worker as Worker6 } from "bullmq";
|
|
754
1194
|
var name6 = "Store ETH Transaction Submission";
|
|
755
1195
|
var queueName6 = "eth-tx-store-submission";
|
|
756
|
-
var createWorker6 =
|
|
757
|
-
const stateMap =
|
|
758
|
-
const
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
submissionHash: existingSubmissionHash
|
|
767
|
-
|
|
1196
|
+
var createWorker6 = (connection2, telemetry2, services) => {
|
|
1197
|
+
const stateMap = assertEx17(services?.ethTxStateMap, () => "ethTxStateMap service not provided");
|
|
1198
|
+
const logger = assertEx17(services?.logger, () => "logger service not provided");
|
|
1199
|
+
const worker = new Worker6(
|
|
1200
|
+
queueName6,
|
|
1201
|
+
async (job) => {
|
|
1202
|
+
return await spanAsync9("bridge:worker:eth-tx-store-submission", async () => {
|
|
1203
|
+
const { tx } = job.data;
|
|
1204
|
+
const hash = await PayloadBuilder9.hash(tx[0]);
|
|
1205
|
+
const state = assertEx17(await stateMap.get(hash), () => `[${hash}] state not found`);
|
|
1206
|
+
const { submissionHash: existingSubmissionHash } = state;
|
|
1207
|
+
if (isDefined8(existingSubmissionHash)) {
|
|
1208
|
+
await job.log(`[${hash}] submissionHash already stored as ${existingSubmissionHash}`);
|
|
1209
|
+
return { submissionHash: existingSubmissionHash };
|
|
1210
|
+
}
|
|
1211
|
+
const childrenValues = await job.getChildrenValues();
|
|
1212
|
+
const jobKey = `${prefix}:${EthTransactionSubmission.queueName}:${hash}`;
|
|
1213
|
+
const childValues = childrenValues?.[jobKey];
|
|
1214
|
+
const submissionHash = childValues?.submissionHash;
|
|
1215
|
+
const resolvedSubmissionHash = assertEx17(submissionHash, () => `[${hash}] child submissionHash not found in children values`);
|
|
1216
|
+
await job.log(`[${hash}] Storing ETH submissionHash`);
|
|
1217
|
+
state.submissionHash = resolvedSubmissionHash;
|
|
1218
|
+
await stateMap.set(hash, state);
|
|
1219
|
+
await job.log(`[${hash}] Stored ETH submissionHash`);
|
|
1220
|
+
return { submissionHash: resolvedSubmissionHash };
|
|
1221
|
+
}, { logger, timeBudgetLimit: 500 });
|
|
1222
|
+
},
|
|
1223
|
+
{
|
|
1224
|
+
connection: connection2,
|
|
1225
|
+
telemetry: telemetry2,
|
|
1226
|
+
prefix
|
|
768
1227
|
}
|
|
769
|
-
|
|
770
|
-
const jobKey = `${prefix}:${EthTransactionSubmission.queueName}:${hash}`;
|
|
771
|
-
const childValues = childrenValues?.[jobKey];
|
|
772
|
-
const submissionHash = childValues?.submissionHash;
|
|
773
|
-
const resolvedSubmissionHash = assertEx16(submissionHash, () => `[${hash}] child submissionHash not found in children values`);
|
|
774
|
-
await job.log(`[${hash}] Storing ETH submissionHash`);
|
|
775
|
-
state.submissionHash = resolvedSubmissionHash;
|
|
776
|
-
await stateMap.set(hash, state);
|
|
777
|
-
await job.log(`[${hash}] Stored ETH submissionHash`);
|
|
778
|
-
return {
|
|
779
|
-
submissionHash: resolvedSubmissionHash
|
|
780
|
-
};
|
|
781
|
-
}, {
|
|
782
|
-
connection: connection2,
|
|
783
|
-
telemetry: telemetry2,
|
|
784
|
-
prefix
|
|
785
|
-
});
|
|
1228
|
+
);
|
|
786
1229
|
worker.on("failed", (job, err) => {
|
|
787
|
-
|
|
1230
|
+
logger.error(`[eth-submit-store] job ${job?.id} failed: ${err.message}`);
|
|
788
1231
|
});
|
|
789
1232
|
worker.on("error", (err) => {
|
|
790
|
-
|
|
1233
|
+
logger.error(`[eth-submit-store] worker error: ${err.message}`);
|
|
791
1234
|
});
|
|
792
|
-
}
|
|
1235
|
+
};
|
|
793
1236
|
var EthTransactionSubmissionStorage = {
|
|
794
1237
|
createWorker: createWorker6,
|
|
795
1238
|
name: name6,
|
|
@@ -797,23 +1240,58 @@ var EthTransactionSubmissionStorage = {
|
|
|
797
1240
|
};
|
|
798
1241
|
|
|
799
1242
|
// src/services/queue/workers/Xl1ReserveTxFulfillment.ts
|
|
800
|
-
import {
|
|
1243
|
+
import {
|
|
1244
|
+
assertEx as assertEx18,
|
|
1245
|
+
delay,
|
|
1246
|
+
hexToBigInt as hexToBigInt10,
|
|
1247
|
+
isDefined as isDefined9,
|
|
1248
|
+
isNull as isNull2,
|
|
1249
|
+
spanAsync as spanAsync10
|
|
1250
|
+
} from "@xylabs/sdk-js";
|
|
801
1251
|
import { PayloadBuilder as PayloadBuilder10 } from "@xyo-network/sdk-js";
|
|
802
1252
|
import { asXL1BlockNumber } from "@xyo-network/xl1-sdk";
|
|
803
|
-
import { Worker as Worker7 } from "bullmq";
|
|
1253
|
+
import { UnrecoverableError as UnrecoverableError2, Worker as Worker7 } from "bullmq";
|
|
804
1254
|
var name7 = "XL1 Reserve Transfer Fulfillment";
|
|
805
1255
|
var queueName7 = "eth-to-xl1-fulfill";
|
|
806
1256
|
var EXP_BLOCKS_AHEAD = 1e3;
|
|
807
1257
|
var POLL_INTERVAL_MS = 5e3;
|
|
808
1258
|
var CONCURRENCY = 32;
|
|
1259
|
+
async function markFulfilled(ctx, attempt) {
|
|
1260
|
+
const {
|
|
1261
|
+
bridgeFlowMetrics,
|
|
1262
|
+
fulfillmentMap,
|
|
1263
|
+
identity,
|
|
1264
|
+
state,
|
|
1265
|
+
xl1TokenAddress
|
|
1266
|
+
} = ctx;
|
|
1267
|
+
state.fulfilled = attempt;
|
|
1268
|
+
state.currentAttempt = void 0;
|
|
1269
|
+
const intent = assertEx18(state.acceptedIntent, () => `[${identity}] acceptedIntent missing on fulfillment \u2014 Stage 1 did not snapshot`);
|
|
1270
|
+
state.acceptedDestinationObservation = buildAcceptedDestinationObservation(intent, attempt.txHash);
|
|
1271
|
+
await fulfillmentMap.set(identity, state);
|
|
1272
|
+
const observedAt = state.canonical?.observedAt;
|
|
1273
|
+
const latencySeconds = observedAt === void 0 ? void 0 : (attempt.submittedAt - observedAt) / 1e3;
|
|
1274
|
+
const acceptedFees = state.acceptedFees;
|
|
1275
|
+
const feeAmount = acceptedFees === void 0 ? 0n : hexToBigInt10(acceptedFees.feeFixed) + hexToBigInt10(acceptedFees.feeVariable);
|
|
1276
|
+
bridgeFlowMetrics.recordSuccess({
|
|
1277
|
+
direction: "inbound",
|
|
1278
|
+
feeAmount,
|
|
1279
|
+
latencySeconds,
|
|
1280
|
+
token: xl1TokenAddress
|
|
1281
|
+
});
|
|
1282
|
+
}
|
|
809
1283
|
async function tryResumeCurrentAttempt(ctx) {
|
|
810
|
-
const {
|
|
811
|
-
|
|
1284
|
+
const {
|
|
1285
|
+
fulfillmentMap,
|
|
1286
|
+
identity,
|
|
1287
|
+
job,
|
|
1288
|
+
state,
|
|
1289
|
+
viewer
|
|
1290
|
+
} = ctx;
|
|
1291
|
+
const attempt = assertEx18(state.currentAttempt, () => "tryResumeCurrentAttempt called without currentAttempt");
|
|
812
1292
|
const found = await viewer.transaction.byHash(attempt.txHash);
|
|
813
1293
|
if (isDefined9(found) && !isNull2(found)) {
|
|
814
|
-
|
|
815
|
-
state.currentAttempt = void 0;
|
|
816
|
-
await fulfillmentMap.set(identity, state);
|
|
1294
|
+
await markFulfilled(ctx, attempt);
|
|
817
1295
|
await job.log(`[${identity}] tx ${attempt.txHash} included on resume`);
|
|
818
1296
|
return;
|
|
819
1297
|
}
|
|
@@ -825,19 +1303,35 @@ async function tryResumeCurrentAttempt(ctx) {
|
|
|
825
1303
|
await job.log(`[${identity}] resumed attempt expired (head=${head}); will build fresh`);
|
|
826
1304
|
}
|
|
827
1305
|
}
|
|
828
|
-
__name(tryResumeCurrentAttempt, "tryResumeCurrentAttempt");
|
|
829
1306
|
async function buildAndSubmitFreshAttempt(ctx) {
|
|
830
|
-
const {
|
|
831
|
-
|
|
1307
|
+
const {
|
|
1308
|
+
account,
|
|
1309
|
+
bridgeId,
|
|
1310
|
+
config,
|
|
1311
|
+
contractAddress,
|
|
1312
|
+
evmChainId,
|
|
1313
|
+
fulfillmentMap,
|
|
1314
|
+
gateway,
|
|
1315
|
+
identity,
|
|
1316
|
+
job,
|
|
1317
|
+
state,
|
|
1318
|
+
viewer,
|
|
1319
|
+
xl1ChainId,
|
|
1320
|
+
xl1TokenAddress
|
|
1321
|
+
} = ctx;
|
|
1322
|
+
const canonical = assertEx18(state.canonical, () => `[${identity}] buildAndSubmitFreshAttempt requires canonical`);
|
|
1323
|
+
const acceptedFees = assertEx18(state.acceptedFees, () => `[${identity}] acceptedFees missing \u2014 Stage 1 did not snapshot`);
|
|
1324
|
+
const feesAmount = hexToBigInt10(acceptedFees.feeFixed) + hexToBigInt10(acceptedFees.feeVariable);
|
|
1325
|
+
const { feesAddress } = await getTransferAddresses(config);
|
|
832
1326
|
const sufficient = await validateSufficientXl1ReserveBalance({
|
|
833
1327
|
amount: canonical.amount,
|
|
834
1328
|
bridgeAccount: account,
|
|
835
1329
|
gateway
|
|
836
1330
|
});
|
|
837
1331
|
if (!sufficient) {
|
|
838
|
-
const msg = `[${identity}] XL1 reserve below required ${canonical.amount.toString()};
|
|
1332
|
+
const msg = `[${identity}] XL1 reserve below required ${canonical.amount.toString()}; terminal \u2014 refill and retry`;
|
|
839
1333
|
await job.log(msg);
|
|
840
|
-
throw new
|
|
1334
|
+
throw new UnrecoverableError2(msg);
|
|
841
1335
|
}
|
|
842
1336
|
const head = await viewer.currentBlockNumber();
|
|
843
1337
|
const nbf = asXL1BlockNumber(head, true);
|
|
@@ -853,6 +1347,8 @@ async function buildAndSubmitFreshAttempt(ctx) {
|
|
|
853
1347
|
evmTokenAddress: canonical.destToken,
|
|
854
1348
|
evmTxHash: canonical.evmTxHash,
|
|
855
1349
|
exp,
|
|
1350
|
+
feesAddress,
|
|
1351
|
+
feesAmount,
|
|
856
1352
|
nbf,
|
|
857
1353
|
xl1ChainId,
|
|
858
1354
|
xl1TokenAddress
|
|
@@ -866,18 +1362,24 @@ async function buildAndSubmitFreshAttempt(ctx) {
|
|
|
866
1362
|
};
|
|
867
1363
|
await fulfillmentMap.set(identity, state);
|
|
868
1364
|
await submitXl1Transaction(tx, [], gateway);
|
|
869
|
-
await job.log(
|
|
1365
|
+
await job.log(
|
|
1366
|
+
`[${identity}] attempt ${state.previousAttempts.length + 1} submitted: txHash=${txHash} nbf=${head} exp=${head + EXP_BLOCKS_AHEAD}`
|
|
1367
|
+
);
|
|
870
1368
|
}
|
|
871
|
-
__name(buildAndSubmitFreshAttempt, "buildAndSubmitFreshAttempt");
|
|
872
1369
|
async function pollUntilIncludedOrExpired(ctx) {
|
|
873
|
-
const {
|
|
874
|
-
|
|
1370
|
+
const {
|
|
1371
|
+
bridgeFlowMetrics,
|
|
1372
|
+
fulfillmentMap,
|
|
1373
|
+
identity,
|
|
1374
|
+
job,
|
|
1375
|
+
state,
|
|
1376
|
+
viewer
|
|
1377
|
+
} = ctx;
|
|
1378
|
+
const attempt = assertEx18(state.currentAttempt, () => "pollUntilIncludedOrExpired requires currentAttempt");
|
|
875
1379
|
while (true) {
|
|
876
1380
|
const found = await viewer.transaction.byHash(attempt.txHash);
|
|
877
1381
|
if (isDefined9(found) && !isNull2(found)) {
|
|
878
|
-
|
|
879
|
-
state.currentAttempt = void 0;
|
|
880
|
-
await fulfillmentMap.set(identity, state);
|
|
1382
|
+
await markFulfilled(ctx, attempt);
|
|
881
1383
|
await job.log(`[${identity}] tx ${attempt.txHash} included`);
|
|
882
1384
|
return;
|
|
883
1385
|
}
|
|
@@ -886,6 +1388,7 @@ async function pollUntilIncludedOrExpired(ctx) {
|
|
|
886
1388
|
state.previousAttempts.push(attempt);
|
|
887
1389
|
state.currentAttempt = void 0;
|
|
888
1390
|
await fulfillmentMap.set(identity, state);
|
|
1391
|
+
bridgeFlowMetrics.recordAttemptExpiration({ direction: "inbound" });
|
|
889
1392
|
const msg = `[${identity}] attempt ${state.previousAttempts.length} expired (head=${head}, exp=${attempt.exp})`;
|
|
890
1393
|
await job.log(msg);
|
|
891
1394
|
throw new Error(msg);
|
|
@@ -893,72 +1396,89 @@ async function pollUntilIncludedOrExpired(ctx) {
|
|
|
893
1396
|
await delay(POLL_INTERVAL_MS);
|
|
894
1397
|
}
|
|
895
1398
|
}
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
const
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
1399
|
+
var createWorker7 = (connection2, telemetry2, services) => {
|
|
1400
|
+
const svc = assertEx18(services, () => "services not provided");
|
|
1401
|
+
const {
|
|
1402
|
+
account,
|
|
1403
|
+
bridgeFlowMetrics,
|
|
1404
|
+
bridgeFulfillmentMap,
|
|
1405
|
+
config,
|
|
1406
|
+
gateway,
|
|
1407
|
+
logger
|
|
1408
|
+
} = svc;
|
|
1409
|
+
const worker = new Worker7(
|
|
1410
|
+
queueName7,
|
|
1411
|
+
async (job) => {
|
|
1412
|
+
await spanAsync10("bridge:worker:eth-to-xl1-fulfill", async () => {
|
|
1413
|
+
const {
|
|
1414
|
+
bridgeId,
|
|
1415
|
+
contractAddress,
|
|
1416
|
+
evmChainId,
|
|
1417
|
+
identity,
|
|
1418
|
+
xl1ChainId,
|
|
1419
|
+
xl1TokenAddress
|
|
1420
|
+
} = job.data;
|
|
1421
|
+
const initialState = await bridgeFulfillmentMap.get(identity);
|
|
1422
|
+
if (initialState?.fulfilled) {
|
|
1423
|
+
await job.log(`[${identity}] already fulfilled \u2014 skipping`);
|
|
1424
|
+
return;
|
|
1425
|
+
}
|
|
1426
|
+
const state = assertEx18(initialState, () => `[${identity}] no fulfillment state \u2014 Stage 1 did not run`);
|
|
1427
|
+
const canonical = assertEx18(state.canonical, () => `[${identity}] canonical missing \u2014 Stage 1 did not write canonical`);
|
|
1428
|
+
const minBridgeAmount = getMinBridgeAmount(config);
|
|
1429
|
+
if (!validateAmountMeetsMinBridgeAmount({ amount: canonical.amount, minBridgeAmount })) {
|
|
1430
|
+
await job.log(`[${identity}] amount ${canonical.amount} below minBridgeAmount ${minBridgeAmount}; terminal`);
|
|
1431
|
+
throw new UnrecoverableError2(`[${identity}] below_min_bridge_amount: amount=${canonical.amount} minBridgeAmount=${minBridgeAmount}`);
|
|
1432
|
+
}
|
|
1433
|
+
const viewer = assertEx18(gateway.connection.viewer, () => `[${identity}] gateway viewer not available`);
|
|
1434
|
+
const ctx = {
|
|
1435
|
+
bridgeFlowMetrics,
|
|
1436
|
+
fulfillmentMap: bridgeFulfillmentMap,
|
|
1437
|
+
identity,
|
|
1438
|
+
job,
|
|
1439
|
+
state,
|
|
1440
|
+
viewer,
|
|
1441
|
+
xl1TokenAddress
|
|
1442
|
+
};
|
|
1443
|
+
if (state.currentAttempt) {
|
|
1444
|
+
await tryResumeCurrentAttempt(ctx);
|
|
1445
|
+
if (state.fulfilled) return;
|
|
1446
|
+
}
|
|
1447
|
+
if (!state.currentAttempt) {
|
|
1448
|
+
await buildAndSubmitFreshAttempt({
|
|
1449
|
+
...ctx,
|
|
1450
|
+
account,
|
|
1451
|
+
bridgeId: BigInt(bridgeId),
|
|
1452
|
+
config,
|
|
1453
|
+
contractAddress,
|
|
1454
|
+
evmChainId,
|
|
1455
|
+
gateway,
|
|
1456
|
+
xl1ChainId,
|
|
1457
|
+
xl1TokenAddress
|
|
1458
|
+
});
|
|
1459
|
+
}
|
|
1460
|
+
await pollUntilIncludedOrExpired(ctx);
|
|
1461
|
+
}, { logger });
|
|
1462
|
+
},
|
|
1463
|
+
{
|
|
1464
|
+
concurrency: CONCURRENCY,
|
|
1465
|
+
connection: connection2,
|
|
1466
|
+
prefix,
|
|
1467
|
+
telemetry: telemetry2
|
|
924
1468
|
}
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
gateway,
|
|
933
|
-
xl1ChainId,
|
|
934
|
-
xl1TokenAddress
|
|
935
|
-
});
|
|
1469
|
+
);
|
|
1470
|
+
worker.on("failed", (job, err) => {
|
|
1471
|
+
logger.error(`[xl1-fulfill] job ${job?.id} failed: ${err.message}`);
|
|
1472
|
+
if (job === void 0) return;
|
|
1473
|
+
const attemptsAllowed = job.opts.attempts ?? 1;
|
|
1474
|
+
if (job.attemptsMade >= attemptsAllowed) {
|
|
1475
|
+
bridgeFlowMetrics.recordTerminalFailure({ direction: "inbound", reason: bucketFailureReason(err.message) });
|
|
936
1476
|
}
|
|
937
|
-
await pollUntilIncludedOrExpired(ctx);
|
|
938
|
-
}, {
|
|
939
|
-
concurrency: CONCURRENCY,
|
|
940
|
-
connection: connection2,
|
|
941
|
-
prefix,
|
|
942
|
-
telemetry: telemetry2
|
|
943
|
-
});
|
|
944
|
-
worker.on("failed", async (job, err) => {
|
|
945
|
-
if (!isDefined9(job)) return;
|
|
946
|
-
const attemptsAllowed = job.opts.attempts ?? Infinity;
|
|
947
|
-
if (job.attemptsMade < attemptsAllowed) return;
|
|
948
|
-
const identity = job.data?.identity;
|
|
949
|
-
if (!isDefined9(identity)) return;
|
|
950
|
-
const state = await bridgeFulfillmentMap.get(identity);
|
|
951
|
-
if (!isDefined9(state)) return;
|
|
952
|
-
state.failed = {
|
|
953
|
-
at: Date.now(),
|
|
954
|
-
reason: `attempts exhausted (${job.attemptsMade}/${attemptsAllowed}): ${err.message}`
|
|
955
|
-
};
|
|
956
|
-
await bridgeFulfillmentMap.set(identity, state);
|
|
957
1477
|
});
|
|
958
1478
|
worker.on("error", (err) => {
|
|
959
|
-
|
|
1479
|
+
logger.error(`[xl1-fulfill] worker error: ${err.message}`);
|
|
960
1480
|
});
|
|
961
|
-
}
|
|
1481
|
+
};
|
|
962
1482
|
var Xl1ReserveTxFulfillment = {
|
|
963
1483
|
createWorker: createWorker7,
|
|
964
1484
|
name: name7,
|
|
@@ -966,26 +1486,34 @@ var Xl1ReserveTxFulfillment = {
|
|
|
966
1486
|
};
|
|
967
1487
|
|
|
968
1488
|
// src/services/queue/workers/Xl1ToEthBridgeParent.ts
|
|
1489
|
+
import { assertEx as assertEx19, spanAsync as spanAsync11 } from "@xylabs/sdk-js";
|
|
969
1490
|
import { Worker as Worker8 } from "bullmq";
|
|
970
1491
|
var name8 = "Bridge XL1 to Ethereum";
|
|
971
1492
|
var queueName8 = "xl1-to-eth-bridge";
|
|
972
|
-
var createWorker8 =
|
|
973
|
-
const
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
1493
|
+
var createWorker8 = (connection2, telemetry2, services) => {
|
|
1494
|
+
const logger = assertEx19(services?.logger, () => "logger service not provided");
|
|
1495
|
+
const worker = new Worker8(
|
|
1496
|
+
queueName8,
|
|
1497
|
+
async (job) => {
|
|
1498
|
+
return await spanAsync11("bridge:worker:xl1-to-eth-bridge", async () => {
|
|
1499
|
+
await job.log(`[${job.name}] start`);
|
|
1500
|
+
await job.log(`[${job.name}] done`);
|
|
1501
|
+
return {};
|
|
1502
|
+
}, { logger, timeBudgetLimit: 5e3 });
|
|
1503
|
+
},
|
|
1504
|
+
{
|
|
1505
|
+
connection: connection2,
|
|
1506
|
+
telemetry: telemetry2,
|
|
1507
|
+
prefix
|
|
1508
|
+
}
|
|
1509
|
+
);
|
|
982
1510
|
worker.on("failed", (job, err) => {
|
|
983
|
-
|
|
1511
|
+
logger.error(`[xl1-to-eth-parent] job ${job?.id} failed: ${err.message}`);
|
|
984
1512
|
});
|
|
985
1513
|
worker.on("error", (err) => {
|
|
986
|
-
|
|
1514
|
+
logger.error(`[xl1-to-eth-parent] worker error: ${err.message}`);
|
|
987
1515
|
});
|
|
988
|
-
}
|
|
1516
|
+
};
|
|
989
1517
|
var Xl1ToEthBridgeParent = {
|
|
990
1518
|
createWorker: createWorker8,
|
|
991
1519
|
name: name8,
|
|
@@ -993,48 +1521,60 @@ var Xl1ToEthBridgeParent = {
|
|
|
993
1521
|
};
|
|
994
1522
|
|
|
995
1523
|
// src/services/queue/workers/Xl1TransactionMonitor.ts
|
|
996
|
-
import {
|
|
1524
|
+
import {
|
|
1525
|
+
assertEx as assertEx20,
|
|
1526
|
+
isDefined as isDefined10,
|
|
1527
|
+
isNull as isNull3,
|
|
1528
|
+
spanAsync as spanAsync12
|
|
1529
|
+
} from "@xylabs/sdk-js";
|
|
997
1530
|
import { PayloadBuilder as PayloadBuilder11 } from "@xyo-network/sdk-js";
|
|
998
|
-
import { UnrecoverableError as
|
|
1531
|
+
import { UnrecoverableError as UnrecoverableError3, Worker as Worker9 } from "bullmq";
|
|
999
1532
|
var name9 = "Monitor Submitted XL1 Transaction";
|
|
1000
1533
|
var queueName9 = "xl1-tx-monitor";
|
|
1001
|
-
var createWorker9 =
|
|
1002
|
-
const gateway =
|
|
1003
|
-
const stateMap =
|
|
1004
|
-
const viewer =
|
|
1005
|
-
const
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1534
|
+
var createWorker9 = (connection2, telemetry2, services) => {
|
|
1535
|
+
const gateway = assertEx20(services?.gateway, () => "gateway service not provided");
|
|
1536
|
+
const stateMap = assertEx20(services?.xl1TxStateMap, () => "xl1TxStateMap service not provided");
|
|
1537
|
+
const viewer = assertEx20(gateway.connection.viewer, () => "viewer not defined on gateway");
|
|
1538
|
+
const logger = assertEx20(services?.logger, () => "logger service not provided");
|
|
1539
|
+
const worker = new Worker9(
|
|
1540
|
+
queueName9,
|
|
1541
|
+
async (job) => {
|
|
1542
|
+
return await spanAsync12("bridge:worker:xl1-tx-monitor", async () => {
|
|
1543
|
+
const { tx } = job.data;
|
|
1544
|
+
const hash = await PayloadBuilder11.hash(tx[0]);
|
|
1545
|
+
const state = assertEx20(await stateMap.get(hash), () => `[${hash}] state not found`);
|
|
1546
|
+
const submissionHash = assertEx20(state.submissionHash, () => `[${hash}] submissionHash not found`);
|
|
1547
|
+
await job.log(`[${hash}] Checking for XL1 transaction inclusion on chain`);
|
|
1548
|
+
const foundTx = await viewer.transaction.byHash(submissionHash);
|
|
1549
|
+
if (isDefined10(foundTx) && !isNull3(foundTx)) {
|
|
1550
|
+
await job.log(`[${hash}] Found transaction on chain`);
|
|
1551
|
+
const submissionHash2 = await PayloadBuilder11.hash(foundTx[0]);
|
|
1552
|
+
return { submissionHash: submissionHash2 };
|
|
1553
|
+
}
|
|
1554
|
+
const currentBlockNumber = await viewer.currentBlockNumber();
|
|
1555
|
+
if (tx[0].exp < currentBlockNumber) {
|
|
1556
|
+
await job.log(
|
|
1557
|
+
`[${hash}] Transaction expired at block ${tx[0].exp}, current block ${currentBlockNumber}`
|
|
1558
|
+
);
|
|
1559
|
+
throw new UnrecoverableError3(`[${hash}] Transaction expired and will never be included`);
|
|
1560
|
+
}
|
|
1561
|
+
await job.log(`[${hash}] Transaction not yet included, retrying later`);
|
|
1562
|
+
throw new Error(`[${hash}] Transaction not yet included`);
|
|
1563
|
+
}, { logger, timeBudgetLimit: 2e3 });
|
|
1564
|
+
},
|
|
1565
|
+
{
|
|
1566
|
+
connection: connection2,
|
|
1567
|
+
telemetry: telemetry2,
|
|
1568
|
+
prefix
|
|
1023
1569
|
}
|
|
1024
|
-
|
|
1025
|
-
throw new Error(`[${hash}] Transaction not yet included`);
|
|
1026
|
-
}, {
|
|
1027
|
-
connection: connection2,
|
|
1028
|
-
telemetry: telemetry2,
|
|
1029
|
-
prefix
|
|
1030
|
-
});
|
|
1570
|
+
);
|
|
1031
1571
|
worker.on("failed", (job, err) => {
|
|
1032
|
-
|
|
1572
|
+
logger.error(`[xl1-monitor] job ${job?.id} failed: ${err.message}`);
|
|
1033
1573
|
});
|
|
1034
1574
|
worker.on("error", (err) => {
|
|
1035
|
-
|
|
1575
|
+
logger.error(`[xl1-monitor] worker error: ${err.message}`);
|
|
1036
1576
|
});
|
|
1037
|
-
}
|
|
1577
|
+
};
|
|
1038
1578
|
var Xl1TransactionMonitor = {
|
|
1039
1579
|
createWorker: createWorker9,
|
|
1040
1580
|
name: name9,
|
|
@@ -1042,40 +1582,42 @@ var Xl1TransactionMonitor = {
|
|
|
1042
1582
|
};
|
|
1043
1583
|
|
|
1044
1584
|
// src/services/queue/workers/Xl1TransactionPreparation.ts
|
|
1045
|
-
import { assertEx as
|
|
1585
|
+
import { assertEx as assertEx21, spanAsync as spanAsync13 } from "@xylabs/sdk-js";
|
|
1046
1586
|
import { PayloadBuilder as PayloadBuilder12 } from "@xyo-network/sdk-js";
|
|
1047
1587
|
import { Worker as Worker10 } from "bullmq";
|
|
1048
1588
|
var name10 = "Prepare XL1 Transaction";
|
|
1049
1589
|
var queueName10 = "xl1-tx-prepare";
|
|
1050
|
-
var createWorker10 =
|
|
1051
|
-
const stateMap =
|
|
1052
|
-
const
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1590
|
+
var createWorker10 = (connection2, telemetry2, services) => {
|
|
1591
|
+
const stateMap = assertEx21(services?.xl1TxStateMap, () => "xl1TxStateMap service not provided");
|
|
1592
|
+
const logger = assertEx21(services?.logger, () => "logger service not provided");
|
|
1593
|
+
const worker = new Worker10(
|
|
1594
|
+
queueName10,
|
|
1595
|
+
async (job) => {
|
|
1596
|
+
return await spanAsync13("bridge:worker:xl1-tx-prepare", async () => {
|
|
1597
|
+
const { tx, offChainPayloads = [] } = job.data;
|
|
1598
|
+
const hash = await PayloadBuilder12.hash(tx[0]);
|
|
1599
|
+
await job.log(`[${hash}] preparing XL1 transaction`);
|
|
1600
|
+
const preparedTx = tx;
|
|
1601
|
+
await job.log(`[${hash}] storing XL1 preparedTx`);
|
|
1602
|
+
await stateMap.set(hash, { offChainPayloads, preparedTx });
|
|
1603
|
+
await job.log(`[${hash}] stored XL1 preparedTx`);
|
|
1604
|
+
await job.log(`[${hash}] prepared XL1 transaction`);
|
|
1605
|
+
return { preparedTx };
|
|
1606
|
+
}, { logger, timeBudgetLimit: 1e3 });
|
|
1607
|
+
},
|
|
1608
|
+
{
|
|
1609
|
+
connection: connection2,
|
|
1610
|
+
telemetry: telemetry2,
|
|
1611
|
+
prefix
|
|
1612
|
+
}
|
|
1613
|
+
);
|
|
1072
1614
|
worker.on("failed", (job, err) => {
|
|
1073
|
-
|
|
1615
|
+
logger.error(`[xl1-prep] job ${job?.id} failed: ${err.message}`);
|
|
1074
1616
|
});
|
|
1075
1617
|
worker.on("error", (err) => {
|
|
1076
|
-
|
|
1618
|
+
logger.error(`[xl1-prep] worker error: ${err.message}`);
|
|
1077
1619
|
});
|
|
1078
|
-
}
|
|
1620
|
+
};
|
|
1079
1621
|
var Xl1TransactionPreparation = {
|
|
1080
1622
|
createWorker: createWorker10,
|
|
1081
1623
|
name: name10,
|
|
@@ -1083,44 +1625,55 @@ var Xl1TransactionPreparation = {
|
|
|
1083
1625
|
};
|
|
1084
1626
|
|
|
1085
1627
|
// src/services/queue/workers/Xl1TransactionSubmission.ts
|
|
1086
|
-
import {
|
|
1628
|
+
import {
|
|
1629
|
+
assertEx as assertEx22,
|
|
1630
|
+
isDefined as isDefined11,
|
|
1631
|
+
spanAsync as spanAsync14
|
|
1632
|
+
} from "@xylabs/sdk-js";
|
|
1087
1633
|
import { PayloadBuilder as PayloadBuilder13 } from "@xyo-network/sdk-js";
|
|
1088
1634
|
import { Worker as Worker11 } from "bullmq";
|
|
1089
1635
|
var name11 = "Submit XL1 Transaction";
|
|
1090
1636
|
var queueName11 = "xl1-tx-submit";
|
|
1091
|
-
var createWorker11 =
|
|
1092
|
-
const gateway =
|
|
1093
|
-
const stateMap =
|
|
1094
|
-
const
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1637
|
+
var createWorker11 = (connection2, telemetry2, services) => {
|
|
1638
|
+
const gateway = assertEx22(services?.gateway, () => "gateway service not provided");
|
|
1639
|
+
const stateMap = assertEx22(services?.xl1TxStateMap, () => "xl1TxStateMap service not provided");
|
|
1640
|
+
const logger = assertEx22(services?.logger, () => "logger service not provided");
|
|
1641
|
+
const worker = new Worker11(
|
|
1642
|
+
queueName11,
|
|
1643
|
+
async (job) => {
|
|
1644
|
+
return await spanAsync14("bridge:worker:xl1-tx-submit", async () => {
|
|
1645
|
+
const { tx } = job.data;
|
|
1646
|
+
const hash = await PayloadBuilder13.hash(tx[0]);
|
|
1647
|
+
const state = assertEx22(await stateMap.get(hash), () => `[${hash}] state not found`);
|
|
1648
|
+
const {
|
|
1649
|
+
preparedTx,
|
|
1650
|
+
offChainPayloads = [],
|
|
1651
|
+
submissionHash: existingSubmissionHash
|
|
1652
|
+
} = state;
|
|
1653
|
+
if (isDefined11(existingSubmissionHash)) {
|
|
1654
|
+
await job.log(`[${hash}] Tx already submitted with submission response hash ${existingSubmissionHash}`);
|
|
1655
|
+
return { submissionHash: existingSubmissionHash };
|
|
1656
|
+
}
|
|
1657
|
+
const txToSubmit = assertEx22(preparedTx, () => `[${hash}] preparedTx not found`);
|
|
1658
|
+
await job.log(`[${hash}] Submitting XL1 tx`);
|
|
1659
|
+
const [submissionHash] = await submitXl1Transaction(txToSubmit, offChainPayloads, gateway);
|
|
1660
|
+
await job.log(`[${hash}] Submitted XL1 tx`);
|
|
1661
|
+
return { submissionHash };
|
|
1662
|
+
}, { logger, timeBudgetLimit: 5e3 });
|
|
1663
|
+
},
|
|
1664
|
+
{
|
|
1665
|
+
connection: connection2,
|
|
1666
|
+
telemetry: telemetry2,
|
|
1667
|
+
prefix
|
|
1104
1668
|
}
|
|
1105
|
-
|
|
1106
|
-
await job.log(`[${hash}] Submitting XL1 tx`);
|
|
1107
|
-
const [submissionHash] = await submitXl1Transaction(txToSubmit, offChainPayloads, gateway);
|
|
1108
|
-
await job.log(`[${hash}] Submitted XL1 tx`);
|
|
1109
|
-
return {
|
|
1110
|
-
submissionHash
|
|
1111
|
-
};
|
|
1112
|
-
}, {
|
|
1113
|
-
connection: connection2,
|
|
1114
|
-
telemetry: telemetry2,
|
|
1115
|
-
prefix
|
|
1116
|
-
});
|
|
1669
|
+
);
|
|
1117
1670
|
worker.on("failed", (job, err) => {
|
|
1118
|
-
|
|
1671
|
+
logger.error(`[xl1-submit] job ${job?.id} failed: ${err.message}`);
|
|
1119
1672
|
});
|
|
1120
1673
|
worker.on("error", (err) => {
|
|
1121
|
-
|
|
1674
|
+
logger.error(`[xl1-submit] worker error: ${err.message}`);
|
|
1122
1675
|
});
|
|
1123
|
-
}
|
|
1676
|
+
};
|
|
1124
1677
|
var Xl1TransactionSubmission = {
|
|
1125
1678
|
createWorker: createWorker11,
|
|
1126
1679
|
name: name11,
|
|
@@ -1128,48 +1681,55 @@ var Xl1TransactionSubmission = {
|
|
|
1128
1681
|
};
|
|
1129
1682
|
|
|
1130
1683
|
// src/services/queue/workers/Xl1TransactionSubmissionStorage.ts
|
|
1131
|
-
import {
|
|
1684
|
+
import {
|
|
1685
|
+
assertEx as assertEx23,
|
|
1686
|
+
isDefined as isDefined12,
|
|
1687
|
+
spanAsync as spanAsync15
|
|
1688
|
+
} from "@xylabs/sdk-js";
|
|
1132
1689
|
import { PayloadBuilder as PayloadBuilder14 } from "@xyo-network/sdk-js";
|
|
1133
1690
|
import { Worker as Worker12 } from "bullmq";
|
|
1134
1691
|
var name12 = "Store XL1 Transaction Submission";
|
|
1135
1692
|
var queueName12 = "xl1-tx-store-submission";
|
|
1136
|
-
var createWorker12 =
|
|
1137
|
-
const stateMap =
|
|
1138
|
-
const
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
submissionHash: existingSubmissionHash
|
|
1147
|
-
|
|
1693
|
+
var createWorker12 = (connection2, telemetry2, services) => {
|
|
1694
|
+
const stateMap = assertEx23(services?.xl1TxStateMap, () => "xl1TxStateMap service not provided");
|
|
1695
|
+
const logger = assertEx23(services?.logger, () => "logger service not provided");
|
|
1696
|
+
const worker = new Worker12(
|
|
1697
|
+
queueName12,
|
|
1698
|
+
async (job) => {
|
|
1699
|
+
return await spanAsync15("bridge:worker:xl1-tx-store-submission", async () => {
|
|
1700
|
+
const { tx } = job.data;
|
|
1701
|
+
const hash = await PayloadBuilder14.hash(tx[0]);
|
|
1702
|
+
const state = assertEx23(await stateMap.get(hash), () => `[${hash}] state not found`);
|
|
1703
|
+
const { submissionHash: existingSubmissionHash } = state;
|
|
1704
|
+
if (isDefined12(existingSubmissionHash)) {
|
|
1705
|
+
await job.log(`[${hash}] submissionHash already stored as ${existingSubmissionHash}`);
|
|
1706
|
+
return { submissionHash: existingSubmissionHash };
|
|
1707
|
+
}
|
|
1708
|
+
const childrenValues = await job.getChildrenValues();
|
|
1709
|
+
const jobKey = `${prefix}:${Xl1TransactionSubmission.queueName}:${hash}`;
|
|
1710
|
+
const childValues = childrenValues?.[jobKey];
|
|
1711
|
+
const submissionHash = childValues?.submissionHash;
|
|
1712
|
+
const resolvedSubmissionHash = assertEx23(submissionHash, () => `[${hash}] child submissionHash not found in children values`);
|
|
1713
|
+
await job.log(`[${hash}] Storing XL1 submissionHash`);
|
|
1714
|
+
state.submissionHash = resolvedSubmissionHash;
|
|
1715
|
+
await stateMap.set(hash, state);
|
|
1716
|
+
await job.log(`[${hash}] Stored XL1 submissionHash`);
|
|
1717
|
+
return { submissionHash: resolvedSubmissionHash };
|
|
1718
|
+
}, { logger, timeBudgetLimit: 500 });
|
|
1719
|
+
},
|
|
1720
|
+
{
|
|
1721
|
+
connection: connection2,
|
|
1722
|
+
telemetry: telemetry2,
|
|
1723
|
+
prefix
|
|
1148
1724
|
}
|
|
1149
|
-
|
|
1150
|
-
const jobKey = `${prefix}:${Xl1TransactionSubmission.queueName}:${hash}`;
|
|
1151
|
-
const childValues = childrenValues?.[jobKey];
|
|
1152
|
-
const submissionHash = childValues?.submissionHash;
|
|
1153
|
-
const resolvedSubmissionHash = assertEx21(submissionHash, () => `[${hash}] child submissionHash not found in children values`);
|
|
1154
|
-
await job.log(`[${hash}] Storing XL1 submissionHash`);
|
|
1155
|
-
state.submissionHash = resolvedSubmissionHash;
|
|
1156
|
-
await stateMap.set(hash, state);
|
|
1157
|
-
await job.log(`[${hash}] Stored XL1 submissionHash`);
|
|
1158
|
-
return {
|
|
1159
|
-
submissionHash: resolvedSubmissionHash
|
|
1160
|
-
};
|
|
1161
|
-
}, {
|
|
1162
|
-
connection: connection2,
|
|
1163
|
-
telemetry: telemetry2,
|
|
1164
|
-
prefix
|
|
1165
|
-
});
|
|
1725
|
+
);
|
|
1166
1726
|
worker.on("failed", (job, err) => {
|
|
1167
|
-
|
|
1727
|
+
logger.error(`[xl1-submit-store] job ${job?.id} failed: ${err.message}`);
|
|
1168
1728
|
});
|
|
1169
1729
|
worker.on("error", (err) => {
|
|
1170
|
-
|
|
1730
|
+
logger.error(`[xl1-submit-store] worker error: ${err.message}`);
|
|
1171
1731
|
});
|
|
1172
|
-
}
|
|
1732
|
+
};
|
|
1173
1733
|
var Xl1TransactionSubmissionStorage = {
|
|
1174
1734
|
createWorker: createWorker12,
|
|
1175
1735
|
name: name12,
|
|
@@ -1177,26 +1737,26 @@ var Xl1TransactionSubmissionStorage = {
|
|
|
1177
1737
|
};
|
|
1178
1738
|
|
|
1179
1739
|
// src/services/queue/workers/createWorkers.ts
|
|
1180
|
-
var createWorkers =
|
|
1740
|
+
var createWorkers = (connection2, telemetry2, services) => {
|
|
1181
1741
|
EthEventVerification.createWorker(connection2, telemetry2, services);
|
|
1182
|
-
EthToXl1BridgeParent.createWorker(connection2, telemetry2);
|
|
1742
|
+
EthToXl1BridgeParent.createWorker(connection2, telemetry2, services);
|
|
1183
1743
|
EthTransactionMonitor.createWorker(connection2, telemetry2, services);
|
|
1184
1744
|
EthTransactionPreparation.createWorker(connection2, telemetry2, services);
|
|
1185
1745
|
EthTransactionSubmission.createWorker(connection2, telemetry2, services);
|
|
1186
1746
|
EthTransactionSubmissionStorage.createWorker(connection2, telemetry2, services);
|
|
1187
1747
|
Xl1ReserveTxFulfillment.createWorker(connection2, telemetry2, services);
|
|
1188
|
-
Xl1ToEthBridgeParent.createWorker(connection2, telemetry2);
|
|
1748
|
+
Xl1ToEthBridgeParent.createWorker(connection2, telemetry2, services);
|
|
1189
1749
|
Xl1TransactionMonitor.createWorker(connection2, telemetry2, services);
|
|
1190
1750
|
Xl1TransactionPreparation.createWorker(connection2, telemetry2, services);
|
|
1191
1751
|
Xl1TransactionSubmission.createWorker(connection2, telemetry2, services);
|
|
1192
1752
|
Xl1TransactionSubmissionStorage.createWorker(connection2, telemetry2, services);
|
|
1193
|
-
}
|
|
1753
|
+
};
|
|
1194
1754
|
|
|
1195
1755
|
// src/services/queue/flows/createEthToXl1BridgeJob/getJobIdForEthToXl1BridgeJob.ts
|
|
1756
|
+
import { buildBridgeIdentity } from "@xyo-network/chain-bridge-shared";
|
|
1196
1757
|
function getJobIdForEthToXl1BridgeJob(context) {
|
|
1197
|
-
return
|
|
1758
|
+
return buildBridgeIdentity(context);
|
|
1198
1759
|
}
|
|
1199
|
-
__name(getJobIdForEthToXl1BridgeJob, "getJobIdForEthToXl1BridgeJob");
|
|
1200
1760
|
|
|
1201
1761
|
// src/services/queue/flows/createEthToXl1BridgeJob/createEthToXl1BridgeJob.ts
|
|
1202
1762
|
var STAGE_2_RETRY = {
|
|
@@ -1206,10 +1766,8 @@ var STAGE_2_RETRY = {
|
|
|
1206
1766
|
type: "fixed"
|
|
1207
1767
|
}
|
|
1208
1768
|
};
|
|
1209
|
-
var STAGE_1_RETRY = {
|
|
1210
|
-
|
|
1211
|
-
};
|
|
1212
|
-
var createEthToXl1BridgeJob = /* @__PURE__ */ __name(async (flowProducer2, context) => {
|
|
1769
|
+
var STAGE_1_RETRY = { attempts: 1 };
|
|
1770
|
+
var createEthToXl1BridgeJob = async (flowProducer2, context) => {
|
|
1213
1771
|
const jobId = getJobIdForEthToXl1BridgeJob(context);
|
|
1214
1772
|
const identity = jobId;
|
|
1215
1773
|
const stageJobData = {
|
|
@@ -1224,175 +1782,121 @@ var createEthToXl1BridgeJob = /* @__PURE__ */ __name(async (flowProducer2, conte
|
|
|
1224
1782
|
name: EthToXl1BridgeParent.name,
|
|
1225
1783
|
queueName: EthToXl1BridgeParent.queueName,
|
|
1226
1784
|
data: stageJobData,
|
|
1227
|
-
opts: {
|
|
1228
|
-
jobId
|
|
1229
|
-
},
|
|
1785
|
+
opts: { jobId },
|
|
1230
1786
|
children: [
|
|
1231
1787
|
{
|
|
1232
1788
|
name: Xl1ReserveTxFulfillment.name,
|
|
1233
1789
|
queueName: Xl1ReserveTxFulfillment.queueName,
|
|
1234
1790
|
data: stageJobData,
|
|
1235
|
-
opts: {
|
|
1236
|
-
jobId,
|
|
1237
|
-
...STAGE_2_RETRY
|
|
1238
|
-
},
|
|
1791
|
+
opts: { jobId, ...STAGE_2_RETRY },
|
|
1239
1792
|
children: [
|
|
1240
1793
|
{
|
|
1241
1794
|
name: EthEventVerification.name,
|
|
1242
1795
|
queueName: EthEventVerification.queueName,
|
|
1243
1796
|
data: stageJobData,
|
|
1244
|
-
opts: {
|
|
1245
|
-
jobId,
|
|
1246
|
-
...STAGE_1_RETRY
|
|
1247
|
-
}
|
|
1797
|
+
opts: { jobId, ...STAGE_1_RETRY }
|
|
1248
1798
|
}
|
|
1249
1799
|
]
|
|
1250
1800
|
}
|
|
1251
1801
|
]
|
|
1252
1802
|
});
|
|
1253
|
-
}
|
|
1803
|
+
};
|
|
1254
1804
|
|
|
1255
1805
|
// src/services/queue/flows/createXl1ToEthBridgeJob/getJobIdForXl1ToEthBridgeJob.ts
|
|
1256
1806
|
import { PayloadBuilder as PayloadBuilder15 } from "@xyo-network/sdk-js";
|
|
1257
|
-
var getJobIdForXl1ToEthBridgeJob =
|
|
1807
|
+
var getJobIdForXl1ToEthBridgeJob = async (tx) => {
|
|
1258
1808
|
const jobId = await PayloadBuilder15.hash(tx[0]);
|
|
1259
1809
|
return jobId;
|
|
1260
|
-
}
|
|
1810
|
+
};
|
|
1261
1811
|
|
|
1262
1812
|
// src/services/queue/flows/createXl1ToEthBridgeJob/createXl1ToEthBridgeJob.ts
|
|
1263
|
-
var createXl1ToEthBridgeJob =
|
|
1813
|
+
var createXl1ToEthBridgeJob = async (flowProducer2, tx, offChainPayloads = []) => {
|
|
1264
1814
|
const jobId = await getJobIdForXl1ToEthBridgeJob(tx);
|
|
1265
1815
|
const flow = await flowProducer2.add({
|
|
1266
1816
|
// Step 0 (runs first as parent job)
|
|
1267
1817
|
name: Xl1ToEthBridgeParent.name,
|
|
1268
1818
|
queueName: Xl1ToEthBridgeParent.queueName,
|
|
1269
|
-
data: {
|
|
1270
|
-
|
|
1271
|
-
offChainPayloads
|
|
1272
|
-
},
|
|
1273
|
-
opts: {
|
|
1274
|
-
jobId
|
|
1275
|
-
},
|
|
1819
|
+
data: { tx, offChainPayloads },
|
|
1820
|
+
opts: { jobId },
|
|
1276
1821
|
children: [
|
|
1277
1822
|
{
|
|
1278
1823
|
// Step 8
|
|
1279
1824
|
name: EthTransactionMonitor.name,
|
|
1280
1825
|
queueName: EthTransactionMonitor.queueName,
|
|
1281
|
-
data: {
|
|
1282
|
-
tx
|
|
1283
|
-
},
|
|
1826
|
+
data: { tx },
|
|
1284
1827
|
opts: {
|
|
1285
1828
|
jobId,
|
|
1286
1829
|
attempts: 60,
|
|
1287
|
-
backoff: {
|
|
1288
|
-
type: "fixed",
|
|
1289
|
-
delay: 5e3
|
|
1290
|
-
}
|
|
1830
|
+
backoff: { type: "fixed", delay: 5e3 }
|
|
1291
1831
|
},
|
|
1292
1832
|
children: [
|
|
1293
1833
|
{
|
|
1294
1834
|
// Step 7
|
|
1295
1835
|
name: EthTransactionSubmissionStorage.name,
|
|
1296
1836
|
queueName: EthTransactionSubmissionStorage.queueName,
|
|
1297
|
-
data: {
|
|
1298
|
-
tx
|
|
1299
|
-
},
|
|
1837
|
+
data: { tx },
|
|
1300
1838
|
opts: {
|
|
1301
1839
|
jobId,
|
|
1302
1840
|
attempts: 60,
|
|
1303
|
-
backoff: {
|
|
1304
|
-
type: "fixed",
|
|
1305
|
-
delay: 5e3
|
|
1306
|
-
}
|
|
1841
|
+
backoff: { type: "fixed", delay: 5e3 }
|
|
1307
1842
|
},
|
|
1308
1843
|
children: [
|
|
1309
1844
|
{
|
|
1310
1845
|
// Step 6
|
|
1311
1846
|
name: EthTransactionSubmission.name,
|
|
1312
1847
|
queueName: EthTransactionSubmission.queueName,
|
|
1313
|
-
data: {
|
|
1314
|
-
|
|
1315
|
-
offChainPayloads
|
|
1316
|
-
},
|
|
1317
|
-
opts: {
|
|
1318
|
-
jobId
|
|
1319
|
-
},
|
|
1848
|
+
data: { tx, offChainPayloads },
|
|
1849
|
+
opts: { jobId },
|
|
1320
1850
|
children: [
|
|
1321
1851
|
{
|
|
1322
1852
|
// Step 5
|
|
1323
1853
|
name: EthTransactionPreparation.name,
|
|
1324
1854
|
queueName: EthTransactionPreparation.queueName,
|
|
1325
|
-
data: {
|
|
1326
|
-
tx,
|
|
1327
|
-
offChainPayloads
|
|
1328
|
-
},
|
|
1855
|
+
data: { tx, offChainPayloads },
|
|
1329
1856
|
opts: {
|
|
1330
1857
|
jobId,
|
|
1331
1858
|
attempts: 60,
|
|
1332
|
-
backoff: {
|
|
1333
|
-
type: "fixed",
|
|
1334
|
-
delay: 5e3
|
|
1335
|
-
}
|
|
1859
|
+
backoff: { type: "fixed", delay: 5e3 }
|
|
1336
1860
|
},
|
|
1337
1861
|
children: [
|
|
1338
1862
|
{
|
|
1339
1863
|
// Step 4
|
|
1340
1864
|
name: Xl1TransactionMonitor.name,
|
|
1341
1865
|
queueName: Xl1TransactionMonitor.queueName,
|
|
1342
|
-
data: {
|
|
1343
|
-
tx
|
|
1344
|
-
},
|
|
1866
|
+
data: { tx },
|
|
1345
1867
|
opts: {
|
|
1346
1868
|
jobId,
|
|
1347
1869
|
attempts: 60,
|
|
1348
|
-
backoff: {
|
|
1349
|
-
type: "fixed",
|
|
1350
|
-
delay: 5e3
|
|
1351
|
-
}
|
|
1870
|
+
backoff: { type: "fixed", delay: 5e3 }
|
|
1352
1871
|
},
|
|
1353
1872
|
children: [
|
|
1354
1873
|
{
|
|
1355
1874
|
// Step 3
|
|
1356
1875
|
name: Xl1TransactionSubmissionStorage.name,
|
|
1357
1876
|
queueName: Xl1TransactionSubmissionStorage.queueName,
|
|
1358
|
-
data: {
|
|
1359
|
-
tx
|
|
1360
|
-
},
|
|
1877
|
+
data: { tx },
|
|
1361
1878
|
opts: {
|
|
1362
1879
|
jobId,
|
|
1363
1880
|
attempts: 60,
|
|
1364
|
-
backoff: {
|
|
1365
|
-
type: "fixed",
|
|
1366
|
-
delay: 5e3
|
|
1367
|
-
}
|
|
1881
|
+
backoff: { type: "fixed", delay: 5e3 }
|
|
1368
1882
|
},
|
|
1369
1883
|
children: [
|
|
1370
1884
|
{
|
|
1371
1885
|
// Step 2
|
|
1372
1886
|
name: Xl1TransactionSubmission.name,
|
|
1373
1887
|
queueName: Xl1TransactionSubmission.queueName,
|
|
1374
|
-
data: {
|
|
1375
|
-
|
|
1376
|
-
},
|
|
1377
|
-
opts: {
|
|
1378
|
-
jobId
|
|
1379
|
-
},
|
|
1888
|
+
data: { tx },
|
|
1889
|
+
opts: { jobId },
|
|
1380
1890
|
children: [
|
|
1381
1891
|
{
|
|
1382
1892
|
// Step 1 (runs first as deepest child)
|
|
1383
1893
|
name: Xl1TransactionPreparation.name,
|
|
1384
1894
|
queueName: Xl1TransactionPreparation.queueName,
|
|
1385
|
-
data: {
|
|
1386
|
-
tx,
|
|
1387
|
-
offChainPayloads
|
|
1388
|
-
},
|
|
1895
|
+
data: { tx, offChainPayloads },
|
|
1389
1896
|
opts: {
|
|
1390
1897
|
jobId,
|
|
1391
1898
|
attempts: 60,
|
|
1392
|
-
backoff: {
|
|
1393
|
-
type: "fixed",
|
|
1394
|
-
delay: 5e3
|
|
1395
|
-
}
|
|
1899
|
+
backoff: { type: "fixed", delay: 5e3 }
|
|
1396
1900
|
}
|
|
1397
1901
|
}
|
|
1398
1902
|
]
|
|
@@ -1412,44 +1916,41 @@ var createXl1ToEthBridgeJob = /* @__PURE__ */ __name(async (flowProducer2, tx, o
|
|
|
1412
1916
|
]
|
|
1413
1917
|
});
|
|
1414
1918
|
return flow;
|
|
1415
|
-
}
|
|
1919
|
+
};
|
|
1416
1920
|
|
|
1417
1921
|
// src/services/queue/flows/createXl1ToEthBridgeJob/getXl1ToEthBridgeJob.ts
|
|
1418
|
-
var getXl1ToEthBridgeJob =
|
|
1922
|
+
var getXl1ToEthBridgeJob = async (flowProducer2, tx) => {
|
|
1419
1923
|
const id = await getJobIdForXl1ToEthBridgeJob(tx);
|
|
1420
|
-
const flow = await flowProducer2.getFlow({
|
|
1421
|
-
queueName: Xl1ToEthBridgeParent.queueName,
|
|
1422
|
-
id
|
|
1423
|
-
});
|
|
1924
|
+
const flow = await flowProducer2.getFlow({ queueName: Xl1ToEthBridgeParent.queueName, id });
|
|
1424
1925
|
return flow;
|
|
1425
|
-
}
|
|
1926
|
+
};
|
|
1426
1927
|
|
|
1427
1928
|
// src/services/queue/getEthToXl1Queues.ts
|
|
1428
1929
|
import { Queue } from "bullmq";
|
|
1429
1930
|
var ethToXl1Queues;
|
|
1430
|
-
var getEthToXl1Queues =
|
|
1931
|
+
var getEthToXl1Queues = (config) => {
|
|
1431
1932
|
if (ethToXl1Queues) return ethToXl1Queues;
|
|
1432
1933
|
const connection2 = getConnection(config);
|
|
1433
1934
|
ethToXl1Queues = {
|
|
1434
|
-
ethEventVerification: new Queue(EthEventVerification.queueName, {
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
}),
|
|
1438
|
-
ethToXl1BridgeParent: new Queue(EthToXl1BridgeParent.queueName, {
|
|
1439
|
-
connection: connection2,
|
|
1440
|
-
prefix
|
|
1441
|
-
}),
|
|
1442
|
-
xl1ReserveTxFulfillment: new Queue(Xl1ReserveTxFulfillment.queueName, {
|
|
1443
|
-
connection: connection2,
|
|
1444
|
-
prefix
|
|
1445
|
-
})
|
|
1935
|
+
ethEventVerification: new Queue(EthEventVerification.queueName, { connection: connection2, prefix }),
|
|
1936
|
+
ethToXl1BridgeParent: new Queue(EthToXl1BridgeParent.queueName, { connection: connection2, prefix }),
|
|
1937
|
+
xl1ReserveTxFulfillment: new Queue(Xl1ReserveTxFulfillment.queueName, { connection: connection2, prefix })
|
|
1446
1938
|
};
|
|
1447
1939
|
return ethToXl1Queues;
|
|
1448
|
-
}
|
|
1940
|
+
};
|
|
1449
1941
|
|
|
1450
1942
|
// src/services/queue/getXl1ToEthQueueJobs.ts
|
|
1451
|
-
var getStatusQueueJobs =
|
|
1452
|
-
const [
|
|
1943
|
+
var getStatusQueueJobs = async (queues, jobId) => {
|
|
1944
|
+
const [
|
|
1945
|
+
ethTransactionMonitorJob,
|
|
1946
|
+
ethTransactionPreparationJob,
|
|
1947
|
+
ethTransactionSubmissionJob,
|
|
1948
|
+
xl1ToEthBridgeParentJob,
|
|
1949
|
+
xl1TransactionMonitorJob,
|
|
1950
|
+
xl1TransactionPreparationJob,
|
|
1951
|
+
xl1TransactionSubmissionJob,
|
|
1952
|
+
xl1TransactionSubmissionStorageJob
|
|
1953
|
+
] = await Promise.all([
|
|
1453
1954
|
queues.ethTransactionMonitor.getJob(jobId),
|
|
1454
1955
|
queues.ethTransactionPreparation.getJob(jobId),
|
|
1455
1956
|
queues.ethTransactionSubmission.getJob(jobId),
|
|
@@ -1469,54 +1970,137 @@ var getStatusQueueJobs = /* @__PURE__ */ __name(async (queues, jobId) => {
|
|
|
1469
1970
|
xl1TransactionSubmissionStorageJob,
|
|
1470
1971
|
xl1TransactionSubmissionJob
|
|
1471
1972
|
};
|
|
1472
|
-
}
|
|
1973
|
+
};
|
|
1473
1974
|
|
|
1474
1975
|
// src/services/queue/getXl1ToEthQueues.ts
|
|
1475
1976
|
import { Queue as Queue2 } from "bullmq";
|
|
1476
1977
|
var xl1ToEthQueues;
|
|
1477
|
-
var getXl1ToEthQueues =
|
|
1978
|
+
var getXl1ToEthQueues = (config) => {
|
|
1478
1979
|
if (xl1ToEthQueues) return xl1ToEthQueues;
|
|
1479
1980
|
const connection2 = getConnection(config);
|
|
1480
1981
|
xl1ToEthQueues = {
|
|
1481
|
-
ethTransactionMonitor: new Queue2(EthTransactionMonitor.queueName, {
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
}),
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
})
|
|
1489
|
-
ethTransactionSubmission: new Queue2(EthTransactionSubmission.queueName, {
|
|
1490
|
-
connection: connection2,
|
|
1491
|
-
prefix
|
|
1492
|
-
}),
|
|
1493
|
-
xl1ToEthBridgeParent: new Queue2(Xl1ToEthBridgeParent.queueName, {
|
|
1494
|
-
connection: connection2,
|
|
1495
|
-
prefix
|
|
1496
|
-
}),
|
|
1497
|
-
xl1TransactionMonitor: new Queue2(Xl1TransactionMonitor.queueName, {
|
|
1498
|
-
connection: connection2,
|
|
1499
|
-
prefix
|
|
1500
|
-
}),
|
|
1501
|
-
xl1TransactionPreparation: new Queue2(Xl1TransactionPreparation.queueName, {
|
|
1502
|
-
connection: connection2,
|
|
1503
|
-
prefix
|
|
1504
|
-
}),
|
|
1505
|
-
xl1TransactionSubmission: new Queue2(Xl1TransactionSubmission.queueName, {
|
|
1506
|
-
connection: connection2,
|
|
1507
|
-
prefix
|
|
1508
|
-
}),
|
|
1509
|
-
xl1TransactionSubmissionStorage: new Queue2(Xl1TransactionSubmissionStorage.queueName, {
|
|
1510
|
-
connection: connection2,
|
|
1511
|
-
prefix
|
|
1512
|
-
})
|
|
1982
|
+
ethTransactionMonitor: new Queue2(EthTransactionMonitor.queueName, { connection: connection2, prefix }),
|
|
1983
|
+
ethTransactionPreparation: new Queue2(EthTransactionPreparation.queueName, { connection: connection2, prefix }),
|
|
1984
|
+
ethTransactionSubmission: new Queue2(EthTransactionSubmission.queueName, { connection: connection2, prefix }),
|
|
1985
|
+
xl1ToEthBridgeParent: new Queue2(Xl1ToEthBridgeParent.queueName, { connection: connection2, prefix }),
|
|
1986
|
+
xl1TransactionMonitor: new Queue2(Xl1TransactionMonitor.queueName, { connection: connection2, prefix }),
|
|
1987
|
+
xl1TransactionPreparation: new Queue2(Xl1TransactionPreparation.queueName, { connection: connection2, prefix }),
|
|
1988
|
+
xl1TransactionSubmission: new Queue2(Xl1TransactionSubmission.queueName, { connection: connection2, prefix }),
|
|
1989
|
+
xl1TransactionSubmissionStorage: new Queue2(Xl1TransactionSubmissionStorage.queueName, { connection: connection2, prefix })
|
|
1513
1990
|
};
|
|
1514
1991
|
return xl1ToEthQueues;
|
|
1515
|
-
}
|
|
1992
|
+
};
|
|
1993
|
+
|
|
1994
|
+
// src/services/queue/retryFailedJobs.ts
|
|
1995
|
+
var DEFAULT_LIMIT = 100;
|
|
1996
|
+
function hasAnyFilter(filters) {
|
|
1997
|
+
return filters.bridgeIds !== void 0 && filters.bridgeIds.length > 0 || filters.since !== void 0 || filters.until !== void 0 || filters.reasonContains !== void 0 || filters.srcAddress !== void 0;
|
|
1998
|
+
}
|
|
1999
|
+
var AVAILABLE_BULK_FILTERS = [
|
|
2000
|
+
"bridgeIds",
|
|
2001
|
+
"since",
|
|
2002
|
+
"until",
|
|
2003
|
+
"reasonContains",
|
|
2004
|
+
"srcAddress"
|
|
2005
|
+
];
|
|
2006
|
+
async function isFilterMatch(job, id, filters, srcAddressLookup) {
|
|
2007
|
+
const {
|
|
2008
|
+
bridgeIds,
|
|
2009
|
+
reasonContains,
|
|
2010
|
+
since,
|
|
2011
|
+
srcAddress,
|
|
2012
|
+
until
|
|
2013
|
+
} = filters;
|
|
2014
|
+
if (bridgeIds !== void 0 && !bridgeIds.includes(id)) return false;
|
|
2015
|
+
if (since !== void 0 || until !== void 0) {
|
|
2016
|
+
const finishedOn = job.finishedOn;
|
|
2017
|
+
if (finishedOn === void 0) return false;
|
|
2018
|
+
if (since !== void 0 && finishedOn < since) return false;
|
|
2019
|
+
if (until !== void 0 && finishedOn > until) return false;
|
|
2020
|
+
}
|
|
2021
|
+
if (reasonContains !== void 0 && !(job.failedReason ?? "").includes(reasonContains)) {
|
|
2022
|
+
return false;
|
|
2023
|
+
}
|
|
2024
|
+
if (srcAddress !== void 0) {
|
|
2025
|
+
if (srcAddressLookup === void 0) return false;
|
|
2026
|
+
const found = await srcAddressLookup(id);
|
|
2027
|
+
if (found === void 0 || found.toLowerCase() !== srcAddress.toLowerCase()) return false;
|
|
2028
|
+
}
|
|
2029
|
+
return true;
|
|
2030
|
+
}
|
|
2031
|
+
async function retryFailedJobs(opts) {
|
|
2032
|
+
const {
|
|
2033
|
+
filters,
|
|
2034
|
+
queue,
|
|
2035
|
+
srcAddressLookup
|
|
2036
|
+
} = opts;
|
|
2037
|
+
const { dryRun = false, limit = DEFAULT_LIMIT } = filters;
|
|
2038
|
+
const failedJobs = await queue.getFailed(0, Math.max(limit - 1, 0));
|
|
2039
|
+
const retried = [];
|
|
2040
|
+
const skipped = [];
|
|
2041
|
+
for (const job of failedJobs) {
|
|
2042
|
+
const id = job.id;
|
|
2043
|
+
if (id === void 0) continue;
|
|
2044
|
+
const matches = await isFilterMatch(job, id, filters, srcAddressLookup);
|
|
2045
|
+
if (!matches) {
|
|
2046
|
+
skipped.push({ id, reason: "filter-mismatch" });
|
|
2047
|
+
continue;
|
|
2048
|
+
}
|
|
2049
|
+
const state = await job.getState();
|
|
2050
|
+
if (state !== "failed") {
|
|
2051
|
+
skipped.push({ id, reason: "not-in-failed-state" });
|
|
2052
|
+
continue;
|
|
2053
|
+
}
|
|
2054
|
+
if (!dryRun) await job.retry();
|
|
2055
|
+
retried.push({ failedReason: job.failedReason, id });
|
|
2056
|
+
}
|
|
2057
|
+
const total = retried.length + skipped.length;
|
|
2058
|
+
return {
|
|
2059
|
+
counts: {
|
|
2060
|
+
retried: retried.length,
|
|
2061
|
+
skipped: skipped.length,
|
|
2062
|
+
total
|
|
2063
|
+
},
|
|
2064
|
+
retried,
|
|
2065
|
+
skipped
|
|
2066
|
+
};
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
// src/services/queue/retrySingleFailedJob.ts
|
|
2070
|
+
async function retrySingleFailedJob(opts) {
|
|
2071
|
+
const {
|
|
2072
|
+
dryRun = false,
|
|
2073
|
+
jobId,
|
|
2074
|
+
queue
|
|
2075
|
+
} = opts;
|
|
2076
|
+
const job = await queue.getJob(jobId);
|
|
2077
|
+
if (job === void 0) {
|
|
2078
|
+
return { bridgeId: jobId, status: "not-found" };
|
|
2079
|
+
}
|
|
2080
|
+
const state = await job.getState();
|
|
2081
|
+
if (state !== "failed") {
|
|
2082
|
+
return { bridgeId: jobId, status: "not-in-failed-state" };
|
|
2083
|
+
}
|
|
2084
|
+
const failedReason = job.failedReason;
|
|
2085
|
+
if (!dryRun) {
|
|
2086
|
+
await job.retry();
|
|
2087
|
+
}
|
|
2088
|
+
return {
|
|
2089
|
+
bridgeId: jobId,
|
|
2090
|
+
failedReason,
|
|
2091
|
+
status: "retried"
|
|
2092
|
+
};
|
|
2093
|
+
}
|
|
1516
2094
|
|
|
1517
2095
|
// src/services/queue/scanner/createEnqueueEthToXl1Bridge.ts
|
|
1518
2096
|
function createEnqueueEthToXl1Bridge(options2) {
|
|
1519
|
-
const {
|
|
2097
|
+
const {
|
|
2098
|
+
evmChainId,
|
|
2099
|
+
evmContractAddress,
|
|
2100
|
+
flowProducer: flowProducer2,
|
|
2101
|
+
xl1ChainId,
|
|
2102
|
+
xl1TokenAddress
|
|
2103
|
+
} = options2;
|
|
1520
2104
|
return async (bridgeId, jobLog) => {
|
|
1521
2105
|
await createEthToXl1BridgeJob(flowProducer2, {
|
|
1522
2106
|
bridgeId,
|
|
@@ -1528,7 +2112,6 @@ function createEnqueueEthToXl1Bridge(options2) {
|
|
|
1528
2112
|
await jobLog?.(`[bridge-scanner] bridgeId ${bridgeId} enqueued`);
|
|
1529
2113
|
};
|
|
1530
2114
|
}
|
|
1531
|
-
__name(createEnqueueEthToXl1Bridge, "createEnqueueEthToXl1Bridge");
|
|
1532
2115
|
|
|
1533
2116
|
// src/services/queue/scanner/EvmBridgeScanner.ts
|
|
1534
2117
|
import { isUndefined as isUndefined2 } from "@xylabs/sdk-js";
|
|
@@ -1537,22 +2120,33 @@ import { isUndefined as isUndefined2 } from "@xylabs/sdk-js";
|
|
|
1537
2120
|
function getEvmBridgeCursorKey(chainId, contractAddress) {
|
|
1538
2121
|
return `${chainId.toLowerCase()}:${contractAddress.toLowerCase()}`;
|
|
1539
2122
|
}
|
|
1540
|
-
__name(getEvmBridgeCursorKey, "getEvmBridgeCursorKey");
|
|
1541
2123
|
|
|
1542
2124
|
// src/services/queue/scanner/EvmBridgeScanner.ts
|
|
1543
2125
|
function createEvmBridgeScanner(options2) {
|
|
1544
|
-
const {
|
|
2126
|
+
const {
|
|
2127
|
+
bridge,
|
|
2128
|
+
bridgeAddress,
|
|
2129
|
+
chainId,
|
|
2130
|
+
confirmationDepth,
|
|
2131
|
+
cursors,
|
|
2132
|
+
deployBlock,
|
|
2133
|
+
enqueue,
|
|
2134
|
+
provider
|
|
2135
|
+
} = options2;
|
|
1545
2136
|
const cursorKey = getEvmBridgeCursorKey(chainId, bridgeAddress);
|
|
1546
2137
|
return {
|
|
1547
2138
|
async scan(options3) {
|
|
1548
2139
|
const jobLog = options3?.jobLog;
|
|
1549
|
-
const blockTag = await resolveEvmBlockTagAtDepth(provider, confirmationDepth);
|
|
1550
|
-
const confirmedHigh = await bridge.nextBridgeToId({
|
|
1551
|
-
blockTag
|
|
1552
|
-
});
|
|
2140
|
+
const blockTag = await resolveEvmBlockTagAtDepth(provider, confirmationDepth, deployBlock);
|
|
2141
|
+
const confirmedHigh = await bridge.nextBridgeToId({ blockTag });
|
|
1553
2142
|
const cursor = await cursors.get(cursorKey);
|
|
1554
2143
|
const lastProcessedId = isUndefined2(cursor) ? 0n : BigInt(cursor.lastProcessedId);
|
|
2144
|
+
const priorProcessedBlock = isUndefined2(cursor?.lastProcessedBlock) ? deployBlock : Number(cursor.lastProcessedBlock);
|
|
2145
|
+
const nextProcessedBlock = typeof blockTag === "number" ? Math.max(priorProcessedBlock, blockTag) : priorProcessedBlock;
|
|
1555
2146
|
if (confirmedHigh <= lastProcessedId) {
|
|
2147
|
+
if (nextProcessedBlock !== priorProcessedBlock || isUndefined2(cursor)) {
|
|
2148
|
+
await cursors.set(cursorKey, { lastProcessedBlock: nextProcessedBlock.toString(), lastProcessedId: lastProcessedId.toString() });
|
|
2149
|
+
}
|
|
1556
2150
|
return {
|
|
1557
2151
|
confirmedHigh,
|
|
1558
2152
|
enqueued: 0,
|
|
@@ -1564,9 +2158,7 @@ function createEvmBridgeScanner(options2) {
|
|
|
1564
2158
|
await enqueue(id, jobLog);
|
|
1565
2159
|
enqueued++;
|
|
1566
2160
|
}
|
|
1567
|
-
await cursors.set(cursorKey, {
|
|
1568
|
-
lastProcessedId: confirmedHigh.toString()
|
|
1569
|
-
});
|
|
2161
|
+
await cursors.set(cursorKey, { lastProcessedBlock: nextProcessedBlock.toString(), lastProcessedId: confirmedHigh.toString() });
|
|
1570
2162
|
return {
|
|
1571
2163
|
confirmedHigh,
|
|
1572
2164
|
enqueued,
|
|
@@ -1575,16 +2167,21 @@ function createEvmBridgeScanner(options2) {
|
|
|
1575
2167
|
}
|
|
1576
2168
|
};
|
|
1577
2169
|
}
|
|
1578
|
-
__name(createEvmBridgeScanner, "createEvmBridgeScanner");
|
|
1579
2170
|
|
|
1580
2171
|
// src/services/queue/scanner/EvmBridgeScannerRunner.ts
|
|
2172
|
+
import { spanAsync as spanAsync16 } from "@xylabs/sdk-js";
|
|
1581
2173
|
import { Queue as Queue3, Worker as Worker13 } from "bullmq";
|
|
1582
2174
|
var QUEUE_NAME = "eth-to-xl1-scanner";
|
|
1583
2175
|
var REPEATABLE_JOB_NAME = "scan";
|
|
1584
2176
|
var REPEATABLE_JOB_ID = "eth-to-xl1-scanner-tick";
|
|
1585
2177
|
function createEvmBridgeScannerRunner(options2) {
|
|
1586
|
-
const {
|
|
1587
|
-
|
|
2178
|
+
const {
|
|
2179
|
+
connection: connection2,
|
|
2180
|
+
intervalMs,
|
|
2181
|
+
logger,
|
|
2182
|
+
scanner,
|
|
2183
|
+
telemetry: telemetry2
|
|
2184
|
+
} = options2;
|
|
1588
2185
|
let queue;
|
|
1589
2186
|
let worker;
|
|
1590
2187
|
return {
|
|
@@ -1595,27 +2192,27 @@ function createEvmBridgeScannerRunner(options2) {
|
|
|
1595
2192
|
telemetry: telemetry2
|
|
1596
2193
|
});
|
|
1597
2194
|
worker = new Worker13(QUEUE_NAME, async (job) => {
|
|
1598
|
-
await
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
2195
|
+
await spanAsync16("bridge:scanner:tick", async () => {
|
|
2196
|
+
await job.log("[scanner] tick start");
|
|
2197
|
+
const result = await scanner.scan({ jobLog: (msg) => job.log(msg) });
|
|
2198
|
+
await job.log(
|
|
2199
|
+
`[scanner] tick complete: enqueued=${result.enqueued} confirmedHigh=${result.confirmedHigh.toString()} lastProcessedId=${result.lastProcessedId.toString()}`
|
|
2200
|
+
);
|
|
2201
|
+
}, { logger, timeBudgetLimit: 2e3 });
|
|
1603
2202
|
}, {
|
|
1604
2203
|
connection: connection2,
|
|
1605
2204
|
prefix,
|
|
1606
2205
|
telemetry: telemetry2
|
|
1607
2206
|
});
|
|
1608
2207
|
worker.on("failed", (job, err) => {
|
|
1609
|
-
logger.error(`[scanner] tick ${job?.id} failed
|
|
2208
|
+
logger.error(`[scanner] tick ${job?.id} failed: ${err.message}`);
|
|
1610
2209
|
});
|
|
1611
2210
|
worker.on("error", (err) => {
|
|
1612
|
-
logger.error(
|
|
2211
|
+
logger.error(`[scanner] worker error: ${err.message}`);
|
|
1613
2212
|
});
|
|
1614
2213
|
await queue.add(REPEATABLE_JOB_NAME, {}, {
|
|
1615
2214
|
jobId: REPEATABLE_JOB_ID,
|
|
1616
|
-
repeat: {
|
|
1617
|
-
every: intervalMs
|
|
1618
|
-
}
|
|
2215
|
+
repeat: { every: intervalMs }
|
|
1619
2216
|
});
|
|
1620
2217
|
},
|
|
1621
2218
|
async stop() {
|
|
@@ -1626,11 +2223,16 @@ function createEvmBridgeScannerRunner(options2) {
|
|
|
1626
2223
|
}
|
|
1627
2224
|
};
|
|
1628
2225
|
}
|
|
1629
|
-
__name(createEvmBridgeScannerRunner, "createEvmBridgeScannerRunner");
|
|
1630
2226
|
|
|
1631
2227
|
// src/services/queue/scanner/buildEvmBridgeScannerRunner.ts
|
|
1632
2228
|
function buildEvmBridgeScannerRunner(options2) {
|
|
1633
|
-
const {
|
|
2229
|
+
const {
|
|
2230
|
+
config,
|
|
2231
|
+
connection: connection2,
|
|
2232
|
+
flowProducer: flowProducer2,
|
|
2233
|
+
services,
|
|
2234
|
+
telemetry: telemetry2
|
|
2235
|
+
} = options2;
|
|
1634
2236
|
const evmChainId = getRemoteChainId(config);
|
|
1635
2237
|
const evmContractAddress = config.remoteBridgeContractAddress;
|
|
1636
2238
|
const xl1ChainId = getXl1ChainId(config);
|
|
@@ -1649,111 +2251,106 @@ function buildEvmBridgeScannerRunner(options2) {
|
|
|
1649
2251
|
chainId: evmChainId,
|
|
1650
2252
|
confirmationDepth: services.remoteConfirmationDepth,
|
|
1651
2253
|
cursors: services.evmBridgeCursorMap,
|
|
2254
|
+
deployBlock: services.evmBridgeDeployBlock,
|
|
1652
2255
|
enqueue,
|
|
1653
2256
|
provider: services.provider
|
|
1654
2257
|
});
|
|
1655
2258
|
return createEvmBridgeScannerRunner({
|
|
1656
2259
|
connection: connection2,
|
|
1657
2260
|
intervalMs,
|
|
1658
|
-
logger,
|
|
2261
|
+
logger: services.logger,
|
|
1659
2262
|
scanner,
|
|
1660
2263
|
telemetry: telemetry2
|
|
1661
2264
|
});
|
|
1662
2265
|
}
|
|
1663
|
-
__name(buildEvmBridgeScannerRunner, "buildEvmBridgeScannerRunner");
|
|
1664
2266
|
|
|
1665
2267
|
// src/services/queue/telemetry.ts
|
|
1666
2268
|
import { isDefined as isDefined13 } from "@xylabs/sdk-js";
|
|
1667
2269
|
import { BullMQOtel } from "bullmq-otel";
|
|
1668
2270
|
var telemetry;
|
|
1669
|
-
var options = {
|
|
1670
|
-
|
|
1671
|
-
};
|
|
1672
|
-
var getTelemetry = /* @__PURE__ */ __name(() => {
|
|
2271
|
+
var options = { enableMetrics: true };
|
|
2272
|
+
var getTelemetry = () => {
|
|
1673
2273
|
if (isDefined13(telemetry)) return telemetry;
|
|
1674
2274
|
telemetry = new BullMQOtel(options);
|
|
1675
2275
|
return telemetry;
|
|
1676
|
-
}
|
|
2276
|
+
};
|
|
1677
2277
|
|
|
1678
2278
|
// src/server/addFlowProducer.ts
|
|
1679
|
-
var addFlowProducer =
|
|
2279
|
+
var addFlowProducer = (app, config) => {
|
|
1680
2280
|
const connection2 = getConnection(config);
|
|
1681
2281
|
const telemetry2 = getTelemetry();
|
|
1682
2282
|
const flowProducer2 = getFlowProducer(connection2, telemetry2);
|
|
1683
2283
|
app.flowProducer = flowProducer2;
|
|
1684
2284
|
return app;
|
|
1685
|
-
}
|
|
2285
|
+
};
|
|
1686
2286
|
|
|
1687
2287
|
// src/server/instrumentation.ts
|
|
1688
2288
|
import { registerInstrumentations } from "@opentelemetry/instrumentation";
|
|
1689
2289
|
import { ExpressInstrumentation } from "@opentelemetry/instrumentation-express";
|
|
1690
2290
|
import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
|
|
1691
|
-
var addInstrumentation =
|
|
1692
|
-
const instrumentations = [
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
];
|
|
1696
|
-
registerInstrumentations({
|
|
1697
|
-
instrumentations
|
|
1698
|
-
});
|
|
1699
|
-
}, "addInstrumentation");
|
|
2291
|
+
var addInstrumentation = () => {
|
|
2292
|
+
const instrumentations = [new HttpInstrumentation(), new ExpressInstrumentation()];
|
|
2293
|
+
registerInstrumentations({ instrumentations });
|
|
2294
|
+
};
|
|
1700
2295
|
|
|
1701
2296
|
// src/server/routes/livez/get.ts
|
|
1702
2297
|
import { setRawResponseFormat } from "@xylabs/express";
|
|
1703
2298
|
import { ReasonPhrases } from "http-status-codes";
|
|
1704
2299
|
var message = ReasonPhrases.OK;
|
|
1705
|
-
var handler =
|
|
2300
|
+
var handler = (_req, res) => {
|
|
1706
2301
|
setRawResponseFormat(res);
|
|
1707
|
-
res.status(200).send({
|
|
1708
|
-
|
|
1709
|
-
});
|
|
1710
|
-
}, "handler");
|
|
2302
|
+
res.status(200).send({ message });
|
|
2303
|
+
};
|
|
1711
2304
|
var getLivez = handler;
|
|
1712
2305
|
|
|
1713
2306
|
// src/server/routes/readyz/get.ts
|
|
1714
2307
|
import { setRawResponseFormat as setRawResponseFormat2 } from "@xylabs/express";
|
|
1715
2308
|
import { ReasonPhrases as ReasonPhrases2 } from "http-status-codes";
|
|
1716
2309
|
var message2 = ReasonPhrases2.OK;
|
|
1717
|
-
var handler2 =
|
|
2310
|
+
var handler2 = (_req, res) => {
|
|
1718
2311
|
setRawResponseFormat2(res);
|
|
1719
|
-
res.status(200).send({
|
|
1720
|
-
|
|
1721
|
-
});
|
|
1722
|
-
}, "handler");
|
|
2312
|
+
res.status(200).send({ message: message2 });
|
|
2313
|
+
};
|
|
1723
2314
|
var getReadyz = handler2;
|
|
1724
2315
|
|
|
1725
2316
|
// src/server/routes/startupz/get.ts
|
|
1726
2317
|
import { setRawResponseFormat as setRawResponseFormat3 } from "@xylabs/express";
|
|
1727
2318
|
import { ReasonPhrases as ReasonPhrases3 } from "http-status-codes";
|
|
1728
2319
|
var message3 = ReasonPhrases3.OK;
|
|
1729
|
-
var handler3 =
|
|
2320
|
+
var handler3 = (_req, res) => {
|
|
1730
2321
|
setRawResponseFormat3(res);
|
|
1731
|
-
res.status(200).send({
|
|
1732
|
-
|
|
1733
|
-
});
|
|
1734
|
-
}, "handler");
|
|
2322
|
+
res.status(200).send({ message: message3 });
|
|
2323
|
+
};
|
|
1735
2324
|
var getStartupz = handler3;
|
|
1736
2325
|
|
|
1737
2326
|
// src/server/routes/addProbeRoutes.ts
|
|
1738
|
-
var addProbeRoutes =
|
|
2327
|
+
var addProbeRoutes = (app) => {
|
|
1739
2328
|
app.get("/livez", getLivez);
|
|
1740
2329
|
app.get("/readyz", getReadyz);
|
|
1741
2330
|
app.get("/startupz", getStartupz);
|
|
1742
|
-
}
|
|
2331
|
+
};
|
|
1743
2332
|
|
|
1744
2333
|
// src/server/routes/bridge/routeDefinitions/routes/bridgeConfig.ts
|
|
1745
2334
|
import { requestHandlerValidator } from "@xylabs/express";
|
|
1746
|
-
import {
|
|
1747
|
-
var
|
|
1748
|
-
var
|
|
1749
|
-
response: BridgeConfigResponseZod
|
|
1750
|
-
});
|
|
1751
|
-
var makeBridgeConfigRoute = /* @__PURE__ */ __name((config) => {
|
|
2335
|
+
import { BridgeConfigResponseZod } from "@xyo-network/chain-bridge-shared";
|
|
2336
|
+
var validateRequest = requestHandlerValidator({ response: BridgeConfigResponseZod });
|
|
2337
|
+
var makeBridgeConfigRoute = (config) => {
|
|
1752
2338
|
return {
|
|
1753
2339
|
method: "get",
|
|
1754
2340
|
path: "/bridge/chains/:chainId/config",
|
|
1755
2341
|
handlers: validateRequest(async (_, res) => {
|
|
1756
|
-
const {
|
|
2342
|
+
const {
|
|
2343
|
+
escrowAddress,
|
|
2344
|
+
feeFixed,
|
|
2345
|
+
feeRateBasisPoints,
|
|
2346
|
+
feesAddress,
|
|
2347
|
+
maxBridgeAmount,
|
|
2348
|
+
minBridgeAmount,
|
|
2349
|
+
remoteChainId,
|
|
2350
|
+
remoteTokenAddress,
|
|
2351
|
+
xl1ChainId,
|
|
2352
|
+
xl1TokenAddress
|
|
2353
|
+
} = await getBridgeSettings(config);
|
|
1757
2354
|
const sanitizedConfig = {
|
|
1758
2355
|
escrowAddress,
|
|
1759
2356
|
feeFixed,
|
|
@@ -1769,95 +2366,199 @@ var makeBridgeConfigRoute = /* @__PURE__ */ __name((config) => {
|
|
|
1769
2366
|
res.json(sanitizedConfig);
|
|
1770
2367
|
})
|
|
1771
2368
|
};
|
|
1772
|
-
}
|
|
2369
|
+
};
|
|
1773
2370
|
|
|
1774
|
-
// src/server/routes/bridge/routeDefinitions/routes/
|
|
2371
|
+
// src/server/routes/bridge/routeDefinitions/routes/bridgeFromRemoteEstimate.ts
|
|
1775
2372
|
import { requestHandlerValidator as requestHandlerValidator2 } from "@xylabs/express";
|
|
1776
|
-
import {
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
2373
|
+
import {
|
|
2374
|
+
assertEx as assertEx24,
|
|
2375
|
+
hexToBigInt as hexToBigInt11,
|
|
2376
|
+
toAddress as toAddress2
|
|
2377
|
+
} from "@xylabs/sdk-js";
|
|
2378
|
+
import {
|
|
2379
|
+
BridgeFromRemoteEstimateBodyZod,
|
|
2380
|
+
BridgeFromRemoteEstimateResponseZod,
|
|
2381
|
+
buildEthToXl1BridgePayloads as buildEthToXl1BridgePayloads3,
|
|
2382
|
+
calculateBridgeFees as calculateBridgeFees3
|
|
2383
|
+
} from "@xyo-network/chain-bridge-shared";
|
|
2384
|
+
import { buildUnsignedTransaction, toXL1BlockNumber } from "@xyo-network/xl1-sdk";
|
|
2385
|
+
import { z as z2 } from "zod/mini";
|
|
1780
2386
|
|
|
1781
2387
|
// src/server/routes/bridge/routeDefinitions/pathParams/ChainIdPathParam.ts
|
|
1782
|
-
import {
|
|
1783
|
-
|
|
2388
|
+
import {
|
|
2389
|
+
asHex as asHex3,
|
|
2390
|
+
HexZod,
|
|
2391
|
+
isDefined as isDefined14
|
|
2392
|
+
} from "@xylabs/sdk-js";
|
|
2393
|
+
import { z } from "zod/mini";
|
|
2394
|
+
var getRemoteChainIdZod = (config) => {
|
|
1784
2395
|
const remoteChainId = getRemoteChainId(config);
|
|
1785
|
-
return HexZod.
|
|
2396
|
+
return HexZod.check(z.refine((val) => {
|
|
1786
2397
|
const chainId = asHex3(val);
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
2398
|
+
return isDefined14(chainId) && chainId === remoteChainId;
|
|
2399
|
+
}, { error: `Only ${remoteChainId} is supported` }));
|
|
2400
|
+
};
|
|
2401
|
+
|
|
2402
|
+
// src/server/routes/bridge/routeDefinitions/routes/bridgeFromRemoteEstimate.ts
|
|
2403
|
+
var makeBridgeFromRemoteEstimateRoute = (config) => {
|
|
2404
|
+
const params = z2.object({ chainId: getRemoteChainIdZod(config) });
|
|
2405
|
+
const validateRequest2 = requestHandlerValidator2({
|
|
2406
|
+
params,
|
|
2407
|
+
body: BridgeFromRemoteEstimateBodyZod,
|
|
2408
|
+
response: BridgeFromRemoteEstimateResponseZod
|
|
1794
2409
|
});
|
|
1795
|
-
|
|
2410
|
+
return {
|
|
2411
|
+
method: "post",
|
|
2412
|
+
path: "/bridge/chains/:chainId/bridgeFromRemote/estimate",
|
|
2413
|
+
handlers: validateRequest2(async (req, res) => {
|
|
2414
|
+
const services = req.app.services;
|
|
2415
|
+
const {
|
|
2416
|
+
account,
|
|
2417
|
+
bridge,
|
|
2418
|
+
gateway
|
|
2419
|
+
} = services;
|
|
2420
|
+
const {
|
|
2421
|
+
srcAddress,
|
|
2422
|
+
srcAmount,
|
|
2423
|
+
destAddress
|
|
2424
|
+
} = req.body;
|
|
2425
|
+
const { maxBridgeAmount, minBridgeAmount } = await getBridgeSettings(config);
|
|
2426
|
+
if (hexToBigInt11(srcAmount) < hexToBigInt11(minBridgeAmount) || hexToBigInt11(srcAmount) > hexToBigInt11(maxBridgeAmount)) {
|
|
2427
|
+
res.status(400).send();
|
|
2428
|
+
return;
|
|
2429
|
+
}
|
|
2430
|
+
const fees = calculateBridgeFees3(srcAmount, getFeeStructure(config));
|
|
2431
|
+
const feesAmount = hexToBigInt11(fees.feeFixed) + hexToBigInt11(fees.feeVariable);
|
|
2432
|
+
const { feesAddress } = await getTransferAddresses(config);
|
|
2433
|
+
const currentNextId = await bridge.nextBridgeToId({ blockTag: "latest" });
|
|
2434
|
+
const projectedBridgeId = currentNextId + 1n;
|
|
2435
|
+
const xl1ChainId = getXl1ChainId(config);
|
|
2436
|
+
const xl1TokenAddress = getXl1TokenAddress(config);
|
|
2437
|
+
const evmChainId = config.remoteChainId;
|
|
2438
|
+
const evmContractAddress = config.remoteBridgeContractAddress;
|
|
2439
|
+
const evmTokenAddress = getRemoteTokenAddress(config);
|
|
2440
|
+
const viewer = assertEx24(gateway.connection.viewer, () => new Error("Viewer not available on gateway connection"));
|
|
2441
|
+
const currentBlockNumber = await viewer.currentBlockNumber();
|
|
2442
|
+
const nbf = toXL1BlockNumber(currentBlockNumber, true);
|
|
2443
|
+
const exp = toXL1BlockNumber(currentBlockNumber + 1e3, true);
|
|
2444
|
+
const { intent, transfer } = buildEthToXl1BridgePayloads3({
|
|
2445
|
+
amount: hexToBigInt11(srcAmount),
|
|
2446
|
+
bridgeAccountAddress: account.address,
|
|
2447
|
+
bridgeId: projectedBridgeId,
|
|
2448
|
+
destAddress: toAddress2(destAddress),
|
|
2449
|
+
evmChainId,
|
|
2450
|
+
evmContractAddress,
|
|
2451
|
+
evmSrcAddress: toAddress2(srcAddress),
|
|
2452
|
+
evmTokenAddress,
|
|
2453
|
+
feesAddress,
|
|
2454
|
+
feesAmount,
|
|
2455
|
+
xl1ChainId,
|
|
2456
|
+
xl1TokenAddress
|
|
2457
|
+
});
|
|
2458
|
+
const [txBw] = await buildUnsignedTransaction(xl1ChainId, [transfer], [intent], nbf, exp, account.address);
|
|
2459
|
+
res.json([txBw, intent, transfer]);
|
|
2460
|
+
})
|
|
2461
|
+
};
|
|
2462
|
+
};
|
|
1796
2463
|
|
|
1797
|
-
// src/server/routes/bridge/routeDefinitions/routes/
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
var
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
z.tuple([
|
|
1811
|
-
BridgeIntentResponseZod,
|
|
1812
|
-
BridgeSourceResponseZod,
|
|
1813
|
-
BridgeDestinationResponseZod
|
|
1814
|
-
])
|
|
1815
|
-
]);
|
|
1816
|
-
function buildIntentAndSource(ctx) {
|
|
1817
|
-
const { canonical, config, nonce } = ctx;
|
|
1818
|
-
const amountHex = toHex4(canonical.amount);
|
|
1819
|
-
const xl1ChainId = getXl1ChainId(config);
|
|
1820
|
-
const xl1TokenAddress = getXl1TokenAddress(config);
|
|
1821
|
-
const evmChainId = config.remoteChainId;
|
|
1822
|
-
const evmTokenAddress = getRemoteTokenAddress(config);
|
|
1823
|
-
const intentFields = BridgeIntentFieldsZod.parse({
|
|
1824
|
-
dest: xl1ChainId,
|
|
1825
|
-
destAddress: canonical.destAddress,
|
|
1826
|
-
destAmount: amountHex,
|
|
1827
|
-
destToken: xl1TokenAddress,
|
|
1828
|
-
nonce,
|
|
1829
|
-
src: evmChainId,
|
|
1830
|
-
srcAddress: canonical.srcAddress,
|
|
1831
|
-
srcAmount: amountHex,
|
|
1832
|
-
srcToken: evmTokenAddress
|
|
2464
|
+
// src/server/routes/bridge/routeDefinitions/routes/bridgeFromRemoteRetry.ts
|
|
2465
|
+
import { requestHandlerValidator as requestHandlerValidator3 } from "@xylabs/express";
|
|
2466
|
+
import { BridgeRetryBodyZod, BridgeRetryResponseZod } from "@xyo-network/chain-bridge-shared";
|
|
2467
|
+
import { z as z3 } from "zod/mini";
|
|
2468
|
+
var makeBridgeFromRemoteRetryRoute = (config) => {
|
|
2469
|
+
const params = z3.object({
|
|
2470
|
+
chainId: getRemoteChainIdZod(config),
|
|
2471
|
+
nonce: z3.string().check(z3.regex(/^\d+$/, { error: "nonce must be a non-negative integer (the bridge contract's id)" }))
|
|
2472
|
+
});
|
|
2473
|
+
const validateRequest2 = requestHandlerValidator3({
|
|
2474
|
+
params,
|
|
2475
|
+
body: BridgeRetryBodyZod,
|
|
2476
|
+
response: BridgeRetryResponseZod
|
|
1833
2477
|
});
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
2478
|
+
return {
|
|
2479
|
+
method: "post",
|
|
2480
|
+
path: "/bridge/chains/:chainId/bridgeFromRemote/retry/:nonce",
|
|
2481
|
+
handlers: validateRequest2(async (req, res) => {
|
|
2482
|
+
const bridgeId = BigInt(req.params.nonce);
|
|
2483
|
+
const identity = getJobIdForEthToXl1BridgeJob({
|
|
2484
|
+
bridgeId,
|
|
2485
|
+
contractAddress: config.remoteBridgeContractAddress,
|
|
2486
|
+
evmChainId: config.remoteChainId
|
|
2487
|
+
});
|
|
2488
|
+
const queues = getEthToXl1Queues(config);
|
|
2489
|
+
const result = await retrySingleFailedJob({
|
|
2490
|
+
dryRun: req.body.dryRun,
|
|
2491
|
+
jobId: identity,
|
|
2492
|
+
queue: queues.xl1ReserveTxFulfillment
|
|
2493
|
+
});
|
|
2494
|
+
if (result.status === "retried" && !req.body.dryRun) {
|
|
2495
|
+
req.app.services.bridgeFlowMetrics.recordRetried({ direction: "inbound", trigger: "api_singular" });
|
|
2496
|
+
}
|
|
2497
|
+
const httpStatus = result.status === "not-found" ? 404 : 200;
|
|
2498
|
+
res.status(httpStatus).json(result);
|
|
2499
|
+
})
|
|
1837
2500
|
};
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
2501
|
+
};
|
|
2502
|
+
|
|
2503
|
+
// src/server/routes/bridge/routeDefinitions/routes/bridgeFromRemoteRetryFailed.ts
|
|
2504
|
+
import { requestHandlerValidator as requestHandlerValidator4 } from "@xylabs/express";
|
|
2505
|
+
import { BridgeRetryFailedBodyZod, BridgeRetryFailedResponseZod } from "@xyo-network/chain-bridge-shared";
|
|
2506
|
+
import { StatusCodes } from "http-status-codes";
|
|
2507
|
+
import { z as z4 } from "zod/mini";
|
|
2508
|
+
var makeBridgeFromRemoteRetryFailedRoute = (config) => {
|
|
2509
|
+
const params = z4.object({ chainId: getRemoteChainIdZod(config) });
|
|
2510
|
+
const validateRequest2 = requestHandlerValidator4({
|
|
2511
|
+
params,
|
|
2512
|
+
body: BridgeRetryFailedBodyZod,
|
|
2513
|
+
response: BridgeRetryFailedResponseZod
|
|
1841
2514
|
});
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
2515
|
+
return {
|
|
2516
|
+
method: "post",
|
|
2517
|
+
path: "/bridge/chains/:chainId/bridgeFromRemote/retryFailed",
|
|
2518
|
+
handlers: validateRequest2(async (req, res) => {
|
|
2519
|
+
const filters = req.body;
|
|
2520
|
+
if (!hasAnyFilter(filters)) {
|
|
2521
|
+
const err = new Error(
|
|
2522
|
+
`At least one filter is required to prevent accidental retry-everything. Available filters: ${AVAILABLE_BULK_FILTERS.join(", ")}.`
|
|
2523
|
+
);
|
|
2524
|
+
err.name = "Bad Request";
|
|
2525
|
+
err.statusCode = StatusCodes.BAD_REQUEST;
|
|
2526
|
+
throw err;
|
|
2527
|
+
}
|
|
2528
|
+
const services = req.app.services;
|
|
2529
|
+
const queues = getEthToXl1Queues(config);
|
|
2530
|
+
const srcAddressLookup = async (jobId) => {
|
|
2531
|
+
const state = await services.bridgeFulfillmentMap.get(jobId);
|
|
2532
|
+
return state?.canonical?.srcAddress;
|
|
2533
|
+
};
|
|
2534
|
+
const result = await retryFailedJobs({
|
|
2535
|
+
filters,
|
|
2536
|
+
queue: queues.xl1ReserveTxFulfillment,
|
|
2537
|
+
srcAddressLookup
|
|
2538
|
+
});
|
|
2539
|
+
if (!filters.dryRun && result.counts.retried > 0) {
|
|
2540
|
+
services.bridgeFlowMetrics.recordRetried({
|
|
2541
|
+
count: result.counts.retried,
|
|
2542
|
+
direction: "inbound",
|
|
2543
|
+
trigger: "api_bulk"
|
|
2544
|
+
});
|
|
2545
|
+
}
|
|
2546
|
+
res.json(result);
|
|
2547
|
+
})
|
|
1845
2548
|
};
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
}
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
2549
|
+
};
|
|
2550
|
+
|
|
2551
|
+
// src/server/routes/bridge/routeDefinitions/routes/bridgeFromRemoteStatus.ts
|
|
2552
|
+
import { requestHandlerValidator as requestHandlerValidator5 } from "@xylabs/express";
|
|
2553
|
+
import { assertEx as assertEx25, isDefined as isDefined15 } from "@xylabs/sdk-js";
|
|
2554
|
+
import { BridgeFromRemoteStatusResponseZod } from "@xyo-network/chain-bridge-shared";
|
|
2555
|
+
import { z as z5 } from "zod/mini";
|
|
2556
|
+
var makeBridgeFromRemoteStatusRoute = (config) => {
|
|
2557
|
+
const params = z5.object({
|
|
1854
2558
|
chainId: getRemoteChainIdZod(config),
|
|
1855
|
-
nonce:
|
|
1856
|
-
});
|
|
1857
|
-
const validateRequest2 = requestHandlerValidator2({
|
|
1858
|
-
params,
|
|
1859
|
-
response: BridgeFromRemoteStatusResponseZod
|
|
2559
|
+
nonce: z5.string().check(z5.regex(/^\d+$/, { error: "nonce must be a non-negative integer (the bridge contract's id)" }))
|
|
1860
2560
|
});
|
|
2561
|
+
const validateRequest2 = requestHandlerValidator5({ params, response: BridgeFromRemoteStatusResponseZod });
|
|
1861
2562
|
return {
|
|
1862
2563
|
method: "get",
|
|
1863
2564
|
path: "/bridge/chains/:chainId/bridgeFromRemote/status/:nonce",
|
|
@@ -1872,56 +2573,77 @@ var makeBridgeFromRemoteStatusRoute = /* @__PURE__ */ __name((config) => {
|
|
|
1872
2573
|
});
|
|
1873
2574
|
const services = req.app.services;
|
|
1874
2575
|
const state = await services.bridgeFulfillmentMap.get(identity);
|
|
1875
|
-
if (state?.failed) return res.sendStatus(504);
|
|
1876
2576
|
if (!state?.canonical) {
|
|
1877
2577
|
const queues = getEthToXl1Queues(config);
|
|
1878
2578
|
const parentJob = await queues.ethToXl1BridgeParent.getJob(identity);
|
|
1879
|
-
if (!
|
|
2579
|
+
if (!isDefined15(parentJob)) return res.sendStatus(404);
|
|
1880
2580
|
return res.json([]);
|
|
1881
2581
|
}
|
|
1882
|
-
const
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
if (!state.fulfilled) return res.json([
|
|
1888
|
-
intent,
|
|
1889
|
-
source
|
|
1890
|
-
]);
|
|
1891
|
-
const destinationFields = BridgeDestinationObservationFieldsZod.parse({
|
|
1892
|
-
...intent,
|
|
1893
|
-
destConfirmation: state.fulfilled.txHash
|
|
1894
|
-
});
|
|
1895
|
-
const destination = {
|
|
1896
|
-
schema: BridgeDestinationObservationSchema,
|
|
1897
|
-
...destinationFields
|
|
1898
|
-
};
|
|
1899
|
-
res.json([
|
|
1900
|
-
intent,
|
|
1901
|
-
source,
|
|
1902
|
-
destination
|
|
1903
|
-
]);
|
|
2582
|
+
const intent = assertEx25(state.acceptedIntent, () => `[${identity}] acceptedIntent missing despite canonical \u2014 stale state`);
|
|
2583
|
+
const source = assertEx25(state.acceptedSourceObservation, () => `[${identity}] acceptedSourceObservation missing despite canonical \u2014 stale state`);
|
|
2584
|
+
if (!state.fulfilled) return res.json([intent, source]);
|
|
2585
|
+
const destination = assertEx25(state.acceptedDestinationObservation, () => `[${identity}] acceptedDestinationObservation missing despite fulfilled \u2014 stale state`);
|
|
2586
|
+
res.json([intent, source, destination]);
|
|
1904
2587
|
})
|
|
1905
2588
|
};
|
|
1906
|
-
}
|
|
2589
|
+
};
|
|
1907
2590
|
|
|
1908
|
-
// src/server/routes/bridge/routeDefinitions/routes/
|
|
1909
|
-
import { requestHandlerValidator as
|
|
1910
|
-
import {
|
|
1911
|
-
import {
|
|
1912
|
-
import {
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
2591
|
+
// src/server/routes/bridge/routeDefinitions/routes/bridgeFromRemoteStatusByTx.ts
|
|
2592
|
+
import { requestHandlerValidator as requestHandlerValidator6 } from "@xylabs/express";
|
|
2593
|
+
import { assertEx as assertEx26 } from "@xylabs/sdk-js";
|
|
2594
|
+
import { BridgeFromRemoteStatusResponseZod as BridgeFromRemoteStatusResponseZod2 } from "@xyo-network/chain-bridge-shared";
|
|
2595
|
+
import { z as z6 } from "zod/mini";
|
|
2596
|
+
async function lookupBridgeFromRemoteStatusByTx(map, evmTxHash) {
|
|
2597
|
+
const needle = evmTxHash.toLowerCase();
|
|
2598
|
+
for await (const [, state] of map) {
|
|
2599
|
+
const canonicalHash = state.canonical?.evmTxHash;
|
|
2600
|
+
if (canonicalHash === void 0) continue;
|
|
2601
|
+
if (canonicalHash.toLowerCase() !== needle) continue;
|
|
2602
|
+
const intent = assertEx26(
|
|
2603
|
+
state.acceptedIntent,
|
|
2604
|
+
() => `[${state.identity}] acceptedIntent missing despite canonical \u2014 stale state`
|
|
2605
|
+
);
|
|
2606
|
+
const source = assertEx26(
|
|
2607
|
+
state.acceptedSourceObservation,
|
|
2608
|
+
() => `[${state.identity}] acceptedSourceObservation missing despite canonical \u2014 stale state`
|
|
2609
|
+
);
|
|
2610
|
+
if (!state.fulfilled) return [intent, source];
|
|
2611
|
+
const destination = assertEx26(
|
|
2612
|
+
state.acceptedDestinationObservation,
|
|
2613
|
+
() => `[${state.identity}] acceptedDestinationObservation missing despite fulfilled \u2014 stale state`
|
|
2614
|
+
);
|
|
2615
|
+
return [intent, source, destination];
|
|
2616
|
+
}
|
|
2617
|
+
return null;
|
|
2618
|
+
}
|
|
2619
|
+
var makeBridgeFromRemoteStatusByTxRoute = (config) => {
|
|
2620
|
+
const params = z6.object({
|
|
2621
|
+
chainId: getRemoteChainIdZod(config),
|
|
2622
|
+
evmTxHash: z6.string().check(z6.regex(/^0x[0-9a-fA-F]{64}$/, { error: "evmTxHash must be a 0x-prefixed 32-byte hex string" }))
|
|
1923
2623
|
});
|
|
1924
|
-
const validateRequest2 =
|
|
2624
|
+
const validateRequest2 = requestHandlerValidator6({ params, response: BridgeFromRemoteStatusResponseZod2 });
|
|
2625
|
+
return {
|
|
2626
|
+
method: "get",
|
|
2627
|
+
path: "/bridge/chains/:chainId/bridgeFromRemote/status/byTx/:evmTxHash",
|
|
2628
|
+
handlers: validateRequest2(async (req, res) => {
|
|
2629
|
+
const services = req.app.services;
|
|
2630
|
+
const result = await lookupBridgeFromRemoteStatusByTx(services.bridgeFulfillmentMap, req.params.evmTxHash);
|
|
2631
|
+
if (result === null) return res.sendStatus(404);
|
|
2632
|
+
res.json(result);
|
|
2633
|
+
})
|
|
2634
|
+
};
|
|
2635
|
+
};
|
|
2636
|
+
|
|
2637
|
+
// src/server/routes/bridge/routeDefinitions/routes/bridgeToRemote.ts
|
|
2638
|
+
import { requestHandlerValidator as requestHandlerValidator7 } from "@xylabs/express";
|
|
2639
|
+
import { isDefined as isDefined16 } from "@xylabs/sdk-js";
|
|
2640
|
+
import { BridgeToRemoteBodyZod, BridgeToRemoteResponseZod } from "@xyo-network/chain-bridge-shared";
|
|
2641
|
+
import { PayloadBuilder as PayloadBuilder16 } from "@xyo-network/sdk-js";
|
|
2642
|
+
import { BridgeSourceObservationFieldsZod as BridgeSourceObservationFieldsZod2, BridgeSourceObservationSchema as BridgeSourceObservationSchema3 } from "@xyo-network/xl1-sdk";
|
|
2643
|
+
import { z as z7 } from "zod/mini";
|
|
2644
|
+
var makeBridgeToRemoteRoute = (config) => {
|
|
2645
|
+
const params = z7.object({ chainId: getRemoteChainIdZod(config) });
|
|
2646
|
+
const validateRequest2 = requestHandlerValidator7({
|
|
1925
2647
|
params,
|
|
1926
2648
|
body: BridgeToRemoteBodyZod,
|
|
1927
2649
|
response: BridgeToRemoteResponseZod
|
|
@@ -1948,56 +2670,38 @@ var makeBridgeToRemoteRoute = /* @__PURE__ */ __name((config) => {
|
|
|
1948
2670
|
res.status(400).send();
|
|
1949
2671
|
return;
|
|
1950
2672
|
}
|
|
1951
|
-
const singedHydratedTransaction = [
|
|
1952
|
-
signedTxBw,
|
|
1953
|
-
[
|
|
1954
|
-
transfer
|
|
1955
|
-
]
|
|
1956
|
-
];
|
|
2673
|
+
const singedHydratedTransaction = [signedTxBw, [transfer]];
|
|
1957
2674
|
const existingFlow = await getXl1ToEthBridgeJob(flowProducer2, singedHydratedTransaction);
|
|
1958
|
-
if (
|
|
2675
|
+
if (isDefined16(existingFlow)) {
|
|
1959
2676
|
res.status(200).send();
|
|
1960
2677
|
return;
|
|
1961
2678
|
}
|
|
1962
|
-
await createXl1ToEthBridgeJob(flowProducer2, singedHydratedTransaction, [
|
|
1963
|
-
bridgeIntent
|
|
1964
|
-
]);
|
|
2679
|
+
await createXl1ToEthBridgeJob(flowProducer2, singedHydratedTransaction, [bridgeIntent]);
|
|
1965
2680
|
const srcConfirmation = await PayloadBuilder16.hash(signedTxBw);
|
|
1966
|
-
const bridgeCommonFieldsZod =
|
|
2681
|
+
const bridgeCommonFieldsZod = z7.object(BridgeSourceObservationFieldsZod2.shape);
|
|
1967
2682
|
const bridgeCommonFields = bridgeCommonFieldsZod.parse(bridgeIntent);
|
|
1968
|
-
const bridgeObservationFields = {
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
const bridgeObservation = new PayloadBuilder16({
|
|
1973
|
-
schema: BridgeSourceObservationSchema3
|
|
1974
|
-
}).fields(bridgeObservationFields).build();
|
|
2683
|
+
const bridgeObservationFields = { ...bridgeCommonFields, srcConfirmation };
|
|
2684
|
+
const bridgeObservation = new PayloadBuilder16(
|
|
2685
|
+
{ schema: BridgeSourceObservationSchema3 }
|
|
2686
|
+
).fields(bridgeObservationFields).build();
|
|
1975
2687
|
res.status(202).json(bridgeObservation);
|
|
1976
2688
|
})
|
|
1977
2689
|
};
|
|
1978
|
-
}
|
|
2690
|
+
};
|
|
1979
2691
|
|
|
1980
2692
|
// src/server/routes/bridge/routeDefinitions/routes/bridgeToRemoteEstimate.ts
|
|
1981
|
-
import { requestHandlerValidator as
|
|
1982
|
-
import {
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
PayloadZodStrictOfSchema3(BridgeIntentSchema6).extend(BridgeIntentFieldsZod3.shape),
|
|
1994
|
-
TransferZod2
|
|
1995
|
-
]);
|
|
1996
|
-
var makeBridgeToRemoteEstimateRoute = /* @__PURE__ */ __name((config, gateway) => {
|
|
1997
|
-
const params = z3.object({
|
|
1998
|
-
chainId: getRemoteChainIdZod(config)
|
|
1999
|
-
});
|
|
2000
|
-
const validateRequest2 = requestHandlerValidator4({
|
|
2693
|
+
import { requestHandlerValidator as requestHandlerValidator8 } from "@xylabs/express";
|
|
2694
|
+
import {
|
|
2695
|
+
assertEx as assertEx27,
|
|
2696
|
+
hexToBigInt as hexToBigInt12,
|
|
2697
|
+
toAddress as toAddress3
|
|
2698
|
+
} from "@xylabs/sdk-js";
|
|
2699
|
+
import { BridgeToRemoteEstimateBodyZod, BridgeToRemoteEstimateResponseZod } from "@xyo-network/chain-bridge-shared";
|
|
2700
|
+
import { buildUnsignedTransaction as buildUnsignedTransaction2, toXL1BlockNumber as toXL1BlockNumber2 } from "@xyo-network/xl1-sdk";
|
|
2701
|
+
import { z as z8 } from "zod/mini";
|
|
2702
|
+
var makeBridgeToRemoteEstimateRoute = (config, gateway) => {
|
|
2703
|
+
const params = z8.object({ chainId: getRemoteChainIdZod(config) });
|
|
2704
|
+
const validateRequest2 = requestHandlerValidator8({
|
|
2001
2705
|
params,
|
|
2002
2706
|
body: BridgeToRemoteEstimateBodyZod,
|
|
2003
2707
|
response: BridgeToRemoteEstimateResponseZod
|
|
@@ -2007,53 +2711,46 @@ var makeBridgeToRemoteEstimateRoute = /* @__PURE__ */ __name((config, gateway) =
|
|
|
2007
2711
|
path: "/bridge/chains/:chainId/bridgeToRemote/estimate",
|
|
2008
2712
|
handlers: validateRequest2(async (req, res) => {
|
|
2009
2713
|
const xl1ChainId = getXl1ChainId(config);
|
|
2010
|
-
const {
|
|
2714
|
+
const {
|
|
2715
|
+
srcAddress,
|
|
2716
|
+
srcAmount,
|
|
2717
|
+
destAddress
|
|
2718
|
+
} = req.body;
|
|
2011
2719
|
const { maxBridgeAmount, minBridgeAmount } = await getBridgeSettings(config);
|
|
2012
|
-
if (
|
|
2720
|
+
if (hexToBigInt12(srcAmount) < hexToBigInt12(minBridgeAmount) || hexToBigInt12(srcAmount) > hexToBigInt12(maxBridgeAmount)) {
|
|
2013
2721
|
res.status(400).send();
|
|
2014
2722
|
return;
|
|
2015
2723
|
}
|
|
2016
2724
|
const [bridgeIntent, transfer] = await generateBridgeEstimate(srcAddress, srcAmount, destAddress, config);
|
|
2017
2725
|
const sender = toAddress3(srcAddress);
|
|
2018
|
-
const viewer =
|
|
2726
|
+
const viewer = assertEx27(gateway.connection.viewer, () => new Error("Viewer not available on gateway connection"));
|
|
2019
2727
|
const currentBlockNumber = await viewer.currentBlockNumber();
|
|
2020
|
-
const nbf =
|
|
2021
|
-
const exp =
|
|
2022
|
-
const [txBw] = await
|
|
2023
|
-
|
|
2024
|
-
], [
|
|
2025
|
-
bridgeIntent
|
|
2026
|
-
], nbf, exp, sender);
|
|
2027
|
-
res.json([
|
|
2028
|
-
txBw,
|
|
2029
|
-
bridgeIntent,
|
|
2030
|
-
transfer
|
|
2031
|
-
]);
|
|
2728
|
+
const nbf = toXL1BlockNumber2(currentBlockNumber, true);
|
|
2729
|
+
const exp = toXL1BlockNumber2(currentBlockNumber + 1e3, true);
|
|
2730
|
+
const [txBw] = await buildUnsignedTransaction2(xl1ChainId, [transfer], [bridgeIntent], nbf, exp, sender);
|
|
2731
|
+
res.json([txBw, bridgeIntent, transfer]);
|
|
2032
2732
|
})
|
|
2033
2733
|
};
|
|
2034
|
-
}
|
|
2734
|
+
};
|
|
2035
2735
|
|
|
2036
2736
|
// src/server/routes/bridge/routeDefinitions/routes/bridgeToRemoteMaxEstimate.ts
|
|
2037
|
-
import { requestHandlerValidator as
|
|
2038
|
-
import {
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
const
|
|
2054
|
-
chainId: getRemoteChainIdZod(config)
|
|
2055
|
-
});
|
|
2056
|
-
const validateRequest2 = requestHandlerValidator5({
|
|
2737
|
+
import { requestHandlerValidator as requestHandlerValidator9 } from "@xylabs/express";
|
|
2738
|
+
import {
|
|
2739
|
+
assertEx as assertEx28,
|
|
2740
|
+
hexToBigInt as hexToBigInt13,
|
|
2741
|
+
toAddress as toAddress4,
|
|
2742
|
+
toHex as toHex4
|
|
2743
|
+
} from "@xylabs/sdk-js";
|
|
2744
|
+
import {
|
|
2745
|
+
BridgeToRemoteMaxEstimateBodyZod,
|
|
2746
|
+
BridgeToRemoteMaxEstimateResponseZod,
|
|
2747
|
+
calculateMaxBridgeAmount
|
|
2748
|
+
} from "@xyo-network/chain-bridge-shared";
|
|
2749
|
+
import { buildUnsignedTransaction as buildUnsignedTransaction3, toXL1BlockNumber as toXL1BlockNumber3 } from "@xyo-network/xl1-sdk";
|
|
2750
|
+
import { z as z9 } from "zod/mini";
|
|
2751
|
+
var makeBridgeToRemoteMaxEstimateRoute = (config, gateway) => {
|
|
2752
|
+
const params = z9.object({ chainId: getRemoteChainIdZod(config) });
|
|
2753
|
+
const validateRequest2 = requestHandlerValidator9({
|
|
2057
2754
|
params,
|
|
2058
2755
|
body: BridgeToRemoteMaxEstimateBodyZod,
|
|
2059
2756
|
response: BridgeToRemoteMaxEstimateResponseZod
|
|
@@ -2062,66 +2759,135 @@ var makeBridgeToRemoteMaxEstimateRoute = /* @__PURE__ */ __name((config, gateway
|
|
|
2062
2759
|
method: "post",
|
|
2063
2760
|
path: "/bridge/chains/:chainId/bridgeToRemote/maxEstimate",
|
|
2064
2761
|
handlers: validateRequest2(async (req, res) => {
|
|
2065
|
-
const {
|
|
2066
|
-
const { srcAddress, srcAmount: balance, destAddress } = req.body;
|
|
2067
|
-
const balanceMax = calculateMaxBridgeAmount(balance, {
|
|
2762
|
+
const {
|
|
2068
2763
|
feeFixed,
|
|
2069
|
-
feeRateBasisPoints
|
|
2070
|
-
|
|
2071
|
-
|
|
2764
|
+
feeRateBasisPoints,
|
|
2765
|
+
maxBridgeAmount: configMax,
|
|
2766
|
+
xl1ChainId
|
|
2767
|
+
} = await getBridgeSettings(config);
|
|
2768
|
+
const {
|
|
2769
|
+
srcAddress,
|
|
2770
|
+
srcAmount: balance,
|
|
2771
|
+
destAddress
|
|
2772
|
+
} = req.body;
|
|
2773
|
+
const balanceMax = calculateMaxBridgeAmount(balance, { feeFixed, feeRateBasisPoints });
|
|
2774
|
+
const maxBridgeAmount = toHex4(hexToBigInt13(balanceMax) < hexToBigInt13(configMax) ? balanceMax : configMax);
|
|
2072
2775
|
const [bridgeIntent, transfer] = await generateBridgeEstimate(srcAddress, maxBridgeAmount, destAddress, config);
|
|
2073
2776
|
const sender = toAddress4(srcAddress);
|
|
2074
|
-
const viewer =
|
|
2777
|
+
const viewer = assertEx28(gateway.connection.viewer, () => new Error("Viewer not available on gateway connection"));
|
|
2075
2778
|
const currentBlockNumber = await viewer.currentBlockNumber();
|
|
2076
|
-
const nbf =
|
|
2077
|
-
const exp =
|
|
2078
|
-
const [txBw] = await
|
|
2079
|
-
|
|
2080
|
-
], [
|
|
2081
|
-
bridgeIntent
|
|
2082
|
-
], nbf, exp, sender);
|
|
2083
|
-
res.json([
|
|
2084
|
-
txBw,
|
|
2085
|
-
bridgeIntent,
|
|
2086
|
-
transfer
|
|
2087
|
-
]);
|
|
2779
|
+
const nbf = toXL1BlockNumber3(currentBlockNumber, true);
|
|
2780
|
+
const exp = toXL1BlockNumber3(currentBlockNumber + 1e3, true);
|
|
2781
|
+
const [txBw] = await buildUnsignedTransaction3(xl1ChainId, [transfer], [bridgeIntent], nbf, exp, sender);
|
|
2782
|
+
res.json([txBw, bridgeIntent, transfer]);
|
|
2088
2783
|
})
|
|
2089
2784
|
};
|
|
2090
|
-
}
|
|
2785
|
+
};
|
|
2091
2786
|
|
|
2092
|
-
// src/server/routes/bridge/routeDefinitions/routes/
|
|
2093
|
-
import { requestHandlerValidator as
|
|
2094
|
-
import {
|
|
2095
|
-
import {
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
var BridgeIntentResponseZod2 = PayloadZodStrictOfSchema5(BridgeIntentSchema8).extend(BridgeIntentFieldsZod5.shape);
|
|
2099
|
-
var BridgeSourceResponseZod2 = PayloadZodStrictOfSchema5(BridgeSourceObservationSchema4).extend(BridgeSourceObservationFieldsZod3.shape);
|
|
2100
|
-
var BridgeDestinationResponseZod2 = PayloadZodStrictOfSchema5(BridgeDestinationObservationSchema2).extend(BridgeDestinationObservationFieldsZod2.shape);
|
|
2101
|
-
var BridgeToRemoteStatusResponseZod = z5.union([
|
|
2102
|
-
z5.tuple([]),
|
|
2103
|
-
z5.tuple([
|
|
2104
|
-
BridgeIntentResponseZod2
|
|
2105
|
-
]),
|
|
2106
|
-
z5.tuple([
|
|
2107
|
-
BridgeIntentResponseZod2,
|
|
2108
|
-
BridgeSourceResponseZod2
|
|
2109
|
-
]),
|
|
2110
|
-
z5.tuple([
|
|
2111
|
-
BridgeIntentResponseZod2,
|
|
2112
|
-
BridgeSourceResponseZod2,
|
|
2113
|
-
BridgeDestinationResponseZod2
|
|
2114
|
-
])
|
|
2115
|
-
]);
|
|
2116
|
-
var makeBridgeToRemoteStatusRoute = /* @__PURE__ */ __name((config) => {
|
|
2117
|
-
const params = z5.object({
|
|
2787
|
+
// src/server/routes/bridge/routeDefinitions/routes/bridgeToRemoteRetry.ts
|
|
2788
|
+
import { requestHandlerValidator as requestHandlerValidator10 } from "@xylabs/express";
|
|
2789
|
+
import { BridgeRetryBodyZod as BridgeRetryBodyZod2, BridgeRetryResponseZod as BridgeRetryResponseZod2 } from "@xyo-network/chain-bridge-shared";
|
|
2790
|
+
import { z as z10 } from "zod/mini";
|
|
2791
|
+
var makeBridgeToRemoteRetryRoute = (config) => {
|
|
2792
|
+
const params = z10.object({
|
|
2118
2793
|
chainId: getRemoteChainIdZod(config),
|
|
2119
|
-
nonce:
|
|
2794
|
+
nonce: z10.string().check(z10.minLength(1))
|
|
2120
2795
|
});
|
|
2121
|
-
const validateRequest2 =
|
|
2796
|
+
const validateRequest2 = requestHandlerValidator10({
|
|
2797
|
+
params,
|
|
2798
|
+
body: BridgeRetryBodyZod2,
|
|
2799
|
+
response: BridgeRetryResponseZod2
|
|
2800
|
+
});
|
|
2801
|
+
return {
|
|
2802
|
+
method: "post",
|
|
2803
|
+
path: "/bridge/chains/:chainId/bridgeToRemote/retry/:nonce",
|
|
2804
|
+
handlers: validateRequest2(async (req, res) => {
|
|
2805
|
+
const jobId = req.params.nonce;
|
|
2806
|
+
const queues = getXl1ToEthQueues(config);
|
|
2807
|
+
const result = await retrySingleFailedJob({
|
|
2808
|
+
dryRun: req.body.dryRun,
|
|
2809
|
+
jobId,
|
|
2810
|
+
queue: queues.ethTransactionMonitor
|
|
2811
|
+
});
|
|
2812
|
+
if (result.status === "retried" && !req.body.dryRun) {
|
|
2813
|
+
req.app.services.bridgeFlowMetrics.recordRetried({ direction: "outbound", trigger: "api_singular" });
|
|
2814
|
+
}
|
|
2815
|
+
const httpStatus = result.status === "not-found" ? 404 : 200;
|
|
2816
|
+
res.status(httpStatus).json(result);
|
|
2817
|
+
})
|
|
2818
|
+
};
|
|
2819
|
+
};
|
|
2820
|
+
|
|
2821
|
+
// src/server/routes/bridge/routeDefinitions/routes/bridgeToRemoteRetryFailed.ts
|
|
2822
|
+
import { requestHandlerValidator as requestHandlerValidator11 } from "@xylabs/express";
|
|
2823
|
+
import { BridgeRetryFailedBodyZod as BridgeRetryFailedBodyZod2, BridgeRetryFailedResponseZod as BridgeRetryFailedResponseZod2 } from "@xyo-network/chain-bridge-shared";
|
|
2824
|
+
import { isBridgeIntent as isBridgeIntent6 } from "@xyo-network/xl1-sdk";
|
|
2825
|
+
import { StatusCodes as StatusCodes2 } from "http-status-codes";
|
|
2826
|
+
import { z as z11 } from "zod/mini";
|
|
2827
|
+
var makeBridgeToRemoteRetryFailedRoute = (config) => {
|
|
2828
|
+
const params = z11.object({ chainId: getRemoteChainIdZod(config) });
|
|
2829
|
+
const validateRequest2 = requestHandlerValidator11({
|
|
2122
2830
|
params,
|
|
2123
|
-
|
|
2831
|
+
body: BridgeRetryFailedBodyZod2,
|
|
2832
|
+
response: BridgeRetryFailedResponseZod2
|
|
2833
|
+
});
|
|
2834
|
+
return {
|
|
2835
|
+
method: "post",
|
|
2836
|
+
path: "/bridge/chains/:chainId/bridgeToRemote/retryFailed",
|
|
2837
|
+
handlers: validateRequest2(async (req, res) => {
|
|
2838
|
+
const filters = req.body;
|
|
2839
|
+
if (!hasAnyFilter(filters)) {
|
|
2840
|
+
const err = new Error(
|
|
2841
|
+
`At least one filter is required to prevent accidental retry-everything. Available filters: ${AVAILABLE_BULK_FILTERS.join(", ")}.`
|
|
2842
|
+
);
|
|
2843
|
+
err.name = "Bad Request";
|
|
2844
|
+
err.statusCode = StatusCodes2.BAD_REQUEST;
|
|
2845
|
+
throw err;
|
|
2846
|
+
}
|
|
2847
|
+
const queues = getXl1ToEthQueues(config);
|
|
2848
|
+
const srcAddressLookup = async (jobId) => {
|
|
2849
|
+
const job = await queues.ethTransactionMonitor.getJob(jobId);
|
|
2850
|
+
const data = job?.data;
|
|
2851
|
+
const bridgeIntent = data?.tx[1].find(isBridgeIntent6);
|
|
2852
|
+
return bridgeIntent?.srcAddress;
|
|
2853
|
+
};
|
|
2854
|
+
const result = await retryFailedJobs({
|
|
2855
|
+
filters,
|
|
2856
|
+
queue: queues.ethTransactionMonitor,
|
|
2857
|
+
srcAddressLookup
|
|
2858
|
+
});
|
|
2859
|
+
if (!filters.dryRun && result.counts.retried > 0) {
|
|
2860
|
+
req.app.services.bridgeFlowMetrics.recordRetried({
|
|
2861
|
+
count: result.counts.retried,
|
|
2862
|
+
direction: "outbound",
|
|
2863
|
+
trigger: "api_bulk"
|
|
2864
|
+
});
|
|
2865
|
+
}
|
|
2866
|
+
res.json(result);
|
|
2867
|
+
})
|
|
2868
|
+
};
|
|
2869
|
+
};
|
|
2870
|
+
|
|
2871
|
+
// src/server/routes/bridge/routeDefinitions/routes/bridgeToRemoteStatus.ts
|
|
2872
|
+
import { requestHandlerValidator as requestHandlerValidator12 } from "@xylabs/express";
|
|
2873
|
+
import { asHex as asHex4, isDefined as isDefined17 } from "@xylabs/sdk-js";
|
|
2874
|
+
import { BridgeToRemoteStatusResponseZod } from "@xyo-network/chain-bridge-shared";
|
|
2875
|
+
import { PayloadBuilder as PayloadBuilder17 } from "@xyo-network/sdk-js";
|
|
2876
|
+
import {
|
|
2877
|
+
asBridgeIntent,
|
|
2878
|
+
BridgeDestinationObservationFieldsZod as BridgeDestinationObservationFieldsZod2,
|
|
2879
|
+
BridgeDestinationObservationSchema as BridgeDestinationObservationSchema2,
|
|
2880
|
+
BridgeSourceObservationFieldsZod as BridgeSourceObservationFieldsZod3,
|
|
2881
|
+
BridgeSourceObservationSchema as BridgeSourceObservationSchema4,
|
|
2882
|
+
isBridgeIntent as isBridgeIntent7
|
|
2883
|
+
} from "@xyo-network/xl1-sdk";
|
|
2884
|
+
import { z as z12 } from "zod/mini";
|
|
2885
|
+
var makeBridgeToRemoteStatusRoute = (config) => {
|
|
2886
|
+
const params = z12.object({
|
|
2887
|
+
chainId: getRemoteChainIdZod(config),
|
|
2888
|
+
nonce: z12.string().check(z12.minLength(1))
|
|
2124
2889
|
});
|
|
2890
|
+
const validateRequest2 = requestHandlerValidator12({ params, response: BridgeToRemoteStatusResponseZod });
|
|
2125
2891
|
return {
|
|
2126
2892
|
method: "get",
|
|
2127
2893
|
path: "/bridge/chains/:chainId/bridgeToRemote/status/:nonce",
|
|
@@ -2132,18 +2898,15 @@ var makeBridgeToRemoteStatusRoute = /* @__PURE__ */ __name((config) => {
|
|
|
2132
2898
|
const statusQueueJobs = await getStatusQueueJobs(queues, jobId);
|
|
2133
2899
|
const { tx, offChainPayloads = [] } = statusQueueJobs.xl1ToEthBridgeParentJob?.data ?? {};
|
|
2134
2900
|
if (!tx) return res.sendStatus(404);
|
|
2135
|
-
const allPayloads = [
|
|
2136
|
-
|
|
2137
|
-
...offChainPayloads
|
|
2138
|
-
];
|
|
2139
|
-
const bridgeIntent = allPayloads.find(isBridgeIntent5);
|
|
2901
|
+
const allPayloads = [...tx[1], ...offChainPayloads];
|
|
2902
|
+
const bridgeIntent = allPayloads.find(isBridgeIntent7);
|
|
2140
2903
|
if (!bridgeIntent) return res.sendStatus(404);
|
|
2141
2904
|
result[0] = asBridgeIntent(PayloadBuilder17.omitMeta(bridgeIntent));
|
|
2142
2905
|
const { xl1TransactionMonitorJob } = statusQueueJobs;
|
|
2143
2906
|
const xl1MonitorState = xl1TransactionMonitorJob ? await xl1TransactionMonitorJob.getState() : void 0;
|
|
2144
2907
|
if (xl1MonitorState === "completed") {
|
|
2145
2908
|
const srcConfirmation = xl1TransactionMonitorJob?.returnvalue?.submissionHash;
|
|
2146
|
-
if (
|
|
2909
|
+
if (isDefined17(srcConfirmation)) {
|
|
2147
2910
|
const schema = BridgeSourceObservationSchema4;
|
|
2148
2911
|
const bridgeSourceObservationFields = BridgeSourceObservationFieldsZod3.parse(bridgeIntent);
|
|
2149
2912
|
const observation = {
|
|
@@ -2158,7 +2921,7 @@ var makeBridgeToRemoteStatusRoute = /* @__PURE__ */ __name((config) => {
|
|
|
2158
2921
|
const ethMonitorState = ethTransactionMonitorJob ? await ethTransactionMonitorJob.getState() : void 0;
|
|
2159
2922
|
if (ethMonitorState === "completed") {
|
|
2160
2923
|
const destConfirmation = asHex4(ethTransactionMonitorJob?.returnvalue?.submissionHash);
|
|
2161
|
-
if (
|
|
2924
|
+
if (isDefined17(destConfirmation)) {
|
|
2162
2925
|
const schema = BridgeDestinationObservationSchema2;
|
|
2163
2926
|
const bridgeDestinationObservationFields = BridgeDestinationObservationFieldsZod2.parse(bridgeIntent);
|
|
2164
2927
|
const observation = {
|
|
@@ -2172,36 +2935,42 @@ var makeBridgeToRemoteStatusRoute = /* @__PURE__ */ __name((config) => {
|
|
|
2172
2935
|
res.json(result);
|
|
2173
2936
|
})
|
|
2174
2937
|
};
|
|
2175
|
-
}
|
|
2938
|
+
};
|
|
2176
2939
|
|
|
2177
2940
|
// src/server/routes/bridge/routeDefinitions/getRouteDefinitions.ts
|
|
2178
|
-
var getRouteDefinitions =
|
|
2941
|
+
var getRouteDefinitions = (config, gateway) => {
|
|
2179
2942
|
return [
|
|
2180
2943
|
makeBridgeConfigRoute(config),
|
|
2181
2944
|
makeBridgeToRemoteEstimateRoute(config, gateway),
|
|
2182
2945
|
makeBridgeToRemoteMaxEstimateRoute(config, gateway),
|
|
2183
2946
|
makeBridgeToRemoteRoute(config),
|
|
2947
|
+
makeBridgeToRemoteRetryRoute(config),
|
|
2948
|
+
makeBridgeToRemoteRetryFailedRoute(config),
|
|
2184
2949
|
makeBridgeToRemoteStatusRoute(config),
|
|
2185
|
-
|
|
2950
|
+
makeBridgeFromRemoteEstimateRoute(config),
|
|
2951
|
+
makeBridgeFromRemoteRetryRoute(config),
|
|
2952
|
+
makeBridgeFromRemoteRetryFailedRoute(config),
|
|
2953
|
+
makeBridgeFromRemoteStatusRoute(config),
|
|
2954
|
+
makeBridgeFromRemoteStatusByTxRoute(config)
|
|
2186
2955
|
];
|
|
2187
|
-
}
|
|
2956
|
+
};
|
|
2188
2957
|
|
|
2189
2958
|
// src/server/routes/bridge/addBridgeRoutes.ts
|
|
2190
|
-
var addBridgeRoutes =
|
|
2959
|
+
var addBridgeRoutes = (app, config, gateway) => {
|
|
2191
2960
|
const routeDefinitions = getRouteDefinitions(config, gateway);
|
|
2192
2961
|
for (const definition of routeDefinitions) {
|
|
2193
2962
|
app[definition.method](definition.path, definition.handlers);
|
|
2194
2963
|
}
|
|
2195
|
-
}
|
|
2964
|
+
};
|
|
2196
2965
|
|
|
2197
2966
|
// src/server/routes/addRoutes.ts
|
|
2198
|
-
var addRoutes =
|
|
2967
|
+
var addRoutes = (app, config, gateway) => {
|
|
2199
2968
|
addProbeRoutes(app);
|
|
2200
2969
|
addBridgeRoutes(app, config, gateway);
|
|
2201
|
-
}
|
|
2970
|
+
};
|
|
2202
2971
|
|
|
2203
2972
|
// src/server/app.ts
|
|
2204
|
-
var getApp =
|
|
2973
|
+
var getApp = (config, gateway) => {
|
|
2205
2974
|
addInstrumentation();
|
|
2206
2975
|
const app = express();
|
|
2207
2976
|
sharedMiddleware(app);
|
|
@@ -2209,22 +2978,28 @@ var getApp = /* @__PURE__ */ __name((config, gateway) => {
|
|
|
2209
2978
|
addRoutes(app, config, gateway);
|
|
2210
2979
|
app.use(standardErrors);
|
|
2211
2980
|
return app;
|
|
2212
|
-
}
|
|
2981
|
+
};
|
|
2213
2982
|
|
|
2214
2983
|
// src/services/getServices.ts
|
|
2215
|
-
import { assertEx as
|
|
2984
|
+
import { assertEx as assertEx29 } from "@xylabs/sdk-js";
|
|
2216
2985
|
import { initEvmProvider, resolveWalletForActor as resolveWalletForActor2 } from "@xyo-network/chain-orchestration";
|
|
2217
2986
|
import { BridgeableToken__factory, LiquidityPoolBridge__factory } from "@xyo-network/typechain";
|
|
2218
|
-
import { getAddress as
|
|
2987
|
+
import { getAddress as getAddress3, Wallet } from "ethers";
|
|
2219
2988
|
|
|
2220
2989
|
// src/services/getIterableMap.ts
|
|
2221
2990
|
import { BaseMongoSdk } from "@xylabs/mongo";
|
|
2222
2991
|
import { MongoMap } from "@xyo-network/chain-protocol-driver-mongodb";
|
|
2223
2992
|
import { hasMongoConfig, mapToMapType } from "@xyo-network/xl1-sdk";
|
|
2224
|
-
var getIterableMap =
|
|
2993
|
+
var getIterableMap = async (config, collection) => {
|
|
2225
2994
|
const mongoConfig = config.storage?.mongo;
|
|
2226
2995
|
if (hasMongoConfig(mongoConfig)) {
|
|
2227
|
-
const {
|
|
2996
|
+
const {
|
|
2997
|
+
connectionString: dbConnectionString,
|
|
2998
|
+
database: dbName,
|
|
2999
|
+
domain: dbDomain,
|
|
3000
|
+
password: dbPassword,
|
|
3001
|
+
username: dbUserName
|
|
3002
|
+
} = mongoConfig;
|
|
2228
3003
|
const payloadSdkConfig = {
|
|
2229
3004
|
dbConnectionString,
|
|
2230
3005
|
dbDomain,
|
|
@@ -2232,237 +3007,98 @@ var getIterableMap = /* @__PURE__ */ __name(async (config, collection) => {
|
|
|
2232
3007
|
dbPassword,
|
|
2233
3008
|
dbUserName
|
|
2234
3009
|
};
|
|
2235
|
-
const sdkBalanceSummaryMap = new BaseMongoSdk({
|
|
2236
|
-
...payloadSdkConfig,
|
|
2237
|
-
collection
|
|
2238
|
-
});
|
|
3010
|
+
const sdkBalanceSummaryMap = new BaseMongoSdk({ ...payloadSdkConfig, collection });
|
|
2239
3011
|
const result = await MongoMap.create({
|
|
2240
3012
|
sdk: sdkBalanceSummaryMap,
|
|
2241
|
-
getCache: {
|
|
2242
|
-
enabled: true,
|
|
2243
|
-
maxEntries: 5e3
|
|
2244
|
-
}
|
|
3013
|
+
getCache: { enabled: true, maxEntries: 5e3 }
|
|
2245
3014
|
});
|
|
2246
3015
|
return result;
|
|
2247
3016
|
} else {
|
|
2248
3017
|
return mapToMapType(/* @__PURE__ */ new Map());
|
|
2249
3018
|
}
|
|
2250
|
-
}
|
|
3019
|
+
};
|
|
2251
3020
|
|
|
2252
3021
|
// src/services/getServices.ts
|
|
2253
|
-
var getServices =
|
|
3022
|
+
var getServices = async (context, gateway, logger, meter) => {
|
|
2254
3023
|
const { config } = context;
|
|
2255
3024
|
const ethTxStateMap = await getIterableMap(config, "liquidity_bridge_xl1_to_eth_eth_tx_state");
|
|
2256
3025
|
const xl1TxStateMap = await getIterableMap(config, "liquidity_bridge_xl1_to_eth_xl1_tx_state");
|
|
2257
3026
|
const evmBridgeCursorMap = await getIterableMap(config, "liquidity_bridge_eth_to_xl1_scanner_cursor");
|
|
2258
3027
|
const bridgeFulfillmentMap = await getIterableMap(config, "liquidity_bridge_eth_to_xl1_fulfillment");
|
|
2259
3028
|
const provider = await initEvmProvider(context);
|
|
2260
|
-
const {
|
|
3029
|
+
const {
|
|
3030
|
+
remoteBridgeContractAddress,
|
|
3031
|
+
remoteChainWalletPrivateKey,
|
|
3032
|
+
remoteTokenAddress,
|
|
3033
|
+
accountPath
|
|
3034
|
+
} = config;
|
|
2261
3035
|
const account = await resolveWalletForActor2(config.name, accountPath);
|
|
2262
3036
|
const wallet = new Wallet(remoteChainWalletPrivateKey, provider);
|
|
2263
|
-
const bridgeableToken = BridgeableToken__factory.connect(
|
|
2264
|
-
const bridge = LiquidityPoolBridge__factory.connect(
|
|
3037
|
+
const bridgeableToken = BridgeableToken__factory.connect(getAddress3(remoteTokenAddress), wallet);
|
|
3038
|
+
const bridge = LiquidityPoolBridge__factory.connect(getAddress3(remoteBridgeContractAddress), wallet);
|
|
2265
3039
|
const bridgeOwner = await bridge.owner();
|
|
2266
|
-
|
|
3040
|
+
assertEx29(bridgeOwner.toLowerCase() === wallet.address.toLowerCase(), () => "Wallet is not the owner of the bridge contract");
|
|
2267
3041
|
const remoteConfirmationDepth = getRemoteConfirmationDepth(config);
|
|
3042
|
+
const evmBridgeDeployBlock = await resolveDeployBlock({
|
|
3043
|
+
configuredDeployBlock: config.remoteBridgeContractDeployBlock,
|
|
3044
|
+
contractAddress: remoteBridgeContractAddress,
|
|
3045
|
+
provider
|
|
3046
|
+
});
|
|
3047
|
+
const bridgeFlowMetrics = createBridgeFlowMetrics({
|
|
3048
|
+
meter,
|
|
3049
|
+
remoteChainId: config.remoteChainId
|
|
3050
|
+
});
|
|
2268
3051
|
return {
|
|
2269
3052
|
account,
|
|
2270
3053
|
bridge,
|
|
3054
|
+
bridgeFlowMetrics,
|
|
2271
3055
|
bridgeableToken,
|
|
2272
3056
|
bridgeFulfillmentMap,
|
|
3057
|
+
config,
|
|
2273
3058
|
ethTxStateMap,
|
|
2274
3059
|
evmBridgeCursorMap,
|
|
3060
|
+
evmBridgeDeployBlock,
|
|
2275
3061
|
gateway,
|
|
3062
|
+
logger,
|
|
2276
3063
|
provider,
|
|
2277
3064
|
remoteConfirmationDepth,
|
|
2278
3065
|
wallet,
|
|
2279
3066
|
xl1TxStateMap
|
|
2280
3067
|
};
|
|
2281
|
-
}
|
|
3068
|
+
};
|
|
2282
3069
|
|
|
2283
3070
|
// src/server/addWorkers.ts
|
|
2284
|
-
var addWorkers =
|
|
3071
|
+
var addWorkers = (config, services) => {
|
|
2285
3072
|
const connection2 = getConnection(config);
|
|
2286
3073
|
const telemetry2 = getTelemetry();
|
|
2287
3074
|
createWorkers(connection2, telemetry2, services);
|
|
2288
|
-
}
|
|
3075
|
+
};
|
|
2289
3076
|
|
|
2290
3077
|
// src/server/server.ts
|
|
2291
3078
|
var hostname = "::";
|
|
2292
|
-
var getServer =
|
|
2293
|
-
const {
|
|
3079
|
+
var getServer = async (context, gateway, logger, meter) => {
|
|
3080
|
+
const { config } = context;
|
|
2294
3081
|
const { port } = config;
|
|
2295
3082
|
const app = getApp(config, gateway);
|
|
2296
|
-
const services = await getServices(context, gateway);
|
|
3083
|
+
const services = await getServices(context, gateway, logger, meter);
|
|
2297
3084
|
app.services = services;
|
|
2298
3085
|
addWorkers(config, services);
|
|
2299
3086
|
const server = await new Promise((resolve, reject) => {
|
|
2300
3087
|
const srv = app.listen(port, hostname, () => {
|
|
2301
|
-
logger
|
|
3088
|
+
logger.log(`[Bridge] Server listening at http://${hostname}:${port}`);
|
|
2302
3089
|
resolve(srv);
|
|
2303
3090
|
});
|
|
2304
3091
|
srv.once("error", reject);
|
|
2305
3092
|
});
|
|
2306
3093
|
server.setTimeout(2e4);
|
|
2307
|
-
return {
|
|
2308
|
-
|
|
2309
|
-
services
|
|
2310
|
-
};
|
|
2311
|
-
}, "getServer");
|
|
2312
|
-
|
|
2313
|
-
// src/telemetry/createBalanceMonitor.ts
|
|
2314
|
-
var DEFAULT_INTERVAL_MS = 6e4;
|
|
2315
|
-
var WEI_PER_GWEI = 10n ** 9n;
|
|
2316
|
-
var TOKEN_DECIMALS = 10n ** 18n;
|
|
2317
|
-
function createBalanceMonitor(config) {
|
|
2318
|
-
const { account, bridge, bridgeableToken, gateway, meter, provider, wallet, intervalMs = DEFAULT_INTERVAL_MS } = config;
|
|
2319
|
-
let timer;
|
|
2320
|
-
const ethWalletGasBalance = meter.createGauge("bridge_eth_wallet_gas_balance_gwei", {
|
|
2321
|
-
description: "ETH balance of the bridge runner wallet (in gwei)",
|
|
2322
|
-
unit: "gwei"
|
|
2323
|
-
});
|
|
2324
|
-
const liquidityTokenBalance = meter.createGauge("bridge_eth_liquidity_token_balance", {
|
|
2325
|
-
description: "ERC-20 token balance of the liquidity source (in whole tokens)",
|
|
2326
|
-
unit: "tokens"
|
|
2327
|
-
});
|
|
2328
|
-
const liquidityTokenAllowance = meter.createGauge("bridge_eth_liquidity_token_allowance", {
|
|
2329
|
-
description: "ERC-20 token allowance from liquidity source to bridge contract (in whole tokens)",
|
|
2330
|
-
unit: "tokens"
|
|
2331
|
-
});
|
|
2332
|
-
const xl1AccountBalance = meter.createGauge("bridge_xl1_account_balance", {
|
|
2333
|
-
description: "XL1 native balance of the bridge reserve account (in whole XL1)",
|
|
2334
|
-
unit: "xl1"
|
|
2335
|
-
});
|
|
2336
|
-
const walletAddress = wallet.address;
|
|
2337
|
-
async function poll() {
|
|
2338
|
-
try {
|
|
2339
|
-
const balance = await provider.getBalance(walletAddress);
|
|
2340
|
-
ethWalletGasBalance.record(Number(balance / WEI_PER_GWEI), {
|
|
2341
|
-
address: walletAddress
|
|
2342
|
-
});
|
|
2343
|
-
} catch (err) {
|
|
2344
|
-
console.error("[BalanceMonitor] Failed to read ETH wallet gas balance:", err);
|
|
2345
|
-
}
|
|
2346
|
-
try {
|
|
2347
|
-
const liquiditySourceAddress = await bridge.liquiditySource();
|
|
2348
|
-
const balance = await bridgeableToken.balanceOf(liquiditySourceAddress);
|
|
2349
|
-
liquidityTokenBalance.record(Number(balance / TOKEN_DECIMALS), {
|
|
2350
|
-
address: liquiditySourceAddress
|
|
2351
|
-
});
|
|
2352
|
-
} catch (err) {
|
|
2353
|
-
console.error("[BalanceMonitor] Failed to read liquidity source token balance:", err);
|
|
2354
|
-
}
|
|
2355
|
-
try {
|
|
2356
|
-
const liquiditySourceAddress = await bridge.liquiditySource();
|
|
2357
|
-
const bridgeAddress = await bridge.getAddress();
|
|
2358
|
-
const allowance = await bridgeableToken.allowance(liquiditySourceAddress, bridgeAddress);
|
|
2359
|
-
liquidityTokenAllowance.record(Number(allowance / TOKEN_DECIMALS), {
|
|
2360
|
-
address: liquiditySourceAddress
|
|
2361
|
-
});
|
|
2362
|
-
} catch (err) {
|
|
2363
|
-
console.error("[BalanceMonitor] Failed to read liquidity source token allowance:", err);
|
|
2364
|
-
}
|
|
2365
|
-
try {
|
|
2366
|
-
const viewer = gateway.connection.viewer;
|
|
2367
|
-
if (viewer) {
|
|
2368
|
-
const balance = await viewer.account.balance.accountBalance(account.address);
|
|
2369
|
-
xl1AccountBalance.record(Number(balance / TOKEN_DECIMALS), {
|
|
2370
|
-
address: account.address.toString()
|
|
2371
|
-
});
|
|
2372
|
-
}
|
|
2373
|
-
} catch (err) {
|
|
2374
|
-
console.error("[BalanceMonitor] Failed to read XL1 account balance:", err);
|
|
2375
|
-
}
|
|
2376
|
-
}
|
|
2377
|
-
__name(poll, "poll");
|
|
2378
|
-
return {
|
|
2379
|
-
start() {
|
|
2380
|
-
void poll();
|
|
2381
|
-
timer = setInterval(() => void poll(), intervalMs);
|
|
2382
|
-
},
|
|
2383
|
-
stop() {
|
|
2384
|
-
if (timer) {
|
|
2385
|
-
clearInterval(timer);
|
|
2386
|
-
timer = void 0;
|
|
2387
|
-
}
|
|
2388
|
-
}
|
|
2389
|
-
};
|
|
2390
|
-
}
|
|
2391
|
-
__name(createBalanceMonitor, "createBalanceMonitor");
|
|
2392
|
-
|
|
2393
|
-
// src/telemetry/createQueueMetrics.ts
|
|
2394
|
-
var DEFAULT_INTERVAL_MS2 = 3e4;
|
|
2395
|
-
function createQueueMetrics(config) {
|
|
2396
|
-
const { meter, queues, intervalMs = DEFAULT_INTERVAL_MS2 } = config;
|
|
2397
|
-
let timer;
|
|
2398
|
-
const waitingGauge = meter.createGauge("bridge_queue_waiting", {
|
|
2399
|
-
description: "Number of waiting jobs in the bridge queue"
|
|
2400
|
-
});
|
|
2401
|
-
const activeGauge = meter.createGauge("bridge_queue_active", {
|
|
2402
|
-
description: "Number of active jobs in the bridge queue"
|
|
2403
|
-
});
|
|
2404
|
-
const completedGauge = meter.createGauge("bridge_queue_completed", {
|
|
2405
|
-
description: "Number of completed jobs in the bridge queue"
|
|
2406
|
-
});
|
|
2407
|
-
const failedGauge = meter.createGauge("bridge_queue_failed", {
|
|
2408
|
-
description: "Number of failed jobs in the bridge queue"
|
|
2409
|
-
});
|
|
2410
|
-
const delayedGauge = meter.createGauge("bridge_queue_delayed", {
|
|
2411
|
-
description: "Number of delayed jobs in the bridge queue"
|
|
2412
|
-
});
|
|
2413
|
-
async function poll() {
|
|
2414
|
-
for (const [name13, queue] of Object.entries(queues)) {
|
|
2415
|
-
try {
|
|
2416
|
-
const counts = await queue.getJobCounts("waiting", "active", "completed", "failed", "delayed");
|
|
2417
|
-
const attrs = {
|
|
2418
|
-
queue_name: name13
|
|
2419
|
-
};
|
|
2420
|
-
waitingGauge.record(counts.waiting ?? 0, attrs);
|
|
2421
|
-
activeGauge.record(counts.active ?? 0, attrs);
|
|
2422
|
-
completedGauge.record(counts.completed ?? 0, attrs);
|
|
2423
|
-
failedGauge.record(counts.failed ?? 0, attrs);
|
|
2424
|
-
delayedGauge.record(counts.delayed ?? 0, attrs);
|
|
2425
|
-
} catch (err) {
|
|
2426
|
-
console.error(`[QueueMetrics] Failed to read job counts for queue ${name13}:`, err);
|
|
2427
|
-
}
|
|
2428
|
-
}
|
|
2429
|
-
}
|
|
2430
|
-
__name(poll, "poll");
|
|
2431
|
-
return {
|
|
2432
|
-
start() {
|
|
2433
|
-
void poll();
|
|
2434
|
-
timer = setInterval(() => void poll(), intervalMs);
|
|
2435
|
-
},
|
|
2436
|
-
stop() {
|
|
2437
|
-
if (timer) {
|
|
2438
|
-
clearInterval(timer);
|
|
2439
|
-
timer = void 0;
|
|
2440
|
-
}
|
|
2441
|
-
}
|
|
2442
|
-
};
|
|
2443
|
-
}
|
|
2444
|
-
__name(createQueueMetrics, "createQueueMetrics");
|
|
3094
|
+
return { server, services };
|
|
3095
|
+
};
|
|
2445
3096
|
|
|
2446
3097
|
// src/BridgeActor.ts
|
|
2447
|
-
function _ts_decorate(decorators, target, key, desc) {
|
|
2448
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2449
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2450
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2451
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2452
|
-
}
|
|
2453
|
-
__name(_ts_decorate, "_ts_decorate");
|
|
2454
3098
|
var BridgeActor = class extends ActorV3 {
|
|
2455
|
-
static {
|
|
2456
|
-
__name(this, "BridgeActor");
|
|
2457
|
-
}
|
|
2458
|
-
static needs = {
|
|
2459
|
-
required: [
|
|
2460
|
-
XyoGatewayRunnerMoniker,
|
|
2461
|
-
XyoViewerMoniker
|
|
2462
|
-
]
|
|
2463
|
-
};
|
|
2464
3099
|
_gatewayRunner;
|
|
2465
3100
|
_balanceMonitor;
|
|
3101
|
+
_inFlightPoller;
|
|
2466
3102
|
_queueMetrics;
|
|
2467
3103
|
_scannerRunner;
|
|
2468
3104
|
server;
|
|
@@ -2483,15 +3119,17 @@ var BridgeActor = class extends ActorV3 {
|
|
|
2483
3119
|
await this._scannerRunner?.stop();
|
|
2484
3120
|
this._scannerRunner = void 0;
|
|
2485
3121
|
this._balanceMonitor?.stop();
|
|
3122
|
+
this._inFlightPoller?.stop();
|
|
2486
3123
|
this._queueMetrics?.stop();
|
|
2487
3124
|
this._balanceMonitor = void 0;
|
|
3125
|
+
this._inFlightPoller = void 0;
|
|
2488
3126
|
this._queueMetrics = void 0;
|
|
2489
3127
|
this.stopServer();
|
|
2490
3128
|
}
|
|
2491
3129
|
async startServer() {
|
|
2492
3130
|
const context = asBridgeConfigContext(this.context, true);
|
|
2493
3131
|
const config = context.config;
|
|
2494
|
-
const { server, services } = await getServer(context, this._gatewayRunner);
|
|
3132
|
+
const { server, services } = await getServer(context, this._gatewayRunner, this.logger, this.meter);
|
|
2495
3133
|
this.server = server;
|
|
2496
3134
|
const connection2 = getConnection(config);
|
|
2497
3135
|
const telemetry2 = getTelemetry();
|
|
@@ -2500,7 +3138,6 @@ var BridgeActor = class extends ActorV3 {
|
|
|
2500
3138
|
config,
|
|
2501
3139
|
connection: connection2,
|
|
2502
3140
|
flowProducer: flowProducer2,
|
|
2503
|
-
logger: this.logger,
|
|
2504
3141
|
services,
|
|
2505
3142
|
telemetry: telemetry2
|
|
2506
3143
|
});
|
|
@@ -2513,6 +3150,7 @@ var BridgeActor = class extends ActorV3 {
|
|
|
2513
3150
|
gateway: services.gateway,
|
|
2514
3151
|
meter: this.meter,
|
|
2515
3152
|
provider: services.provider,
|
|
3153
|
+
remoteChainId: config.remoteChainId,
|
|
2516
3154
|
wallet: services.wallet
|
|
2517
3155
|
});
|
|
2518
3156
|
this._balanceMonitor.start();
|
|
@@ -2522,9 +3160,16 @@ var BridgeActor = class extends ActorV3 {
|
|
|
2522
3160
|
};
|
|
2523
3161
|
this._queueMetrics = createQueueMetrics({
|
|
2524
3162
|
meter: this.meter,
|
|
2525
|
-
queues
|
|
3163
|
+
queues,
|
|
3164
|
+
remoteChainId: config.remoteChainId
|
|
2526
3165
|
});
|
|
2527
3166
|
this._queueMetrics.start();
|
|
3167
|
+
this._inFlightPoller = createInFlightPoller({
|
|
3168
|
+
bridgeFulfillmentMap: services.bridgeFulfillmentMap,
|
|
3169
|
+
meter: this.meter,
|
|
3170
|
+
remoteChainId: config.remoteChainId
|
|
3171
|
+
});
|
|
3172
|
+
this._inFlightPoller.start();
|
|
2528
3173
|
}
|
|
2529
3174
|
}
|
|
2530
3175
|
stopServer() {
|
|
@@ -2532,7 +3177,13 @@ var BridgeActor = class extends ActorV3 {
|
|
|
2532
3177
|
this.server = void 0;
|
|
2533
3178
|
}
|
|
2534
3179
|
};
|
|
2535
|
-
BridgeActor
|
|
3180
|
+
__publicField(BridgeActor, "needs", {
|
|
3181
|
+
required: [
|
|
3182
|
+
XyoGatewayRunnerMoniker,
|
|
3183
|
+
XyoViewerMoniker
|
|
3184
|
+
]
|
|
3185
|
+
});
|
|
3186
|
+
BridgeActor = __decorateClass([
|
|
2536
3187
|
creatable()
|
|
2537
3188
|
], BridgeActor);
|
|
2538
3189
|
|
|
@@ -2542,7 +3193,7 @@ import { BridgeConfigZod } from "@xyo-network/chain-orchestration";
|
|
|
2542
3193
|
// src/run.ts
|
|
2543
3194
|
import { exists } from "@xylabs/sdk-js";
|
|
2544
3195
|
import { initActorWallet } from "@xyo-network/chain-orchestration";
|
|
2545
|
-
var getBridgeActor =
|
|
3196
|
+
var getBridgeActor = async (config, locator) => {
|
|
2546
3197
|
const account = await initActorWallet({
|
|
2547
3198
|
config,
|
|
2548
3199
|
logger: locator.context.logger,
|
|
@@ -2554,17 +3205,15 @@ var getBridgeActor = /* @__PURE__ */ __name(async (config, locator) => {
|
|
|
2554
3205
|
config,
|
|
2555
3206
|
locator
|
|
2556
3207
|
});
|
|
2557
|
-
}
|
|
2558
|
-
var runBridge =
|
|
3208
|
+
};
|
|
3209
|
+
var runBridge = async (config, orchestrator, locator) => {
|
|
2559
3210
|
const bridge = await getBridgeActor(config, locator);
|
|
2560
|
-
const actors = [
|
|
2561
|
-
bridge
|
|
2562
|
-
].filter(exists);
|
|
3211
|
+
const actors = [bridge].filter(exists);
|
|
2563
3212
|
for (const actor of actors) {
|
|
2564
3213
|
await orchestrator.registerActor(actor);
|
|
2565
3214
|
}
|
|
2566
3215
|
await orchestrator.start();
|
|
2567
|
-
}
|
|
3216
|
+
};
|
|
2568
3217
|
|
|
2569
3218
|
// src/command.ts
|
|
2570
3219
|
function bridgeCommand(getConfiguration, getLocatorsFromConfig) {
|
|
@@ -2572,22 +3221,22 @@ function bridgeCommand(getConfiguration, getLocatorsFromConfig) {
|
|
|
2572
3221
|
command: "bridge",
|
|
2573
3222
|
deprecated: 'Use "start bridge" instead',
|
|
2574
3223
|
describe: "Run a XL1 Bridge Node",
|
|
2575
|
-
handler:
|
|
3224
|
+
handler: async () => {
|
|
2576
3225
|
const configuration = getConfiguration();
|
|
2577
|
-
const { locators, orchestrator } = await getLocatorsFromConfig([
|
|
2578
|
-
"bridge"
|
|
2579
|
-
], configuration);
|
|
3226
|
+
const { locators, orchestrator } = await getLocatorsFromConfig(["bridge"], configuration);
|
|
2580
3227
|
await runBridge(BridgeConfigZod.parse(locators["bridge"].context.config), orchestrator, locators["bridge"]);
|
|
2581
|
-
}
|
|
3228
|
+
}
|
|
2582
3229
|
};
|
|
2583
3230
|
}
|
|
2584
|
-
__name(bridgeCommand, "bridgeCommand");
|
|
2585
3231
|
export {
|
|
2586
3232
|
BridgeActor,
|
|
2587
3233
|
bridgeCommand,
|
|
3234
|
+
bucketFailureReason,
|
|
2588
3235
|
createBalanceMonitor,
|
|
3236
|
+
createBridgeFlowMetrics,
|
|
3237
|
+
createInFlightPoller,
|
|
2589
3238
|
createQueueMetrics,
|
|
2590
3239
|
getBridgeActor,
|
|
2591
3240
|
runBridge
|
|
2592
3241
|
};
|
|
2593
|
-
//# sourceMappingURL=index.mjs.map
|
|
3242
|
+
//# sourceMappingURL=index.mjs.map
|