@portal-hq/web 3.13.2 → 3.14.0-alpha.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/lib/commonjs/index.js +127 -9
- package/lib/commonjs/index.test.js +13 -0
- package/lib/commonjs/integrations/delegations/index.js +109 -2
- package/lib/commonjs/integrations/delegations/index.test.js +171 -0
- package/lib/commonjs/integrations/ramps/noah/index.test.js +18 -5
- package/lib/commonjs/integrations/trading/index.js +16 -5
- package/lib/commonjs/integrations/trading/lifi/index.js +297 -25
- package/lib/commonjs/integrations/trading/lifi/lifi.tradeAsset.test.js +360 -0
- package/lib/commonjs/integrations/trading/lifi/lifiStatusPoll.js +118 -0
- package/lib/commonjs/integrations/trading/lifi/lifiStatusPoll.test.js +66 -0
- package/lib/commonjs/integrations/trading/zero-x/index.js +129 -26
- package/lib/commonjs/integrations/trading/zero-x/index.test.js +163 -1
- package/lib/commonjs/integrations/yield/index.js +18 -4
- package/lib/commonjs/integrations/yield/yieldxyz.getValidators.test.js +71 -0
- package/lib/commonjs/integrations/yield/yieldxyz.highLevel.test.js +330 -0
- package/lib/commonjs/integrations/yield/yieldxyz.js +517 -1
- package/lib/commonjs/internal/pollLoop.js +64 -0
- package/lib/commonjs/internal/pollLoop.test.js +100 -0
- package/lib/commonjs/internal/stripStalePlanningNonce.js +65 -0
- package/lib/commonjs/internal/stripStalePlanningNonce.test.js +35 -0
- package/lib/commonjs/internal/waitForEvmOrUserOpConfirmation.js +155 -0
- package/lib/commonjs/internal/waitForEvmOrUserOpConfirmation.test.js +33 -0
- package/lib/commonjs/internal/waitForEvmTxConfirmation.js +104 -0
- package/lib/commonjs/internal/waitForSolanaTxConfirmation.js +106 -0
- package/lib/commonjs/internal/yieldEvmNetwork.js +60 -0
- package/lib/commonjs/mpc/index.js +116 -1
- package/lib/commonjs/provider/index.js +17 -0
- package/lib/commonjs/shared/trace/index.js +0 -1
- package/lib/esm/index.js +127 -9
- package/lib/esm/index.test.js +13 -0
- package/lib/esm/integrations/delegations/index.js +109 -2
- package/lib/esm/integrations/delegations/index.test.js +171 -0
- package/lib/esm/integrations/ramps/noah/index.test.js +18 -5
- package/lib/esm/integrations/trading/index.js +16 -5
- package/lib/esm/integrations/trading/lifi/index.js +292 -25
- package/lib/esm/integrations/trading/lifi/lifi.tradeAsset.test.js +332 -0
- package/lib/esm/integrations/trading/lifi/lifiStatusPoll.js +113 -0
- package/lib/esm/integrations/trading/lifi/lifiStatusPoll.test.js +64 -0
- package/lib/esm/integrations/trading/zero-x/index.js +129 -26
- package/lib/esm/integrations/trading/zero-x/index.test.js +141 -2
- package/lib/esm/integrations/yield/index.js +18 -4
- package/lib/esm/integrations/yield/yieldxyz.getValidators.test.js +66 -0
- package/lib/esm/integrations/yield/yieldxyz.highLevel.test.js +325 -0
- package/lib/esm/integrations/yield/yieldxyz.js +517 -1
- package/lib/esm/internal/pollLoop.js +59 -0
- package/lib/esm/internal/pollLoop.test.js +98 -0
- package/lib/esm/internal/stripStalePlanningNonce.js +61 -0
- package/lib/esm/internal/stripStalePlanningNonce.test.js +33 -0
- package/lib/esm/internal/waitForEvmOrUserOpConfirmation.js +151 -0
- package/lib/esm/internal/waitForEvmOrUserOpConfirmation.test.js +31 -0
- package/lib/esm/internal/waitForEvmTxConfirmation.js +100 -0
- package/lib/esm/internal/waitForSolanaTxConfirmation.js +102 -0
- package/lib/esm/internal/yieldEvmNetwork.js +55 -0
- package/lib/esm/mpc/index.js +116 -1
- package/lib/esm/provider/index.js +17 -0
- package/lib/esm/shared/trace/index.js +0 -1
- package/noah-types.d.ts +16 -2
- package/package.json +3 -2
- package/src/index.test.ts +15 -0
- package/src/index.ts +203 -14
- package/src/integrations/delegations/index.test.ts +251 -0
- package/src/integrations/delegations/index.ts +202 -4
- package/src/integrations/ramps/noah/index.test.ts +18 -5
- package/src/integrations/trading/index.ts +10 -7
- package/src/integrations/trading/lifi/index.ts +388 -28
- package/src/integrations/trading/lifi/lifi.tradeAsset.test.ts +436 -0
- package/src/integrations/trading/lifi/lifiStatusPoll.test.ts +74 -0
- package/src/integrations/trading/lifi/lifiStatusPoll.ts +158 -0
- package/src/integrations/trading/zero-x/index.test.ts +297 -1
- package/src/integrations/trading/zero-x/index.ts +181 -27
- package/src/integrations/yield/index.ts +24 -4
- package/src/integrations/yield/yieldxyz.getValidators.test.ts +70 -0
- package/src/integrations/yield/yieldxyz.highLevel.test.ts +403 -0
- package/src/integrations/yield/yieldxyz.ts +740 -8
- package/src/internal/pollLoop.test.ts +109 -0
- package/src/internal/pollLoop.ts +87 -0
- package/src/internal/stripStalePlanningNonce.test.ts +38 -0
- package/src/internal/stripStalePlanningNonce.ts +66 -0
- package/src/internal/waitForEvmOrUserOpConfirmation.test.ts +31 -0
- package/src/internal/waitForEvmOrUserOpConfirmation.ts +194 -0
- package/src/internal/waitForEvmTxConfirmation.ts +155 -0
- package/src/internal/waitForSolanaTxConfirmation.ts +135 -0
- package/src/internal/yieldEvmNetwork.ts +57 -0
- package/src/mpc/index.ts +142 -1
- package/src/provider/index.ts +25 -0
- package/src/shared/trace/index.ts +0 -1
- package/src/shared/types/README.md +6 -0
- package/src/shared/types/api.ts +12 -1
- package/src/shared/types/common.ts +332 -20
- package/src/shared/types/delegations.ts +10 -0
- package/src/shared/types/index.ts +1 -0
- package/src/shared/types/lifi.ts +82 -0
- package/src/shared/types/noah.ts +124 -33
- package/src/shared/types/yieldxyz.ts +186 -0
- package/src/shared/types/zero-x.ts +66 -0
- package/types.d.ts +6 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @jest-environment jsdom
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
22
|
+
if (mod && mod.__esModule) return mod;
|
|
23
|
+
var result = {};
|
|
24
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
25
|
+
__setModuleDefault(result, mod);
|
|
26
|
+
return result;
|
|
27
|
+
};
|
|
28
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
29
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
30
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
31
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
32
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
33
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
34
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
38
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
39
|
+
};
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
const index_1 = __importStar(require("./index"));
|
|
42
|
+
const mpc_1 = __importDefault(require("../../../mpc"));
|
|
43
|
+
const portal_1 = __importDefault(require("../../../__mocks/portal/portal"));
|
|
44
|
+
const constants_1 = require("../../../__mocks/constants");
|
|
45
|
+
describe('normalizeChainToNetwork', () => {
|
|
46
|
+
it('maps hex chain ids to eip155', () => {
|
|
47
|
+
expect((0, index_1.normalizeChainToNetwork)('0x1')).toBe('eip155:1');
|
|
48
|
+
expect((0, index_1.normalizeChainToNetwork)('0xa')).toBe('eip155:10');
|
|
49
|
+
expect((0, index_1.normalizeChainToNetwork)('0x89')).toBe('eip155:137');
|
|
50
|
+
});
|
|
51
|
+
it('preserves existing eip155 and numeric decimal strings', () => {
|
|
52
|
+
expect((0, index_1.normalizeChainToNetwork)('eip155:42161')).toBe('eip155:42161');
|
|
53
|
+
expect((0, index_1.normalizeChainToNetwork)('42161')).toBe('eip155:42161');
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
describe('LiFi tradeAsset', () => {
|
|
57
|
+
let mpc;
|
|
58
|
+
let lifi;
|
|
59
|
+
beforeEach(() => {
|
|
60
|
+
jest.clearAllMocks();
|
|
61
|
+
portal_1.default.host = constants_1.mockHost;
|
|
62
|
+
mpc = new mpc_1.default({ portal: portal_1.default });
|
|
63
|
+
lifi = new index_1.default({
|
|
64
|
+
mpc,
|
|
65
|
+
signAndSendTransaction: () => __awaiter(void 0, void 0, void 0, function* () { return '0xsent'; }),
|
|
66
|
+
waitForConfirmation: () => __awaiter(void 0, void 0, void 0, function* () { return false; }),
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
it('throws when waitForConfirmation returns false and does not poll bridge status', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
70
|
+
jest
|
|
71
|
+
.spyOn(mpc, 'getLifiRoutes')
|
|
72
|
+
.mockResolvedValue(constants_1.mockLifiGetRoutesResponse);
|
|
73
|
+
jest
|
|
74
|
+
.spyOn(mpc, 'getLifiRouteStep')
|
|
75
|
+
.mockResolvedValue(constants_1.mockLifiGetRouteStepResponse);
|
|
76
|
+
const pollSpy = jest
|
|
77
|
+
.spyOn(lifi, 'pollStatus')
|
|
78
|
+
.mockRejectedValue(new Error('pollStatus should not run'));
|
|
79
|
+
yield expect(lifi.tradeAsset({
|
|
80
|
+
fromChain: 'eip155:8453',
|
|
81
|
+
toChain: 'eip155:42161',
|
|
82
|
+
fromToken: 'ETH',
|
|
83
|
+
toToken: 'USDC',
|
|
84
|
+
amount: '1000000000000000',
|
|
85
|
+
fromAddress: constants_1.mockEip155Address,
|
|
86
|
+
})).rejects.toThrow(/on-chain confirmation did not complete/);
|
|
87
|
+
expect(pollSpy).not.toHaveBeenCalled();
|
|
88
|
+
}));
|
|
89
|
+
it('strips nonce from EVM transaction request before sign', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
90
|
+
const swapStep = Object.assign(Object.assign({}, constants_1.mockLifiStep), { type: 'swap', action: Object.assign(Object.assign({}, constants_1.mockLifiAction), { fromChainId: 'eip155:1', toChainId: 'eip155:1' }) });
|
|
91
|
+
const route = Object.assign(Object.assign({}, constants_1.mockLifiRoute), { fromChainId: 'eip155:1', toChainId: 'eip155:1', steps: [swapStep] });
|
|
92
|
+
const routesRes = {
|
|
93
|
+
data: {
|
|
94
|
+
rawResponse: {
|
|
95
|
+
routes: [route],
|
|
96
|
+
unavailableRoutes: { filteredOut: [], failed: [] },
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
const stepRes = {
|
|
101
|
+
data: {
|
|
102
|
+
rawResponse: Object.assign(Object.assign({}, swapStep), { transactionRequest: Object.assign(Object.assign({}, swapStep.transactionRequest), { nonce: '0xdeadbeef' }) }),
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
jest.spyOn(mpc, 'getLifiRoutes').mockResolvedValue(routesRes);
|
|
106
|
+
jest.spyOn(mpc, 'getLifiRouteStep').mockResolvedValue(stepRes);
|
|
107
|
+
const signer = jest.fn().mockResolvedValue('0xsigned');
|
|
108
|
+
const waiter = jest.fn().mockResolvedValue(true);
|
|
109
|
+
const lifiOk = new index_1.default({
|
|
110
|
+
mpc,
|
|
111
|
+
signAndSendTransaction: signer,
|
|
112
|
+
waitForConfirmation: waiter,
|
|
113
|
+
});
|
|
114
|
+
yield lifiOk.tradeAsset({
|
|
115
|
+
fromChain: 'eip155:1',
|
|
116
|
+
toChain: 'eip155:1',
|
|
117
|
+
fromToken: 'ETH',
|
|
118
|
+
toToken: 'USDC',
|
|
119
|
+
amount: '1000000000000000',
|
|
120
|
+
fromAddress: constants_1.mockEip155Address,
|
|
121
|
+
});
|
|
122
|
+
expect(signer).toHaveBeenCalledTimes(1);
|
|
123
|
+
const sent = signer.mock.calls[0][0];
|
|
124
|
+
expect(sent.nonce).toBeUndefined();
|
|
125
|
+
}));
|
|
126
|
+
it('emits onProgress failed on unexpected errors (outer catch)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
127
|
+
const onProgress = jest.fn();
|
|
128
|
+
jest.spyOn(mpc, 'getLifiRoutes').mockRejectedValue(new Error('network down'));
|
|
129
|
+
yield expect(lifi.tradeAsset({
|
|
130
|
+
fromChain: 'eip155:8453',
|
|
131
|
+
toChain: 'eip155:42161',
|
|
132
|
+
fromToken: 'ETH',
|
|
133
|
+
toToken: 'USDC',
|
|
134
|
+
amount: '1000000000000000',
|
|
135
|
+
fromAddress: constants_1.mockEip155Address,
|
|
136
|
+
onProgress,
|
|
137
|
+
}, {
|
|
138
|
+
signAndSendTransaction: () => __awaiter(void 0, void 0, void 0, function* () { return '0x'; }),
|
|
139
|
+
waitForConfirmation: () => __awaiter(void 0, void 0, void 0, function* () { return true; }),
|
|
140
|
+
})).rejects.toThrow('network down');
|
|
141
|
+
expect(onProgress).toHaveBeenCalledWith('failed', expect.objectContaining({ errorMessage: 'network down' }));
|
|
142
|
+
}));
|
|
143
|
+
it('throws when routeIndex is out of bounds', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
144
|
+
jest
|
|
145
|
+
.spyOn(mpc, 'getLifiRoutes')
|
|
146
|
+
.mockResolvedValue(constants_1.mockLifiGetRoutesResponse);
|
|
147
|
+
const lifiOk = new index_1.default({
|
|
148
|
+
mpc,
|
|
149
|
+
signAndSendTransaction: () => __awaiter(void 0, void 0, void 0, function* () { return '0x'; }),
|
|
150
|
+
waitForConfirmation: () => __awaiter(void 0, void 0, void 0, function* () { return true; }),
|
|
151
|
+
});
|
|
152
|
+
yield expect(lifiOk.tradeAsset({
|
|
153
|
+
fromChain: 'eip155:8453',
|
|
154
|
+
toChain: 'eip155:42161',
|
|
155
|
+
fromToken: 'ETH',
|
|
156
|
+
toToken: 'USDC',
|
|
157
|
+
amount: '1000000000000000',
|
|
158
|
+
fromAddress: constants_1.mockEip155Address,
|
|
159
|
+
routeIndex: 99,
|
|
160
|
+
})).rejects.toThrow(/no route available at index 99/);
|
|
161
|
+
}));
|
|
162
|
+
it('throws when getRouteStep returns error', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
163
|
+
jest
|
|
164
|
+
.spyOn(mpc, 'getLifiRoutes')
|
|
165
|
+
.mockResolvedValue(constants_1.mockLifiGetRoutesResponse);
|
|
166
|
+
jest.spyOn(mpc, 'getLifiRouteStep').mockResolvedValue({
|
|
167
|
+
error: 'step build failed',
|
|
168
|
+
});
|
|
169
|
+
const lifiOk = new index_1.default({
|
|
170
|
+
mpc,
|
|
171
|
+
signAndSendTransaction: () => __awaiter(void 0, void 0, void 0, function* () { return '0x'; }),
|
|
172
|
+
waitForConfirmation: () => __awaiter(void 0, void 0, void 0, function* () { return true; }),
|
|
173
|
+
});
|
|
174
|
+
yield expect(lifiOk.tradeAsset({
|
|
175
|
+
fromChain: 'eip155:8453',
|
|
176
|
+
toChain: 'eip155:42161',
|
|
177
|
+
fromToken: 'ETH',
|
|
178
|
+
toToken: 'USDC',
|
|
179
|
+
amount: '1000000000000000',
|
|
180
|
+
fromAddress: constants_1.mockEip155Address,
|
|
181
|
+
})).rejects.toThrow('getRouteStep: step build failed');
|
|
182
|
+
}));
|
|
183
|
+
it('cross-chain step calls pollStatus with fast statusPoll options', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
184
|
+
jest
|
|
185
|
+
.spyOn(mpc, 'getLifiRoutes')
|
|
186
|
+
.mockResolvedValue(constants_1.mockLifiGetRoutesResponse);
|
|
187
|
+
jest
|
|
188
|
+
.spyOn(mpc, 'getLifiRouteStep')
|
|
189
|
+
.mockResolvedValue(constants_1.mockLifiGetRouteStepResponse);
|
|
190
|
+
jest
|
|
191
|
+
.spyOn(mpc, 'getLifiStatus')
|
|
192
|
+
.mockResolvedValue(constants_1.mockLifiGetStatusResponse);
|
|
193
|
+
const pollSpy = jest.spyOn(index_1.default.prototype, 'pollStatus');
|
|
194
|
+
const signer = jest.fn().mockResolvedValue('0xstep1');
|
|
195
|
+
const lifiOk = new index_1.default({
|
|
196
|
+
mpc,
|
|
197
|
+
signAndSendTransaction: signer,
|
|
198
|
+
waitForConfirmation: () => __awaiter(void 0, void 0, void 0, function* () { return true; }),
|
|
199
|
+
});
|
|
200
|
+
yield lifiOk.tradeAsset({
|
|
201
|
+
fromChain: 'eip155:8453',
|
|
202
|
+
toChain: 'eip155:42161',
|
|
203
|
+
fromToken: 'ETH',
|
|
204
|
+
toToken: 'USDC',
|
|
205
|
+
amount: '1000000000000000',
|
|
206
|
+
fromAddress: constants_1.mockEip155Address,
|
|
207
|
+
statusPoll: {
|
|
208
|
+
initialDelayMs: 0,
|
|
209
|
+
everyMs: 5,
|
|
210
|
+
timeoutMs: 20000,
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
expect(signer).toHaveBeenCalled();
|
|
214
|
+
expect(pollSpy).toHaveBeenCalled();
|
|
215
|
+
pollSpy.mockRestore();
|
|
216
|
+
}));
|
|
217
|
+
it('same-chain swap does not call pollStatus', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
218
|
+
const swapStep = Object.assign(Object.assign({}, constants_1.mockLifiStep), { type: 'swap', action: Object.assign(Object.assign({}, constants_1.mockLifiAction), { fromChainId: 'eip155:1', toChainId: 'eip155:1' }) });
|
|
219
|
+
const route = Object.assign(Object.assign({}, constants_1.mockLifiRoute), { fromChainId: 'eip155:1', toChainId: 'eip155:1', steps: [swapStep] });
|
|
220
|
+
const routesRes = {
|
|
221
|
+
data: {
|
|
222
|
+
rawResponse: {
|
|
223
|
+
routes: [route],
|
|
224
|
+
unavailableRoutes: { filteredOut: [], failed: [] },
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
const stepRes = {
|
|
229
|
+
data: {
|
|
230
|
+
rawResponse: Object.assign(Object.assign({}, swapStep), { transactionRequest: Object.assign(Object.assign({}, swapStep.transactionRequest), { chainId: 'eip155:1' }) }),
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
jest.spyOn(mpc, 'getLifiRoutes').mockResolvedValue(routesRes);
|
|
234
|
+
jest.spyOn(mpc, 'getLifiRouteStep').mockResolvedValue(stepRes);
|
|
235
|
+
const pollSpy = jest.spyOn(index_1.default.prototype, 'pollStatus');
|
|
236
|
+
const lifiOk = new index_1.default({
|
|
237
|
+
mpc,
|
|
238
|
+
signAndSendTransaction: () => __awaiter(void 0, void 0, void 0, function* () { return '0xswap'; }),
|
|
239
|
+
waitForConfirmation: () => __awaiter(void 0, void 0, void 0, function* () { return true; }),
|
|
240
|
+
});
|
|
241
|
+
yield lifiOk.tradeAsset({
|
|
242
|
+
fromChain: 'eip155:1',
|
|
243
|
+
toChain: 'eip155:1',
|
|
244
|
+
fromToken: 'ETH',
|
|
245
|
+
toToken: 'USDC',
|
|
246
|
+
amount: '1000000000000000',
|
|
247
|
+
fromAddress: constants_1.mockEip155Address,
|
|
248
|
+
});
|
|
249
|
+
expect(pollSpy).not.toHaveBeenCalled();
|
|
250
|
+
pollSpy.mockRestore();
|
|
251
|
+
}));
|
|
252
|
+
it('throws when signer returns empty hash', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
253
|
+
jest
|
|
254
|
+
.spyOn(mpc, 'getLifiRoutes')
|
|
255
|
+
.mockResolvedValue(constants_1.mockLifiGetRoutesResponse);
|
|
256
|
+
jest
|
|
257
|
+
.spyOn(mpc, 'getLifiRouteStep')
|
|
258
|
+
.mockResolvedValue(constants_1.mockLifiGetRouteStepResponse);
|
|
259
|
+
const onProgress = jest.fn();
|
|
260
|
+
const lifiOk = new index_1.default({
|
|
261
|
+
mpc,
|
|
262
|
+
signAndSendTransaction: () => __awaiter(void 0, void 0, void 0, function* () { return ' '; }),
|
|
263
|
+
waitForConfirmation: () => __awaiter(void 0, void 0, void 0, function* () { return true; }),
|
|
264
|
+
});
|
|
265
|
+
yield expect(lifiOk.tradeAsset({
|
|
266
|
+
fromChain: 'eip155:8453',
|
|
267
|
+
toChain: 'eip155:42161',
|
|
268
|
+
fromToken: 'ETH',
|
|
269
|
+
toToken: 'USDC',
|
|
270
|
+
amount: '1000000000000000',
|
|
271
|
+
fromAddress: constants_1.mockEip155Address,
|
|
272
|
+
onProgress,
|
|
273
|
+
})).rejects.toThrow(/Invalid transaction hash/);
|
|
274
|
+
expect(onProgress).toHaveBeenCalledWith('failed', expect.objectContaining({ errorMessage: expect.stringContaining('Invalid') }));
|
|
275
|
+
}));
|
|
276
|
+
it('emits failed only ONCE when signer returns empty hash (no duplicate)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
277
|
+
jest
|
|
278
|
+
.spyOn(mpc, 'getLifiRoutes')
|
|
279
|
+
.mockResolvedValue(constants_1.mockLifiGetRoutesResponse);
|
|
280
|
+
jest
|
|
281
|
+
.spyOn(mpc, 'getLifiRouteStep')
|
|
282
|
+
.mockResolvedValue(constants_1.mockLifiGetRouteStepResponse);
|
|
283
|
+
const onProgress = jest.fn();
|
|
284
|
+
const lifiOk = new index_1.default({
|
|
285
|
+
mpc,
|
|
286
|
+
signAndSendTransaction: () => __awaiter(void 0, void 0, void 0, function* () { return ''; }),
|
|
287
|
+
waitForConfirmation: () => __awaiter(void 0, void 0, void 0, function* () { return true; }),
|
|
288
|
+
});
|
|
289
|
+
yield expect(lifiOk.tradeAsset({
|
|
290
|
+
fromChain: 'eip155:8453',
|
|
291
|
+
toChain: 'eip155:42161',
|
|
292
|
+
fromToken: 'ETH',
|
|
293
|
+
toToken: 'USDC',
|
|
294
|
+
amount: '1000000000000000',
|
|
295
|
+
fromAddress: constants_1.mockEip155Address,
|
|
296
|
+
onProgress,
|
|
297
|
+
})).rejects.toThrow(/Invalid transaction hash/);
|
|
298
|
+
const failedCalls = onProgress.mock.calls.filter((call) => call[0] === 'failed');
|
|
299
|
+
expect(failedCalls).toHaveLength(1);
|
|
300
|
+
expect(failedCalls[0][1]).toMatchObject({
|
|
301
|
+
errorMessage: expect.stringContaining('Invalid transaction hash'),
|
|
302
|
+
stepIndex: 0,
|
|
303
|
+
totalSteps: 1,
|
|
304
|
+
});
|
|
305
|
+
}));
|
|
306
|
+
it('emits failed only ONCE when waitForConfirmation returns false (no duplicate)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
307
|
+
jest
|
|
308
|
+
.spyOn(mpc, 'getLifiRoutes')
|
|
309
|
+
.mockResolvedValue(constants_1.mockLifiGetRoutesResponse);
|
|
310
|
+
jest
|
|
311
|
+
.spyOn(mpc, 'getLifiRouteStep')
|
|
312
|
+
.mockResolvedValue(constants_1.mockLifiGetRouteStepResponse);
|
|
313
|
+
const onProgress = jest.fn();
|
|
314
|
+
const lifiOk = new index_1.default({
|
|
315
|
+
mpc,
|
|
316
|
+
signAndSendTransaction: () => __awaiter(void 0, void 0, void 0, function* () { return '0xhash'; }),
|
|
317
|
+
waitForConfirmation: () => __awaiter(void 0, void 0, void 0, function* () { return false; }),
|
|
318
|
+
});
|
|
319
|
+
yield expect(lifiOk.tradeAsset({
|
|
320
|
+
fromChain: 'eip155:8453',
|
|
321
|
+
toChain: 'eip155:42161',
|
|
322
|
+
fromToken: 'ETH',
|
|
323
|
+
toToken: 'USDC',
|
|
324
|
+
amount: '1000000000000000',
|
|
325
|
+
fromAddress: constants_1.mockEip155Address,
|
|
326
|
+
onProgress,
|
|
327
|
+
})).rejects.toThrow(/on-chain confirmation did not complete/);
|
|
328
|
+
const failedCalls = onProgress.mock.calls.filter((call) => call[0] === 'failed');
|
|
329
|
+
expect(failedCalls).toHaveLength(1);
|
|
330
|
+
expect(failedCalls[0][1]).toMatchObject({
|
|
331
|
+
errorMessage: expect.stringContaining('on-chain confirmation'),
|
|
332
|
+
txHash: '0xhash',
|
|
333
|
+
stepIndex: 0,
|
|
334
|
+
totalSteps: 1,
|
|
335
|
+
});
|
|
336
|
+
}));
|
|
337
|
+
it('throws when waitForConfirmation throws', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
338
|
+
jest
|
|
339
|
+
.spyOn(mpc, 'getLifiRoutes')
|
|
340
|
+
.mockResolvedValue(constants_1.mockLifiGetRoutesResponse);
|
|
341
|
+
jest
|
|
342
|
+
.spyOn(mpc, 'getLifiRouteStep')
|
|
343
|
+
.mockResolvedValue(constants_1.mockLifiGetRouteStepResponse);
|
|
344
|
+
const lifiOk = new index_1.default({
|
|
345
|
+
mpc,
|
|
346
|
+
signAndSendTransaction: () => __awaiter(void 0, void 0, void 0, function* () { return '0xabc'; }),
|
|
347
|
+
waitForConfirmation: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
348
|
+
throw new Error('waiter boom');
|
|
349
|
+
}),
|
|
350
|
+
});
|
|
351
|
+
yield expect(lifiOk.tradeAsset({
|
|
352
|
+
fromChain: 'eip155:8453',
|
|
353
|
+
toChain: 'eip155:42161',
|
|
354
|
+
fromToken: 'ETH',
|
|
355
|
+
toToken: 'USDC',
|
|
356
|
+
amount: '1000000000000000',
|
|
357
|
+
fromAddress: constants_1.mockEip155Address,
|
|
358
|
+
})).rejects.toThrow('waiter boom');
|
|
359
|
+
}));
|
|
360
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.pollLifiStatusUntilTerminal = exports.isLikelyLifiIndexerNotReadyMessage = void 0;
|
|
13
|
+
/**
|
|
14
|
+
* LiFi status polling: domain tick (DONE / FAILED / indexer lag) over shared {@link pollLoop}.
|
|
15
|
+
*/
|
|
16
|
+
const logger_1 = require("../../../logger");
|
|
17
|
+
const pollLoop_1 = require("../../../internal/pollLoop");
|
|
18
|
+
const LOG_PREFIX = '[LiFi.pollStatus]';
|
|
19
|
+
const INDEXER_LAG_MESSAGE_SNIPPETS = [
|
|
20
|
+
'not an evm transaction',
|
|
21
|
+
'no transfer information found',
|
|
22
|
+
'transfer not found',
|
|
23
|
+
];
|
|
24
|
+
function isLikelyLifiIndexerNotReadyMessage(message) {
|
|
25
|
+
const m = message.toLowerCase();
|
|
26
|
+
return INDEXER_LAG_MESSAGE_SNIPPETS.some((s) => m.includes(s));
|
|
27
|
+
}
|
|
28
|
+
exports.isLikelyLifiIndexerNotReadyMessage = isLikelyLifiIndexerNotReadyMessage;
|
|
29
|
+
function pollLifiStatusUntilTerminal(params) {
|
|
30
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
31
|
+
let consecutiveHardErrors = 0;
|
|
32
|
+
try {
|
|
33
|
+
return yield (0, pollLoop_1.pollLoop)({
|
|
34
|
+
tick: () => __awaiter(this, void 0, void 0, function* () {
|
|
35
|
+
return lifiStatusPollTick(params, {
|
|
36
|
+
get: () => consecutiveHardErrors,
|
|
37
|
+
set: (n) => {
|
|
38
|
+
consecutiveHardErrors = n;
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}),
|
|
42
|
+
intervalMs: params.everyMs,
|
|
43
|
+
initialDelayMs: params.initialDelayMs,
|
|
44
|
+
backoff: params.backoff,
|
|
45
|
+
timeoutMs: params.timeoutMs,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (error instanceof pollLoop_1.PollLoopTimeoutError) {
|
|
50
|
+
throw new Error(`${LOG_PREFIX} timed out after ${params.timeoutMs}ms waiting for LiFi terminal status`);
|
|
51
|
+
}
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
exports.pollLifiStatusUntilTerminal = pollLifiStatusUntilTerminal;
|
|
57
|
+
function lifiStatusPollTick(params, counter) {
|
|
58
|
+
var _a, _b, _c;
|
|
59
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
60
|
+
try {
|
|
61
|
+
const res = yield params.getStatus(params.request);
|
|
62
|
+
if (res.error) {
|
|
63
|
+
logger_1.sdkLogger.warn(`${LOG_PREFIX} response error field`, res.error);
|
|
64
|
+
return bumpHardError(counter, params.maxConsecutiveErrors);
|
|
65
|
+
}
|
|
66
|
+
counter.set(0);
|
|
67
|
+
const raw = (_a = res.data) === null || _a === void 0 ? void 0 : _a.rawResponse;
|
|
68
|
+
if (!raw) {
|
|
69
|
+
return { kind: 'continue' };
|
|
70
|
+
}
|
|
71
|
+
const { status } = raw;
|
|
72
|
+
if (status === 'INVALID') {
|
|
73
|
+
return {
|
|
74
|
+
kind: 'throw',
|
|
75
|
+
error: new Error(`${LOG_PREFIX} LiFi status INVALID for ${params.request.txHash}`),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (status === 'DONE') {
|
|
79
|
+
return { kind: 'stop', value: raw };
|
|
80
|
+
}
|
|
81
|
+
if (status === 'FAILED') {
|
|
82
|
+
const detail = (_c = (_b = raw.substatusMessage) !== null && _b !== void 0 ? _b : raw.substatus) !== null && _c !== void 0 ? _c : 'LiFi transfer FAILED';
|
|
83
|
+
return {
|
|
84
|
+
kind: 'throw',
|
|
85
|
+
error: new Error(`${LOG_PREFIX} ${detail}`),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (params.onUpdate) {
|
|
89
|
+
const carryOn = params.onUpdate(raw);
|
|
90
|
+
if (carryOn === false) {
|
|
91
|
+
return { kind: 'stop', value: raw };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return { kind: 'continue' };
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
98
|
+
if (isLikelyLifiIndexerNotReadyMessage(message)) {
|
|
99
|
+
logger_1.sdkLogger.debug(`${LOG_PREFIX} indexer not ready, will retry`, message);
|
|
100
|
+
counter.set(0);
|
|
101
|
+
return { kind: 'continue' };
|
|
102
|
+
}
|
|
103
|
+
logger_1.sdkLogger.warn(`${LOG_PREFIX} getStatus error`, error);
|
|
104
|
+
return bumpHardError(counter, params.maxConsecutiveErrors);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function bumpHardError(counter, max) {
|
|
109
|
+
const next = counter.get() + 1;
|
|
110
|
+
counter.set(next);
|
|
111
|
+
if (next >= max) {
|
|
112
|
+
return {
|
|
113
|
+
kind: 'throw',
|
|
114
|
+
error: new Error(`${LOG_PREFIX} too many consecutive getStatus failures (${max})`),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
return { kind: 'continue' };
|
|
118
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @jest-environment jsdom
|
|
4
|
+
*/
|
|
5
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
6
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
7
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
8
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
9
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
10
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
11
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const lifiStatusPoll_1 = require("./lifiStatusPoll");
|
|
16
|
+
describe('lifiStatusPoll', () => {
|
|
17
|
+
describe('isLikelyLifiIndexerNotReadyMessage', () => {
|
|
18
|
+
it('returns true for known indexer lag phrases', () => {
|
|
19
|
+
expect((0, lifiStatusPoll_1.isLikelyLifiIndexerNotReadyMessage)('No transfer information found')).toBe(true);
|
|
20
|
+
expect((0, lifiStatusPoll_1.isLikelyLifiIndexerNotReadyMessage)('transfer not found')).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
it('returns false for unrelated errors', () => {
|
|
23
|
+
expect((0, lifiStatusPoll_1.isLikelyLifiIndexerNotReadyMessage)('Network error 400')).toBe(false);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
describe('pollLifiStatusUntilTerminal', () => {
|
|
27
|
+
it('treats response error field as hard error without resetting counter incorrectly', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
28
|
+
const getStatus = jest
|
|
29
|
+
.fn()
|
|
30
|
+
.mockResolvedValueOnce({
|
|
31
|
+
error: 'temporary',
|
|
32
|
+
data: undefined,
|
|
33
|
+
})
|
|
34
|
+
.mockResolvedValueOnce({
|
|
35
|
+
data: { rawResponse: { status: 'DONE' } },
|
|
36
|
+
});
|
|
37
|
+
const result = yield (0, lifiStatusPoll_1.pollLifiStatusUntilTerminal)({
|
|
38
|
+
getStatus,
|
|
39
|
+
request: { txHash: '0x1' },
|
|
40
|
+
everyMs: 5,
|
|
41
|
+
initialDelayMs: 0,
|
|
42
|
+
timeoutMs: 5000,
|
|
43
|
+
maxConsecutiveErrors: 10,
|
|
44
|
+
});
|
|
45
|
+
expect(result.status).toBe('DONE');
|
|
46
|
+
expect(getStatus).toHaveBeenCalledTimes(2);
|
|
47
|
+
}));
|
|
48
|
+
it('retries on indexer lag message without counting as hard error', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
49
|
+
const getStatus = jest
|
|
50
|
+
.fn()
|
|
51
|
+
.mockRejectedValueOnce(new Error('transfer not found'))
|
|
52
|
+
.mockResolvedValue({
|
|
53
|
+
data: { rawResponse: { status: 'DONE' } },
|
|
54
|
+
});
|
|
55
|
+
const result = yield (0, lifiStatusPoll_1.pollLifiStatusUntilTerminal)({
|
|
56
|
+
getStatus,
|
|
57
|
+
request: { txHash: '0x1' },
|
|
58
|
+
everyMs: 5,
|
|
59
|
+
initialDelayMs: 0,
|
|
60
|
+
timeoutMs: 5000,
|
|
61
|
+
maxConsecutiveErrors: 2,
|
|
62
|
+
});
|
|
63
|
+
expect(result.status).toBe('DONE');
|
|
64
|
+
}));
|
|
65
|
+
});
|
|
66
|
+
});
|