@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,135 @@
|
|
|
1
|
+
import { sdkLogger } from '../logger'
|
|
2
|
+
|
|
3
|
+
const LOG_PREFIX = '[Portal.waitForConfirmation]'
|
|
4
|
+
|
|
5
|
+
export type SolanaConfirmationCommitment =
|
|
6
|
+
| 'processed'
|
|
7
|
+
| 'confirmed'
|
|
8
|
+
| 'finalized'
|
|
9
|
+
|
|
10
|
+
export type SolanaRequestFn = (
|
|
11
|
+
method: string,
|
|
12
|
+
params: unknown[],
|
|
13
|
+
network: string,
|
|
14
|
+
) => Promise<unknown>
|
|
15
|
+
|
|
16
|
+
export interface WaitForSolanaTxConfirmationOptions {
|
|
17
|
+
pollIntervalMs: number
|
|
18
|
+
timeoutMs: number
|
|
19
|
+
commitment: SolanaConfirmationCommitment
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const COMMITMENT_ORDER: Record<SolanaConfirmationCommitment, number> = {
|
|
23
|
+
processed: 0,
|
|
24
|
+
confirmed: 1,
|
|
25
|
+
finalized: 2,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function sleep(ms: number): Promise<void> {
|
|
29
|
+
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function statusMeetsCommitment(
|
|
33
|
+
confirmationStatus: string | undefined,
|
|
34
|
+
required: SolanaConfirmationCommitment,
|
|
35
|
+
): boolean {
|
|
36
|
+
if (!confirmationStatus) return false
|
|
37
|
+
const level = COMMITMENT_ORDER[confirmationStatus as SolanaConfirmationCommitment]
|
|
38
|
+
const need = COMMITMENT_ORDER[required]
|
|
39
|
+
if (level === undefined || need === undefined) return false
|
|
40
|
+
return level >= need
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface SignatureStatusRow {
|
|
44
|
+
err?: unknown
|
|
45
|
+
confirmationStatus?: string
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function parseSignatureStatusesResult(raw: unknown): SignatureStatusRow | null {
|
|
49
|
+
if (raw == null || typeof raw !== 'object') return null
|
|
50
|
+
const outer = raw as { value?: unknown; result?: unknown }
|
|
51
|
+
const arr = outer.value ?? outer.result
|
|
52
|
+
if (!Array.isArray(arr) || arr.length === 0) return null
|
|
53
|
+
const first = arr[0]
|
|
54
|
+
if (first == null || typeof first !== 'object') return null
|
|
55
|
+
return first as SignatureStatusRow
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Polls Solana `getSignatureStatuses` via {@link request} (routed through the
|
|
60
|
+
* iframe RPC proxy) until the signature reaches the requested commitment,
|
|
61
|
+
* fails on-chain, or times out.
|
|
62
|
+
*/
|
|
63
|
+
export async function waitForSolanaTxConfirmation(
|
|
64
|
+
signature: string,
|
|
65
|
+
network: string,
|
|
66
|
+
request: SolanaRequestFn,
|
|
67
|
+
options: WaitForSolanaTxConfirmationOptions,
|
|
68
|
+
): Promise<boolean> {
|
|
69
|
+
const { pollIntervalMs, timeoutMs, commitment } = options
|
|
70
|
+
|
|
71
|
+
sdkLogger.debug(`${LOG_PREFIX} waiting for Solana confirmation`, {
|
|
72
|
+
signature,
|
|
73
|
+
network,
|
|
74
|
+
pollIntervalMs,
|
|
75
|
+
timeoutMs,
|
|
76
|
+
commitment,
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const deadline = Date.now() + timeoutMs
|
|
80
|
+
|
|
81
|
+
while (Date.now() < deadline) {
|
|
82
|
+
try {
|
|
83
|
+
const raw = await request(
|
|
84
|
+
'getSignatureStatuses',
|
|
85
|
+
[[signature], { searchTransactionHistory: true }],
|
|
86
|
+
network,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
const rpcResponse = raw as { result?: unknown; error?: unknown }
|
|
90
|
+
if (rpcResponse.error) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`${LOG_PREFIX} RPC error for getSignatureStatuses: ${JSON.stringify(rpcResponse.error)}`,
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const statusRow = parseSignatureStatusesResult(
|
|
97
|
+
rpcResponse.result ?? raw,
|
|
98
|
+
)
|
|
99
|
+
if (statusRow != null) {
|
|
100
|
+
if (statusRow.err != null) {
|
|
101
|
+
sdkLogger.debug(`${LOG_PREFIX} Solana tx error on-chain`, {
|
|
102
|
+
signature,
|
|
103
|
+
network,
|
|
104
|
+
err: statusRow.err,
|
|
105
|
+
})
|
|
106
|
+
return false
|
|
107
|
+
}
|
|
108
|
+
if (statusMeetsCommitment(statusRow.confirmationStatus, commitment)) {
|
|
109
|
+
sdkLogger.debug(`${LOG_PREFIX} Solana commitment met`, {
|
|
110
|
+
signature,
|
|
111
|
+
network,
|
|
112
|
+
confirmationStatus: statusRow.confirmationStatus,
|
|
113
|
+
commitment,
|
|
114
|
+
})
|
|
115
|
+
return true
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
} catch (error) {
|
|
119
|
+
sdkLogger.warn(`${LOG_PREFIX} Solana poll transient error`, {
|
|
120
|
+
signature,
|
|
121
|
+
network,
|
|
122
|
+
error: error instanceof Error ? error.message : String(error),
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const remaining = deadline - Date.now()
|
|
127
|
+
if (remaining <= 0) break
|
|
128
|
+
await sleep(Math.min(pollIntervalMs, Math.max(0, remaining)))
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
sdkLogger.warn(
|
|
132
|
+
`${LOG_PREFIX} timeout after ${timeoutMs}ms waiting for Solana confirmation on ${signature} (${network}). Returning false.`,
|
|
133
|
+
)
|
|
134
|
+
return false
|
|
135
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Yield / Portal-style EVM network slugs → EIP-155 CAIP-2.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const YIELD_EVM_SLUG_TO_CAIP2: Record<string, string> = {
|
|
6
|
+
// Common friendly names (subset of typical integrations)
|
|
7
|
+
ethereum: 'eip155:1',
|
|
8
|
+
eth: 'eip155:1',
|
|
9
|
+
mainnet: 'eip155:1',
|
|
10
|
+
polygon: 'eip155:137',
|
|
11
|
+
matic: 'eip155:137',
|
|
12
|
+
arbitrum: 'eip155:42161',
|
|
13
|
+
optimism: 'eip155:10',
|
|
14
|
+
base: 'eip155:8453',
|
|
15
|
+
blast: 'eip155:81457',
|
|
16
|
+
scroll: 'eip155:534352',
|
|
17
|
+
mantle: 'eip155:5000',
|
|
18
|
+
mode: 'eip155:34443',
|
|
19
|
+
opbnb: 'eip155:204',
|
|
20
|
+
// Testnets and alternate slugs (e.g. *-sepolia) mapped to EIP-155 CAIP-2
|
|
21
|
+
'ethereum-sepolia': 'eip155:11155111',
|
|
22
|
+
'ethereum-goerli': 'eip155:5',
|
|
23
|
+
'ethereum-holesky': 'eip155:17000',
|
|
24
|
+
'ethereum-hoodi': 'eip155:560048',
|
|
25
|
+
binance: 'eip155:56',
|
|
26
|
+
bsc: 'eip155:56',
|
|
27
|
+
'avalanche-c': 'eip155:43114',
|
|
28
|
+
'avalanche-c-atomic': 'eip155:43114',
|
|
29
|
+
gnosis: 'eip155:100',
|
|
30
|
+
zksync: 'eip155:324',
|
|
31
|
+
linea: 'eip155:59144',
|
|
32
|
+
celo: 'eip155:42220',
|
|
33
|
+
fantom: 'eip155:250',
|
|
34
|
+
harmony: 'eip155:1666600000',
|
|
35
|
+
moonriver: 'eip155:1285',
|
|
36
|
+
okc: 'eip155:66',
|
|
37
|
+
viction: 'eip155:88',
|
|
38
|
+
core: 'eip155:1116',
|
|
39
|
+
cronos: 'eip155:25',
|
|
40
|
+
evmos: 'eip155:9001',
|
|
41
|
+
unichain: 'eip155:130',
|
|
42
|
+
sonic: 'eip155:146',
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Resolve a Yield transaction `network` string to EIP-155 CAIP-2 when known. */
|
|
46
|
+
export function resolveYieldNetworkToCaip2(
|
|
47
|
+
network: string,
|
|
48
|
+
): string | undefined {
|
|
49
|
+
if (typeof network !== 'string' || network === '') return undefined
|
|
50
|
+
if (network.startsWith('eip155:')) return network
|
|
51
|
+
if (network.toLowerCase().startsWith('solana')) return undefined
|
|
52
|
+
return YIELD_EVM_SLUG_TO_CAIP2[network.toLowerCase()]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function isYieldEvmNetwork(network: string): boolean {
|
|
56
|
+
return resolveYieldNetworkToCaip2(network) !== undefined
|
|
57
|
+
}
|
package/src/mpc/index.ts
CHANGED
|
@@ -41,6 +41,10 @@ import type {
|
|
|
41
41
|
FundParams,
|
|
42
42
|
FundResponse,
|
|
43
43
|
} from '../../types'
|
|
44
|
+
import type {
|
|
45
|
+
GetTransactionHistoryParams,
|
|
46
|
+
GetTransactionHistoryResponse,
|
|
47
|
+
} from '../shared/types'
|
|
44
48
|
import {
|
|
45
49
|
YieldXyzEnterRequest,
|
|
46
50
|
YieldXyzEnterYieldResponse,
|
|
@@ -57,6 +61,9 @@ import {
|
|
|
57
61
|
YieldXyzManageYieldResponse,
|
|
58
62
|
YieldXyzTrackTransactionRequest,
|
|
59
63
|
YieldXyzTrackTransactionResponse,
|
|
64
|
+
YieldXyzGetYieldDefaultsRequest,
|
|
65
|
+
YieldXyzGetYieldDefaultsResponse,
|
|
66
|
+
YieldXyzGetYieldValidatorsResponse,
|
|
60
67
|
LifiQuoteRequest,
|
|
61
68
|
LifiQuoteResponse,
|
|
62
69
|
LifiRoutesRequest,
|
|
@@ -118,6 +125,8 @@ import {
|
|
|
118
125
|
TransferFromRequest,
|
|
119
126
|
TransferFromResponse,
|
|
120
127
|
RawSignOptions,
|
|
128
|
+
RpcProxyRequest,
|
|
129
|
+
RpcProxyResponse,
|
|
121
130
|
} from '../shared/types'
|
|
122
131
|
import {
|
|
123
132
|
ScreenAddressApiResponse,
|
|
@@ -125,7 +134,7 @@ import {
|
|
|
125
134
|
} from '../../hypernative'
|
|
126
135
|
import { generateTraceId } from '../shared/trace'
|
|
127
136
|
|
|
128
|
-
const WEB_SDK_VERSION = '3.
|
|
137
|
+
const WEB_SDK_VERSION = '3.14.0-alpha.0'
|
|
129
138
|
|
|
130
139
|
class Mpc {
|
|
131
140
|
public iframe?: HTMLIFrameElement
|
|
@@ -452,12 +461,25 @@ class Mpc {
|
|
|
452
461
|
})
|
|
453
462
|
}
|
|
454
463
|
|
|
464
|
+
/**
|
|
465
|
+
* @deprecated This method is deprecated and will be removed in a future version.
|
|
466
|
+
* Please use `getTransactionHistory()` instead, which uses the new Portal v3 API
|
|
467
|
+
* endpoint and returns the unified transaction format across all chains.
|
|
468
|
+
*/
|
|
455
469
|
public async getTransactions(
|
|
456
470
|
chainId: string,
|
|
457
471
|
limit?: number,
|
|
458
472
|
offset?: number,
|
|
459
473
|
order?: GetTransactionsOrder,
|
|
460
474
|
): Promise<Transaction[]> {
|
|
475
|
+
// Log deprecation warning
|
|
476
|
+
sdkLogger.warn(
|
|
477
|
+
'[DEPRECATED] getTransactions() is deprecated and will be removed in a future version. ' +
|
|
478
|
+
'Please use getTransactionHistory() instead, which provides improved type safety with ' +
|
|
479
|
+
'discriminated unions for regular transactions and UserOperations, and proper polymorphic ' +
|
|
480
|
+
'response types for Solana vs unified formats.'
|
|
481
|
+
)
|
|
482
|
+
|
|
461
483
|
return this.handleRequestToIframeAndPost({
|
|
462
484
|
methodMessage: 'portal:getTransactions',
|
|
463
485
|
errorMessage: 'portal:getTransactionsError',
|
|
@@ -471,6 +493,32 @@ class Mpc {
|
|
|
471
493
|
})
|
|
472
494
|
}
|
|
473
495
|
|
|
496
|
+
/**
|
|
497
|
+
* Retrieves transaction history for the client's wallet on the specified chain.
|
|
498
|
+
*
|
|
499
|
+
* This method uses the new Portal v3 API endpoint and returns the unified
|
|
500
|
+
* transaction format. Supports EVM (EIP-155), Solana, Bitcoin, Tron, and Stellar chains.
|
|
501
|
+
*
|
|
502
|
+
* @param params - Request parameters
|
|
503
|
+
* @param params.chainId - Chain ID in CAIP-2 format (e.g., 'eip155:1', 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp')
|
|
504
|
+
* @param params.limit - Maximum number of transactions to return (default: 50)
|
|
505
|
+
* @param params.offset - Number of transactions to skip (default: 0)
|
|
506
|
+
* @param params.order - Sort order ('asc' or 'desc')
|
|
507
|
+
* @param params.address - Override wallet address (EVM only)
|
|
508
|
+
* @param params.userOperations - Filter for ERC-4337 UserOperations (EVM only)
|
|
509
|
+
* @returns Promise resolving to transaction history response
|
|
510
|
+
*/
|
|
511
|
+
public async getTransactionHistory(
|
|
512
|
+
params: GetTransactionHistoryParams,
|
|
513
|
+
): Promise<GetTransactionHistoryResponse> {
|
|
514
|
+
return this.handleRequestToIframeAndPost({
|
|
515
|
+
methodMessage: 'portal:getTransactionHistory',
|
|
516
|
+
errorMessage: 'portal:getTransactionHistoryError',
|
|
517
|
+
resultMessage: 'portal:getTransactionHistoryResult',
|
|
518
|
+
data: params,
|
|
519
|
+
})
|
|
520
|
+
}
|
|
521
|
+
|
|
474
522
|
public async setBackupStatus(
|
|
475
523
|
status: string,
|
|
476
524
|
backupIds: string[],
|
|
@@ -640,6 +688,28 @@ class Mpc {
|
|
|
640
688
|
})
|
|
641
689
|
}
|
|
642
690
|
|
|
691
|
+
public async getYieldXyzDefaults(
|
|
692
|
+
data?: YieldXyzGetYieldDefaultsRequest,
|
|
693
|
+
): Promise<YieldXyzGetYieldDefaultsResponse> {
|
|
694
|
+
return this.handleRequestToIframeAndPost({
|
|
695
|
+
methodMessage: 'portal:yieldxyz:getDefaults',
|
|
696
|
+
errorMessage: 'portal:yieldxyz:getDefaultsError',
|
|
697
|
+
resultMessage: 'portal:yieldxyz:getDefaultsResult',
|
|
698
|
+
data: data ?? {},
|
|
699
|
+
})
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
public async getYieldXyzValidators(
|
|
703
|
+
yieldId: string,
|
|
704
|
+
): Promise<YieldXyzGetYieldValidatorsResponse> {
|
|
705
|
+
return this.handleRequestToIframeAndPost({
|
|
706
|
+
methodMessage: 'portal:yieldxyz:getValidators',
|
|
707
|
+
errorMessage: 'portal:yieldxyz:getValidatorsError',
|
|
708
|
+
resultMessage: 'portal:yieldxyz:getValidatorsResult',
|
|
709
|
+
data: yieldId,
|
|
710
|
+
})
|
|
711
|
+
}
|
|
712
|
+
|
|
643
713
|
public async getLifiRoutes(
|
|
644
714
|
data: LifiRoutesRequest,
|
|
645
715
|
): Promise<LifiRoutesResponse> {
|
|
@@ -1122,6 +1192,8 @@ class Mpc {
|
|
|
1122
1192
|
mpcVersion: this.portal.mpcVersion,
|
|
1123
1193
|
featureFlags: this.portal.featureFlags,
|
|
1124
1194
|
logLevel: this.portal.getLogLevel(),
|
|
1195
|
+
rpcConfig: this.portal.rpcConfig,
|
|
1196
|
+
iframeRpcConfig: this.portal.iframeRpcConfig,
|
|
1125
1197
|
}
|
|
1126
1198
|
|
|
1127
1199
|
const message = {
|
|
@@ -1210,6 +1282,75 @@ class Mpc {
|
|
|
1210
1282
|
})
|
|
1211
1283
|
}
|
|
1212
1284
|
|
|
1285
|
+
public async rpcRequest(
|
|
1286
|
+
data: Omit<RpcProxyRequest, 'requestId'>,
|
|
1287
|
+
options?: { timeoutMs?: number; traceId?: string },
|
|
1288
|
+
): Promise<RpcProxyResponse> {
|
|
1289
|
+
const { timeoutMs = 30_000, traceId } = options ?? {}
|
|
1290
|
+
const requestId =
|
|
1291
|
+
crypto.randomUUID?.() ?? `${Date.now()}-${Math.random()}`
|
|
1292
|
+
const resolvedTraceId = traceId ?? generateTraceId()
|
|
1293
|
+
|
|
1294
|
+
sdkLogger.debug('[Portal] rpcRequest', {
|
|
1295
|
+
requestId,
|
|
1296
|
+
method: data.method,
|
|
1297
|
+
chainId: data.chainId,
|
|
1298
|
+
traceId: resolvedTraceId,
|
|
1299
|
+
timeoutMs,
|
|
1300
|
+
})
|
|
1301
|
+
|
|
1302
|
+
return new Promise((resolve, reject) => {
|
|
1303
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined
|
|
1304
|
+
|
|
1305
|
+
const cleanup = () => {
|
|
1306
|
+
window.removeEventListener('message', handleResponse)
|
|
1307
|
+
if (timeoutId !== undefined) {
|
|
1308
|
+
clearTimeout(timeoutId)
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
const handleResponse = (event: MessageEvent) => {
|
|
1313
|
+
const { origin } = event
|
|
1314
|
+
if (origin !== this.getOrigin()) return
|
|
1315
|
+
|
|
1316
|
+
const { type, data: result } = event.data
|
|
1317
|
+
if (
|
|
1318
|
+
type === 'portal:rpc:requestResult' &&
|
|
1319
|
+
result?.requestId === requestId
|
|
1320
|
+
) {
|
|
1321
|
+
cleanup()
|
|
1322
|
+
resolve(result as RpcProxyResponse)
|
|
1323
|
+
} else if (
|
|
1324
|
+
type === 'portal:rpc:requestError' &&
|
|
1325
|
+
result?.requestId === requestId
|
|
1326
|
+
) {
|
|
1327
|
+
cleanup()
|
|
1328
|
+
reject(new Error(result.message ?? 'RPC proxy error'))
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
timeoutId = setTimeout(() => {
|
|
1333
|
+
cleanup()
|
|
1334
|
+
const msg = `RPC request ${requestId} (${data.method}) timed out after ${timeoutMs}ms`
|
|
1335
|
+
sdkLogger.error('[Portal] rpcRequest timeout', {
|
|
1336
|
+
requestId,
|
|
1337
|
+
method: data.method,
|
|
1338
|
+
chainId: data.chainId,
|
|
1339
|
+
timeoutMs,
|
|
1340
|
+
})
|
|
1341
|
+
reject(new Error(msg))
|
|
1342
|
+
}, timeoutMs)
|
|
1343
|
+
|
|
1344
|
+
window.addEventListener('message', handleResponse)
|
|
1345
|
+
|
|
1346
|
+
this.postMessage({
|
|
1347
|
+
type: 'portal:rpc:request',
|
|
1348
|
+
data: { ...data, requestId },
|
|
1349
|
+
traceId: resolvedTraceId,
|
|
1350
|
+
})
|
|
1351
|
+
})
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1213
1354
|
private postMessage(event: { type: string; data: any; traceId?: string }) {
|
|
1214
1355
|
this.iframe?.contentWindow?.postMessage(event, this.getOrigin())
|
|
1215
1356
|
}
|
package/src/provider/index.ts
CHANGED
|
@@ -157,6 +157,12 @@ const signerMethods = [
|
|
|
157
157
|
RequestMethod.sol_signTransaction,
|
|
158
158
|
]
|
|
159
159
|
|
|
160
|
+
const iframeProxiedMethodStrings: string[] = [
|
|
161
|
+
'eth_getTransactionReceipt',
|
|
162
|
+
'eth_getUserOperationReceipt',
|
|
163
|
+
'getSignatureStatuses',
|
|
164
|
+
]
|
|
165
|
+
|
|
160
166
|
class Provider {
|
|
161
167
|
public events: Record<string, RegisteredEventHandler[]>
|
|
162
168
|
|
|
@@ -419,6 +425,25 @@ class Provider {
|
|
|
419
425
|
params,
|
|
420
426
|
traceId,
|
|
421
427
|
}: RequestArguments): Promise<any> {
|
|
428
|
+
if (iframeProxiedMethodStrings.includes(method)) {
|
|
429
|
+
sdkLogger.info(
|
|
430
|
+
`[PortalProvider] routing ${method} through iframe (chainId=${String(chainId)})`,
|
|
431
|
+
{ traceId },
|
|
432
|
+
)
|
|
433
|
+
return this.portal.mpc.rpcRequest(
|
|
434
|
+
{
|
|
435
|
+
method,
|
|
436
|
+
params: Array.isArray(params)
|
|
437
|
+
? params
|
|
438
|
+
: params != null
|
|
439
|
+
? [params]
|
|
440
|
+
: [],
|
|
441
|
+
chainId: chainId as string,
|
|
442
|
+
},
|
|
443
|
+
{ traceId },
|
|
444
|
+
)
|
|
445
|
+
}
|
|
446
|
+
|
|
422
447
|
const requestBody = {
|
|
423
448
|
body: JSON.stringify({
|
|
424
449
|
jsonrpc: '2.0',
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Shared types (canonical source)
|
|
2
|
+
|
|
3
|
+
Type definitions in this folder are the single source of truth for Web and related packages.
|
|
4
|
+
|
|
5
|
+
- **Browser (`packages/browser`)** — run `yarn shared:symlink` (dev) or `yarn shared:copy` (CI/publish) from the browser package so `src/shared` mirrors `packages/shared/src`.
|
|
6
|
+
- **Do not** maintain divergent copies of these files under `packages/browser/src/shared/types`; edit here and re-sync the browser package.
|
package/src/shared/types/api.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* API response types shared between browser and iframe packages
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type { FeatureFlags, GDriveConfig, PasskeyConfig } from './common'
|
|
5
|
+
import type { FeatureFlags, GDriveConfig, PasskeyConfig, RpcConfig } from './common'
|
|
6
6
|
|
|
7
7
|
// Asset response types
|
|
8
8
|
export interface GetAssetsResponse {
|
|
@@ -147,6 +147,17 @@ export interface IframeConfigurationOptions {
|
|
|
147
147
|
/** When set to 'none', the iframe SDK will not log to the console. Passed from parent via portal:configure. */
|
|
148
148
|
logLevel?: IframeLogLevel
|
|
149
149
|
|
|
150
|
+
/** RPC endpoint map (CAIP-2 chainId → URL). Passed from parent so the iframe can proxy RPC calls. */
|
|
151
|
+
rpcConfig?: RpcConfig
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Optional RPC map used only for iframe-proxied JSON-RPC (receipt polling, Solana status, etc.).
|
|
155
|
+
* When set, the iframe `fetch`es these URLs instead of {@link rpcConfig}. The parent still uses
|
|
156
|
+
* `rpcConfig` for signing (`mpc.sign` / `getRpcUrl`). Use in local dev when the gateway URL must
|
|
157
|
+
* match presignature but the iframe origin is blocked by CORS (e.g. same-origin or local proxy).
|
|
158
|
+
*/
|
|
159
|
+
iframeRpcConfig?: RpcConfig
|
|
160
|
+
|
|
150
161
|
// One of these three is required for authentication
|
|
151
162
|
apiKey?: string
|
|
152
163
|
authToken?: string
|