@xyo-network/chain-bridge 1.19.16 → 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.
- package/dist/node/index.mjs +256 -145
- package/dist/node/index.mjs.map +1 -1
- package/dist/node/queue/flowProducer.d.ts +2 -1
- package/dist/node/queue/flowProducer.d.ts.map +1 -1
- package/dist/node/queue/flows/createXl1ToEthBridgeJob.d.ts.map +1 -1
- package/dist/node/queue/index.d.ts +1 -0
- package/dist/node/queue/index.d.ts.map +1 -1
- package/dist/node/queue/telemetry.d.ts +3 -0
- package/dist/node/queue/telemetry.d.ts.map +1 -0
- package/dist/node/queue/workers/EthTransactionMonitor.d.ts +9 -0
- package/dist/node/queue/workers/EthTransactionMonitor.d.ts.map +1 -1
- package/dist/node/queue/workers/EthTransactionPreparation.d.ts +7 -1
- package/dist/node/queue/workers/EthTransactionPreparation.d.ts.map +1 -1
- package/dist/node/queue/workers/EthTransactionSubmission.d.ts +8 -0
- package/dist/node/queue/workers/EthTransactionSubmission.d.ts.map +1 -1
- package/dist/node/queue/workers/WorkerDescription.d.ts +2 -1
- package/dist/node/queue/workers/WorkerDescription.d.ts.map +1 -1
- package/dist/node/queue/workers/Xl1ToEthBridgeParent.d.ts +3 -0
- package/dist/node/queue/workers/Xl1ToEthBridgeParent.d.ts.map +1 -1
- package/dist/node/queue/workers/Xl1TransactionMonitor.d.ts +6 -0
- package/dist/node/queue/workers/Xl1TransactionMonitor.d.ts.map +1 -1
- package/dist/node/queue/workers/Xl1TransactionPreparation.d.ts +7 -0
- package/dist/node/queue/workers/Xl1TransactionPreparation.d.ts.map +1 -1
- package/dist/node/queue/workers/Xl1TransactionSubmission.d.ts +8 -0
- package/dist/node/queue/workers/Xl1TransactionSubmission.d.ts.map +1 -1
- package/dist/node/queue/workers/createWorkers.d.ts +2 -1
- package/dist/node/queue/workers/createWorkers.d.ts.map +1 -1
- package/dist/node/queue/workers/util/AsyncLogger.d.ts +5 -0
- package/dist/node/queue/workers/util/AsyncLogger.d.ts.map +1 -0
- package/dist/node/queue/workers/util/index.d.ts +3 -2
- package/dist/node/queue/workers/util/index.d.ts.map +1 -1
- package/dist/node/queue/workers/util/{validateSufficientAllowance.d.ts → validateSufficientLiquiditySourceAllowance.d.ts} +3 -7
- package/dist/node/queue/workers/util/validateSufficientLiquiditySourceAllowance.d.ts.map +1 -0
- package/dist/node/queue/workers/util/{validateSufficientBalance.d.ts → validateSufficientLiquiditySourceBalance.d.ts} +3 -7
- package/dist/node/queue/workers/util/validateSufficientLiquiditySourceBalance.d.ts.map +1 -0
- package/dist/node/queue/workers/util/validateSufficientRunnerEthBalanceForGas.d.ts +14 -0
- package/dist/node/queue/workers/util/validateSufficientRunnerEthBalanceForGas.d.ts.map +1 -0
- package/dist/node/server/addFlowProducer.d.ts.map +1 -1
- package/dist/node/server/addWorkers.d.ts.map +1 -1
- package/dist/node/server/routes/bridge/routeDefinitions/routes/bridgeToRemoteStatus.d.ts.map +1 -1
- package/dist/node/util/BridgeFees.d.ts +7 -1
- package/dist/node/util/BridgeFees.d.ts.map +1 -1
- package/package.json +30 -35
- package/src/config/getBridgeWalletAccount.ts +1 -1
- package/src/config/getGateway.ts +1 -1
- package/src/queue/flowProducer.ts +3 -2
- package/src/queue/flows/createXl1ToEthBridgeJob.ts +12 -2
- package/src/queue/index.ts +1 -0
- package/src/queue/telemetry.ts +12 -0
- package/src/queue/workers/EthTransactionMonitor.ts +10 -6
- package/src/queue/workers/EthTransactionPreparation.ts +17 -9
- package/src/queue/workers/EthTransactionSubmission.ts +8 -28
- package/src/queue/workers/WorkerDescription.ts +2 -1
- package/src/queue/workers/Xl1ToEthBridgeParent.ts +8 -5
- package/src/queue/workers/Xl1TransactionMonitor.ts +7 -5
- package/src/queue/workers/Xl1TransactionPreparation.ts +7 -7
- package/src/queue/workers/Xl1TransactionSubmission.ts +6 -5
- package/src/queue/workers/createWorkers.ts +9 -8
- package/src/queue/workers/util/AsyncLogger.ts +5 -0
- package/src/queue/workers/util/index.ts +3 -2
- package/src/queue/workers/util/{validateSufficientAllowance.ts → validateSufficientLiquiditySourceAllowance.ts} +3 -6
- package/src/queue/workers/util/{validateSufficientBalance.ts → validateSufficientLiquiditySourceBalance.ts} +3 -6
- package/src/queue/workers/util/validateSufficientRunnerEthBalanceForGas.ts +57 -0
- package/src/server/addFlowProducer.ts +5 -2
- package/src/server/addWorkers.ts +6 -2
- package/src/server/routes/bridge/routeDefinitions/routes/bridgeToRemoteStatus.ts +130 -65
- package/src/services/getServices.ts +1 -1
- package/src/util/BridgeFees.ts +10 -1
- package/dist/node/queue/workers/util/validateSufficientAllowance.d.ts.map +0 -1
- package/dist/node/queue/workers/util/validateSufficientBalance.d.ts.map +0 -1
|
@@ -1,102 +1,167 @@
|
|
|
1
1
|
import type { RouteDefinition } from '@xylabs/express'
|
|
2
2
|
import { requestHandlerValidator } from '@xylabs/express'
|
|
3
|
-
import {
|
|
4
|
-
asAddress, asHex, toHex,
|
|
5
|
-
} from '@xylabs/sdk-js'
|
|
3
|
+
import { asHex, isDefined } from '@xylabs/sdk-js'
|
|
6
4
|
import type { BridgeConfig } from '@xyo-network/chain-orchestration'
|
|
7
|
-
import { PayloadZodStrictOfSchema } from '@xyo-network/sdk-js'
|
|
8
|
-
import type {
|
|
5
|
+
import { PayloadBuilder, PayloadZodStrictOfSchema } from '@xyo-network/sdk-js'
|
|
6
|
+
import type {
|
|
7
|
+
BridgeDestinationObservation, BridgeSourceObservation, SignedHydratedTransaction,
|
|
8
|
+
} from '@xyo-network/xl1-sdk'
|
|
9
9
|
import {
|
|
10
|
-
BridgeDestinationObservationFieldsZod, BridgeDestinationObservationSchema, BridgeIntentFieldsZod, BridgeIntentSchema,
|
|
11
|
-
BridgeSourceObservationSchema,
|
|
10
|
+
asBridgeIntent, BridgeDestinationObservationFieldsZod, BridgeDestinationObservationSchema, BridgeIntentFieldsZod, BridgeIntentSchema,
|
|
11
|
+
BridgeSourceObservationFieldsZod, BridgeSourceObservationSchema, isBridgeIntent,
|
|
12
12
|
} from '@xyo-network/xl1-sdk'
|
|
13
|
+
import type { Job } from 'bullmq'
|
|
14
|
+
import { Queue } from 'bullmq'
|
|
13
15
|
import { z } from 'zod'
|
|
14
16
|
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
+
import {
|
|
18
|
+
EthTransactionMonitor, EthTransactionPreparation, EthTransactionSubmission, getConnection, Xl1ToEthBridgeParent, Xl1TransactionMonitor,
|
|
19
|
+
Xl1TransactionPreparation, Xl1TransactionSubmission,
|
|
20
|
+
} from '../../../../../queue/index.ts'
|
|
17
21
|
import { getRemoteChainIdZod } from '../pathParams/index.ts'
|
|
18
22
|
|
|
23
|
+
const BridgeIntentResponseZod = PayloadZodStrictOfSchema(BridgeIntentSchema)
|
|
24
|
+
.extend(BridgeIntentFieldsZod.shape)
|
|
25
|
+
|
|
26
|
+
const BridgeSourceResponseZod = PayloadZodStrictOfSchema(BridgeSourceObservationSchema)
|
|
27
|
+
.extend(BridgeSourceObservationFieldsZod.shape)
|
|
28
|
+
|
|
29
|
+
const BridgeDestinationResponseZod = PayloadZodStrictOfSchema(BridgeDestinationObservationSchema)
|
|
30
|
+
.extend(BridgeDestinationObservationFieldsZod.shape)
|
|
31
|
+
|
|
19
32
|
export const BridgeToRemoteStatusResponseZod = z.union([
|
|
20
33
|
z.tuple([]),
|
|
21
|
-
z.tuple([
|
|
22
|
-
z.tuple([
|
|
23
|
-
|
|
24
|
-
PayloadZodStrictOfSchema(BridgeSourceObservationSchema).extend(BridgeSourceObservationFieldsZod.shape),
|
|
25
|
-
]),
|
|
26
|
-
z.tuple([
|
|
27
|
-
PayloadZodStrictOfSchema(BridgeIntentSchema).extend(BridgeIntentFieldsZod.shape),
|
|
28
|
-
PayloadZodStrictOfSchema(BridgeSourceObservationSchema).extend(BridgeSourceObservationFieldsZod.shape),
|
|
29
|
-
PayloadZodStrictOfSchema(BridgeDestinationObservationSchema).extend(BridgeDestinationObservationFieldsZod.shape),
|
|
30
|
-
]),
|
|
34
|
+
z.tuple([BridgeIntentResponseZod]),
|
|
35
|
+
z.tuple([BridgeIntentResponseZod, BridgeSourceResponseZod]),
|
|
36
|
+
z.tuple([BridgeIntentResponseZod, BridgeSourceResponseZod, BridgeDestinationResponseZod]),
|
|
31
37
|
])
|
|
32
38
|
export type BridgeToRemoteStatusResponse = z.infer<typeof BridgeToRemoteStatusResponseZod>
|
|
33
39
|
|
|
40
|
+
interface StatusQueues {
|
|
41
|
+
ethTransactionMonitor: Queue
|
|
42
|
+
ethTransactionPreparation: Queue
|
|
43
|
+
ethTransactionSubmission: Queue
|
|
44
|
+
xl1ToEthBridgeParent: Queue
|
|
45
|
+
xl1TransactionMonitor: Queue
|
|
46
|
+
xl1TransactionPreparation: Queue
|
|
47
|
+
xl1TransactionSubmission: Queue
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let statusQueues: StatusQueues | undefined
|
|
51
|
+
|
|
52
|
+
const getStatusQueues = (config: BridgeConfig): StatusQueues => {
|
|
53
|
+
if (statusQueues) return statusQueues
|
|
54
|
+
const connection = getConnection(config)
|
|
55
|
+
statusQueues = {
|
|
56
|
+
ethTransactionMonitor: new Queue(EthTransactionMonitor.queueName, { connection }),
|
|
57
|
+
ethTransactionPreparation: new Queue(EthTransactionPreparation.queueName, { connection }),
|
|
58
|
+
ethTransactionSubmission: new Queue(EthTransactionSubmission.queueName, { connection }),
|
|
59
|
+
xl1ToEthBridgeParent: new Queue(Xl1ToEthBridgeParent.queueName, { connection }),
|
|
60
|
+
xl1TransactionMonitor: new Queue(Xl1TransactionMonitor.queueName, { connection }),
|
|
61
|
+
xl1TransactionPreparation: new Queue(Xl1TransactionPreparation.queueName, { connection }),
|
|
62
|
+
xl1TransactionSubmission: new Queue(Xl1TransactionSubmission.queueName, { connection }),
|
|
63
|
+
}
|
|
64
|
+
return statusQueues
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface StatusQueueJobs {
|
|
68
|
+
ethTransactionMonitorJob?: Job
|
|
69
|
+
ethTransactionPreparationJob?: Job
|
|
70
|
+
ethTransactionSubmissionJob?: Job
|
|
71
|
+
xl1ToEthBridgeParentJob?: Job
|
|
72
|
+
xl1TransactionMonitorJob?: Job
|
|
73
|
+
xl1TransactionPreparationJob?: Job
|
|
74
|
+
xl1TransactionSubmissionJob?: Job
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Checks all the relevant status queues for a job with the given id and returns the jobs found in an object
|
|
79
|
+
* @param queues The status queues to check for jobs
|
|
80
|
+
* @param jobId The job id to look for in the queues, which is the same as the tx hash of the bridge transaction
|
|
81
|
+
* @returns An object containing the jobs found in the status queues corresponding to the given job id
|
|
82
|
+
*/
|
|
83
|
+
const getStatusQueueJobs = async (queues: StatusQueues, jobId: string): Promise<StatusQueueJobs> => {
|
|
84
|
+
const [
|
|
85
|
+
ethTransactionMonitorJob,
|
|
86
|
+
ethTransactionPreparationJob,
|
|
87
|
+
ethTransactionSubmissionJob,
|
|
88
|
+
xl1ToEthBridgeParentJob,
|
|
89
|
+
xl1TransactionMonitorJob,
|
|
90
|
+
xl1TransactionPreparationJob,
|
|
91
|
+
xl1TransactionSubmissionJob,
|
|
92
|
+
] = await Promise.all([
|
|
93
|
+
queues.ethTransactionMonitor.getJob(jobId),
|
|
94
|
+
queues.ethTransactionPreparation.getJob(jobId),
|
|
95
|
+
queues.ethTransactionSubmission.getJob(jobId),
|
|
96
|
+
queues.xl1ToEthBridgeParent.getJob(jobId),
|
|
97
|
+
queues.xl1TransactionMonitor.getJob(jobId),
|
|
98
|
+
queues.xl1TransactionPreparation.getJob(jobId),
|
|
99
|
+
queues.xl1TransactionSubmission.getJob(jobId),
|
|
100
|
+
])
|
|
101
|
+
return {
|
|
102
|
+
ethTransactionMonitorJob,
|
|
103
|
+
ethTransactionPreparationJob,
|
|
104
|
+
ethTransactionSubmissionJob,
|
|
105
|
+
xl1ToEthBridgeParentJob,
|
|
106
|
+
xl1TransactionMonitorJob,
|
|
107
|
+
xl1TransactionPreparationJob,
|
|
108
|
+
xl1TransactionSubmissionJob,
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
34
112
|
export const makeBridgeToRemoteStatusRoute = (config: BridgeConfig): RouteDefinition => {
|
|
35
113
|
const params = z.object({
|
|
36
114
|
chainId: getRemoteChainIdZod(config),
|
|
37
115
|
nonce: z.string().nonempty(),
|
|
38
116
|
})
|
|
39
|
-
const query = z.object({ mockStatus: z.coerce.number().default(0) })
|
|
40
117
|
|
|
41
|
-
const validateRequest = requestHandlerValidator({
|
|
42
|
-
params, query, response: BridgeToRemoteStatusResponseZod,
|
|
43
|
-
})
|
|
118
|
+
const validateRequest = requestHandlerValidator({ params, response: BridgeToRemoteStatusResponseZod })
|
|
44
119
|
|
|
45
120
|
return {
|
|
46
121
|
method: 'get',
|
|
47
122
|
path: '/bridge/chains/:chainId/bridgeToRemote/status/:nonce',
|
|
48
123
|
handlers: validateRequest(async (req, res) => {
|
|
49
|
-
const
|
|
50
|
-
const { mockStatus = 0 } = req.query
|
|
124
|
+
const jobId = req.params.nonce
|
|
51
125
|
const result: z.infer<typeof BridgeToRemoteStatusResponseZod> = [] as unknown as z.infer<typeof BridgeToRemoteStatusResponseZod>
|
|
126
|
+
const queues = getStatusQueues(config)
|
|
52
127
|
|
|
53
|
-
const
|
|
54
|
-
remoteTokenAddress, xl1ChainId, xl1TokenAddress,
|
|
55
|
-
} = await getBridgeSettings(config)
|
|
128
|
+
const statusQueueJobs = await getStatusQueueJobs(queues, jobId)
|
|
56
129
|
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
const srcAmount = asHex('0x200', true)
|
|
60
|
-
const srcToken = xl1TokenAddress
|
|
130
|
+
// Check for the transaction hash in any of the queues
|
|
131
|
+
const tx = Object.values(statusQueueJobs).map(job => job?.data?.tx as SignedHydratedTransaction | undefined).find(tx => isDefined(tx))
|
|
61
132
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const destAmount = asHex('0x100', true)
|
|
65
|
-
const destToken = remoteTokenAddress
|
|
133
|
+
// If the transaction does not exist in any of them return Not Found
|
|
134
|
+
if (!tx) return res.sendStatus(404)
|
|
66
135
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
136
|
+
// If the transaction is not the right shape return Not Found
|
|
137
|
+
const bridgeIntent = tx[1].find(isBridgeIntent)
|
|
138
|
+
if (!bridgeIntent) return res.sendStatus(404)
|
|
139
|
+
|
|
140
|
+
// At this point we know we have a valid bridge intent, so we can start building the response
|
|
141
|
+
result[0] = asBridgeIntent(PayloadBuilder.omitMeta(bridgeIntent))
|
|
142
|
+
|
|
143
|
+
// Check the state of the XL1 monitor job to determine if we can include the source observation
|
|
144
|
+
const { xl1TransactionMonitorJob } = statusQueueJobs
|
|
145
|
+
const xl1MonitorState = xl1TransactionMonitorJob ? await xl1TransactionMonitorJob.getState() : undefined
|
|
146
|
+
const srcConfirmation = asHex(jobId)
|
|
147
|
+
if (xl1MonitorState === 'completed' && isDefined(srcConfirmation)) {
|
|
148
|
+
const bridgeCommonFieldsZod = z.object({}).extend(BridgeSourceObservationFieldsZod.shape)
|
|
149
|
+
const bridgeCommonFields = bridgeCommonFieldsZod.parse(bridgeIntent)
|
|
73
150
|
const observation: BridgeSourceObservation = {
|
|
74
|
-
schema: BridgeSourceObservationSchema,
|
|
75
|
-
dest,
|
|
76
|
-
destAddress,
|
|
77
|
-
destAmount,
|
|
78
|
-
destToken,
|
|
79
|
-
src,
|
|
80
|
-
srcAddress,
|
|
81
|
-
srcAmount,
|
|
82
|
-
srcToken,
|
|
83
|
-
srcConfirmation: toHex('0x8888'),
|
|
151
|
+
schema: BridgeSourceObservationSchema, ...bridgeCommonFields, srcConfirmation,
|
|
84
152
|
}
|
|
85
153
|
result[1] = observation
|
|
86
154
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
srcToken,
|
|
98
|
-
destConfirmation: toHex('0x9999'),
|
|
99
|
-
}
|
|
155
|
+
|
|
156
|
+
// Check the state of the ETH monitor job to determine if we can include the destination observation
|
|
157
|
+
const { ethTransactionMonitorJob } = statusQueueJobs
|
|
158
|
+
const ethMonitorState = ethTransactionMonitorJob ? await ethTransactionMonitorJob.getState() : undefined
|
|
159
|
+
const submissionHash = (ethTransactionMonitorJob?.returnvalue as { submissionHash?: string } | undefined)?.submissionHash
|
|
160
|
+
const destConfirmation = asHex(submissionHash)
|
|
161
|
+
if (ethMonitorState === 'completed' && isDefined(submissionHash)) {
|
|
162
|
+
const bridgeDestinationFieldsZod = z.object({}).extend(BridgeDestinationObservationFieldsZod.shape)
|
|
163
|
+
const bridgeDestinationFields = bridgeDestinationFieldsZod.parse({ ...bridgeIntent, destConfirmation })
|
|
164
|
+
const observation: BridgeDestinationObservation = { schema: BridgeDestinationObservationSchema, ...bridgeDestinationFields }
|
|
100
165
|
result[2] = observation
|
|
101
166
|
}
|
|
102
167
|
|
|
@@ -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
|
|
package/src/util/BridgeFees.ts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
type Hex, HexZod, type JsonObject,
|
|
3
|
+
} from '@xylabs/sdk-js'
|
|
4
|
+
import { z } from 'zod'
|
|
2
5
|
|
|
3
6
|
export interface BridgeFees extends JsonObject {
|
|
4
7
|
feeFixed: Hex
|
|
5
8
|
feeVariable: Hex
|
|
6
9
|
srcAmount: Hex
|
|
7
10
|
}
|
|
11
|
+
|
|
12
|
+
export const BridgeFeesZod = z.object({
|
|
13
|
+
feeFixed: HexZod,
|
|
14
|
+
feeVariable: HexZod,
|
|
15
|
+
srcAmount: HexZod,
|
|
16
|
+
})
|
|
@@ -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"}
|