@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.
Files changed (96) hide show
  1. package/lib/commonjs/index.js +127 -9
  2. package/lib/commonjs/index.test.js +13 -0
  3. package/lib/commonjs/integrations/delegations/index.js +109 -2
  4. package/lib/commonjs/integrations/delegations/index.test.js +171 -0
  5. package/lib/commonjs/integrations/ramps/noah/index.test.js +18 -5
  6. package/lib/commonjs/integrations/trading/index.js +16 -5
  7. package/lib/commonjs/integrations/trading/lifi/index.js +297 -25
  8. package/lib/commonjs/integrations/trading/lifi/lifi.tradeAsset.test.js +360 -0
  9. package/lib/commonjs/integrations/trading/lifi/lifiStatusPoll.js +118 -0
  10. package/lib/commonjs/integrations/trading/lifi/lifiStatusPoll.test.js +66 -0
  11. package/lib/commonjs/integrations/trading/zero-x/index.js +129 -26
  12. package/lib/commonjs/integrations/trading/zero-x/index.test.js +163 -1
  13. package/lib/commonjs/integrations/yield/index.js +18 -4
  14. package/lib/commonjs/integrations/yield/yieldxyz.getValidators.test.js +71 -0
  15. package/lib/commonjs/integrations/yield/yieldxyz.highLevel.test.js +330 -0
  16. package/lib/commonjs/integrations/yield/yieldxyz.js +517 -1
  17. package/lib/commonjs/internal/pollLoop.js +64 -0
  18. package/lib/commonjs/internal/pollLoop.test.js +100 -0
  19. package/lib/commonjs/internal/stripStalePlanningNonce.js +65 -0
  20. package/lib/commonjs/internal/stripStalePlanningNonce.test.js +35 -0
  21. package/lib/commonjs/internal/waitForEvmOrUserOpConfirmation.js +155 -0
  22. package/lib/commonjs/internal/waitForEvmOrUserOpConfirmation.test.js +33 -0
  23. package/lib/commonjs/internal/waitForEvmTxConfirmation.js +104 -0
  24. package/lib/commonjs/internal/waitForSolanaTxConfirmation.js +106 -0
  25. package/lib/commonjs/internal/yieldEvmNetwork.js +60 -0
  26. package/lib/commonjs/mpc/index.js +116 -1
  27. package/lib/commonjs/provider/index.js +17 -0
  28. package/lib/commonjs/shared/trace/index.js +0 -1
  29. package/lib/esm/index.js +127 -9
  30. package/lib/esm/index.test.js +13 -0
  31. package/lib/esm/integrations/delegations/index.js +109 -2
  32. package/lib/esm/integrations/delegations/index.test.js +171 -0
  33. package/lib/esm/integrations/ramps/noah/index.test.js +18 -5
  34. package/lib/esm/integrations/trading/index.js +16 -5
  35. package/lib/esm/integrations/trading/lifi/index.js +292 -25
  36. package/lib/esm/integrations/trading/lifi/lifi.tradeAsset.test.js +332 -0
  37. package/lib/esm/integrations/trading/lifi/lifiStatusPoll.js +113 -0
  38. package/lib/esm/integrations/trading/lifi/lifiStatusPoll.test.js +64 -0
  39. package/lib/esm/integrations/trading/zero-x/index.js +129 -26
  40. package/lib/esm/integrations/trading/zero-x/index.test.js +141 -2
  41. package/lib/esm/integrations/yield/index.js +18 -4
  42. package/lib/esm/integrations/yield/yieldxyz.getValidators.test.js +66 -0
  43. package/lib/esm/integrations/yield/yieldxyz.highLevel.test.js +325 -0
  44. package/lib/esm/integrations/yield/yieldxyz.js +517 -1
  45. package/lib/esm/internal/pollLoop.js +59 -0
  46. package/lib/esm/internal/pollLoop.test.js +98 -0
  47. package/lib/esm/internal/stripStalePlanningNonce.js +61 -0
  48. package/lib/esm/internal/stripStalePlanningNonce.test.js +33 -0
  49. package/lib/esm/internal/waitForEvmOrUserOpConfirmation.js +151 -0
  50. package/lib/esm/internal/waitForEvmOrUserOpConfirmation.test.js +31 -0
  51. package/lib/esm/internal/waitForEvmTxConfirmation.js +100 -0
  52. package/lib/esm/internal/waitForSolanaTxConfirmation.js +102 -0
  53. package/lib/esm/internal/yieldEvmNetwork.js +55 -0
  54. package/lib/esm/mpc/index.js +116 -1
  55. package/lib/esm/provider/index.js +17 -0
  56. package/lib/esm/shared/trace/index.js +0 -1
  57. package/noah-types.d.ts +16 -2
  58. package/package.json +3 -2
  59. package/src/index.test.ts +15 -0
  60. package/src/index.ts +203 -14
  61. package/src/integrations/delegations/index.test.ts +251 -0
  62. package/src/integrations/delegations/index.ts +202 -4
  63. package/src/integrations/ramps/noah/index.test.ts +18 -5
  64. package/src/integrations/trading/index.ts +10 -7
  65. package/src/integrations/trading/lifi/index.ts +388 -28
  66. package/src/integrations/trading/lifi/lifi.tradeAsset.test.ts +436 -0
  67. package/src/integrations/trading/lifi/lifiStatusPoll.test.ts +74 -0
  68. package/src/integrations/trading/lifi/lifiStatusPoll.ts +158 -0
  69. package/src/integrations/trading/zero-x/index.test.ts +297 -1
  70. package/src/integrations/trading/zero-x/index.ts +181 -27
  71. package/src/integrations/yield/index.ts +24 -4
  72. package/src/integrations/yield/yieldxyz.getValidators.test.ts +70 -0
  73. package/src/integrations/yield/yieldxyz.highLevel.test.ts +403 -0
  74. package/src/integrations/yield/yieldxyz.ts +740 -8
  75. package/src/internal/pollLoop.test.ts +109 -0
  76. package/src/internal/pollLoop.ts +87 -0
  77. package/src/internal/stripStalePlanningNonce.test.ts +38 -0
  78. package/src/internal/stripStalePlanningNonce.ts +66 -0
  79. package/src/internal/waitForEvmOrUserOpConfirmation.test.ts +31 -0
  80. package/src/internal/waitForEvmOrUserOpConfirmation.ts +194 -0
  81. package/src/internal/waitForEvmTxConfirmation.ts +155 -0
  82. package/src/internal/waitForSolanaTxConfirmation.ts +135 -0
  83. package/src/internal/yieldEvmNetwork.ts +57 -0
  84. package/src/mpc/index.ts +142 -1
  85. package/src/provider/index.ts +25 -0
  86. package/src/shared/trace/index.ts +0 -1
  87. package/src/shared/types/README.md +6 -0
  88. package/src/shared/types/api.ts +12 -1
  89. package/src/shared/types/common.ts +332 -20
  90. package/src/shared/types/delegations.ts +10 -0
  91. package/src/shared/types/index.ts +1 -0
  92. package/src/shared/types/lifi.ts +82 -0
  93. package/src/shared/types/noah.ts +124 -33
  94. package/src/shared/types/yieldxyz.ts +186 -0
  95. package/src/shared/types/zero-x.ts +66 -0
  96. package/types.d.ts +6 -0
