@hyperlane-xyz/rebalancer 0.1.2 → 1.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/README.md +134 -14
- package/dist/config/RebalancerConfig.d.ts +2 -2
- package/dist/config/RebalancerConfig.d.ts.map +1 -1
- package/dist/config/RebalancerConfig.js +4 -3
- package/dist/config/RebalancerConfig.js.map +1 -1
- package/dist/config/RebalancerConfig.test.js +434 -163
- package/dist/config/RebalancerConfig.test.js.map +1 -1
- package/dist/config/types.d.ts +1650 -290
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +124 -46
- package/dist/config/types.js.map +1 -1
- package/dist/core/Rebalancer.d.ts +14 -7
- package/dist/core/Rebalancer.d.ts.map +1 -1
- package/dist/core/Rebalancer.js +168 -99
- package/dist/core/Rebalancer.js.map +1 -1
- package/dist/core/Rebalancer.test.d.ts +2 -0
- package/dist/core/Rebalancer.test.d.ts.map +1 -0
- package/dist/core/Rebalancer.test.js +391 -0
- package/dist/core/Rebalancer.test.js.map +1 -0
- package/dist/core/RebalancerService.d.ts +16 -2
- package/dist/core/RebalancerService.d.ts.map +1 -1
- package/dist/core/RebalancerService.js +164 -21
- package/dist/core/RebalancerService.js.map +1 -1
- package/dist/core/RebalancerService.test.d.ts +2 -0
- package/dist/core/RebalancerService.test.d.ts.map +1 -0
- package/dist/core/RebalancerService.test.js +809 -0
- package/dist/core/RebalancerService.test.js.map +1 -0
- package/dist/factories/RebalancerContextFactory.d.ts +11 -0
- package/dist/factories/RebalancerContextFactory.d.ts.map +1 -1
- package/dist/factories/RebalancerContextFactory.js +60 -13
- package/dist/factories/RebalancerContextFactory.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/interfaces/IMonitor.d.ts +6 -8
- package/dist/interfaces/IMonitor.d.ts.map +1 -1
- package/dist/interfaces/IMonitor.js.map +1 -1
- package/dist/interfaces/IRebalancer.d.ts +20 -4
- package/dist/interfaces/IRebalancer.d.ts.map +1 -1
- package/dist/interfaces/IStrategy.d.ts +18 -2
- package/dist/interfaces/IStrategy.d.ts.map +1 -1
- package/dist/metrics/Metrics.d.ts +4 -2
- package/dist/metrics/Metrics.d.ts.map +1 -1
- package/dist/metrics/Metrics.js +21 -1
- package/dist/metrics/Metrics.js.map +1 -1
- package/dist/metrics/scripts/metrics.d.ts +2 -0
- package/dist/metrics/scripts/metrics.d.ts.map +1 -1
- package/dist/metrics/scripts/metrics.js +12 -0
- package/dist/metrics/scripts/metrics.js.map +1 -1
- package/dist/monitor/Monitor.d.ts +8 -3
- package/dist/monitor/Monitor.d.ts.map +1 -1
- package/dist/monitor/Monitor.js +75 -15
- package/dist/monitor/Monitor.js.map +1 -1
- package/dist/strategy/BaseStrategy.d.ts +51 -5
- package/dist/strategy/BaseStrategy.d.ts.map +1 -1
- package/dist/strategy/BaseStrategy.js +199 -19
- package/dist/strategy/BaseStrategy.js.map +1 -1
- package/dist/strategy/CollateralDeficitStrategy.d.ts +65 -0
- package/dist/strategy/CollateralDeficitStrategy.d.ts.map +1 -0
- package/dist/strategy/CollateralDeficitStrategy.js +245 -0
- package/dist/strategy/CollateralDeficitStrategy.js.map +1 -0
- package/dist/strategy/CollateralDeficitStrategy.test.d.ts +2 -0
- package/dist/strategy/CollateralDeficitStrategy.test.d.ts.map +1 -0
- package/dist/strategy/CollateralDeficitStrategy.test.js +364 -0
- package/dist/strategy/CollateralDeficitStrategy.test.js.map +1 -0
- package/dist/strategy/CompositeStrategy.d.ts +18 -0
- package/dist/strategy/CompositeStrategy.d.ts.map +1 -0
- package/dist/strategy/CompositeStrategy.js +63 -0
- package/dist/strategy/CompositeStrategy.js.map +1 -0
- package/dist/strategy/CompositeStrategy.test.d.ts +2 -0
- package/dist/strategy/CompositeStrategy.test.d.ts.map +1 -0
- package/dist/strategy/CompositeStrategy.test.js +265 -0
- package/dist/strategy/CompositeStrategy.test.js.map +1 -0
- package/dist/strategy/MinAmountStrategy.d.ts +12 -5
- package/dist/strategy/MinAmountStrategy.d.ts.map +1 -1
- package/dist/strategy/MinAmountStrategy.js +23 -14
- package/dist/strategy/MinAmountStrategy.js.map +1 -1
- package/dist/strategy/MinAmountStrategy.test.js +88 -20
- package/dist/strategy/MinAmountStrategy.test.js.map +1 -1
- package/dist/strategy/StrategyFactory.d.ts +15 -6
- package/dist/strategy/StrategyFactory.d.ts.map +1 -1
- package/dist/strategy/StrategyFactory.js +48 -10
- package/dist/strategy/StrategyFactory.js.map +1 -1
- package/dist/strategy/StrategyFactory.test.js +2 -2
- package/dist/strategy/StrategyFactory.test.js.map +1 -1
- package/dist/strategy/WeightedStrategy.d.ts +13 -4
- package/dist/strategy/WeightedStrategy.d.ts.map +1 -1
- package/dist/strategy/WeightedStrategy.js +18 -6
- package/dist/strategy/WeightedStrategy.js.map +1 -1
- package/dist/strategy/WeightedStrategy.test.js +108 -18
- package/dist/strategy/WeightedStrategy.test.js.map +1 -1
- package/dist/strategy/index.d.ts +2 -0
- package/dist/strategy/index.d.ts.map +1 -1
- package/dist/strategy/index.js +2 -0
- package/dist/strategy/index.js.map +1 -1
- package/dist/test/helpers.d.ts +93 -3
- package/dist/test/helpers.d.ts.map +1 -1
- package/dist/test/helpers.js +267 -10
- package/dist/test/helpers.js.map +1 -1
- package/dist/tracking/ActionTracker.d.ts +49 -0
- package/dist/tracking/ActionTracker.d.ts.map +1 -0
- package/dist/tracking/ActionTracker.js +422 -0
- package/dist/tracking/ActionTracker.js.map +1 -0
- package/dist/tracking/ActionTracker.test.d.ts +2 -0
- package/dist/tracking/ActionTracker.test.d.ts.map +1 -0
- package/dist/tracking/ActionTracker.test.js +637 -0
- package/dist/tracking/ActionTracker.test.js.map +1 -0
- package/dist/tracking/IActionTracker.d.ts +101 -0
- package/dist/tracking/IActionTracker.d.ts.map +1 -0
- package/dist/tracking/IActionTracker.js +2 -0
- package/dist/tracking/IActionTracker.js.map +1 -0
- package/dist/tracking/InflightContextAdapter.d.ts +18 -0
- package/dist/tracking/InflightContextAdapter.d.ts.map +1 -0
- package/dist/tracking/InflightContextAdapter.js +35 -0
- package/dist/tracking/InflightContextAdapter.js.map +1 -0
- package/dist/tracking/InflightContextAdapter.test.d.ts +2 -0
- package/dist/tracking/InflightContextAdapter.test.d.ts.map +1 -0
- package/dist/tracking/InflightContextAdapter.test.js +172 -0
- package/dist/tracking/InflightContextAdapter.test.js.map +1 -0
- package/dist/tracking/index.d.ts +7 -0
- package/dist/tracking/index.d.ts.map +1 -0
- package/dist/tracking/index.js +6 -0
- package/dist/tracking/index.js.map +1 -0
- package/dist/tracking/store/IStore.d.ts +41 -0
- package/dist/tracking/store/IStore.d.ts.map +1 -0
- package/dist/tracking/store/IStore.js +2 -0
- package/dist/tracking/store/IStore.js.map +1 -0
- package/dist/tracking/store/InMemoryStore.d.ts +21 -0
- package/dist/tracking/store/InMemoryStore.d.ts.map +1 -0
- package/dist/tracking/store/InMemoryStore.js +40 -0
- package/dist/tracking/store/InMemoryStore.js.map +1 -0
- package/dist/tracking/store/InMemoryStore.test.d.ts +2 -0
- package/dist/tracking/store/InMemoryStore.test.d.ts.map +1 -0
- package/dist/tracking/store/InMemoryStore.test.js +290 -0
- package/dist/tracking/store/InMemoryStore.test.js.map +1 -0
- package/dist/tracking/store/index.d.ts +3 -0
- package/dist/tracking/store/index.d.ts.map +1 -0
- package/dist/tracking/store/index.js +2 -0
- package/dist/tracking/store/index.js.map +1 -0
- package/dist/tracking/types.d.ts +43 -0
- package/dist/tracking/types.d.ts.map +1 -0
- package/dist/tracking/types.js +2 -0
- package/dist/tracking/types.js.map +1 -0
- package/dist/utils/ExplorerClient.d.ts +39 -1
- package/dist/utils/ExplorerClient.d.ts.map +1 -1
- package/dist/utils/ExplorerClient.js +205 -2
- package/dist/utils/ExplorerClient.js.map +1 -1
- package/dist/utils/balanceUtils.js +2 -2
- package/dist/utils/balanceUtils.js.map +1 -1
- package/dist/utils/balanceUtils.test.js +1 -0
- package/dist/utils/balanceUtils.test.js.map +1 -1
- package/dist/utils/bridgeUtils.d.ts +1 -3
- package/dist/utils/bridgeUtils.d.ts.map +1 -1
- package/dist/utils/bridgeUtils.js +1 -5
- package/dist/utils/bridgeUtils.js.map +1 -1
- package/dist/utils/bridgeUtils.test.js +3 -14
- package/dist/utils/bridgeUtils.test.js.map +1 -1
- package/package.json +11 -9
- package/src/config/RebalancerConfig.test.ts +459 -163
- package/src/config/RebalancerConfig.ts +5 -3
- package/src/config/types.ts +159 -52
- package/src/core/Rebalancer.test.ts +632 -0
- package/src/core/Rebalancer.ts +247 -157
- package/src/core/RebalancerService.test.ts +1144 -0
- package/src/core/RebalancerService.ts +245 -23
- package/src/factories/RebalancerContextFactory.ts +115 -14
- package/src/index.ts +16 -4
- package/src/interfaces/IMonitor.ts +15 -8
- package/src/interfaces/IRebalancer.ts +22 -4
- package/src/interfaces/IStrategy.ts +23 -2
- package/src/metrics/Metrics.ts +26 -5
- package/src/metrics/scripts/metrics.ts +14 -0
- package/src/monitor/Monitor.ts +109 -22
- package/src/strategy/BaseStrategy.ts +316 -26
- package/src/strategy/CollateralDeficitStrategy.test.ts +551 -0
- package/src/strategy/CollateralDeficitStrategy.ts +390 -0
- package/src/strategy/CompositeStrategy.test.ts +405 -0
- package/src/strategy/CompositeStrategy.ts +102 -0
- package/src/strategy/MinAmountStrategy.test.ts +189 -88
- package/src/strategy/MinAmountStrategy.ts +44 -13
- package/src/strategy/StrategyFactory.test.ts +2 -2
- package/src/strategy/StrategyFactory.ts +91 -8
- package/src/strategy/WeightedStrategy.test.ts +187 -72
- package/src/strategy/WeightedStrategy.ts +41 -7
- package/src/strategy/index.ts +2 -0
- package/src/test/helpers.ts +418 -14
- package/src/tracking/ActionTracker.test.ts +783 -0
- package/src/tracking/ActionTracker.ts +647 -0
- package/src/tracking/IActionTracker.ts +140 -0
- package/src/tracking/InflightContextAdapter.test.ts +203 -0
- package/src/tracking/InflightContextAdapter.ts +42 -0
- package/src/tracking/index.ts +36 -0
- package/src/tracking/store/IStore.ts +48 -0
- package/src/tracking/store/InMemoryStore.test.ts +338 -0
- package/src/tracking/store/InMemoryStore.ts +58 -0
- package/src/tracking/store/index.ts +2 -0
- package/src/tracking/types.ts +74 -0
- package/src/utils/ExplorerClient.ts +266 -3
- package/src/utils/balanceUtils.test.ts +1 -0
- package/src/utils/balanceUtils.ts +2 -2
- package/src/utils/bridgeUtils.test.ts +3 -15
- package/src/utils/bridgeUtils.ts +0 -10
- package/dist/core/WithInflightGuard.d.ts +0 -20
- package/dist/core/WithInflightGuard.d.ts.map +0 -1
- package/dist/core/WithInflightGuard.js +0 -47
- package/dist/core/WithInflightGuard.js.map +0 -1
- package/dist/core/WithInflightGuard.test.d.ts +0 -2
- package/dist/core/WithInflightGuard.test.d.ts.map +0 -1
- package/dist/core/WithInflightGuard.test.js +0 -64
- package/dist/core/WithInflightGuard.test.js.map +0 -1
- package/dist/core/WithSemaphore.d.ts +0 -22
- package/dist/core/WithSemaphore.d.ts.map +0 -1
- package/dist/core/WithSemaphore.js +0 -67
- package/dist/core/WithSemaphore.js.map +0 -1
- package/dist/core/WithSemaphore.test.d.ts +0 -2
- package/dist/core/WithSemaphore.test.d.ts.map +0 -1
- package/dist/core/WithSemaphore.test.js +0 -83
- package/dist/core/WithSemaphore.test.js.map +0 -1
- package/src/core/WithInflightGuard.test.ts +0 -131
- package/src/core/WithInflightGuard.ts +0 -67
- package/src/core/WithSemaphore.test.ts +0 -111
- package/src/core/WithSemaphore.ts +0 -92
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
import chai, { expect } from 'chai';
|
|
2
|
+
import chaiAsPromised from 'chai-as-promised';
|
|
3
|
+
import { pino } from 'pino';
|
|
4
|
+
import Sinon from 'sinon';
|
|
5
|
+
import { EthJsonRpcBlockParameterTag } from '@hyperlane-xyz/sdk';
|
|
6
|
+
import { ActionTracker } from './ActionTracker.js';
|
|
7
|
+
import { InMemoryStore } from './store/InMemoryStore.js';
|
|
8
|
+
chai.use(chaiAsPromised);
|
|
9
|
+
const testLogger = pino({ level: 'silent' });
|
|
10
|
+
describe('ActionTracker', () => {
|
|
11
|
+
let transferStore;
|
|
12
|
+
let rebalanceIntentStore;
|
|
13
|
+
let rebalanceActionStore;
|
|
14
|
+
let explorerClient;
|
|
15
|
+
let core;
|
|
16
|
+
let config;
|
|
17
|
+
let tracker;
|
|
18
|
+
let mailboxStub;
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
transferStore = new InMemoryStore();
|
|
21
|
+
rebalanceIntentStore = new InMemoryStore();
|
|
22
|
+
rebalanceActionStore = new InMemoryStore();
|
|
23
|
+
// Create stub for ExplorerClient methods with default return values
|
|
24
|
+
const explorerGetInflightUserTransfers = Sinon.stub().resolves([]);
|
|
25
|
+
const explorerGetInflightRebalanceActions = Sinon.stub().resolves([]);
|
|
26
|
+
explorerClient = {
|
|
27
|
+
getInflightUserTransfers: explorerGetInflightUserTransfers,
|
|
28
|
+
getInflightRebalanceActions: explorerGetInflightRebalanceActions,
|
|
29
|
+
};
|
|
30
|
+
// Create stub for mailbox
|
|
31
|
+
mailboxStub = {
|
|
32
|
+
delivered: Sinon.stub().resolves(false),
|
|
33
|
+
};
|
|
34
|
+
// Create stub for HyperlaneCore
|
|
35
|
+
const coreGetContracts = Sinon.stub().returns({ mailbox: mailboxStub });
|
|
36
|
+
const multiProviderGetChainName = Sinon.stub().callsFake((domain) => `chain${domain}`);
|
|
37
|
+
core = {
|
|
38
|
+
getContracts: coreGetContracts,
|
|
39
|
+
multiProvider: {
|
|
40
|
+
getChainName: multiProviderGetChainName,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
config = {
|
|
44
|
+
routersByDomain: {
|
|
45
|
+
1: '0xrouter1',
|
|
46
|
+
2: '0xrouter2',
|
|
47
|
+
3: '0xrouter3',
|
|
48
|
+
},
|
|
49
|
+
bridges: ['0xbridge1', '0xbridge2'],
|
|
50
|
+
rebalancerAddress: '0xrebalancer',
|
|
51
|
+
};
|
|
52
|
+
tracker = new ActionTracker(transferStore, rebalanceIntentStore, rebalanceActionStore, explorerClient, core, config, testLogger);
|
|
53
|
+
});
|
|
54
|
+
describe('initialize', () => {
|
|
55
|
+
it('should query for inflight rebalance messages and create synthetic entities', async () => {
|
|
56
|
+
const inflightMessages = [
|
|
57
|
+
{
|
|
58
|
+
msg_id: '0xmsg1',
|
|
59
|
+
origin_domain_id: 1,
|
|
60
|
+
destination_domain_id: 2,
|
|
61
|
+
sender: '0xrouter1',
|
|
62
|
+
recipient: '0xrouter2',
|
|
63
|
+
origin_tx_hash: '0xtx1',
|
|
64
|
+
origin_tx_sender: '0xrebalancer',
|
|
65
|
+
origin_tx_recipient: '0xrouter1',
|
|
66
|
+
is_delivered: false,
|
|
67
|
+
message_body: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064',
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
explorerClient.getInflightRebalanceActions.resolves(inflightMessages);
|
|
71
|
+
explorerClient.getInflightUserTransfers.resolves([]);
|
|
72
|
+
// Ensure mailbox returns false so action stays in_progress
|
|
73
|
+
mailboxStub.delivered.resolves(false);
|
|
74
|
+
await tracker.initialize();
|
|
75
|
+
// Verify ExplorerClient was called twice:
|
|
76
|
+
// 1. During startup recovery in initialize()
|
|
77
|
+
// 2. During syncRebalanceActions() called from initialize()
|
|
78
|
+
expect(explorerClient.getInflightRebalanceActions.callCount).to.equal(2);
|
|
79
|
+
// Verify synthetic intent and action were created
|
|
80
|
+
const intents = await rebalanceIntentStore.getAll();
|
|
81
|
+
expect(intents).to.have.lengthOf(1);
|
|
82
|
+
expect(intents[0].status).to.equal('in_progress');
|
|
83
|
+
expect(intents[0].amount).to.equal(100n);
|
|
84
|
+
const actions = await rebalanceActionStore.getAll();
|
|
85
|
+
expect(actions).to.have.lengthOf(1);
|
|
86
|
+
expect(actions[0].id).to.equal('0xmsg1');
|
|
87
|
+
expect(actions[0].status).to.equal('in_progress');
|
|
88
|
+
expect(actions[0].messageId).to.equal('0xmsg1');
|
|
89
|
+
});
|
|
90
|
+
it('should skip creating action if it already exists', async () => {
|
|
91
|
+
const inflightMessages = [
|
|
92
|
+
{
|
|
93
|
+
msg_id: '0xmsg1',
|
|
94
|
+
origin_domain_id: 1,
|
|
95
|
+
destination_domain_id: 2,
|
|
96
|
+
sender: '0xrouter1',
|
|
97
|
+
recipient: '0xrouter2',
|
|
98
|
+
origin_tx_hash: '0xtx1',
|
|
99
|
+
origin_tx_sender: '0xrebalancer',
|
|
100
|
+
origin_tx_recipient: '0xrouter1',
|
|
101
|
+
is_delivered: false,
|
|
102
|
+
message_body: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064',
|
|
103
|
+
},
|
|
104
|
+
];
|
|
105
|
+
// Pre-create action
|
|
106
|
+
await rebalanceActionStore.save({
|
|
107
|
+
id: '0xmsg1',
|
|
108
|
+
status: 'in_progress',
|
|
109
|
+
intentId: 'existing-intent',
|
|
110
|
+
messageId: '0xmsg1',
|
|
111
|
+
origin: 1,
|
|
112
|
+
destination: 2,
|
|
113
|
+
amount: 100n,
|
|
114
|
+
createdAt: Date.now(),
|
|
115
|
+
updatedAt: Date.now(),
|
|
116
|
+
});
|
|
117
|
+
explorerClient.getInflightRebalanceActions.resolves(inflightMessages);
|
|
118
|
+
explorerClient.getInflightUserTransfers.resolves([]);
|
|
119
|
+
await tracker.initialize();
|
|
120
|
+
// Verify no additional action was created
|
|
121
|
+
const actions = await rebalanceActionStore.getAll();
|
|
122
|
+
expect(actions).to.have.lengthOf(1);
|
|
123
|
+
// Verify no intent was created either
|
|
124
|
+
const intents = await rebalanceIntentStore.getAll();
|
|
125
|
+
expect(intents).to.have.lengthOf(0);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
describe('syncTransfers', () => {
|
|
129
|
+
it('should create new transfers from Explorer messages', async () => {
|
|
130
|
+
const inflightMessages = [
|
|
131
|
+
{
|
|
132
|
+
msg_id: '0xmsg1',
|
|
133
|
+
origin_domain_id: 1,
|
|
134
|
+
destination_domain_id: 2,
|
|
135
|
+
sender: '0xuser1',
|
|
136
|
+
recipient: '0xuser2',
|
|
137
|
+
origin_tx_hash: '0xtx1',
|
|
138
|
+
origin_tx_sender: '0xuser1',
|
|
139
|
+
origin_tx_recipient: '0xrouter1',
|
|
140
|
+
is_delivered: false,
|
|
141
|
+
message_body: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064',
|
|
142
|
+
},
|
|
143
|
+
];
|
|
144
|
+
explorerClient.getInflightUserTransfers.resolves(inflightMessages);
|
|
145
|
+
await tracker.syncTransfers();
|
|
146
|
+
const transfers = await transferStore.getAll();
|
|
147
|
+
expect(transfers).to.have.lengthOf(1);
|
|
148
|
+
expect(transfers[0].id).to.equal('0xmsg1');
|
|
149
|
+
expect(transfers[0].status).to.equal('in_progress');
|
|
150
|
+
expect(transfers[0].sender).to.equal('0xuser1');
|
|
151
|
+
expect(transfers[0].amount).to.equal(100n);
|
|
152
|
+
});
|
|
153
|
+
it('should not duplicate transfers that already exist', async () => {
|
|
154
|
+
// Pre-create transfer
|
|
155
|
+
await transferStore.save({
|
|
156
|
+
id: '0xmsg1',
|
|
157
|
+
status: 'in_progress',
|
|
158
|
+
messageId: '0xmsg1',
|
|
159
|
+
origin: 1,
|
|
160
|
+
destination: 2,
|
|
161
|
+
amount: 100n,
|
|
162
|
+
sender: '0xuser1',
|
|
163
|
+
recipient: '0xuser2',
|
|
164
|
+
createdAt: Date.now(),
|
|
165
|
+
updatedAt: Date.now(),
|
|
166
|
+
});
|
|
167
|
+
const inflightMessages = [
|
|
168
|
+
{
|
|
169
|
+
msg_id: '0xmsg1',
|
|
170
|
+
origin_domain_id: 1,
|
|
171
|
+
destination_domain_id: 2,
|
|
172
|
+
sender: '0xuser1',
|
|
173
|
+
recipient: '0xuser2',
|
|
174
|
+
origin_tx_hash: '0xtx1',
|
|
175
|
+
origin_tx_sender: '0xuser1',
|
|
176
|
+
origin_tx_recipient: '0xrouter1',
|
|
177
|
+
is_delivered: false,
|
|
178
|
+
message_body: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064',
|
|
179
|
+
},
|
|
180
|
+
];
|
|
181
|
+
explorerClient.getInflightUserTransfers.resolves(inflightMessages);
|
|
182
|
+
await tracker.syncTransfers();
|
|
183
|
+
const transfers = await transferStore.getAll();
|
|
184
|
+
expect(transfers).to.have.lengthOf(1);
|
|
185
|
+
});
|
|
186
|
+
it('should mark transfers as complete when delivered', async () => {
|
|
187
|
+
// Pre-create transfer
|
|
188
|
+
await transferStore.save({
|
|
189
|
+
id: '0xmsg1',
|
|
190
|
+
status: 'in_progress',
|
|
191
|
+
messageId: '0xmsg1',
|
|
192
|
+
origin: 1,
|
|
193
|
+
destination: 2,
|
|
194
|
+
amount: 100n,
|
|
195
|
+
sender: '0xuser1',
|
|
196
|
+
recipient: '0xuser2',
|
|
197
|
+
createdAt: Date.now(),
|
|
198
|
+
updatedAt: Date.now(),
|
|
199
|
+
});
|
|
200
|
+
explorerClient.getInflightUserTransfers.resolves([]);
|
|
201
|
+
mailboxStub.delivered.resolves(true);
|
|
202
|
+
await tracker.syncTransfers();
|
|
203
|
+
const transfer = await transferStore.get('0xmsg1');
|
|
204
|
+
expect(transfer?.status).to.equal('complete');
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
describe('syncRebalanceIntents', () => {
|
|
208
|
+
it('should mark intents as complete when fully fulfilled', async () => {
|
|
209
|
+
const intent = {
|
|
210
|
+
id: 'intent-1',
|
|
211
|
+
status: 'in_progress',
|
|
212
|
+
origin: 1,
|
|
213
|
+
destination: 2,
|
|
214
|
+
amount: 100n,
|
|
215
|
+
fulfilledAmount: 100n,
|
|
216
|
+
createdAt: Date.now(),
|
|
217
|
+
updatedAt: Date.now(),
|
|
218
|
+
};
|
|
219
|
+
await rebalanceIntentStore.save(intent);
|
|
220
|
+
await tracker.syncRebalanceIntents();
|
|
221
|
+
const updated = await rebalanceIntentStore.get('intent-1');
|
|
222
|
+
expect(updated?.status).to.equal('complete');
|
|
223
|
+
});
|
|
224
|
+
it('should not mark intents as complete if not fully fulfilled', async () => {
|
|
225
|
+
const intent = {
|
|
226
|
+
id: 'intent-1',
|
|
227
|
+
status: 'in_progress',
|
|
228
|
+
origin: 1,
|
|
229
|
+
destination: 2,
|
|
230
|
+
amount: 100n,
|
|
231
|
+
fulfilledAmount: 50n,
|
|
232
|
+
createdAt: Date.now(),
|
|
233
|
+
updatedAt: Date.now(),
|
|
234
|
+
};
|
|
235
|
+
await rebalanceIntentStore.save(intent);
|
|
236
|
+
await tracker.syncRebalanceIntents();
|
|
237
|
+
const updated = await rebalanceIntentStore.get('intent-1');
|
|
238
|
+
expect(updated?.status).to.equal('in_progress');
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
describe('syncRebalanceActions', () => {
|
|
242
|
+
it('should mark actions as complete when delivered and update parent intent', async () => {
|
|
243
|
+
const intent = {
|
|
244
|
+
id: 'intent-1',
|
|
245
|
+
status: 'in_progress',
|
|
246
|
+
origin: 1,
|
|
247
|
+
destination: 2,
|
|
248
|
+
amount: 100n,
|
|
249
|
+
fulfilledAmount: 0n,
|
|
250
|
+
createdAt: Date.now(),
|
|
251
|
+
updatedAt: Date.now(),
|
|
252
|
+
};
|
|
253
|
+
const action = {
|
|
254
|
+
id: 'action-1',
|
|
255
|
+
status: 'in_progress',
|
|
256
|
+
intentId: 'intent-1',
|
|
257
|
+
messageId: '0xmsg1',
|
|
258
|
+
origin: 1,
|
|
259
|
+
destination: 2,
|
|
260
|
+
amount: 100n,
|
|
261
|
+
createdAt: Date.now(),
|
|
262
|
+
updatedAt: Date.now(),
|
|
263
|
+
};
|
|
264
|
+
await rebalanceIntentStore.save(intent);
|
|
265
|
+
await rebalanceActionStore.save(action);
|
|
266
|
+
mailboxStub.delivered.resolves(true);
|
|
267
|
+
await tracker.syncRebalanceActions();
|
|
268
|
+
// Action should be complete
|
|
269
|
+
const updatedAction = await rebalanceActionStore.get('action-1');
|
|
270
|
+
expect(updatedAction?.status).to.equal('complete');
|
|
271
|
+
// Intent should be updated and complete
|
|
272
|
+
const updatedIntent = await rebalanceIntentStore.get('intent-1');
|
|
273
|
+
expect(updatedIntent?.fulfilledAmount).to.equal(100n);
|
|
274
|
+
expect(updatedIntent?.status).to.equal('complete');
|
|
275
|
+
});
|
|
276
|
+
it('should not mark actions as complete if not delivered', async () => {
|
|
277
|
+
const action = {
|
|
278
|
+
id: 'action-1',
|
|
279
|
+
status: 'in_progress',
|
|
280
|
+
intentId: 'intent-1',
|
|
281
|
+
messageId: '0xmsg1',
|
|
282
|
+
origin: 1,
|
|
283
|
+
destination: 2,
|
|
284
|
+
amount: 100n,
|
|
285
|
+
createdAt: Date.now(),
|
|
286
|
+
updatedAt: Date.now(),
|
|
287
|
+
};
|
|
288
|
+
await rebalanceActionStore.save(action);
|
|
289
|
+
mailboxStub.delivered.resolves(false);
|
|
290
|
+
await tracker.syncRebalanceActions();
|
|
291
|
+
const updatedAction = await rebalanceActionStore.get('action-1');
|
|
292
|
+
expect(updatedAction?.status).to.equal('in_progress');
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
describe('getInProgressTransfers', () => {
|
|
296
|
+
it('should return only in_progress transfers', async () => {
|
|
297
|
+
await transferStore.save({
|
|
298
|
+
id: 'transfer-1',
|
|
299
|
+
status: 'in_progress',
|
|
300
|
+
messageId: '0xmsg1',
|
|
301
|
+
origin: 1,
|
|
302
|
+
destination: 2,
|
|
303
|
+
amount: 100n,
|
|
304
|
+
sender: '0xsender1',
|
|
305
|
+
recipient: '0xrecipient1',
|
|
306
|
+
createdAt: Date.now(),
|
|
307
|
+
updatedAt: Date.now(),
|
|
308
|
+
});
|
|
309
|
+
await transferStore.save({
|
|
310
|
+
id: 'transfer-2',
|
|
311
|
+
status: 'complete',
|
|
312
|
+
messageId: '0xmsg2',
|
|
313
|
+
origin: 2,
|
|
314
|
+
destination: 3,
|
|
315
|
+
amount: 200n,
|
|
316
|
+
sender: '0xsender2',
|
|
317
|
+
recipient: '0xrecipient2',
|
|
318
|
+
createdAt: Date.now(),
|
|
319
|
+
updatedAt: Date.now(),
|
|
320
|
+
});
|
|
321
|
+
const result = await tracker.getInProgressTransfers();
|
|
322
|
+
expect(result).to.have.lengthOf(1);
|
|
323
|
+
expect(result[0].id).to.equal('transfer-1');
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
describe('getActiveRebalanceIntents', () => {
|
|
327
|
+
it('should return only in_progress intents (origin tx confirmed)', async () => {
|
|
328
|
+
await rebalanceIntentStore.save({
|
|
329
|
+
id: 'intent-1',
|
|
330
|
+
status: 'not_started',
|
|
331
|
+
origin: 1,
|
|
332
|
+
destination: 2,
|
|
333
|
+
amount: 100n,
|
|
334
|
+
fulfilledAmount: 0n,
|
|
335
|
+
createdAt: Date.now(),
|
|
336
|
+
updatedAt: Date.now(),
|
|
337
|
+
});
|
|
338
|
+
await rebalanceIntentStore.save({
|
|
339
|
+
id: 'intent-2',
|
|
340
|
+
status: 'in_progress',
|
|
341
|
+
origin: 2,
|
|
342
|
+
destination: 3,
|
|
343
|
+
amount: 200n,
|
|
344
|
+
fulfilledAmount: 50n,
|
|
345
|
+
createdAt: Date.now(),
|
|
346
|
+
updatedAt: Date.now(),
|
|
347
|
+
});
|
|
348
|
+
await rebalanceIntentStore.save({
|
|
349
|
+
id: 'intent-3',
|
|
350
|
+
status: 'complete',
|
|
351
|
+
origin: 3,
|
|
352
|
+
destination: 1,
|
|
353
|
+
amount: 300n,
|
|
354
|
+
fulfilledAmount: 300n,
|
|
355
|
+
createdAt: Date.now(),
|
|
356
|
+
updatedAt: Date.now(),
|
|
357
|
+
});
|
|
358
|
+
// Only in_progress intents are returned - their origin tx is confirmed
|
|
359
|
+
// so simulation only needs to add to destination (origin already deducted on-chain)
|
|
360
|
+
const result = await tracker.getActiveRebalanceIntents();
|
|
361
|
+
expect(result).to.have.lengthOf(1);
|
|
362
|
+
expect(result[0].id).to.equal('intent-2');
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
describe('createRebalanceIntent', () => {
|
|
366
|
+
it('should create a new intent with status not_started', async () => {
|
|
367
|
+
const result = await tracker.createRebalanceIntent({
|
|
368
|
+
origin: 1,
|
|
369
|
+
destination: 2,
|
|
370
|
+
amount: 100n,
|
|
371
|
+
priority: 1,
|
|
372
|
+
strategyType: 'MinAmountStrategy',
|
|
373
|
+
});
|
|
374
|
+
expect(result.status).to.equal('not_started');
|
|
375
|
+
expect(result.origin).to.equal(1);
|
|
376
|
+
expect(result.destination).to.equal(2);
|
|
377
|
+
expect(result.amount).to.equal(100n);
|
|
378
|
+
expect(result.fulfilledAmount).to.equal(0n);
|
|
379
|
+
expect(result.priority).to.equal(1);
|
|
380
|
+
expect(result.strategyType).to.equal('MinAmountStrategy');
|
|
381
|
+
const stored = await rebalanceIntentStore.get(result.id);
|
|
382
|
+
expect(stored).to.deep.equal(result);
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
describe('createRebalanceAction', () => {
|
|
386
|
+
it('should create action and transition intent from not_started to in_progress', async () => {
|
|
387
|
+
const intent = {
|
|
388
|
+
id: 'intent-1',
|
|
389
|
+
status: 'not_started',
|
|
390
|
+
origin: 1,
|
|
391
|
+
destination: 2,
|
|
392
|
+
amount: 100n,
|
|
393
|
+
fulfilledAmount: 0n,
|
|
394
|
+
createdAt: Date.now(),
|
|
395
|
+
updatedAt: Date.now(),
|
|
396
|
+
};
|
|
397
|
+
await rebalanceIntentStore.save(intent);
|
|
398
|
+
const result = await tracker.createRebalanceAction({
|
|
399
|
+
intentId: 'intent-1',
|
|
400
|
+
origin: 1,
|
|
401
|
+
destination: 2,
|
|
402
|
+
amount: 100n,
|
|
403
|
+
messageId: '0xmsg1',
|
|
404
|
+
txHash: '0xtx1',
|
|
405
|
+
});
|
|
406
|
+
expect(result.status).to.equal('in_progress');
|
|
407
|
+
expect(result.intentId).to.equal('intent-1');
|
|
408
|
+
expect(result.messageId).to.equal('0xmsg1');
|
|
409
|
+
const updatedIntent = await rebalanceIntentStore.get('intent-1');
|
|
410
|
+
expect(updatedIntent?.status).to.equal('in_progress');
|
|
411
|
+
});
|
|
412
|
+
it('should not transition intent if already in_progress', async () => {
|
|
413
|
+
const intent = {
|
|
414
|
+
id: 'intent-1',
|
|
415
|
+
status: 'in_progress',
|
|
416
|
+
origin: 1,
|
|
417
|
+
destination: 2,
|
|
418
|
+
amount: 100n,
|
|
419
|
+
fulfilledAmount: 50n,
|
|
420
|
+
createdAt: Date.now(),
|
|
421
|
+
updatedAt: Date.now(),
|
|
422
|
+
};
|
|
423
|
+
await rebalanceIntentStore.save(intent);
|
|
424
|
+
await tracker.createRebalanceAction({
|
|
425
|
+
intentId: 'intent-1',
|
|
426
|
+
origin: 1,
|
|
427
|
+
destination: 2,
|
|
428
|
+
amount: 50n,
|
|
429
|
+
messageId: '0xmsg2',
|
|
430
|
+
txHash: '0xtx2',
|
|
431
|
+
});
|
|
432
|
+
const updatedIntent = await rebalanceIntentStore.get('intent-1');
|
|
433
|
+
expect(updatedIntent?.status).to.equal('in_progress');
|
|
434
|
+
expect(updatedIntent?.fulfilledAmount).to.equal(50n); // Should not change
|
|
435
|
+
});
|
|
436
|
+
});
|
|
437
|
+
describe('completeRebalanceAction', () => {
|
|
438
|
+
it('should mark action as complete and update parent intent fulfilledAmount', async () => {
|
|
439
|
+
const intent = {
|
|
440
|
+
id: 'intent-1',
|
|
441
|
+
status: 'in_progress',
|
|
442
|
+
origin: 1,
|
|
443
|
+
destination: 2,
|
|
444
|
+
amount: 100n,
|
|
445
|
+
fulfilledAmount: 0n,
|
|
446
|
+
createdAt: Date.now(),
|
|
447
|
+
updatedAt: Date.now(),
|
|
448
|
+
};
|
|
449
|
+
const action = {
|
|
450
|
+
id: 'action-1',
|
|
451
|
+
status: 'in_progress',
|
|
452
|
+
intentId: 'intent-1',
|
|
453
|
+
messageId: '0xmsg1',
|
|
454
|
+
origin: 1,
|
|
455
|
+
destination: 2,
|
|
456
|
+
amount: 100n,
|
|
457
|
+
createdAt: Date.now(),
|
|
458
|
+
updatedAt: Date.now(),
|
|
459
|
+
};
|
|
460
|
+
await rebalanceIntentStore.save(intent);
|
|
461
|
+
await rebalanceActionStore.save(action);
|
|
462
|
+
await tracker.completeRebalanceAction('action-1');
|
|
463
|
+
const updatedAction = await rebalanceActionStore.get('action-1');
|
|
464
|
+
expect(updatedAction?.status).to.equal('complete');
|
|
465
|
+
const updatedIntent = await rebalanceIntentStore.get('intent-1');
|
|
466
|
+
expect(updatedIntent?.fulfilledAmount).to.equal(100n);
|
|
467
|
+
expect(updatedIntent?.status).to.equal('complete');
|
|
468
|
+
});
|
|
469
|
+
it('should throw error when action not found', async () => {
|
|
470
|
+
await expect(tracker.completeRebalanceAction('non-existent')).to.be.rejectedWith('RebalanceAction non-existent not found');
|
|
471
|
+
});
|
|
472
|
+
});
|
|
473
|
+
describe('cancelRebalanceIntent', () => {
|
|
474
|
+
it('should mark intent as cancelled', async () => {
|
|
475
|
+
const intent = {
|
|
476
|
+
id: 'intent-1',
|
|
477
|
+
status: 'not_started',
|
|
478
|
+
origin: 1,
|
|
479
|
+
destination: 2,
|
|
480
|
+
amount: 100n,
|
|
481
|
+
fulfilledAmount: 0n,
|
|
482
|
+
createdAt: Date.now(),
|
|
483
|
+
updatedAt: Date.now(),
|
|
484
|
+
};
|
|
485
|
+
await rebalanceIntentStore.save(intent);
|
|
486
|
+
await tracker.cancelRebalanceIntent('intent-1');
|
|
487
|
+
const updated = await rebalanceIntentStore.get('intent-1');
|
|
488
|
+
expect(updated?.status).to.equal('cancelled');
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
describe('failRebalanceAction', () => {
|
|
492
|
+
it('should mark action as failed', async () => {
|
|
493
|
+
const action = {
|
|
494
|
+
id: 'action-1',
|
|
495
|
+
status: 'in_progress',
|
|
496
|
+
intentId: 'intent-1',
|
|
497
|
+
messageId: '0xmsg1',
|
|
498
|
+
origin: 1,
|
|
499
|
+
destination: 2,
|
|
500
|
+
amount: 100n,
|
|
501
|
+
createdAt: Date.now(),
|
|
502
|
+
updatedAt: Date.now(),
|
|
503
|
+
};
|
|
504
|
+
await rebalanceActionStore.save(action);
|
|
505
|
+
await tracker.failRebalanceAction('action-1');
|
|
506
|
+
const updated = await rebalanceActionStore.get('action-1');
|
|
507
|
+
expect(updated?.status).to.equal('failed');
|
|
508
|
+
});
|
|
509
|
+
});
|
|
510
|
+
describe('Explorer query parameters', () => {
|
|
511
|
+
it('should pass routersByDomain to getInflightRebalanceActions for warp route filtering', async () => {
|
|
512
|
+
explorerClient.getInflightRebalanceActions.resolves([]);
|
|
513
|
+
explorerClient.getInflightUserTransfers.resolves([]);
|
|
514
|
+
await tracker.initialize();
|
|
515
|
+
const call = explorerClient.getInflightRebalanceActions.firstCall;
|
|
516
|
+
expect(call).to.not.be.null;
|
|
517
|
+
const params = call.args[0];
|
|
518
|
+
expect(params.routersByDomain).to.deep.equal(config.routersByDomain);
|
|
519
|
+
expect(params.bridges).to.deep.equal(config.bridges);
|
|
520
|
+
expect(params.rebalancerAddress).to.equal(config.rebalancerAddress);
|
|
521
|
+
});
|
|
522
|
+
it('should pass routersByDomain to getInflightUserTransfers for warp route filtering', async () => {
|
|
523
|
+
explorerClient.getInflightRebalanceActions.resolves([]);
|
|
524
|
+
explorerClient.getInflightUserTransfers.resolves([]);
|
|
525
|
+
await tracker.initialize();
|
|
526
|
+
const call = explorerClient.getInflightUserTransfers.firstCall;
|
|
527
|
+
expect(call).to.not.be.null;
|
|
528
|
+
const params = call.args[0];
|
|
529
|
+
expect(params.routersByDomain).to.deep.equal(config.routersByDomain);
|
|
530
|
+
expect(params.excludeTxSender).to.equal(config.rebalancerAddress);
|
|
531
|
+
});
|
|
532
|
+
});
|
|
533
|
+
describe('confirmedBlockTags synchronization', () => {
|
|
534
|
+
it('should use provided blockTag in syncTransfers delivery check', async () => {
|
|
535
|
+
await transferStore.save({
|
|
536
|
+
id: '0xmsg1',
|
|
537
|
+
status: 'in_progress',
|
|
538
|
+
messageId: '0xmsg1',
|
|
539
|
+
origin: 1,
|
|
540
|
+
destination: 2,
|
|
541
|
+
amount: 100n,
|
|
542
|
+
sender: '0xuser1',
|
|
543
|
+
recipient: '0xuser2',
|
|
544
|
+
createdAt: Date.now(),
|
|
545
|
+
updatedAt: Date.now(),
|
|
546
|
+
});
|
|
547
|
+
explorerClient.getInflightUserTransfers.resolves([]);
|
|
548
|
+
mailboxStub.delivered.resolves(true);
|
|
549
|
+
const confirmedBlockTags = { chain2: 12345 };
|
|
550
|
+
await tracker.syncTransfers(confirmedBlockTags);
|
|
551
|
+
expect(mailboxStub.delivered.calledOnce).to.be.true;
|
|
552
|
+
const call = mailboxStub.delivered.firstCall;
|
|
553
|
+
expect(call.args[0]).to.equal('0xmsg1');
|
|
554
|
+
expect(call.args[1]).to.deep.equal({ blockTag: 12345 });
|
|
555
|
+
const transfer = await transferStore.get('0xmsg1');
|
|
556
|
+
expect(transfer?.status).to.equal('complete');
|
|
557
|
+
});
|
|
558
|
+
it('should use provided blockTag in syncRebalanceActions delivery check', async () => {
|
|
559
|
+
const intent = {
|
|
560
|
+
id: 'intent-1',
|
|
561
|
+
status: 'in_progress',
|
|
562
|
+
origin: 1,
|
|
563
|
+
destination: 2,
|
|
564
|
+
amount: 100n,
|
|
565
|
+
fulfilledAmount: 0n,
|
|
566
|
+
createdAt: Date.now(),
|
|
567
|
+
updatedAt: Date.now(),
|
|
568
|
+
};
|
|
569
|
+
const action = {
|
|
570
|
+
id: 'action-1',
|
|
571
|
+
status: 'in_progress',
|
|
572
|
+
intentId: 'intent-1',
|
|
573
|
+
messageId: '0xmsg1',
|
|
574
|
+
origin: 1,
|
|
575
|
+
destination: 2,
|
|
576
|
+
amount: 100n,
|
|
577
|
+
createdAt: Date.now(),
|
|
578
|
+
updatedAt: Date.now(),
|
|
579
|
+
};
|
|
580
|
+
await rebalanceIntentStore.save(intent);
|
|
581
|
+
await rebalanceActionStore.save(action);
|
|
582
|
+
explorerClient.getInflightRebalanceActions.resolves([]);
|
|
583
|
+
mailboxStub.delivered.resolves(true);
|
|
584
|
+
const confirmedBlockTags = { chain2: 99999 };
|
|
585
|
+
await tracker.syncRebalanceActions(confirmedBlockTags);
|
|
586
|
+
expect(mailboxStub.delivered.calledOnce).to.be.true;
|
|
587
|
+
const call = mailboxStub.delivered.firstCall;
|
|
588
|
+
expect(call.args[0]).to.equal('0xmsg1');
|
|
589
|
+
expect(call.args[1]).to.deep.equal({ blockTag: 99999 });
|
|
590
|
+
const updatedAction = await rebalanceActionStore.get('action-1');
|
|
591
|
+
expect(updatedAction?.status).to.equal('complete');
|
|
592
|
+
});
|
|
593
|
+
it('should handle string blockTags (like "safe" or "finalized")', async () => {
|
|
594
|
+
await transferStore.save({
|
|
595
|
+
id: '0xmsg1',
|
|
596
|
+
status: 'in_progress',
|
|
597
|
+
messageId: '0xmsg1',
|
|
598
|
+
origin: 1,
|
|
599
|
+
destination: 2,
|
|
600
|
+
amount: 100n,
|
|
601
|
+
sender: '0xuser1',
|
|
602
|
+
recipient: '0xuser2',
|
|
603
|
+
createdAt: Date.now(),
|
|
604
|
+
updatedAt: Date.now(),
|
|
605
|
+
});
|
|
606
|
+
explorerClient.getInflightUserTransfers.resolves([]);
|
|
607
|
+
mailboxStub.delivered.resolves(false);
|
|
608
|
+
const confirmedBlockTags = {
|
|
609
|
+
chain2: EthJsonRpcBlockParameterTag.Finalized,
|
|
610
|
+
};
|
|
611
|
+
await tracker.syncTransfers(confirmedBlockTags);
|
|
612
|
+
expect(mailboxStub.delivered.calledOnce).to.be.true;
|
|
613
|
+
const call = mailboxStub.delivered.firstCall;
|
|
614
|
+
expect(call.args[1]).to.deep.equal({ blockTag: 'finalized' });
|
|
615
|
+
});
|
|
616
|
+
it('should handle undefined blockTag for chain not in confirmedBlockTags', async () => {
|
|
617
|
+
await transferStore.save({
|
|
618
|
+
id: '0xmsg1',
|
|
619
|
+
status: 'in_progress',
|
|
620
|
+
messageId: '0xmsg1',
|
|
621
|
+
origin: 1,
|
|
622
|
+
destination: 3,
|
|
623
|
+
amount: 100n,
|
|
624
|
+
sender: '0xuser1',
|
|
625
|
+
recipient: '0xuser2',
|
|
626
|
+
createdAt: Date.now(),
|
|
627
|
+
updatedAt: Date.now(),
|
|
628
|
+
});
|
|
629
|
+
explorerClient.getInflightUserTransfers.resolves([]);
|
|
630
|
+
mailboxStub.delivered.resolves(false);
|
|
631
|
+
const confirmedBlockTags = { chain2: 12345 };
|
|
632
|
+
await tracker.syncTransfers(confirmedBlockTags);
|
|
633
|
+
expect(mailboxStub.delivered.calledOnce).to.be.true;
|
|
634
|
+
});
|
|
635
|
+
});
|
|
636
|
+
});
|
|
637
|
+
//# sourceMappingURL=ActionTracker.test.js.map
|