@portal-hq/web 3.13.1 → 3.13.2-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 +438 -0
  16. package/lib/commonjs/integrations/yield/yieldxyz.js +541 -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 +433 -0
  44. package/lib/esm/integrations/yield/yieldxyz.js +541 -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 +536 -0
  74. package/src/integrations/yield/yieldxyz.ts +762 -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 +193 -0
  95. package/src/shared/types/zero-x.ts +66 -0
  96. package/types.d.ts +6 -0
@@ -0,0 +1,100 @@
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
+ const pollLoop_1 = require("./pollLoop");
13
+ describe('pollLoop', () => {
14
+ it('returns immediately when tick returns stop', () => __awaiter(void 0, void 0, void 0, function* () {
15
+ const tick = jest.fn().mockResolvedValue({
16
+ kind: 'stop',
17
+ value: 42,
18
+ });
19
+ yield expect((0, pollLoop_1.pollLoop)({
20
+ tick,
21
+ intervalMs: 1000,
22
+ timeoutMs: 5000,
23
+ })).resolves.toBe(42);
24
+ expect(tick).toHaveBeenCalledTimes(1);
25
+ }));
26
+ it('rejects when tick returns throw', () => __awaiter(void 0, void 0, void 0, function* () {
27
+ const err = new Error('tick failed');
28
+ const tick = jest.fn().mockResolvedValue({
29
+ kind: 'throw',
30
+ error: err,
31
+ });
32
+ yield expect((0, pollLoop_1.pollLoop)({
33
+ tick,
34
+ intervalMs: 100,
35
+ timeoutMs: 1000,
36
+ })).rejects.toThrow('tick failed');
37
+ }));
38
+ it('throws PollLoopTimeoutError when tick always continues', () => __awaiter(void 0, void 0, void 0, function* () {
39
+ jest.useFakeTimers();
40
+ const tick = jest.fn().mockResolvedValue({ kind: 'continue' });
41
+ const p = (0, pollLoop_1.pollLoop)({
42
+ tick,
43
+ intervalMs: 10,
44
+ timeoutMs: 100,
45
+ });
46
+ const expectTimeout = expect(p).rejects.toBeInstanceOf(pollLoop_1.PollLoopTimeoutError);
47
+ yield jest.runAllTimersAsync();
48
+ yield expectTimeout;
49
+ jest.useRealTimers();
50
+ }));
51
+ it('waits initialDelayMs before first tick', () => __awaiter(void 0, void 0, void 0, function* () {
52
+ jest.useFakeTimers();
53
+ const tick = jest.fn().mockResolvedValue({
54
+ kind: 'stop',
55
+ value: true,
56
+ });
57
+ const p = (0, pollLoop_1.pollLoop)({
58
+ tick,
59
+ intervalMs: 100,
60
+ initialDelayMs: 300,
61
+ timeoutMs: 2000,
62
+ });
63
+ expect(tick).not.toHaveBeenCalled();
64
+ yield jest.advanceTimersByTimeAsync(299);
65
+ expect(tick).not.toHaveBeenCalled();
66
+ yield jest.advanceTimersByTimeAsync(1);
67
+ yield expect(p).resolves.toBe(true);
68
+ expect(tick).toHaveBeenCalledTimes(1);
69
+ jest.useRealTimers();
70
+ }));
71
+ it('increases sleep interval with backoff until maxIntervalMs', () => __awaiter(void 0, void 0, void 0, function* () {
72
+ jest.useFakeTimers();
73
+ let n = 0;
74
+ const tick = jest.fn().mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () {
75
+ n += 1;
76
+ if (n < 3) {
77
+ return { kind: 'continue' };
78
+ }
79
+ return { kind: 'stop', value: 'ok' };
80
+ }));
81
+ const p = (0, pollLoop_1.pollLoop)({
82
+ tick,
83
+ intervalMs: 10,
84
+ timeoutMs: 10000,
85
+ backoff: { factor: 100, maxIntervalMs: 500 },
86
+ });
87
+ const settled = p.then((v) => v);
88
+ yield Promise.resolve();
89
+ expect(tick).toHaveBeenCalledTimes(1);
90
+ yield jest.advanceTimersByTimeAsync(10);
91
+ yield Promise.resolve();
92
+ expect(tick).toHaveBeenCalledTimes(2);
93
+ yield jest.advanceTimersByTimeAsync(500);
94
+ yield Promise.resolve();
95
+ expect(tick).toHaveBeenCalledTimes(3);
96
+ yield settled;
97
+ expect(yield settled).toBe('ok');
98
+ jest.useRealTimers();
99
+ }));
100
+ });
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ /**
3
+ * Removes a stale `nonce` from EVM tx payloads planned ahead of signing so the
4
+ * signer can fetch a fresh nonce at broadcast time (multi-step Yield / LiFi).
5
+ */
6
+ var __rest = (this && this.__rest) || function (s, e) {
7
+ var t = {};
8
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
9
+ t[p] = s[p];
10
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
11
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
12
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
13
+ t[p[i]] = s[p[i]];
14
+ }
15
+ return t;
16
+ };
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.stripStalePlanningNonceIfJsonObject = void 0;
19
+ function unwrapSingleKeyTransactionObject(obj) {
20
+ const keys = Object.keys(obj);
21
+ if (keys.length !== 1) {
22
+ return obj;
23
+ }
24
+ const firstKey = keys[0];
25
+ const inner = obj[firstKey];
26
+ // Type guard: only unwrap if inner value is a plain object
27
+ if (typeof inner === 'object' &&
28
+ inner !== null &&
29
+ !Array.isArray(inner)) {
30
+ return inner;
31
+ }
32
+ return obj;
33
+ }
34
+ /**
35
+ * If the payload is JSON (object or JSON string), drop `nonce` from the inner tx object.
36
+ * Otherwise returns the input unchanged.
37
+ */
38
+ function stripStalePlanningNonceIfJsonObject(unsigned) {
39
+ if (typeof unsigned === 'object' &&
40
+ unsigned !== null &&
41
+ !Array.isArray(unsigned)) {
42
+ const spread = Object.assign({}, unsigned);
43
+ const inner = unwrapSingleKeyTransactionObject(spread);
44
+ const { nonce: _staleNonce } = inner, rest = __rest(inner, ["nonce"]);
45
+ return rest;
46
+ }
47
+ if (typeof unsigned === 'string') {
48
+ try {
49
+ const parsed = JSON.parse(unsigned);
50
+ // Validate parsed result is a plain object
51
+ if (typeof parsed === 'object' &&
52
+ parsed !== null &&
53
+ !Array.isArray(parsed)) {
54
+ const inner = unwrapSingleKeyTransactionObject(parsed);
55
+ const { nonce: _staleNonce } = inner, rest = __rest(inner, ["nonce"]);
56
+ return rest;
57
+ }
58
+ }
59
+ catch (_a) {
60
+ return unsigned;
61
+ }
62
+ }
63
+ return unsigned;
64
+ }
65
+ exports.stripStalePlanningNonceIfJsonObject = stripStalePlanningNonceIfJsonObject;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ /**
3
+ * @jest-environment node
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const stripStalePlanningNonce_1 = require("./stripStalePlanningNonce");
7
+ describe('stripStalePlanningNonceIfJsonObject', () => {
8
+ it('removes nonce from a flat tx object', () => {
9
+ const tx = { from: '0xa', to: '0xb', nonce: '0x5', data: '0x' };
10
+ expect((0, stripStalePlanningNonce_1.stripStalePlanningNonceIfJsonObject)(tx)).toEqual({
11
+ from: '0xa',
12
+ to: '0xb',
13
+ data: '0x',
14
+ });
15
+ });
16
+ it('unwraps single-key wrapper then strips nonce', () => {
17
+ const wrapped = {
18
+ transaction: { from: '0xa', nonce: 1, value: '0x0' },
19
+ };
20
+ expect((0, stripStalePlanningNonce_1.stripStalePlanningNonceIfJsonObject)(wrapped)).toEqual({
21
+ from: '0xa',
22
+ value: '0x0',
23
+ });
24
+ });
25
+ it('parses JSON string and strips nonce', () => {
26
+ const s = JSON.stringify({ to: '0xt', nonce: '2', gas: '21000' });
27
+ expect((0, stripStalePlanningNonce_1.stripStalePlanningNonceIfJsonObject)(s)).toEqual({
28
+ to: '0xt',
29
+ gas: '21000',
30
+ });
31
+ });
32
+ it('returns opaque string on parse error', () => {
33
+ expect((0, stripStalePlanningNonce_1.stripStalePlanningNonceIfJsonObject)('not-json')).toBe('not-json');
34
+ });
35
+ });
@@ -0,0 +1,155 @@
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.waitForEvmOrUserOpConfirmation = void 0;
13
+ const logger_1 = require("../logger");
14
+ const pollLoop_1 = require("./pollLoop");
15
+ const LOG_PREFIX = '[Portal.waitForConfirmation]';
16
+ function parseStatusToOutcome(status) {
17
+ if (typeof status === 'boolean')
18
+ return status ? 'success' : 'failed';
19
+ if (typeof status === 'number')
20
+ return status === 1 ? 'success' : 'failed';
21
+ if (typeof status === 'string') {
22
+ const normalized = status.trim().toLowerCase();
23
+ if (normalized === '0x1' || normalized === '1')
24
+ return 'success';
25
+ if (normalized === '0x0' || normalized === '0')
26
+ return 'failed';
27
+ }
28
+ return 'unknown';
29
+ }
30
+ function parseTxReceiptState(raw) {
31
+ if (raw == null || typeof raw !== 'object')
32
+ return 'not_found';
33
+ const receipt = raw;
34
+ if (receipt.blockNumber == null)
35
+ return 'pending';
36
+ const outcome = parseStatusToOutcome(receipt.status);
37
+ if (outcome === 'success')
38
+ return 'success';
39
+ if (outcome === 'failed')
40
+ return 'failed';
41
+ return 'pending';
42
+ }
43
+ function parseUserOpReceiptState(raw) {
44
+ if (raw == null || typeof raw !== 'object')
45
+ return 'not_found';
46
+ const receipt = raw;
47
+ if (typeof receipt.success === 'boolean') {
48
+ return receipt.success ? 'success' : 'failed';
49
+ }
50
+ const nested = receipt.receipt;
51
+ if (nested == null)
52
+ return 'pending';
53
+ if (nested.blockNumber == null)
54
+ return 'pending';
55
+ const outcome = parseStatusToOutcome(nested.status);
56
+ if (outcome === 'success')
57
+ return 'success';
58
+ if (outcome === 'failed')
59
+ return 'failed';
60
+ return 'pending';
61
+ }
62
+ function isMethodNotSupportedError(error) {
63
+ const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
64
+ return (message.includes('method not found') ||
65
+ message.includes('does not exist/is not available') ||
66
+ message.includes('-32601') ||
67
+ message.includes('eth_getuseroperationreceipt'));
68
+ }
69
+ function waitForEvmOrUserOpConfirmation(hash, network, request, options = {}) {
70
+ return __awaiter(this, void 0, void 0, function* () {
71
+ const { pollIntervalMs = 4000, timeoutMs = 300000, onTimeout = 'resolve_false', lockModeAfterDetection = true, } = options;
72
+ let mode = 'auto';
73
+ let userOpMethodSupported = true;
74
+ logger_1.sdkLogger.debug(`${LOG_PREFIX} waiting for tx/userOp confirmation`, {
75
+ hash,
76
+ network,
77
+ pollIntervalMs,
78
+ timeoutMs,
79
+ mode,
80
+ });
81
+ const tick = () => __awaiter(this, void 0, void 0, function* () {
82
+ if (mode !== 'userOp') {
83
+ try {
84
+ const txRaw = yield request('eth_getTransactionReceipt', [hash], network);
85
+ const txState = parseTxReceiptState(txRaw);
86
+ if (txState === 'success')
87
+ return { kind: 'stop', value: true };
88
+ if (txState === 'failed')
89
+ return { kind: 'stop', value: false };
90
+ if (lockModeAfterDetection && mode === 'auto' && txState === 'pending') {
91
+ mode = 'tx';
92
+ logger_1.sdkLogger.debug(`${LOG_PREFIX} mode locked`, { hash, network, mode });
93
+ }
94
+ }
95
+ catch (error) {
96
+ logger_1.sdkLogger.warn(`${LOG_PREFIX} tx receipt poll transient error`, {
97
+ hash,
98
+ network,
99
+ error: error instanceof Error ? error.message : String(error),
100
+ });
101
+ }
102
+ }
103
+ if (mode !== 'tx' && userOpMethodSupported) {
104
+ try {
105
+ const userOpRaw = yield request('eth_getUserOperationReceipt', [hash], network);
106
+ const userOpState = parseUserOpReceiptState(userOpRaw);
107
+ if (userOpState === 'success')
108
+ return { kind: 'stop', value: true };
109
+ if (userOpState === 'failed')
110
+ return { kind: 'stop', value: false };
111
+ if (lockModeAfterDetection &&
112
+ mode === 'auto' &&
113
+ userOpState === 'pending') {
114
+ mode = 'userOp';
115
+ logger_1.sdkLogger.debug(`${LOG_PREFIX} mode locked`, { hash, network, mode });
116
+ }
117
+ }
118
+ catch (error) {
119
+ if (isMethodNotSupportedError(error)) {
120
+ userOpMethodSupported = false;
121
+ logger_1.sdkLogger.debug(`${LOG_PREFIX} eth_getUserOperationReceipt unsupported; disabling userOp polling`, { hash, network });
122
+ }
123
+ else {
124
+ logger_1.sdkLogger.warn(`${LOG_PREFIX} userOp receipt poll transient error`, {
125
+ hash,
126
+ network,
127
+ error: error instanceof Error ? error.message : String(error),
128
+ });
129
+ }
130
+ }
131
+ }
132
+ return { kind: 'continue' };
133
+ });
134
+ try {
135
+ return yield (0, pollLoop_1.pollLoop)({
136
+ tick,
137
+ intervalMs: pollIntervalMs,
138
+ initialDelayMs: 0,
139
+ timeoutMs,
140
+ });
141
+ }
142
+ catch (error) {
143
+ if (error instanceof pollLoop_1.PollLoopTimeoutError) {
144
+ const msg = `${LOG_PREFIX} timeout after ${timeoutMs}ms waiting for confirmation on ${hash} (${network}).`;
145
+ if (onTimeout === 'throw') {
146
+ throw new Error(`${msg} Confirmation not reached.`);
147
+ }
148
+ logger_1.sdkLogger.warn(`${msg} Returning false (resolve_false).`);
149
+ return false;
150
+ }
151
+ throw error;
152
+ }
153
+ });
154
+ }
155
+ exports.waitForEvmOrUserOpConfirmation = waitForEvmOrUserOpConfirmation;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ /**
3
+ * @jest-environment node
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 waitForEvmOrUserOpConfirmation_1 = require("./waitForEvmOrUserOpConfirmation");
16
+ describe('waitForEvmOrUserOpConfirmation', () => {
17
+ beforeEach(() => {
18
+ jest.useFakeTimers();
19
+ });
20
+ afterEach(() => {
21
+ jest.useRealTimers();
22
+ });
23
+ it('returns false on timeout when receipt never appears', () => __awaiter(void 0, void 0, void 0, function* () {
24
+ const request = jest.fn().mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return null; }));
25
+ const done = (0, waitForEvmOrUserOpConfirmation_1.waitForEvmOrUserOpConfirmation)('0x1', 'eip155:1', request, {
26
+ pollIntervalMs: 1000,
27
+ timeoutMs: 2500,
28
+ });
29
+ yield jest.advanceTimersByTimeAsync(3100);
30
+ yield expect(done).resolves.toBe(false);
31
+ expect(request.mock.calls.some((c) => c[0] === 'eth_getTransactionReceipt')).toBe(true);
32
+ }));
33
+ });
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ /**
3
+ * Shared EVM transaction confirmation via `eth_getTransactionReceipt` + {@link pollLoop}.
4
+ *
5
+ * **Use in each domain:**
6
+ * - **Yield** (multi-step): Prefer {@link Portal.waitForConfirmation} (returns `false` on soft timeout)
7
+ * or standalone `pollForEvmReceipt` (same engine).
8
+ * - **LiFi** (`tradeAsset`): Use `onTimeout: 'throw'` with the same RPC `request` as Portal's
9
+ * gateway so a stuck tx fails before bridge status polling.
10
+ * - **Legacy swaps** (`@portal-hq/swaps`): Call `portal.waitForConfirmation(txHash, caip2)` after
11
+ * `eth_sendTransaction` so "submitted" reflects chain inclusion when the network is known.
12
+ *
13
+ * Domain-specific orchestration (LiFi status, Yield multi-tx, route steps) stays outside this module.
14
+ */
15
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
16
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
17
+ return new (P || (P = Promise))(function (resolve, reject) {
18
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
19
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
20
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
21
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
22
+ });
23
+ };
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.waitForEvmTxConfirmation = void 0;
26
+ const logger_1 = require("../logger");
27
+ const pollLoop_1 = require("./pollLoop");
28
+ const LOG_PREFIX = '[Portal.evmReceipt]';
29
+ function evmReceiptPollTick(ctx) {
30
+ return __awaiter(this, void 0, void 0, function* () {
31
+ const { txHash, network, request, pollIntervalMs } = ctx;
32
+ try {
33
+ const raw = yield request('eth_getTransactionReceipt', [txHash], network);
34
+ const receipt = raw;
35
+ if ((receipt === null || receipt === void 0 ? void 0 : receipt.blockNumber) != null) {
36
+ if (receipt.status === '0x0') {
37
+ return {
38
+ kind: 'throw',
39
+ error: new Error(`${LOG_PREFIX} Transaction ${txHash} was reverted on-chain (status 0x0). ` +
40
+ 'The transaction did not succeed.'),
41
+ };
42
+ }
43
+ logger_1.sdkLogger.debug(`${LOG_PREFIX} receipt confirmed`, {
44
+ txHash,
45
+ network,
46
+ blockNumber: receipt.blockNumber,
47
+ status: receipt.status,
48
+ });
49
+ return { kind: 'stop', value: true };
50
+ }
51
+ logger_1.sdkLogger.debug(`${LOG_PREFIX} not yet mined, retrying`, { txHash, network });
52
+ return { kind: 'continue' };
53
+ }
54
+ catch (error) {
55
+ if (error instanceof Error && error.message.includes('reverted')) {
56
+ return { kind: 'throw', error };
57
+ }
58
+ logger_1.sdkLogger.warn(`${LOG_PREFIX} transient error polling receipt for ${txHash} (${network}), will retry in ${pollIntervalMs}ms:`, error);
59
+ return { kind: 'continue' };
60
+ }
61
+ });
62
+ }
63
+ /**
64
+ * Poll until the tx is mined with success (`0x1`), reverted (`0x0` → throw), or timeout.
65
+ * Pending receipts and transient RPC errors retry with warn logs; logs always include `txHash` / `network` where relevant.
66
+ */
67
+ function waitForEvmTxConfirmation(txHash, network, request, options = {}) {
68
+ return __awaiter(this, void 0, void 0, function* () {
69
+ const { pollIntervalMs = 4000, timeoutMs = 900000, onTimeout = 'resolve_false', } = options;
70
+ logger_1.sdkLogger.debug(`${LOG_PREFIX} waiting for receipt`, {
71
+ txHash,
72
+ network,
73
+ pollIntervalMs,
74
+ timeoutMs,
75
+ onTimeout,
76
+ });
77
+ const tickCtx = {
78
+ txHash,
79
+ network,
80
+ request,
81
+ pollIntervalMs,
82
+ };
83
+ try {
84
+ return yield (0, pollLoop_1.pollLoop)({
85
+ tick: () => evmReceiptPollTick(tickCtx),
86
+ intervalMs: pollIntervalMs,
87
+ initialDelayMs: 0,
88
+ timeoutMs,
89
+ });
90
+ }
91
+ catch (error) {
92
+ if (error instanceof pollLoop_1.PollLoopTimeoutError) {
93
+ const msg = `${LOG_PREFIX} timeout after ${timeoutMs}ms waiting for receipt on ${txHash} (${network}).`;
94
+ if (onTimeout === 'throw') {
95
+ throw new Error(`${msg} Transaction may still confirm; aborting this wait.`);
96
+ }
97
+ logger_1.sdkLogger.warn(`${msg} Transaction may still confirm. Proceeding optimistically.`);
98
+ return false;
99
+ }
100
+ throw error;
101
+ }
102
+ });
103
+ }
104
+ exports.waitForEvmTxConfirmation = waitForEvmTxConfirmation;
@@ -0,0 +1,106 @@
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.waitForSolanaTxConfirmation = void 0;
13
+ const logger_1 = require("../logger");
14
+ const LOG_PREFIX = '[Portal.waitForConfirmation]';
15
+ const COMMITMENT_ORDER = {
16
+ processed: 0,
17
+ confirmed: 1,
18
+ finalized: 2,
19
+ };
20
+ function sleep(ms) {
21
+ return new Promise((resolve) => setTimeout(resolve, ms));
22
+ }
23
+ function statusMeetsCommitment(confirmationStatus, required) {
24
+ if (!confirmationStatus)
25
+ return false;
26
+ const level = COMMITMENT_ORDER[confirmationStatus];
27
+ const need = COMMITMENT_ORDER[required];
28
+ if (level === undefined || need === undefined)
29
+ return false;
30
+ return level >= need;
31
+ }
32
+ function parseSignatureStatusesResult(raw) {
33
+ var _a;
34
+ if (raw == null || typeof raw !== 'object')
35
+ return null;
36
+ const outer = raw;
37
+ const arr = (_a = outer.value) !== null && _a !== void 0 ? _a : outer.result;
38
+ if (!Array.isArray(arr) || arr.length === 0)
39
+ return null;
40
+ const first = arr[0];
41
+ if (first == null || typeof first !== 'object')
42
+ return null;
43
+ return first;
44
+ }
45
+ /**
46
+ * Polls Solana `getSignatureStatuses` via {@link request} (routed through the
47
+ * iframe RPC proxy) until the signature reaches the requested commitment,
48
+ * fails on-chain, or times out.
49
+ */
50
+ function waitForSolanaTxConfirmation(signature, network, request, options) {
51
+ var _a;
52
+ return __awaiter(this, void 0, void 0, function* () {
53
+ const { pollIntervalMs, timeoutMs, commitment } = options;
54
+ logger_1.sdkLogger.debug(`${LOG_PREFIX} waiting for Solana confirmation`, {
55
+ signature,
56
+ network,
57
+ pollIntervalMs,
58
+ timeoutMs,
59
+ commitment,
60
+ });
61
+ const deadline = Date.now() + timeoutMs;
62
+ while (Date.now() < deadline) {
63
+ try {
64
+ const raw = yield request('getSignatureStatuses', [[signature], { searchTransactionHistory: true }], network);
65
+ const rpcResponse = raw;
66
+ if (rpcResponse.error) {
67
+ throw new Error(`${LOG_PREFIX} RPC error for getSignatureStatuses: ${JSON.stringify(rpcResponse.error)}`);
68
+ }
69
+ const statusRow = parseSignatureStatusesResult((_a = rpcResponse.result) !== null && _a !== void 0 ? _a : raw);
70
+ if (statusRow != null) {
71
+ if (statusRow.err != null) {
72
+ logger_1.sdkLogger.debug(`${LOG_PREFIX} Solana tx error on-chain`, {
73
+ signature,
74
+ network,
75
+ err: statusRow.err,
76
+ });
77
+ return false;
78
+ }
79
+ if (statusMeetsCommitment(statusRow.confirmationStatus, commitment)) {
80
+ logger_1.sdkLogger.debug(`${LOG_PREFIX} Solana commitment met`, {
81
+ signature,
82
+ network,
83
+ confirmationStatus: statusRow.confirmationStatus,
84
+ commitment,
85
+ });
86
+ return true;
87
+ }
88
+ }
89
+ }
90
+ catch (error) {
91
+ logger_1.sdkLogger.warn(`${LOG_PREFIX} Solana poll transient error`, {
92
+ signature,
93
+ network,
94
+ error: error instanceof Error ? error.message : String(error),
95
+ });
96
+ }
97
+ const remaining = deadline - Date.now();
98
+ if (remaining <= 0)
99
+ break;
100
+ yield sleep(Math.min(pollIntervalMs, Math.max(0, remaining)));
101
+ }
102
+ logger_1.sdkLogger.warn(`${LOG_PREFIX} timeout after ${timeoutMs}ms waiting for Solana confirmation on ${signature} (${network}). Returning false.`);
103
+ return false;
104
+ });
105
+ }
106
+ exports.waitForSolanaTxConfirmation = waitForSolanaTxConfirmation;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ /**
3
+ * Yield / Portal-style EVM network slugs → EIP-155 CAIP-2.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.isYieldEvmNetwork = exports.resolveYieldNetworkToCaip2 = void 0;
7
+ const YIELD_EVM_SLUG_TO_CAIP2 = {
8
+ // Common friendly names (subset of typical integrations)
9
+ ethereum: 'eip155:1',
10
+ eth: 'eip155:1',
11
+ mainnet: 'eip155:1',
12
+ polygon: 'eip155:137',
13
+ matic: 'eip155:137',
14
+ arbitrum: 'eip155:42161',
15
+ optimism: 'eip155:10',
16
+ base: 'eip155:8453',
17
+ blast: 'eip155:81457',
18
+ scroll: 'eip155:534352',
19
+ mantle: 'eip155:5000',
20
+ mode: 'eip155:34443',
21
+ opbnb: 'eip155:204',
22
+ // Testnets and alternate slugs (e.g. *-sepolia) mapped to EIP-155 CAIP-2
23
+ 'ethereum-sepolia': 'eip155:11155111',
24
+ 'ethereum-goerli': 'eip155:5',
25
+ 'ethereum-holesky': 'eip155:17000',
26
+ 'ethereum-hoodi': 'eip155:560048',
27
+ binance: 'eip155:56',
28
+ bsc: 'eip155:56',
29
+ 'avalanche-c': 'eip155:43114',
30
+ 'avalanche-c-atomic': 'eip155:43114',
31
+ gnosis: 'eip155:100',
32
+ zksync: 'eip155:324',
33
+ linea: 'eip155:59144',
34
+ celo: 'eip155:42220',
35
+ fantom: 'eip155:250',
36
+ harmony: 'eip155:1666600000',
37
+ moonriver: 'eip155:1285',
38
+ okc: 'eip155:66',
39
+ viction: 'eip155:88',
40
+ core: 'eip155:1116',
41
+ cronos: 'eip155:25',
42
+ evmos: 'eip155:9001',
43
+ unichain: 'eip155:130',
44
+ sonic: 'eip155:146',
45
+ };
46
+ /** Resolve a Yield transaction `network` string to EIP-155 CAIP-2 when known. */
47
+ function resolveYieldNetworkToCaip2(network) {
48
+ if (typeof network !== 'string' || network === '')
49
+ return undefined;
50
+ if (network.startsWith('eip155:'))
51
+ return network;
52
+ if (network.toLowerCase().startsWith('solana'))
53
+ return undefined;
54
+ return YIELD_EVM_SLUG_TO_CAIP2[network.toLowerCase()];
55
+ }
56
+ exports.resolveYieldNetworkToCaip2 = resolveYieldNetworkToCaip2;
57
+ function isYieldEvmNetwork(network) {
58
+ return resolveYieldNetworkToCaip2(network) !== undefined;
59
+ }
60
+ exports.isYieldEvmNetwork = isYieldEvmNetwork;