@@ -0,0 +1,330 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
15
+ return (mod && mod.__esModule) ? mod : { "default": mod };
16
+ };
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ const yieldxyz_1 = __importDefault(require("./yieldxyz"));
19
+ const mpc_1 = __importDefault(require("../../mpc"));
20
+ const portal_1 = __importDefault(require("../../__mocks/portal/portal"));
21
+ const constants_1 = require("../../__mocks/constants");
22
+ describe('YieldXyz high-level (deposit, withdraw, defaults)', () => {
23
+ let mpc;
24
+ let yieldXyz;
25
+ beforeEach(() => {
26
+ jest.clearAllMocks();
27
+ portal_1.default.host = constants_1.mockHost;
28
+ mpc = new mpc_1.default({ portal: portal_1.default });
29
+ yieldXyz = new yieldxyz_1.default({ mpc });
30
+ yieldXyz.setSignAndSendTransaction(jest.fn().mockResolvedValue('0xabc'));
31
+ });
32
+ function enterResponseWithTxs(txs) {
33
+ return {
34
+ data: {
35
+ rawResponse: {
36
+ id: 'action-1',
37
+ intent: 'enter',
38
+ type: 'STAKE',
39
+ yieldId: 'resolved-yield',
40
+ address: constants_1.mockAddress,
41
+ createdAt: '2024-01-01T00:00:00Z',
42
+ status: 'CREATED',
43
+ executionPattern: 'synchronous',
44
+ transactions: txs,
45
+ },
46
+ },
47
+ };
48
+ }
49
+ function exitResponseWithTxs(txs) {
50
+ return {
51
+ data: {
52
+ rawResponse: {
53
+ id: 'action-1',
54
+ intent: 'exit',
55
+ type: 'UNSTAKE',
56
+ yieldId: 'resolved-yield',
57
+ address: constants_1.mockAddress,
58
+ createdAt: '2024-01-01T00:00:00Z',
59
+ status: 'CREATED',
60
+ executionPattern: 'synchronous',
61
+ transactions: txs,
62
+ },
63
+ },
64
+ };
65
+ }
66
+ it('deposit throws when no signer is configured', () => __awaiter(void 0, void 0, void 0, function* () {
67
+ const y = new yieldxyz_1.default({ mpc });
68
+ yield expect(y.deposit({ yieldId: 'y1', amount: '1', address: constants_1.mockAddress })).rejects.toThrow('[YieldXyz] No signer configured. Call setSignAndSendTransaction()');
69
+ }));
70
+ it('deposit throws when chain is not full CAIP-2', () => __awaiter(void 0, void 0, void 0, function* () {
71
+ yield expect(yieldXyz.deposit({
72
+ chain: '1',
73
+ token: 'ETH',
74
+ amount: '1',
75
+ address: constants_1.mockAddress,
76
+ })).rejects.toThrow('full CAIP-2');
77
+ }));
78
+ it('deposit throws when neither yieldId nor chain+token', () => __awaiter(void 0, void 0, void 0, function* () {
79
+ yield expect(yieldXyz.deposit({ amount: '1', address: constants_1.mockAddress })).rejects.toThrow('Provide either yieldId');
80
+ }));
81
+ it('resolveYieldIdFromPortalDefaults: throws when defaults endpoint returns error', () => __awaiter(void 0, void 0, void 0, function* () {
82
+ jest.spyOn(mpc, 'getYieldXyzDefaults').mockResolvedValue({
83
+ error: 'boom',
84
+ });
85
+ yield expect(yieldXyz.deposit({
86
+ chain: 'eip155:1',
87
+ token: 'ETH',
88
+ amount: '1',
89
+ address: constants_1.mockAddress,
90
+ })).rejects.toThrow('Failed to get yield defaults');
91
+ }));
92
+ it('resolveYieldIdFromPortalDefaults: throws when no data', () => __awaiter(void 0, void 0, void 0, function* () {
93
+ jest.spyOn(mpc, 'getYieldXyzDefaults').mockResolvedValue({});
94
+ yield expect(yieldXyz.deposit({
95
+ chain: 'eip155:1',
96
+ token: 'ETH',
97
+ amount: '1',
98
+ address: constants_1.mockAddress,
99
+ })).rejects.toThrow('No data returned from yield defaults endpoint');
100
+ }));
101
+ it('resolveYieldIdFromPortalDefaults: throws when key missing', () => __awaiter(void 0, void 0, void 0, function* () {
102
+ jest.spyOn(mpc, 'getYieldXyzDefaults').mockResolvedValue({
103
+ data: { 'eip155:1:WETH': { yieldId: 'y', opportunity: null } },
104
+ });
105
+ yield expect(yieldXyz.deposit({
106
+ chain: 'eip155:1',
107
+ token: 'ETH',
108
+ amount: '1',
109
+ address: constants_1.mockAddress,
110
+ })).rejects.toThrow('No default yield for key "eip155:1:ETH"');
111
+ }));
112
+ it('resolveYieldIdFromPortalDefaults: trims token for map key', () => __awaiter(void 0, void 0, void 0, function* () {
113
+ jest.spyOn(mpc, 'getYieldXyzDefaults').mockResolvedValue({
114
+ data: {
115
+ 'eip155:1:ETH': { yieldId: 'yield-from-defaults', opportunity: null },
116
+ },
117
+ });
118
+ const enterSpy = jest
119
+ .spyOn(mpc, 'enterYieldXyzYield')
120
+ .mockResolvedValue(enterResponseWithTxs([
121
+ {
122
+ id: 'tx1',
123
+ network: 'ethereum',
124
+ unsignedTransaction: { to: '0x1', data: '0x' },
125
+ },
126
+ ]));
127
+ jest.spyOn(mpc, 'trackYieldXyzTransaction').mockResolvedValue({
128
+ data: { rawResponse: { status: 'BROADCASTED' } },
129
+ });
130
+ yield yieldXyz.deposit({
131
+ chain: 'eip155:1',
132
+ token: ' ETH ',
133
+ amount: '1',
134
+ address: constants_1.mockAddress,
135
+ });
136
+ expect(mpc.getYieldXyzDefaults).toHaveBeenCalledWith({
137
+ includeOpportunities: false,
138
+ });
139
+ expect(enterSpy).toHaveBeenCalledWith(expect.objectContaining({ yieldId: 'yield-from-defaults' }));
140
+ }));
141
+ it('deposit merges arguments so top-level amount wins', () => __awaiter(void 0, void 0, void 0, function* () {
142
+ const enterSpy = jest
143
+ .spyOn(mpc, 'enterYieldXyzYield')
144
+ .mockResolvedValue(enterResponseWithTxs([
145
+ {
146
+ id: 'tx1',
147
+ network: 'eip155:1',
148
+ unsignedTransaction: { to: '0x2', data: '0x' },
149
+ },
150
+ ]));
151
+ jest.spyOn(mpc, 'trackYieldXyzTransaction').mockResolvedValue({});
152
+ yield yieldXyz.deposit({
153
+ yieldId: 'y1',
154
+ amount: '10',
155
+ address: constants_1.mockAddress,
156
+ arguments: { amount: '99', validatorAddress: '0xv' },
157
+ });
158
+ expect(enterSpy).toHaveBeenCalledWith({
159
+ yieldId: 'y1',
160
+ address: constants_1.mockAddress,
161
+ arguments: {
162
+ amount: '10',
163
+ validatorAddress: '0xv',
164
+ },
165
+ });
166
+ }));
167
+ it('withdraw merges arguments so top-level amount wins (consistent with deposit)', () => __awaiter(void 0, void 0, void 0, function* () {
168
+ const exitSpy = jest.spyOn(mpc, 'exitYieldXyzYield').mockResolvedValue(exitResponseWithTxs([
169
+ {
170
+ id: 'tx1',
171
+ network: 'eip155:1',
172
+ unsignedTransaction: { to: '0x2', data: '0x' },
173
+ },
174
+ ]));
175
+ jest.spyOn(mpc, 'trackYieldXyzTransaction').mockResolvedValue({});
176
+ yield yieldXyz.withdraw({
177
+ yieldId: 'y1',
178
+ amount: '10',
179
+ address: constants_1.mockAddress,
180
+ arguments: { amount: '77' },
181
+ });
182
+ expect(exitSpy).toHaveBeenCalledWith({
183
+ yieldId: 'y1',
184
+ address: constants_1.mockAddress,
185
+ arguments: { amount: '10' },
186
+ });
187
+ }));
188
+ it('executeAndTrack: throws when no transactions', () => __awaiter(void 0, void 0, void 0, function* () {
189
+ jest.spyOn(mpc, 'enterYieldXyzYield').mockResolvedValue({
190
+ data: {
191
+ rawResponse: {
192
+ id: 'a',
193
+ intent: 'enter',
194
+ type: 'STAKE',
195
+ yieldId: 'y',
196
+ address: constants_1.mockAddress,
197
+ createdAt: '2024-01-01T00:00:00Z',
198
+ status: 'CREATED',
199
+ executionPattern: 'synchronous',
200
+ transactions: [],
201
+ },
202
+ },
203
+ });
204
+ yield expect(yieldXyz.deposit({ yieldId: 'y', amount: '1', address: constants_1.mockAddress })).rejects.toThrow('No transactions in yield action response.');
205
+ }));
206
+ it('executeAndTrack: single tx signs, tracks, returns hashes', () => __awaiter(void 0, void 0, void 0, function* () {
207
+ const sign = jest.fn().mockResolvedValue('0xsig');
208
+ yieldXyz.setSignAndSendTransaction(sign);
209
+ jest.spyOn(mpc, 'enterYieldXyzYield').mockResolvedValue(enterResponseWithTxs([
210
+ {
211
+ id: 'tx1',
212
+ network: 'ethereum',
213
+ unsignedTransaction: JSON.stringify({
214
+ to: '0xabc',
215
+ nonce: '0x1',
216
+ data: '0x',
217
+ }),
218
+ },
219
+ ]));
220
+ const trackSpy = jest
221
+ .spyOn(mpc, 'trackYieldXyzTransaction')
222
+ .mockResolvedValue({});
223
+ const result = yield yieldXyz.deposit({ yieldId: 'rid', amount: '1', address: constants_1.mockAddress });
224
+ expect(sign).toHaveBeenCalledTimes(1);
225
+ expect(trackSpy).toHaveBeenCalledWith({
226
+ transactionId: 'tx1',
227
+ hash: '0xsig',
228
+ });
229
+ expect(result.hashes).toEqual(['0xsig']);
230
+ expect(result.yieldId).toBe('resolved-yield');
231
+ }));
232
+ it('executeAndTrack: multi-tx sequential', () => __awaiter(void 0, void 0, void 0, function* () {
233
+ const sign = jest
234
+ .fn()
235
+ .mockResolvedValueOnce('0x1')
236
+ .mockResolvedValueOnce('0x2');
237
+ yieldXyz.setSignAndSendTransaction(sign);
238
+ jest.spyOn(mpc, 'enterYieldXyzYield').mockResolvedValue(enterResponseWithTxs([
239
+ {
240
+ id: 'a',
241
+ network: 'eip155:1',
242
+ unsignedTransaction: { to: '0x1' },
243
+ },
244
+ {
245
+ id: 'b',
246
+ network: 'eip155:1',
247
+ unsignedTransaction: { to: '0x2' },
248
+ },
249
+ ]));
250
+ jest.spyOn(mpc, 'trackYieldXyzTransaction').mockResolvedValue({});
251
+ const result = yield yieldXyz.deposit({ yieldId: 'r', amount: '1', address: constants_1.mockAddress });
252
+ expect(sign).toHaveBeenCalledTimes(2);
253
+ expect(result.hashes).toEqual(['0x1', '0x2']);
254
+ }));
255
+ it('waitForConfirmation false: still tracks but no confirmed progress', () => __awaiter(void 0, void 0, void 0, function* () {
256
+ const progress = [];
257
+ const waiter = jest.fn().mockResolvedValue(false);
258
+ const y = new yieldxyz_1.default({
259
+ mpc,
260
+ waitForConfirmation: waiter,
261
+ });
262
+ y.setSignAndSendTransaction(jest.fn().mockResolvedValue('0xh'));
263
+ jest.spyOn(mpc, 'enterYieldXyzYield').mockResolvedValue(enterResponseWithTxs([
264
+ {
265
+ id: 'tx1',
266
+ network: 'eip155:1',
267
+ unsignedTransaction: { to: '0x1' },
268
+ },
269
+ ]));
270
+ const trackSpy = jest
271
+ .spyOn(mpc, 'trackYieldXyzTransaction')
272
+ .mockResolvedValue({});
273
+ yield y.deposit({ yieldId: 'y', amount: '1', address: constants_1.mockAddress }, {
274
+ onProgress: (e) => progress.push(e.step),
275
+ });
276
+ expect(progress).toEqual([
277
+ 'signing',
278
+ 'submitted',
279
+ 'confirming',
280
+ ]);
281
+ expect(progress.includes('confirmed')).toBe(false);
282
+ expect(trackSpy).toHaveBeenCalledWith({
283
+ transactionId: 'tx1',
284
+ hash: '0xh',
285
+ });
286
+ }));
287
+ it('waitForConfirmation true: emits confirmed', () => __awaiter(void 0, void 0, void 0, function* () {
288
+ const progress = [];
289
+ const y = new yieldxyz_1.default({
290
+ mpc,
291
+ waitForConfirmation: jest.fn().mockResolvedValue(true),
292
+ });
293
+ y.setSignAndSendTransaction(jest.fn().mockResolvedValue('0xh'));
294
+ jest.spyOn(mpc, 'enterYieldXyzYield').mockResolvedValue(enterResponseWithTxs([
295
+ {
296
+ id: 'tx1',
297
+ network: 'eip155:1',
298
+ unsignedTransaction: { to: '0x1' },
299
+ },
300
+ ]));
301
+ jest.spyOn(mpc, 'trackYieldXyzTransaction').mockResolvedValue({});
302
+ yield y.deposit({ yieldId: 'y', amount: '1', address: constants_1.mockAddress }, {
303
+ onProgress: (e) => progress.push(e.step),
304
+ });
305
+ expect(progress).toContain('confirmed');
306
+ }));
307
+ it('echoes chain and token on result when resolved from defaults', () => __awaiter(void 0, void 0, void 0, function* () {
308
+ jest.spyOn(mpc, 'getYieldXyzDefaults').mockResolvedValue({
309
+ data: {
310
+ 'eip155:1:ETH': { yieldId: 'ydef', opportunity: null },
311
+ },
312
+ });
313
+ jest.spyOn(mpc, 'enterYieldXyzYield').mockResolvedValue(enterResponseWithTxs([
314
+ {
315
+ id: 'tx1',
316
+ network: 'eip155:1',
317
+ unsignedTransaction: { to: '0x1' },
318
+ },
319
+ ]));
320
+ jest.spyOn(mpc, 'trackYieldXyzTransaction').mockResolvedValue({});
321
+ const r = yield yieldXyz.deposit({
322
+ chain: 'eip155:1',
323
+ token: 'ETH',
324
+ amount: '1',
325
+ address: constants_1.mockAddress,
326
+ });
327
+ expect(r.chain).toBe('eip155:1');
328
+ expect(r.token).toBe('ETH');
329
+ }));
330
+ });