@xyo-network/chain-bridge 1.19.17 → 1.19.18

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.
Files changed (64) hide show
  1. package/dist/node/index.mjs +145 -92
  2. package/dist/node/index.mjs.map +1 -1
  3. package/dist/node/queue/flowProducer.d.ts +2 -1
  4. package/dist/node/queue/flowProducer.d.ts.map +1 -1
  5. package/dist/node/queue/index.d.ts +1 -0
  6. package/dist/node/queue/index.d.ts.map +1 -1
  7. package/dist/node/queue/telemetry.d.ts +3 -0
  8. package/dist/node/queue/telemetry.d.ts.map +1 -0
  9. package/dist/node/queue/workers/EthTransactionMonitor.d.ts +9 -0
  10. package/dist/node/queue/workers/EthTransactionMonitor.d.ts.map +1 -1
  11. package/dist/node/queue/workers/EthTransactionPreparation.d.ts +7 -1
  12. package/dist/node/queue/workers/EthTransactionPreparation.d.ts.map +1 -1
  13. package/dist/node/queue/workers/EthTransactionSubmission.d.ts +8 -0
  14. package/dist/node/queue/workers/EthTransactionSubmission.d.ts.map +1 -1
  15. package/dist/node/queue/workers/WorkerDescription.d.ts +2 -1
  16. package/dist/node/queue/workers/WorkerDescription.d.ts.map +1 -1
  17. package/dist/node/queue/workers/Xl1ToEthBridgeParent.d.ts +3 -0
  18. package/dist/node/queue/workers/Xl1ToEthBridgeParent.d.ts.map +1 -1
  19. package/dist/node/queue/workers/Xl1TransactionMonitor.d.ts +6 -0
  20. package/dist/node/queue/workers/Xl1TransactionMonitor.d.ts.map +1 -1
  21. package/dist/node/queue/workers/Xl1TransactionPreparation.d.ts +7 -0
  22. package/dist/node/queue/workers/Xl1TransactionPreparation.d.ts.map +1 -1
  23. package/dist/node/queue/workers/Xl1TransactionSubmission.d.ts +8 -0
  24. package/dist/node/queue/workers/Xl1TransactionSubmission.d.ts.map +1 -1
  25. package/dist/node/queue/workers/createWorkers.d.ts +2 -1
  26. package/dist/node/queue/workers/createWorkers.d.ts.map +1 -1
  27. package/dist/node/queue/workers/util/AsyncLogger.d.ts +5 -0
  28. package/dist/node/queue/workers/util/AsyncLogger.d.ts.map +1 -0
  29. package/dist/node/queue/workers/util/index.d.ts +3 -2
  30. package/dist/node/queue/workers/util/index.d.ts.map +1 -1
  31. package/dist/node/queue/workers/util/{validateSufficientAllowance.d.ts → validateSufficientLiquiditySourceAllowance.d.ts} +3 -7
  32. package/dist/node/queue/workers/util/validateSufficientLiquiditySourceAllowance.d.ts.map +1 -0
  33. package/dist/node/queue/workers/util/{validateSufficientBalance.d.ts → validateSufficientLiquiditySourceBalance.d.ts} +3 -7
  34. package/dist/node/queue/workers/util/validateSufficientLiquiditySourceBalance.d.ts.map +1 -0
  35. package/dist/node/queue/workers/util/validateSufficientRunnerEthBalanceForGas.d.ts +14 -0
  36. package/dist/node/queue/workers/util/validateSufficientRunnerEthBalanceForGas.d.ts.map +1 -0
  37. package/dist/node/server/addFlowProducer.d.ts.map +1 -1
  38. package/dist/node/server/addWorkers.d.ts.map +1 -1
  39. package/package.json +26 -30
  40. package/src/config/getBridgeWalletAccount.ts +1 -1
  41. package/src/config/getGateway.ts +1 -1
  42. package/src/queue/flowProducer.ts +3 -2
  43. package/src/queue/index.ts +1 -0
  44. package/src/queue/telemetry.ts +12 -0
  45. package/src/queue/workers/EthTransactionMonitor.ts +10 -6
  46. package/src/queue/workers/EthTransactionPreparation.ts +17 -9
  47. package/src/queue/workers/EthTransactionSubmission.ts +8 -28
  48. package/src/queue/workers/WorkerDescription.ts +2 -1
  49. package/src/queue/workers/Xl1ToEthBridgeParent.ts +8 -5
  50. package/src/queue/workers/Xl1TransactionMonitor.ts +7 -5
  51. package/src/queue/workers/Xl1TransactionPreparation.ts +7 -7
  52. package/src/queue/workers/Xl1TransactionSubmission.ts +6 -5
  53. package/src/queue/workers/createWorkers.ts +9 -8
  54. package/src/queue/workers/util/AsyncLogger.ts +5 -0
  55. package/src/queue/workers/util/index.ts +3 -2
  56. package/src/queue/workers/util/{validateSufficientAllowance.ts → validateSufficientLiquiditySourceAllowance.ts} +3 -6
  57. package/src/queue/workers/util/{validateSufficientBalance.ts → validateSufficientLiquiditySourceBalance.ts} +3 -6
  58. package/src/queue/workers/util/validateSufficientRunnerEthBalanceForGas.ts +57 -0
  59. package/src/server/addFlowProducer.ts +5 -2
  60. package/src/server/addWorkers.ts +6 -2
  61. package/src/server/routes/bridge/routeDefinitions/routes/bridgeToRemoteStatus.ts +3 -3
  62. package/src/services/getServices.ts +1 -1
  63. package/dist/node/queue/workers/util/validateSufficientAllowance.d.ts.map +0 -1
  64. package/dist/node/queue/workers/util/validateSufficientBalance.d.ts.map +0 -1
