import PortalConnect from '@portal-hq/connect' import Keychain from '@portal-hq/keychain' import { type GatewayLike, Provider } from '@portal-hq/provider' import { EventHandler, SigningRequestParams } from '@portal-hq/provider/types' import { ALL_CHAIN_IDS, BITCOIN_CHAIN_IDS, DEFAULT_CHAIN_ID_CAIP2, DEFAULT_CHAIN_ID_NUMERIC, DEFAULT_HOSTS, EVM_CHAIN_IDS, EvaluateTransactionOperationType, Events, IPortalApi, IPortalProvider, KeychainAdapter, MpcStatuses, NAMESPACE_TO_CURVE, PortalRequestMethod, ProgressCallback, type RawSignOptions, type RequestOptions, SOLANA_CHAIN_IDS, type SendAssetParams, sdkLogger, } from '@portal-hq/utils' import type { AddressesByNamespace, BackupConfigs, BackupSharePairMetadata, BlockaidValidateTrxRes, EvaluateTransactionParam, SigningSharePairMetadata, } from '@portal-hq/utils/types' import { AssetsResponse, type BackupOptions, BroadcastBitcoinP2wpkhTransactionResponse, BroadcastParam, BuildBitcoinP2wpkhTransactionResponse, FeatureFlags, FundParams, FundResponse, ILogger, LogLevel, type PortalOptions, SendAssetResponse, } from '../types' import PortalApi from './api' import { AssetManager } from './assets' import { Delegations, isSolanaChain } from './delegations' import { EvmAccountType } from './evmAccountType' import { waitForEvmOrUserOpConfirmation } from './internal/waitForEvmOrUserOpConfirmation' import { waitForSolanaTxConfirmation } from './internal/waitForSolanaTxConfirmation' import PortalMpc, { BackupMethods } from './mpc' import { Ramps } from './ramps' import { Security } from './security' import { Trading } from './trading' import { WalletManager } from './wallet' import Yield from './yield' import { isYieldEvmNetwork, resolveYieldNetworkToCaip2, } from './yield/yieldEvmNetwork' // Prevent tree-shaking of runtime-critical constants in React Native builds. // These values must be preserved to avoid "undefined" errors at runtime. void [ DEFAULT_CHAIN_ID_CAIP2, EVM_CHAIN_IDS, SOLANA_CHAIN_IDS, BITCOIN_CHAIN_IDS, ALL_CHAIN_IDS, NAMESPACE_TO_CURVE, MpcStatuses, PortalRequestMethod, ] const PORTAL_RPC_HOSTNAMES = new Set( Object.values(DEFAULT_HOSTS).map((h) => h.toLowerCase()), ) /** Error message for missing gateway configuration */ const MISSING_GATEWAY_ERROR = (network: string) => `[Portal.waitForConfirmation] No gateway URL configured for network "${network}". ` + 'Add it to gatewayConfig.' /** True when the RPC host is Portal-managed (prod, staging, or regional), not a third-party URL. */ function isPortalHostedRpcHostname(hostname: string): boolean { const host = hostname.toLowerCase() if (PORTAL_RPC_HOSTNAMES.has(host)) return true // Dev/staging use *.portalhq.dev; prod may use region-specific *.portalhq.io, etc. return ( host === 'portalhq.io' || host.endsWith('.portalhq.io') || host === 'portalhq.dev' || host.endsWith('.portalhq.dev') ) } // Exports export { default as PortalApi } from './api' export { default as PortalContext, PortalContextProvider, usePortal, } from './context' export { BackupMethods, PortalCurve, PortalSharePairStatus, PortalNamespace, MpcError, MpcErrorCodes, } from './mpc' export { YieldXyz, type IYieldXyz, type YieldXyzOptions, PortalYieldXyzApi, type IPortalYieldXyzApi, type PortalYieldXyzApiOptions, type EvmReceiptPollerOptions, type EvmRequestFn, } from './yield' export { default as Yield, type YieldConstructorOptions } from './yield' export { Noah, type INoah, type NoahOptions, Ramps, type IRamps, type RampsOptions, PortalNoahApi, type IPortalNoahApi, type PortalNoahApiOptions, } from './ramps' export { type IPortalLifiTradingApi, type PortalLifiTradingApiOptions, type ILifi, type IZeroX, type ZeroXOptions, type TradingOptions, } from './trading' export { type IHypernative, type HypernativeOptions, type IBlockaid, type BlockaidOptions, } from './security' export { isSolanaChain, type IDelegations, type DelegationsOptions, type DelegationSubmitOptions, type DelegationSubmitProgress, type ApproveDelegationRequest, type ApproveDelegationResponse, type RevokeDelegationRequest, type RevokeDelegationResponse, type GetDelegationStatusRequest, type DelegationStatusResponse, type TransferFromRequest, type TransferFromResponse, } from './delegations' export { type IEvmAccountType, type EvmAccountTypeOptions, WalletAccountType, } from './evmAccountType' export { WalletManager, type IWalletManager } from './wallet' export { AssetManager, type IAssetManager } from './assets' export { type Address, type Dapp, type PortalOptions } from '../types' export { EvaluateTransactionOperationType, PortalRequestMethod, CHAIN_NAMESPACES, SUPPORTED_BITCOIN_P2WPKH_CHAINS, FRIENDLY_CHAIN_NAME_TO_CAIP2, CAIP2_CHAIN_ID_REGEX, DEFAULT_CHAIN_ID_NUMERIC, DEFAULT_CHAIN_ID_CAIP2, EVM_CHAIN_IDS, SOLANA_CHAIN_IDS, BITCOIN_CHAIN_IDS, ALL_CHAIN_IDS, NAMESPACE_TO_CURVE, DEFAULT_HOSTS, MpcStatuses, type ChainNamespace, type FriendlyChainName, } from '@portal-hq/utils' export type { YieldOpportunity, YieldXyzToken, YieldManageAction, YieldXyzActionTransaction, YieldUnsignedTransaction, YieldTransactionStatus, YieldBalanceItem, YieldXyzBalancePendingAction, YieldXyzHistoricalActionItem, YieldXyzActionType, YieldTransactionResult, YieldXyzMechanicsType, YieldXyzSort, YieldXyzActionStatus, YieldXyzActionIntent, YieldXyzGetYieldsRequest, YieldXyzGetYieldsResponse, YieldXyzEnterRequest, YieldXyzEnterYieldResponse, YieldXyzExitRequest, YieldXyzExitResponse, YieldXyzManageYieldRequest, YieldXyzManageYieldResponse, YieldXyzGetBalancesRequest, YieldXyzGetBalancesResponse, YieldXyzGetHistoricalActionsRequest, YieldXyzGetHistoricalActionsResponse, YieldXyzTrackTransactionRequest, YieldXyzTrackTransactionResponse, YieldXyzGetTransactionRequest, YieldXyzGetTransactionResponse, YieldDiscoverParams, YieldDiscoverResponse, YieldEnterParams, YieldExitParams, YieldManageParams, YieldActionResponse, YieldSubmitProgress, YieldXyzDepositWithdrawChain, YieldDepositParamsByYieldId, YieldDepositParamsByChainAndToken, YieldDepositParams, YieldWithdrawParams, YieldDepositResult, YieldWithdrawResult, YieldSubmitOptions, YieldDefaultYieldsMap, YieldDefaultYieldEntry, YieldXyzGetYieldDefaultsRequest, YieldXyzGetYieldDefaultsResponse, YieldXyzGetYieldValidatorsResponse, YieldGetBalancesParams, YieldBalancesResponse, YieldGetHistoricalActionsParams, YieldHistoricalActionsResponse, LifiRoutesRequest, LifiRoutesResponse, LifiRoutesRequestOptions, LifiRoutesRawResponse, LifiRoutesData, LifiQuoteRequest, LifiQuoteResponse, LifiQuoteData, LifiQuoteErrorResponse, LifiStatusRequest, LifiStatusResponse, LifiStatusData, LifiStatusRawResponse, LifiPollStatusOptions, LifiTradeAssetParams, LifiTradeAssetOptions, LifiTradeAssetResult, LifiTradeAssetProgressStatus, LifiTradeAssetProgressData, LifiStepTransactionRequest, LifiStepTransactionResponse, LifiStepTransactionData, LifiRoute, LifiStep, LifiTransactionRequest, LifiToken, LifiAction, LifiEstimate, LifiInternalStep, LifiToolDetails, LifiFeeCost, LifiGasCost, LifiFeeSplit, LifiBid, LifiEstimateData, LifiToolsConfiguration, LifiTimingOptions, LifiTimingStrategy, LifiUnavailableRoutes, LifiFilteredRoute, LifiFailedRoute, LifiSubpathError, LifiTransactionInfo, LifiReceivingInfo, LifiIncludedSwapStep, LifiMetadata, LifiRoutesOrder, LifiQuoteOrder, LifiStepType, LifiGasCostType, LifiErrorType, LifiErrorCode, ZeroExQuoteRequest, ZeroExQuoteResponse, ZeroExQuoteData, ZeroExPriceRequest, ZeroExPriceResponse, ZeroExPriceData, ZeroExSourcesResponse, ZeroExFees, ZeroExIssues, ZeroExTransaction, ZeroXTradeAssetParams, ZeroXTradeAssetOptions, ZeroXTradeAssetResult, ZeroXTradeAssetProgressStatus, ZeroXTradeAssetProgressData, } from '../types' export type { PortalApiSuccessEnvelope, NoahInitiateKycRequest, NoahInitiateKycResponse, NoahInitiateKycResponseData, NoahInitiatePayinRequest, NoahInitiatePayinResponse, NoahInitiatePayinResponseData, StreetAddress, BankToAddressRelatedPaymentMethod, BankDetails, NoahSimulatePayinRequest, NoahSimulatePayinResponse, NoahSimulatePayinResponseData, NoahGetPayoutCountriesResponse, NoahGetPayoutCountriesResponseData, NoahGetPayoutChannelsRequest, NoahGetPayoutChannelsResponse, NoahGetPayoutChannelsResponseData, ChannelLimits, ChannelCalculated, Channel, NoahGetPayoutChannelFormResponse, NoahGetPayoutChannelFormResponseData, PaymentMethodDetails, AccountHolderDetails, IssuerDetails, PaymentMethod, NoahGetPaymentMethodsResponse, NoahGetPaymentMethodsResponseData, NoahGetPayoutQuoteRequest, NoahGetPayoutQuoteResponse, NoahGetPayoutQuoteResponseData, FormNextStep, NoahInitiatePayoutRequest, NoahInitiatePayoutResponse, NoahInitiatePayoutResponseData, AmountCondition, DepositSourceTriggerCondition, NoahSolanaCaipId, NoahSingleOnchainDepositSourceTriggerInput, } from './ramps/noah' /** Convert numeric values to hex strings for EVM tx params; native signer expects 0x-prefixed hex. */ function toHexString(v: unknown): string { if (v === undefined || v === null) return '' if (typeof v === 'number') return v >= 0 ? `0x${v.toString(16)}` : '0x0' if (typeof v === 'bigint') return v >= BigInt(0) ? `0x${v.toString(16)}` : '0x0' if (typeof v === 'string') { const s = v.trim() if (s === '') return '' if (s.startsWith('0x') || s.startsWith('0X')) return s if (/^(0|[1-9]\d*)$/.test(s)) { try { return `0x${BigInt(s).toString(16)}` } catch { return s } } if (/^[0-9a-fA-F]+$/.test(s) && /[a-fA-F]/.test(s)) { return `0x${s}` } return s } return String(v) } /** Known EVM tx keys that must be 0x-prefixed hex (or decimal string converted to hex). */ const EVM_HEX_KEYS = new Set([ 'data', 'from', 'to', 'value', 'gas', 'gasLimit', 'maxFeePerGas', 'maxPriorityFeePerGas', 'gasPrice', 'nonce', 'chainId', 'type', ]) /** * Normalize EVM transaction to Record for native signer: * - Unwraps nested { transaction: {...} } if present (Yield/API may return this). * - Maps gasLimit -> gas. * - Ensures all hex fields are 0x-prefixed hex strings (normalizing decimal strings as needed). * - Converts numeric values to 0x-prefixed hex. */ function normalizeEvmTransactionParams( obj: Record, ): Record { let source = obj const keys = Object.keys(obj) if ( keys.length === 1 && typeof obj[keys[0]] === 'object' && obj[keys[0]] !== null && !Array.isArray(obj[keys[0]]) ) { source = obj[keys[0]] as Record } const out: Record = {} for (const [k, v] of Object.entries(source)) { if (v === undefined || v === null) continue const key = k === 'gasLimit' ? 'gas' : k const isHexKey = EVM_HEX_KEYS.has(k) const isNumber = typeof v === 'number' out[key] = isHexKey || isNumber ? toHexString(v) : String(v) } if (out.value === undefined || out.value === '') out.value = '0x0' return out } export class Portal { public api: IPortalApi public apiKey: string public backup: BackupOptions public gatewayConfig: GatewayLike public mpc: PortalMpc public provider: IPortalProvider public featureFlags: FeatureFlags public chainId: number public trading: Trading public yield: Yield public ramps: Ramps public security: Security public delegations: Delegations public evmAccountType: EvmAccountType public wallet: WalletManager public assets: AssetManager private apiHost: string private isSimulator: boolean private keychain: KeychainAdapter private mpcHost: string private enclaveMPCHost: string private version = 'v6' private webSocketHost: string private logger: ILogger = console /** * @deprecated The Portal SDK is now multi-wallet. * Use `addresses` instead. */ get address(): Promise { return this.provider.address } get addresses(): Promise { return this.provider.addresses } get autoApprove(): boolean { return this.provider.autoApprove } constructor({ // Required apiKey, backup, gatewayConfig, // Optional chainId = DEFAULT_CHAIN_ID_NUMERIC, isSimulator = false, autoApprove = false, keychain = new Keychain(), version = 'v6', apiHost = DEFAULT_HOSTS.API, mpcHost = DEFAULT_HOSTS.MPC, enclaveMPCHost = DEFAULT_HOSTS.ENCLAVE_MPC, webSocketHost = DEFAULT_HOSTS.WEBSOCKET, featureFlags = {}, logLevel = 'none', logger = console, }: PortalOptions) { this.apiHost = apiHost this.apiKey = apiKey this.backup = backup this.gatewayConfig = gatewayConfig this.isSimulator = isSimulator this.keychain = keychain this.mpcHost = mpcHost this.enclaveMPCHost = enclaveMPCHost this.version = version this.webSocketHost = webSocketHost this.featureFlags = { enableSdkPerformanceMetrics: featureFlags.enableSdkPerformanceMetrics ?? false, useEnclaveMPCApi: featureFlags.useEnclaveMPCApi ?? false, usePresignatures: featureFlags.usePresignatures ?? false, ...featureFlags, } this.chainId = chainId this.logger = logger sdkLogger.configure(logLevel, logger) if (version != 'v6') { throw new Error( 'Mpc Version is not supported. Only v6 is currently supported.', ) } // Initialize the Portal MPC Client (before Provider so we can pass presignatureSource for internal signing) this.mpc = new PortalMpc({ apiKey, keychain: this.keychain, backup: this.backup, portal: this, isSimulator, host: mpcHost, version: version, apiHost: apiHost, }) // Initialize the Portal Provider (presignatureSource only passed when usePresignatures is enabled) this.provider = new Provider({ apiKey, keychain, gatewayConfig, chainId: `eip155:${chainId}`, autoApprove, apiHost, mpcHost, enclaveMPCHost: this.enclaveMPCHost, version, featureFlags: this.featureFlags, presignatureSource: this.featureFlags.usePresignatures === true ? this.mpc : undefined, }) // Initialize the Portal API this.api = new PortalApi({ apiKey, host: apiHost, provider: this.provider, featureFlags: this.featureFlags, }) const yieldSignAndSend = ( transaction: unknown, network: string, ): Promise => { const chainIdResolved = resolveYieldNetworkToCaip2(network) ?? network const method = chainIdResolved.startsWith('solana') ? PortalRequestMethod.SolSignAndSendTransaction : PortalRequestMethod.EthSendTransaction let payload: unknown = transaction if (!chainIdResolved.startsWith('solana')) { const obj: Record = typeof transaction === 'string' ? (() => { try { return JSON.parse(transaction) as Record } catch { return { data: transaction } } })() : (transaction as Record) sdkLogger.debug( '[Portal] signAndSendTransaction raw (EVM):', JSON.stringify(obj).slice(0, 500), ) payload = normalizeEvmTransactionParams(obj) sdkLogger.debug('[Portal] signAndSendTransaction normalized:', payload) } return this.request(method, [payload], chainIdResolved) } // Initialize sub-modules. // Trading high-level executions (LiFi / 0x) use the same default signer and // confirmation waiter strategy as Yield: per-call overrides still win, but callers // get a working out-of-the-box path from Portal without passing execution options. this.trading = new Trading(this.api, { lifi: { signAndSendTransaction: yieldSignAndSend, waitForConfirmation: (txHash, network) => this.waitForConfirmation(txHash, network), evmRequestFn: this.yieldGatewayJsonRpc.bind(this), }, zeroX: { signAndSendTransaction: yieldSignAndSend, waitForConfirmation: (txHash, network) => this.waitForConfirmation(txHash, network), }, }) this.security = new Security(this.api) this.yield = new Yield({ api: this.api, // Portal.waitForConfirmation polls EVM receipt via gateway RPC. // Per-call overrides are passed via deposit/withdraw options instead. waitForConfirmation: (txHash, network) => this.waitForConfirmation(txHash, network), }) // Wire the MPC signer via the setter instead of the constructor so that // YieldXyz does not require signAndSendTransaction at construction time. this.yield.yieldxyz.setSignAndSendTransaction(yieldSignAndSend) this.delegations = new Delegations({ api: this.api }) // Wire the MPC signer via the setter instead of the constructor so that // Delegations does not require signAndSendTransaction at construction time. this.delegations.setSignAndSendTransaction((transaction, chainId) => { // Solana and EVM use different RPC methods for signing and broadcasting: // Solana requires a base64-encoded transaction via sol_signAndSendTransaction, // while EVM sends a hex-encoded signed tx via eth_sendTransaction. const method = isSolanaChain(chainId) ? PortalRequestMethod.SolSignAndSendTransaction : PortalRequestMethod.EthSendTransaction return this.request(method, [transaction], chainId) }) this.ramps = new Ramps({ api: this.api }) this.evmAccountType = new EvmAccountType({ api: this.api }) this.wallet = new WalletManager({ api: this.api, mpc: this.mpc, keychain: this.keychain, }) this.assets = new AssetManager({ api: this.api, provider: this.provider, getAddress: () => this.address, rawSign: (message: string, cid?: string, options?: RawSignOptions) => this.rawSign(message, cid, options), getChainId: () => this.chainId, }) // Update storage adapters for (const adapter of Object.values(backup)) { adapter.api = this.api } // Update Keychain keychain.api = this.api // Initialize presignature buffers if wallet already exists // Run asynchronously without blocking constructor this.mpc .isReady() .then((ready) => { if (ready) { sdkLogger.debug( '[Portal] Wallet ready, initializing presignature buffers', ) this.mpc.initializePresignatureBuffers().catch((error) => { sdkLogger.warn( '[Portal] Failed to initialize presignature buffers on init:', error, ) }) } }) .catch(() => { // Ignore errors - wallet may not exist yet }) // Monitor initializations try { this.api .identify({}) .then(() => { void this.api.track(Events.PortalInitialized, {}) }) .catch(() => { // Noop }) } catch (err) { // Do nothing because we don't want metrics gathering // to block the Portal initialization } } /*********************************** * Logging ***********************************/ public setLogLevel(level: LogLevel): void { sdkLogger.configure(level, this.logger) } public getLogLevel(): LogLevel { return sdkLogger.getLogLevel() } /** * Waits until a transaction is confirmed: EVM via `eth_getTransactionReceipt` (and user-op * receipt when applicable) against `gatewayConfig`; Solana via `getSignatureStatuses` against * the Solana RPC URL in `gatewayConfig`. Returns `false` for unsupported networks. * Used by Yield deposit/withdraw and trading flows as the default strategy; override per call * with `waitForConfirmation` in options when needed. * * @returns `true` if confirmed, `false` if timeout/failed/unsupported network */ public async waitForConfirmation( txHash: string, network: string, ): Promise { if (isYieldEvmNetwork(network)) { return waitForEvmOrUserOpConfirmation( txHash, network, this.yieldGatewayJsonRpc.bind(this), { pollIntervalMs: 4_000, timeoutMs: 900_000, onTimeout: 'resolve_false', lockModeAfterDetection: true, }, ) } if (network.toLowerCase().startsWith('solana:')) { const rpcUrl = this.resolveYieldGatewayRpcUrl(network) if (!rpcUrl) { throw new Error(MISSING_GATEWAY_ERROR(network)) } const headers = this.buildYieldGatewayJsonRpcHeaders(rpcUrl) return waitForSolanaTxConfirmation(txHash, rpcUrl, { pollIntervalMs: 4_000, timeoutMs: 900_000, commitment: 'confirmed', headers, }) } sdkLogger.warn( `[Portal.waitForConfirmation] Unsupported network: "${network}". ` + 'Returning false (cannot verify confirmation).', ) return false } /** Resolves JSON-RPC URL from `gatewayConfig` for a Yield-style `network` key (CAIP-2 or slug). */ private resolveYieldGatewayRpcUrl(network: string): string | undefined { const gw = this.gatewayConfig if (typeof gw === 'string') return gw const direct = gw[network] if (direct) return direct const caip2 = resolveYieldNetworkToCaip2(network) return caip2 ? gw[caip2] : undefined } private buildYieldGatewayJsonRpcHeaders( rpcUrl: string, ): Record { const headers: Record = { 'Content-Type': 'application/json', } try { const host = new URL(rpcUrl).hostname if (this.apiKey && isPortalHostedRpcHostname(host)) { headers.Authorization = `Bearer ${this.apiKey}` } } catch { // ignore malformed URL — fetch will fail with a clear error } return headers } /** JSON-RPC to the configured gateway URL (read-only calls such as eth_getTransactionReceipt). */ private async yieldGatewayJsonRpc( method: string, params: unknown[], network: string, ): Promise { const rpcUrl = this.resolveYieldGatewayRpcUrl(network) if (!rpcUrl) { throw new Error(MISSING_GATEWAY_ERROR(network)) } const headers = this.buildYieldGatewayJsonRpcHeaders(rpcUrl) const res = await fetch(rpcUrl, { method: 'POST', headers, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }), }) if (!res.ok) { const text = await res.text() throw new Error( `[Portal.waitForConfirmation] HTTP ${res.status} ${res.statusText} for ${method} (${network}): ` + text.slice(0, 200), ) } const json = (await res.json()) as { result?: unknown; error?: unknown } if (json.error) { throw new Error( `[Portal.waitForConfirmation] RPC error for ${method}: ${JSON.stringify(json.error)}`, ) } return json.result } /*********************************** * Wallet — delegates to this.wallet ***********************************/ public async createWallet( progress?: ProgressCallback, ): Promise { return this.wallet.create(progress) } public async backupWallet( method: BackupMethods, progress?: ProgressCallback, backupConfig: BackupConfigs = {}, ): Promise { return this.wallet.backup(method, progress, backupConfig) } public async recoverWallet( cipherText: string = '', method: BackupMethods, progress?: ProgressCallback, backupConfig: BackupConfigs = {}, ): Promise { return this.wallet.recover(cipherText, method, progress, backupConfig) } public async provisionWallet( cipherText: string, method: BackupMethods, progress?: ProgressCallback, backupConfig: BackupConfigs = {}, ): Promise { return this.wallet.recover(cipherText, method, progress, backupConfig) } public async doesWalletExist(chainId?: string): Promise { return this.wallet.doesExist(chainId) } public async isWalletOnDevice(chainId?: string): Promise { return this.wallet.isOnDevice(chainId) } public async isWalletBackedUp(chainId?: string): Promise { return this.wallet.isBackedUp(chainId) } public async isWalletRecoverable(chainId?: string): Promise { return this.wallet.isRecoverable(chainId) } public async availableRecoveryMethods( chainId?: string, ): Promise { return this.wallet.availableRecoveryMethods(chainId) } public async getSigningSharesMetadata( chainId?: string, ): Promise { return this.wallet.getSigningSharesMetadata(chainId) } public async getBackupSharesMetadata( chainId?: string, ): Promise { return this.wallet.getBackupSharesMetadata(chainId) } /** * @deprecated The Portal SDK is now multi-wallet. * Use deleteShares() instead. */ public async deleteAddress(): Promise { return this.wallet.deleteAddress() } /** * @deprecated The Portal SDK is now multi-wallet. * Use deleteShares() instead. */ public async deleteSigningShare(): Promise { return this.wallet.deleteSigningShare() } public async deleteShares(): Promise { return this.wallet.deleteShares() } /*********************************** * Assets — delegates to this.assets ***********************************/ public async getBalanceAsNumber(chainId: string): Promise { return this.assets.getBalanceAsNumber(chainId) } public async buildBitcoinP2wpkhTransaction( params: { to: string; token: string; amount: string }, chainId: string, ): Promise { return this.assets.buildBitcoinP2wpkhTransaction(params, chainId) } public async broadcastBitcoinP2wpkhTransaction( params: BroadcastParam, chainId: string, ): Promise { return this.assets.broadcastBitcoinP2wpkhTransaction(params, chainId) } /** * @deprecated Use object-based parameters instead: sendAsset({ to, token, amount, sponsorGas, signatureApprovalMemo }, chain) */ public sendAsset( to: string, token: string, amount: string, chain?: string, ): Promise public sendAsset( params: SendAssetParams, chain?: string, ): Promise public async sendAsset( arg1: string | SendAssetParams, ...rest: (string | undefined)[] ): Promise { let params: SendAssetParams let chain: string | undefined if (typeof arg1 === 'string') { sdkLogger.warn( '[Portal] sendAsset(to, token, amount, chain) is deprecated. ' + 'Use sendAsset({ to, token, amount, sponsorGas }, chain) instead.', ) params = { to: arg1, token: rest[0]!, amount: rest[1]! } chain = rest[2] } else { params = arg1 chain = rest[0] } return this.assets.send(params, chain) } public async receiveTestnetAsset( chainId: string, params: FundParams, ): Promise { return this.assets.receiveTestnetAsset(chainId, params) } public async getAssets(chainId: string): Promise { return this.assets.getAssets(chainId) } /*********************************** * API — direct delegates ***********************************/ public async evaluateTransaction( params: EvaluateTransactionParam, chainId: string, operationType?: EvaluateTransactionOperationType, ): Promise { return this.api.evaluateTransaction(params, chainId, operationType) } /*********************************** * Provider — direct delegates ***********************************/ public emit(event: string, payload?: any) { this.provider.emit(event, payload) } public on(event: string, callback: EventHandler) { this.provider.on(event, callback) } public removeEventListener( event: string, callback: EventHandler | undefined, ) { this.provider.removeEventListener(event, callback) } public async rawSign( message: string, chainId?: string, options?: RawSignOptions, ): Promise { return this.provider.request({ method: PortalRequestMethod.RawSign, params: [message], chainId, options, }) } // Preferred overload public async request( method: PortalRequestMethod, params: unknown[], chainId?: string, options?: RequestOptions, ): Promise /** * @deprecated Use the PortalRequestMethod enum instead of raw string methods. */ public async request( method: string, params: unknown[], chainId?: string, options?: RequestOptions, ): Promise public async request( method: PortalRequestMethod | string, params: unknown[], chainId?: string, options?: RequestOptions, ): Promise { if ( !Object.values(PortalRequestMethod).includes( method as PortalRequestMethod, ) ) { sdkLogger.warn( '[Portal] request(string, ...) is deprecated. Use PortalRequestMethod enum instead.', ) } return this.provider.request({ method, params, chainId, options, }) } public async updateChain(chainId: string) { await this.provider.setChainId(chainId) } /*********************************** * Deprecated provider helpers ***********************************/ /** * @deprecated Use request(PortalRequestMethod.EthEstimateGas, [transaction], chainId) instead. */ public ethEstimateGas( transaction: SigningRequestParams, chainId?: string, ): Promise { sdkLogger.warn( '[Portal] ethEstimateGas() is deprecated. Use request(PortalRequestMethod.EthEstimateGas, ...) instead.', ) return this.provider.request({ method: PortalRequestMethod.EthEstimateGas, params: [transaction], chainId, }) } /** * @deprecated Use request(PortalRequestMethod.EthGasPrice, [], chainId) instead. */ public ethGasPrice(chainId?: string): Promise { sdkLogger.warn( '[Portal] ethGasPrice() is deprecated. Use request(PortalRequestMethod.EthGasPrice, ...) instead.', ) return this.provider.request({ method: PortalRequestMethod.EthGasPrice, params: [], chainId, }) } /** * @deprecated Use request(PortalRequestMethod.EthGetBalance, [address], chainId) instead. */ public async ethGetBalance(chainId?: string): Promise { sdkLogger.warn( '[Portal] ethGetBalance() is deprecated. Use request(PortalRequestMethod.EthGetBalance, ...) instead.', ) const address = await this.address return this.provider.request({ method: PortalRequestMethod.EthGetBalance, params: [address], chainId, }) } /** * @deprecated Use `request(PortalRequestMethod.EthSendTransaction, [transaction], chainId, options)` instead * You can pass `{ signatureApprovalMemo: 'your memo' }` in the options. */ public async ethSendTransaction( transaction: SigningRequestParams, chainId?: string, ): Promise { sdkLogger.warn( '[Portal] ethSendTransaction() is deprecated. Use request(PortalRequestMethod.EthSendTransaction, [transaction], chainId, options) instead. ', ) return this.provider.request({ method: PortalRequestMethod.EthSendTransaction, params: [transaction], chainId, }) } /** * @deprecated Use `request(PortalRequestMethod.EthSign, [address, message], chainId, options)` instead * You can pass `{ signatureApprovalMemo: 'your memo' }` in the options. */ public async ethSign( message: string, chainId?: string, signatureApprovalMemo?: string, ): Promise { sdkLogger.warn( '[Portal] ethSign() is deprecated. Use request(PortalRequestMethod.EthSign, [address, message], chainId, options) instead.', ) const address = await this.address const options = signatureApprovalMemo !== undefined ? { signatureApprovalMemo } : undefined return this.provider.request({ method: PortalRequestMethod.EthSign, params: [address, message], chainId, options, }) } /** * @deprecated Use `request(PortalRequestMethod.EthSignTransaction, [transaction], chainId, options)` instead * You can pass `{ signatureApprovalMemo: 'your memo' }` in the options. */ public async ethSignTransaction( transaction: SigningRequestParams, chainId?: string, ): Promise { sdkLogger.warn( '[Portal] ethSignTransaction() is deprecated. Use request(PortalRequestMethod.EthSignTransaction, [transaction], chainId, options) instead.', ) return this.provider.request({ method: PortalRequestMethod.EthSignTransaction, params: [transaction], chainId, }) } /** * @deprecated Use `request(PortalRequestMethod.EthSignTypedDataV4, [address, typedData], chainId, options)` instead * You can pass `{ signatureApprovalMemo: 'your memo' }` in the options. */ public async ethSignTypedData( typedData: string, chainId?: string, ): Promise { sdkLogger.warn( '[Portal] ethSignTypedData() is deprecated. Use request(PortalRequestMethod.EthSignTypedDataV4, [address, typedData], chainId, options) instead.', ) const address = await this.address return this.provider.request({ method: PortalRequestMethod.EthSignTypedDataV4, params: [address, typedData], chainId, }) } /** * @deprecated Use `request(PortalRequestMethod.PersonalSign, [message, address], chainId, options)` instead * You can pass `{ signatureApprovalMemo: 'your memo' }` in the options. */ public async personalSign(message: string, chainId?: string): Promise { sdkLogger.warn( '[Portal] personalSign() is deprecated. Use request(PortalRequestMethod.PersonalSign, [message, address], chainId, options) instead.', ) const address = await this.address return this.provider.request({ method: PortalRequestMethod.PersonalSign, params: [message, address], chainId, }) } /*********************************** * Portal Connect ***********************************/ public createPortalConnectInstance(chainId: number): PortalConnect { return new PortalConnect({ apiKey: this.apiKey, chainId, keychain: this.keychain, gatewayConfig: this.gatewayConfig, isSimulator: this.isSimulator, autoApprove: this.autoApprove, version: this.version, apiHost: this.apiHost, mpcHost: this.mpcHost, enclaveMPCHost: this.enclaveMPCHost, webSocketServer: this.webSocketHost, }) } } export default Portal