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