@@ -3,21 +3,22 @@ import { PayloadBuilder } from '@xyo-network/sdk-js'
3
3
  import { type SignedHydratedTransaction } from '@xyo-network/xl1-sdk'
4
4
  import type { Job } from 'bullmq'
5
5
  import { Worker } from 'bullmq'
6
+ import type { BullMQOtel } from 'bullmq-otel'
6
7
  import type { Redis } from 'ioredis'
7
8
 
8
9
  import type { EthTxState, IBridgeServiceCollection } from '../../services/index.ts'
9
10
  import { submitEthTransaction } from './util/index.ts'
10
11
  import type { WorkerDescription } from './WorkerDescription.ts'
11
12
 
12
- type JobData = { tx: SignedHydratedTransaction }
13
+ export type EthTransactionSubmissionJobData = { tx: SignedHydratedTransaction }
13
14
 
14
- interface ReturnType {
15
+ export interface EthTransactionSubmissionJobReturn {
15
16
  submissionHash: Required<EthTxState>['submissionHash']
16
17
  }
17
18
 
18
19
  const name = 'Submit ETH Transaction'
19
20
  const queueName = 'eth-tx-submit'
20
- const createWorker = (connection: Redis, services?: IBridgeServiceCollection) => {
21
+ const createWorker = (connection: Redis, telemetry?: BullMQOtel, services?: IBridgeServiceCollection) => {
21
22
  const bridge = assertEx(services?.bridge, () => 'bridge service not provided')
22
23
  const bridgeableToken = assertEx(services?.bridgeableToken, () => 'bridgeableToken service not provided')
23
24
  const wallet = assertEx(services?.wallet, () => 'wallet service not provided')
@@ -25,11 +26,10 @@ const createWorker = (connection: Redis, services?: IBridgeServiceCollection) =>
25
26
 
26
27
  const worker = new Worker(
27
28
  queueName,
28
- async (job: Job<JobData, ReturnType>) => {
29
+ async (job: Job<EthTransactionSubmissionJobData, EthTransactionSubmissionJobReturn>) => {
29
30
  const { tx } = job.data
30
31
  const hash = await PayloadBuilder.hash(tx[0])
31
32
  const state = assertEx(await stateMap.get(hash), () => `[${hash}] state not found`)
32
- const preparedTx = assertEx(state?.preparedTx, () => `[${hash}] preparedTx not found`)
33
33
 
34
34
  // Idempotency check against resubmission
35
35
  const { submissionHash: existingSubmissionHash } = state
@@ -40,8 +40,6 @@ const createWorker = (connection: Redis, services?: IBridgeServiceCollection) =>
40
40
 
41
41
  // Submit the transaction to the Ethereum network
42
42
  await job.log(`[${hash}] Submitting ETH tx`)
43
- // const submissionResponse = await wallet.sendTransaction(preparedTx)
44
- // const submissionHash = submissionResponse.hash
45
43
 
46
44
  const submissionHash = assertEx(await submitEthTransaction(tx, bridgeableToken, bridge, wallet), () => `[${hash}] submissionHash not found in receipt`)
47
45
 
@@ -54,28 +52,10 @@ const createWorker = (connection: Redis, services?: IBridgeServiceCollection) =>
54
52
  await job.log(`[${hash}] Stored ETH submissionHash`)
55
53
 
56
54
  return { submissionHash }
57
- // const hash = await PayloadBuilder.hash(tx[0])
58
- // await job.log(`[${hash}] Obtaining bridge intent from tx`)
59
- // const bridgeIntent = assertEx(tx[1].find(isBridgeIntent), () => 'No bridge intent found in transaction payload')
60
- // await job.log(`[${hash}] submitting ETH transaction`)
61
- // const amount = hexToBigInt(bridgeIntent.destAmount)
62
- // const srcAddress = getAddress(bridgeIntent.srcAddress)
63
- // const destAddress = getAddress(bridgeIntent.destAddress)
64
- // const tx = await bridge.bridgeFromRemote(srcAddress, destAddress, amount)
65
- // // const nonce = await wallet.getNonce()
66
- // // const tx = await bridge.bridgeFromRemote(srcAddress, destAddress, amount, { nonce })
67
- // await job.log(`[${hash}] submitted ETH transaction`)
68
- // const confirmation = await tx.wait()
69
- // const transactionResponse = await confirmation?.getTransaction()
70
- // const destConfirmation = asHex(transactionResponse?.hash ?? '', true)
71
- // const block = await transactionResponse?.getBlock()
72
- // await job.log(`[${hash}] confirmed ETH transaction with hash ${destConfirmation} in block ${block?.number}`)
73
- // const { schema, ...rest } = bridgeIntent
74
- // const result: BridgeDestinationObservation = new PayloadBuilder<BridgeDestinationObservation>({ schema: BridgeDestinationObservationSchema })
75
- // .fields({ ...rest, destConfirmation }).build()
76
- // return result
77
55
  },
78
- { connection, concurrency: 1 },
56
+ {
57
+ connection, telemetry, concurrency: 1,
58
+ },
79
59
  )
80
60
 
81
61
  worker.on('failed', (job, err) => {
@@ -1,9 +1,10 @@
1
+ import type { BullMQOtel } from 'bullmq-otel'
1
2
  import type { Redis } from 'ioredis'
2
3
 
3
4
  import type { IBridgeServiceCollection } from '../../services/index.ts'
4
5
 
5
6
  export interface WorkerDescription {
6
- createWorker: (connection: Redis, services?: IBridgeServiceCollection) => void
7
+ createWorker: (connection: Redis, telemetry?: BullMQOtel, services?: IBridgeServiceCollection) => void
7
8
  name: string
8
9
  queueName: string
9
10
  }
@@ -1,22 +1,25 @@
1
1
  import type { Job } from 'bullmq'
2
2
  import { Worker } from 'bullmq'
3
+ import type { BullMQOtel } from 'bullmq-otel'
3
4
  import type { Redis } from 'ioredis'
4
5
 
5
6
  import type { WorkerDescription } from './WorkerDescription.ts'
6
7
 
8
+ export type Xl1ToEthBridgeParentJobData = {}
9
+ export interface Xl1ToEthBridgeParentJobReturn { }
10
+
7
11
  const name = 'Bridge XL1 to Ethereum'
8
12
  const queueName = 'xl1-to-eth-bridge'
9
- const createWorker = (connection: Redis) => {
13
+ const createWorker = (connection: Redis, telemetry?: BullMQOtel) => {
10
14
  const worker = new Worker(
11
15
  queueName,
12
- async (job: Job) => {
16
+ async (job: Job<Xl1ToEthBridgeParentJobData, Xl1ToEthBridgeParentJobReturn>) => {
13
17
  await job.log(`[${job.name}] start`)
14
18
  // Parent job has no work other than waiting on children
15
19
  await job.log(`[${job.name}] done`)
16
- // const values = await job.getChildrenValues()
17
- return { ok: true }
20
+ return {}
18
21
  },
19
- { connection },
22
+ { connection, telemetry },
20
23
  )
21
24
 
22
25
  worker.on('failed', (job, err) => {
@@ -5,25 +5,26 @@ import { PayloadBuilder } from '@xyo-network/sdk-js'
5
5
  import type { SignedHydratedTransaction } from '@xyo-network/xl1-sdk'
6
6
  import type { Job } from 'bullmq'
7
7
  import { UnrecoverableError, Worker } from 'bullmq'
8
+ import type { BullMQOtel } from 'bullmq-otel'
8
9
  import type { Redis } from 'ioredis'
9
10
 
10
11
  import type { IBridgeServiceCollection } from '../../services/index.ts'
11
12
  import type { WorkerDescription } from './WorkerDescription.ts'
12
13
 
13
- type JobData = { tx: SignedHydratedTransaction }
14
+ export type Xl1TransactionMonitorJobData = { tx: SignedHydratedTransaction }
14
15
 
15
- interface ReturnType {
16
+ export interface Xl1TransactionMonitorJobReturn {
16
17
  }
17
18
 
18
19
  const name = 'Monitor Submitted XL1 Transaction'
19
20
  const queueName = 'xl1-tx-monitor'
20
- const createWorker = (connection: Redis, services?: IBridgeServiceCollection) => {
21
+ const createWorker = (connection: Redis, telemetry?: BullMQOtel, services?: IBridgeServiceCollection) => {
21
22
  const gateway = assertEx(services?.gateway, () => 'gateway service not provided')
22
23
  const stateMap = assertEx(services?.xl1TxStateMap, () => 'xl1TxStateMap service not provided')
23
24
 
24
25
  const worker = new Worker(
25
26
  queueName,
26
- async (job: Job<JobData, ReturnType>) => {
27
+ async (job: Job<Xl1TransactionMonitorJobData, Xl1TransactionMonitorJobReturn>) => {
27
28
  const { tx } = job.data
28
29
  // Get the hash of the transaction
29
30
  const hash = await PayloadBuilder.hash(tx[0])
@@ -35,6 +36,7 @@ const createWorker = (connection: Redis, services?: IBridgeServiceCollection) =>
35
36
  // Check for transaction inclusion on chain
36
37
  await job.log(`[${hash}] Checking for XL1 transaction inclusion on chain`)
37
38
  const foundTx = await viewer.transactionByHash(submissionHash)
39
+ // const foundTx = await viewer.transaction.byHash(submissionHash)
38
40
 
39
41
  // Transaction found on chain
40
42
  if (isDefined(foundTx) && !isNull(foundTx)) {
@@ -59,7 +61,7 @@ const createWorker = (connection: Redis, services?: IBridgeServiceCollection) =>
59
61
  await job.log(`[${hash}] Transaction not yet included, retrying later`)
60
62
  throw new Error(`[${hash}] Transaction not yet included`)
61
63
  },
62
- { connection },
64
+ { connection, telemetry },
63
65
  )
64
66
 
65
67
  worker.on('failed', (job, err) => {
@@ -3,23 +3,23 @@ import { PayloadBuilder } from '@xyo-network/sdk-js'
3
3
  import type { SignedHydratedTransaction } from '@xyo-network/xl1-sdk'
4
4
  import type { Job } from 'bullmq'
5
5
  import { Worker } from 'bullmq'
6
+ import type { BullMQOtel } from 'bullmq-otel'
6
7
  import type { Redis } from 'ioredis'
7
8
 
8
9
  import type { IBridgeServiceCollection } from '../../services/index.ts'
9
10
  import type { WorkerDescription } from './WorkerDescription.ts'
10
11
 
11
- type JobData = { tx: SignedHydratedTransaction }
12
-
13
- interface ReturnType {}
12
+ export type Xl1TransactionPreparationJobData = { tx: SignedHydratedTransaction }
13
+ export interface Xl1TransactionPreparationJobReturn { preparedTx: SignedHydratedTransaction }
14
14
 
15
15
  const name = 'Prepare XL1 Transaction'
16
16
  const queueName = 'xl1-tx-prepare'
17
- const createWorker = (connection: Redis, services?: IBridgeServiceCollection) => {
17
+ const createWorker = (connection: Redis, telemetry?: BullMQOtel, services?: IBridgeServiceCollection) => {
18
18
  const stateMap = assertEx(services?.xl1TxStateMap, () => 'xl1TxStateMap service not provided')
19
19
 
20
20
  const worker = new Worker(
21
21
  queueName,
22
- async (job: Job<JobData, ReturnType>) => {
22
+ async (job: Job<Xl1TransactionPreparationJobData, Xl1TransactionPreparationJobReturn>) => {
23
23
  const { tx } = job.data
24
24
  const hash = await PayloadBuilder.hash(tx[0])
25
25
  await job.log(`[${hash}] preparing XL1 transaction`)
@@ -28,9 +28,9 @@ const createWorker = (connection: Redis, services?: IBridgeServiceCollection) =>
28
28
  await stateMap.set(hash, { preparedTx })
29
29
  await job.log(`[${hash}] stored XL1 preparedTx`)
30
30
  await job.log(`[${hash}] prepared XL1 transaction`)
31
- return {}
31
+ return { preparedTx }
32
32
  },
33
- { connection },
33
+ { connection, telemetry },
34
34
  )
35
35
 
36
36
  worker.on('failed', (job, err) => {
@@ -3,27 +3,28 @@ import { PayloadBuilder } from '@xyo-network/sdk-js'
3
3
  import { type SignedHydratedTransaction } from '@xyo-network/xl1-sdk'
4
4
  import type { Job } from 'bullmq'
5
5
  import { Worker } from 'bullmq'
6
+ import type { BullMQOtel } from 'bullmq-otel'
6
7
  import type { Redis } from 'ioredis'
7
8
 
8
9
  import type { IBridgeServiceCollection, Xl1TxState } from '../../services/index.ts'
9
10
  import { submitXl1Transaction } from './util/index.ts'
10
11
  import type { WorkerDescription } from './WorkerDescription.ts'
11
12
 
12
- type JobData = { tx: SignedHydratedTransaction }
13
+ export type Xl1TransactionSubmissionJobData = { tx: SignedHydratedTransaction }
13
14
 
14
- interface ReturnType {
15
+ export interface Xl1TransactionSubmissionJobReturn {
15
16
  submissionHash: Required<Xl1TxState>['submissionHash']
16
17
  }
17
18
 
18
19
  const name = 'Submit XL1 Transaction'
19
20
  const queueName = 'xl1-tx-submit'
20
- const createWorker = (connection: Redis, services?: IBridgeServiceCollection) => {
21
+ const createWorker = (connection: Redis, telemetry?: BullMQOtel, services?: IBridgeServiceCollection) => {
21
22
  const gateway = assertEx(services?.gateway, () => 'gateway service not provided')
22
23
  const stateMap = assertEx(services?.xl1TxStateMap, () => 'xl1TxStateMap service not provided')
23
24
 
24
25
  const worker = new Worker(
25
26
  queueName,
26
- async (job: Job<JobData, ReturnType>) => {
27
+ async (job: Job<Xl1TransactionSubmissionJobData, Xl1TransactionSubmissionJobReturn>) => {
27
28
  const { tx } = job.data
28
29
  // Get the hash of the transaction
29
30
  const hash = await PayloadBuilder.hash(tx[0])
@@ -54,7 +55,7 @@ const createWorker = (connection: Redis, services?: IBridgeServiceCollection) =>
54
55
 
55
56
  return { submissionHash }
56
57
  },
57
- { connection },
58
+ { connection, telemetry },
58
59
  )
59
60
 
60
61
  worker.on('failed', (job, err) => {
@@ -1,3 +1,4 @@
1
+ import type { BullMQOtel } from 'bullmq-otel'
1
2
  import type { Redis } from 'ioredis'
2
3
 
3
4
  import type { IBridgeServiceCollection } from '../../services/index.ts'
@@ -11,12 +12,12 @@ import {
11
12
  Xl1TransactionSubmission,
12
13
  } from './index.ts'
13
14
 
14
- export const createWorkers = (connection: Redis, services: IBridgeServiceCollection) => {
15
- Xl1ToEthBridgeParent.createWorker(connection)
16
- Xl1TransactionPreparation.createWorker(connection, services)
17
- Xl1TransactionSubmission.createWorker(connection, services)
18
- Xl1TransactionMonitor.createWorker(connection, services)
19
- EthTransactionPreparation.createWorker(connection, services)
20
- EthTransactionSubmission.createWorker(connection, services)
21
- EthTransactionMonitor.createWorker(connection, services)
15
+ export const createWorkers = (connection: Redis, telemetry: BullMQOtel, services: IBridgeServiceCollection) => {
16
+ Xl1ToEthBridgeParent.createWorker(connection, telemetry)
17
+ Xl1TransactionPreparation.createWorker(connection, telemetry, services)
18
+ Xl1TransactionSubmission.createWorker(connection, telemetry, services)
19
+ Xl1TransactionMonitor.createWorker(connection, telemetry, services)
20
+ EthTransactionPreparation.createWorker(connection, telemetry, services)
21
+ EthTransactionSubmission.createWorker(connection, telemetry, services)
22
+ EthTransactionMonitor.createWorker(connection, telemetry, services)
22
23
  }
@@ -0,0 +1,5 @@
1
+ import type { Promisable } from '@xylabs/sdk-js'
2
+
3
+ export interface AsyncLogger {
4
+ log: (message: string) => Promisable<unknown>
5
+ }
@@ -1,4 +1,5 @@
1
1
  export * from './submitEthTransaction.ts'
2
2
  export * from './submitXl1Transaction.ts'
3
- export * from './validateSufficientAllowance.ts'
4
- export * from './validateSufficientBalance.ts'
3
+ export * from './validateSufficientLiquiditySourceAllowance.ts'
4
+ export * from './validateSufficientLiquiditySourceBalance.ts'
5
+ export * from './validateSufficientRunnerEthBalanceForGas.ts'
@@ -1,11 +1,8 @@
1
- import type { Promisable } from '@xylabs/sdk-js'
2
1
  import { assertEx, hexToBigInt } from '@xylabs/sdk-js'
3
2
  import type { BridgeableToken, LiquidityPoolBridge } from '@xyo-network/typechain'
4
3
  import { isBridgeIntent, type SignedHydratedTransaction } from '@xyo-network/xl1-sdk'
5
4
 
6
- interface IAsyncLogger {
7
- log: (message: string) => Promisable<unknown>
8
- }
5
+ import type { AsyncLogger } from './AsyncLogger.ts'
9
6
 
10
7
  /**
11
8
  * Checks if the liquidity source has sufficient allowance for the bridge to execute the transaction. This doesn't
@@ -17,11 +14,11 @@ interface IAsyncLogger {
17
14
  * @param logger Optional logger for asynchronous logging
18
15
  * @returns True if the liquidity source allowance is sufficient to execute the bridge
19
16
  */
20
- export const validateSufficientAllowance = async (
17
+ export const validateSufficientLiquiditySourceAllowance = async (
21
18
  tx: SignedHydratedTransaction,
22
19
  bridgeableToken: BridgeableToken,
23
20
  bridge: LiquidityPoolBridge,
24
- logger?: IAsyncLogger,
21
+ logger?: AsyncLogger,
25
22
  ) => {
26
23
  // Get the amount being bridged from the bridge intent
27
24
  const bridgeIntent = assertEx(tx[1].find(isBridgeIntent), () => 'No bridge intent found')
@@ -1,11 +1,8 @@
1
- import type { Promisable } from '@xylabs/sdk-js'
2
1
  import { assertEx, hexToBigInt } from '@xylabs/sdk-js'
3
2
  import type { BridgeableToken, LiquidityPoolBridge } from '@xyo-network/typechain'
4
3
  import { isBridgeIntent, type SignedHydratedTransaction } from '@xyo-network/xl1-sdk'
5
4
 
6
- interface IAsyncLogger {
7
- log: (message: string) => Promisable<unknown>
8
- }
5
+ import type { AsyncLogger } from './AsyncLogger.ts'
9
6
 
10
7
  /**
11
8
  * Checks if the liquidity source has sufficient balance for the bridge to execute the transaction. This doesn't
@@ -17,11 +14,11 @@ interface IAsyncLogger {
17
14
  * @param logger Optional logger for asynchronous logging
18
15
  * @returns True if the liquidity source balance is sufficient to execute the bridge
19
16
  */
20
- export const validateSufficientBalance = async (
17
+ export const validateSufficientLiquiditySourceBalance = async (
21
18
  tx: SignedHydratedTransaction,
22
19
  bridgeableToken: BridgeableToken,
23
20
  bridge: LiquidityPoolBridge,
24
- logger?: IAsyncLogger,
21
+ logger?: AsyncLogger,
25
22
  ) => {
26
23
  // Get the amount being bridged from the bridge intent
27
24
  const bridgeIntent = assertEx(tx[1].find(isBridgeIntent), () => 'No bridge intent found')
@@ -0,0 +1,57 @@
1
+ import { assertEx } from '@xylabs/sdk-js'
2
+ import type { ContractTransaction, Wallet } from 'ethers'
3
+
4
+ import type { AsyncLogger } from './AsyncLogger.ts'
5
+
6
+ const DEFAULT_GAS_BUFFER_BPS = 2000n // 20%
7
+
8
+ /**
9
+ * Validates that the wallet (the account that will submit the tx) has enough ETH
10
+ * to cover the estimated gas cost (plus a buffer) for the transaction.
11
+ * @param preparedTx The transaction that is being prepared for submission.
12
+ * This should be a fully populated transaction (to/from/data/value) except for gas fields.
13
+ * @param wallet The wallet that will be used to submit the transaction.
14
+ * @param logger Optional logger for asynchronous logging.
15
+ * @param bufferBps The buffer to apply to the estimated gas cost, in basis points (1/100th of a percent).
16
+ * @returns A boolean indicating whether the wallet has sufficient ETH to cover the estimated gas cost with the buffer applied.
17
+ */
18
+ export const validateSufficientRunnerEthBalanceForGas = async (
19
+ preparedTx: ContractTransaction,
20
+ wallet: Wallet,
21
+ logger?: AsyncLogger,
22
+ bufferBps: bigint = DEFAULT_GAS_BUFFER_BPS,
23
+ ) => {
24
+ const provider = assertEx(wallet.provider, () => 'Wallet provider is not defined')
25
+
26
+ // Fee data (EIP-1559 preferred, gasPrice fallback)
27
+ const feeData = await provider.getFeeData()
28
+ const perGas = feeData.maxFeePerGas ?? feeData.gasPrice
29
+ if (perGas == null) {
30
+ await logger?.log('[gas] unable to resolve gas price / maxFeePerGas from provider')
31
+ return true // don’t hard-fail; provider may not support fee data
32
+ }
33
+
34
+ // Estimate gas
35
+ const transactionRequest = {
36
+ ...preparedTx,
37
+ from: await wallet.getAddress(),
38
+ }
39
+ const estGas = await provider.estimateGas(transactionRequest)
40
+
41
+ // Include any value sent with the tx
42
+ const txValue = preparedTx?.value ?? 0n
43
+
44
+ // Required = (gas * perGas + value) * (1 + buffer)
45
+ const baseRequired = estGas * perGas + txValue
46
+ const required = (baseRequired * (10_000n + bufferBps)) / 10_000n
47
+
48
+ const balance = await provider.getBalance(await wallet.getAddress())
49
+
50
+ await logger?.log(
51
+ `[gas] runner=${await wallet.getAddress()} balance=${balance.toString()} `
52
+ + `estGas=${estGas.toString()} perGas=${perGas.toString()} value=${txValue.toString()} `
53
+ + `requiredWithBuffer=${required.toString()} bufferBps=${bufferBps.toString()}`,
54
+ )
55
+
56
+ return balance >= required
57
+ }
@@ -1,11 +1,14 @@
1
1
  import type { BridgeConfig } from '@xyo-network/chain-orchestration'
2
2
  import type { Express } from 'express'
3
3
 
4
- import { getConnection, getFlowProducer } from '../queue/index.ts'
4
+ import {
5
+ getConnection, getFlowProducer, getTelemetry,
6
+ } from '../queue/index.ts'
5
7
 
6
8
  export const addFlowProducer = (app: Express, config: BridgeConfig): Express => {
7
9
  const connection = getConnection(config)
8
- const flowProducer = getFlowProducer(connection)
10
+ const telemetry = getTelemetry()
11
+ const flowProducer = getFlowProducer(connection, telemetry)
9
12
  app.flowProducer = flowProducer
10
13
  return app
11
14
  }
@@ -1,9 +1,13 @@
1
1
  import type { BridgeConfig } from '@xyo-network/chain-orchestration'
2
2
 
3
- import { createWorkers, getConnection } from '../queue/index.ts'
3
+ import {
4
+ createWorkers, getConnection,
5
+ getTelemetry,
6
+ } from '../queue/index.ts'
4
7
  import type { IBridgeServiceCollection } from '../services/index.ts'
5
8
 
6
9
  export const addWorkers = (config: BridgeConfig, services: IBridgeServiceCollection) => {
7
10
  const connection = getConnection(config)
8
- createWorkers(connection, services)
11
+ const telemetry = getTelemetry()
12
+ createWorkers(connection, telemetry, services)
9
13
  }
@@ -156,9 +156,9 @@ export const makeBridgeToRemoteStatusRoute = (config: BridgeConfig): RouteDefini
156
156
  // Check the state of the ETH monitor job to determine if we can include the destination observation
157
157
  const { ethTransactionMonitorJob } = statusQueueJobs
158
158
  const ethMonitorState = ethTransactionMonitorJob ? await ethTransactionMonitorJob.getState() : undefined
159
- const blockHash = (ethTransactionMonitorJob?.returnvalue as { blockHash?: string } | undefined)?.blockHash
160
- const destConfirmation = asHex(blockHash)
161
- if (ethMonitorState === 'completed' && isDefined(blockHash)) {
159
+ const submissionHash = (ethTransactionMonitorJob?.returnvalue as { submissionHash?: string } | undefined)?.submissionHash
160
+ const destConfirmation = asHex(submissionHash)
161
+ if (ethMonitorState === 'completed' && isDefined(submissionHash)) {
162
162
  const bridgeDestinationFieldsZod = z.object({}).extend(BridgeDestinationObservationFieldsZod.shape)
163
163
  const bridgeDestinationFields = bridgeDestinationFieldsZod.parse({ ...bridgeIntent, destConfirmation })
164
164
  const observation: BridgeDestinationObservation = { schema: BridgeDestinationObservationSchema, ...bridgeDestinationFields }
@@ -2,8 +2,8 @@ import type { Hash } from '@xylabs/sdk-js'
2
2
  import { assertEx, isDefined } from '@xylabs/sdk-js'
3
3
  import type { BridgeConfig, BridgeConfigContext } from '@xyo-network/chain-orchestration'
4
4
  import { initEvmProvider } from '@xyo-network/chain-orchestration'
5
+ import { HDWallet } from '@xyo-network/sdk-js'
5
6
  import { BridgeableToken__factory, LiquidityPoolBridge__factory } from '@xyo-network/typechain'
6
- import { HDWallet } from '@xyo-network/wallet'
7
7
  import type { XyoGatewayRunner } from '@xyo-network/xl1-sdk'
8
8
  import { getAddress, Wallet } from 'ethers'
9
9
 
@@ -1 +0,0 @@
1
- {"version":3,"file":"validateSufficientAllowance.d.ts","sourceRoot":"","sources":["../../../../../src/queue/workers/util/validateSufficientAllowance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAEhD,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAClF,OAAO,EAAkB,KAAK,yBAAyB,EAAE,MAAM,sBAAsB,CAAA;AAErF,UAAU,YAAY;IACpB,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,UAAU,CAAC,OAAO,CAAC,CAAA;CAC9C;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,GACtC,IAAI,yBAAyB,EAC7B,iBAAiB,eAAe,EAChC,QAAQ,mBAAmB,EAC3B,SAAS,YAAY,qBActB,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"validateSufficientBalance.d.ts","sourceRoot":"","sources":["../../../../../src/queue/workers/util/validateSufficientBalance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAEhD,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAClF,OAAO,EAAkB,KAAK,yBAAyB,EAAE,MAAM,sBAAsB,CAAA;AAErF,UAAU,YAAY;IACpB,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,UAAU,CAAC,OAAO,CAAC,CAAA;CAC9C;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,yBAAyB,GACpC,IAAI,yBAAyB,EAC7B,iBAAiB,eAAe,EAChC,QAAQ,mBAAmB,EAC3B,SAAS,YAAY,qBAatB,CAAA"}