@avalabs/fusion-sdk 0.17.0 → 0.18.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/dist/constants.cjs +1 -1
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +14 -2
- package/dist/constants.d.ts +14 -2
- package/dist/constants.js +1 -1
- package/dist/constants.js.map +1 -1
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts +6 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.js +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/mod.cjs +1 -1
- package/dist/mod.d.cts +4 -4
- package/dist/mod.d.ts +4 -4
- package/dist/mod.js +1 -1
- package/dist/transfer-service/avalanche-cct/_handlers/stream-quotes.cjs +1 -1
- package/dist/transfer-service/avalanche-cct/_handlers/stream-quotes.cjs.map +1 -1
- package/dist/transfer-service/avalanche-cct/_handlers/stream-quotes.js +1 -1
- package/dist/transfer-service/avalanche-cct/_handlers/stream-quotes.js.map +1 -1
- package/dist/transfer-service/avalanche-cct/_handlers/transfer-asset.cjs +1 -1
- package/dist/transfer-service/avalanche-cct/_handlers/transfer-asset.cjs.map +1 -1
- package/dist/transfer-service/avalanche-cct/_handlers/transfer-asset.js +1 -1
- package/dist/transfer-service/avalanche-cct/_handlers/transfer-asset.js.map +1 -1
- package/dist/transfer-service/markr/_api.cjs +1 -1
- package/dist/transfer-service/markr/_api.cjs.map +1 -1
- package/dist/transfer-service/markr/_api.js +1 -1
- package/dist/transfer-service/markr/_api.js.map +1 -1
- package/dist/transfer-service/markr/_handlers/estimate-native-fee.cjs +1 -1
- package/dist/transfer-service/markr/_handlers/estimate-native-fee.cjs.map +1 -1
- package/dist/transfer-service/markr/_handlers/estimate-native-fee.js +1 -1
- package/dist/transfer-service/markr/_handlers/estimate-native-fee.js.map +1 -1
- package/dist/transfer-service/markr/_handlers/transfer-asset-hyperliquid-withdraw.cjs +2 -0
- package/dist/transfer-service/markr/_handlers/transfer-asset-hyperliquid-withdraw.cjs.map +1 -0
- package/dist/transfer-service/markr/_handlers/transfer-asset-hyperliquid-withdraw.js +2 -0
- package/dist/transfer-service/markr/_handlers/transfer-asset-hyperliquid-withdraw.js.map +1 -0
- package/dist/transfer-service/markr/_handlers/transfer-asset.cjs +1 -1
- package/dist/transfer-service/markr/_handlers/transfer-asset.cjs.map +1 -1
- package/dist/transfer-service/markr/_handlers/transfer-asset.js +1 -1
- package/dist/transfer-service/markr/_handlers/transfer-asset.js.map +1 -1
- package/dist/transfer-service/markr/_hyperliquid-eip712.cjs +2 -0
- package/dist/transfer-service/markr/_hyperliquid-eip712.cjs.map +1 -0
- package/dist/transfer-service/markr/_hyperliquid-eip712.js +2 -0
- package/dist/transfer-service/markr/_hyperliquid-eip712.js.map +1 -0
- package/dist/transfer-service/markr/_hyperliquid-schema.cjs +2 -0
- package/dist/transfer-service/markr/_hyperliquid-schema.cjs.map +1 -0
- package/dist/transfer-service/markr/_hyperliquid-schema.js +2 -0
- package/dist/transfer-service/markr/_hyperliquid-schema.js.map +1 -0
- package/dist/transfer-service/markr/_hyperliquid.cjs +2 -0
- package/dist/transfer-service/markr/_hyperliquid.cjs.map +1 -0
- package/dist/transfer-service/markr/_hyperliquid.js +2 -0
- package/dist/transfer-service/markr/_hyperliquid.js.map +1 -0
- package/dist/transfer-service/markr/_schema.cjs +1 -1
- package/dist/transfer-service/markr/_schema.cjs.map +1 -1
- package/dist/transfer-service/markr/_schema.js +1 -1
- package/dist/transfer-service/markr/_schema.js.map +1 -1
- package/dist/transfer-service/markr/_type-guards.cjs +1 -1
- package/dist/transfer-service/markr/_type-guards.cjs.map +1 -1
- package/dist/transfer-service/markr/_type-guards.js +1 -1
- package/dist/transfer-service/markr/_type-guards.js.map +1 -1
- package/dist/transfer-service/markr/markr-service.cjs.map +1 -1
- package/dist/transfer-service/markr/markr-service.js.map +1 -1
- package/dist/types/service.d.cts +13 -2
- package/dist/types/service.d.ts +13 -2
- package/dist/types/signer.d.cts +37 -1
- package/dist/types/signer.d.ts +37 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_api.cjs","names":["fetchJson","combineUrlPathnames","SupportedChainsResponseSchema","ResponseValidationError","TokenListResponseSchema","fetchEventStream","QuoteResponseSchema","SwapResponseSchema","PartnerInfoResponseSchema","AbortedError","CrossChainStatusResponseSchema","SpenderAddressResponseSchema"],"sources":["../../../src/transfer-service/markr/_api.ts"],"sourcesContent":["import type { Address as SolanaAddress } from '@solana/kit';\nimport type { Address as EvmAddress } from 'viem';\nimport { AbortedError, ResponseValidationError } from '../../errors';\nimport { combineUrlPathnames, fetchEventStream, fetchJson } from '../fetch-utilities';\nimport {\n CrossChainStatusResponseSchema,\n PartnerInfoResponseSchema,\n QuoteResponseSchema,\n SpenderAddressResponseSchema,\n SupportedChainsResponseSchema,\n SwapResponseSchema,\n TokenListResponseSchema,\n type CrossChainStatusResponse,\n type PartnerInfoResponse,\n type QuoteResponseData,\n type SpenderAddressResponse,\n type SupportedChainsResponse,\n type SwapResponse,\n type TokenListResponse,\n} from './_schema';\nimport type { Caip2ChainId } from '../../types/caip';\nimport type { Fetch } from '../../types/utility-types';\n\nexport interface ApiOptions {\n apiBaseUrl: URL;\n apiToken?: string;\n fetch?: Fetch;\n}\n\nfunction getAuthHeaders(apiToken?: string): Record<string, string> | undefined {\n if (!apiToken) {\n return undefined;\n }\n\n return { Authorization: `Bearer ${apiToken}` };\n}\n\nexport async function markrGetInfoChains({\n apiBaseUrl,\n apiToken,\n fetch: customFetch,\n}: ApiOptions): Promise<SupportedChainsResponse> {\n const endpoint = '/info/chains';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, { headers: getAuthHeaders(apiToken), fetch: customFetch });\n\n const parsed = SupportedChainsResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(`Invalid response from Markr \"${endpoint}\" endpoint.`, parsed.error.issues);\n }\n\n return parsed.data;\n}\n\nexport async function markrGetTokenList(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n chainId: number,\n): Promise<TokenListResponse> {\n const endpoint = `/tokens/${chainId}/list`;\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, { headers: getAuthHeaders(apiToken), fetch: customFetch });\n\n const parsed = TokenListResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(`Invalid response from Markr \"${endpoint}\" endpoint.`, parsed.error.issues);\n }\n\n return parsed.data;\n}\n\nexport interface MarkrStreamQuoteParams {\n amount: string;\n appId: string;\n chainId: number | Caip2ChainId;\n destinationChainId?: number | Caip2ChainId;\n slippage?: number;\n tokenIn: string;\n tokenInDecimals: number;\n tokenOut: string;\n tokenOutDecimals: number;\n /**\n * Required ONLY for cross-chain swaps involving EVM.\n *\n * EVM -> EVM (different chains)\n * EVM -> Solana\n * Solana -> EVM\n */\n userEvmAddress?: EvmAddress;\n /**\n * Required ONLY for cross-chain swaps involving Solana.\n *\n * Solana -> EVM\n * EVM -> Solana\n * Solana -> Solana\n */\n userSolanaAddress?: SolanaAddress;\n}\n\nexport interface MarkrStreamQuoteOptions {\n onDone: () => void;\n onError: (error: Error) => void;\n onQuote: (data: QuoteResponseData) => void;\n signal?: AbortSignal;\n}\n\nexport async function markrStreamQuote(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n params: MarkrStreamQuoteParams,\n { onDone, onError, onQuote, signal }: MarkrStreamQuoteOptions,\n): Promise<ReadonlyArray<QuoteResponseData>> {\n const endpoint = '/quote';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const quotes: QuoteResponseData[] = [];\n\n try {\n for await (const event of fetchEventStream(url, {\n body: JSON.stringify(params),\n fetch: customFetch,\n headers: getAuthHeaders(apiToken),\n signal,\n })) {\n const parsedData = QuoteResponseSchema.safeParse(JSON.parse(event.data));\n\n if (!parsedData.success) {\n onError(\n new ResponseValidationError(\n `Invalid quote data received from Markr \"${endpoint}\" endpoint.`,\n parsedData.error.issues,\n ),\n );\n continue;\n }\n\n if (!('done' in parsedData.data)) {\n onQuote(parsedData.data);\n quotes.push(parsedData.data);\n } else {\n onDone();\n }\n }\n } catch (error) {\n if (error instanceof Error) {\n onError(error);\n } else {\n onError(new Error('An unknown error occurred during the quote stream.', { cause: error }));\n }\n\n onDone();\n }\n\n return quotes;\n}\n\nexport interface MarkrGetSwapParams {\n appId: string;\n /** Input amount in token's smallest unit */\n amountIn: string;\n /** Return raw transaction data alongside wrapped data */\n debug?: boolean;\n /** Minimum expected output amount */\n minAmountOut: string;\n /** Input token contract address */\n tokenIn: string;\n /** Output token contract address */\n tokenOut: string;\n /** Solana wallet public key (required for Solana swaps) */\n userPublicKey?: string;\n /** Quote UUID from previous quote request */\n uuid: string;\n}\n\nexport async function markrSwap(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n params: MarkrGetSwapParams,\n): Promise<SwapResponse> {\n const endpoint = '/swap';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, {\n body: JSON.stringify(params),\n fetch: customFetch,\n headers: getAuthHeaders(apiToken),\n method: 'POST',\n });\n\n const parsed = SwapResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(`Invalid response from Markr \"${endpoint}\" endpoint.`, parsed.error.issues);\n }\n\n return parsed.data;\n}\n\nexport async function markrGetPartnerInfo({\n apiBaseUrl,\n apiToken,\n fetch: customFetch,\n}: ApiOptions): Promise<PartnerInfoResponse> {\n const endpoint = '/info/partner';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, { headers: getAuthHeaders(apiToken), fetch: customFetch });\n\n const parsed = PartnerInfoResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(`Invalid response from Markr \"${endpoint}\" endpoint.`, parsed.error.issues);\n }\n\n return parsed.data;\n}\n\nexport async function markrGetCrossChainStatus(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n sourceTxHash: string,\n { signal }: { signal?: AbortSignal } = {},\n): Promise<CrossChainStatusResponse> {\n const endpoint = `/cross-chain/status/${sourceTxHash}`;\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, {\n headers: getAuthHeaders(apiToken),\n fetch: customFetch,\n retries: 10,\n retryOn(ctx) {\n const method = (ctx.init.method ?? 'GET').toUpperCase();\n\n if (!['GET', 'HEAD', 'OPTIONS', 'TRACE'].includes(method)) {\n return false;\n }\n\n if (ctx.error) {\n if (ctx.error instanceof AbortedError) {\n return false;\n }\n\n return true;\n }\n\n const status = ctx.response?.status ?? 0;\n\n // Retry on 404 requests since the endpoint may not return a result immediately after the source transaction is executed.\n if (status === 404 || status === 408 || status === 425 || status === 429 || (status >= 500 && status <= 599)) {\n return true;\n }\n\n return false;\n },\n signal,\n });\n\n const parsed = CrossChainStatusResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(\n `Invalid response from Markr \"${endpoint}\" endpoint.`,\n parsed.error.issues,\n rawJson,\n );\n }\n\n return parsed.data;\n}\n\n/**\n * Returns the spender address (wrapped contract) that needs to be approved\n * for token swaps on the specified chain.\n *\n * Not applicable for Solana.\n */\nexport async function markrGetSpenderAddress(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n {\n chainId,\n crossChainSwap,\n quoteId,\n }: {\n chainId: number;\n crossChainSwap?: boolean;\n quoteId: string;\n },\n): Promise<SpenderAddressResponse> {\n const endpoint = `/spender-address?chainId=${chainId}&cross=${crossChainSwap ? 'true' : 'false'}&uuid=${quoteId}`;\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, { headers: getAuthHeaders(apiToken), fetch: customFetch });\n\n const parsed = SpenderAddressResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(`Invalid response from Markr \"${endpoint}\" endpoint.`, parsed.error.issues);\n }\n\n return parsed.data;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"_api.cjs","names":["fetchJson","combineUrlPathnames","SupportedChainsResponseSchema","ResponseValidationError","TokenListResponseSchema","fetchEventStream","QuoteResponseSchema","SwapResponseSchema","PartnerInfoResponseSchema","AbortedError","CrossChainStatusResponseSchema","SpenderAddressResponseSchema","MarkrAuthorizeResponseSchema"],"sources":["../../../src/transfer-service/markr/_api.ts"],"sourcesContent":["import type { Address as SolanaAddress } from '@solana/kit';\nimport type { Address as EvmAddress } from 'viem';\nimport { AbortedError, ResponseValidationError } from '../../errors';\nimport { combineUrlPathnames, fetchEventStream, fetchJson } from '../fetch-utilities';\nimport {\n CrossChainStatusResponseSchema,\n MarkrAuthorizeResponseSchema,\n PartnerInfoResponseSchema,\n QuoteResponseSchema,\n SpenderAddressResponseSchema,\n SupportedChainsResponseSchema,\n SwapResponseSchema,\n TokenListResponseSchema,\n type CrossChainStatusResponse,\n type PartnerInfoResponse,\n type QuoteResponseData,\n type SpenderAddressResponse,\n type SupportedChainsResponse,\n type SwapResponse,\n type TokenListResponse,\n} from './_schema';\nimport type { Caip2ChainId } from '../../types/caip';\nimport type { Fetch } from '../../types/utility-types';\n\nexport interface ApiOptions {\n apiBaseUrl: URL;\n apiToken?: string;\n fetch?: Fetch;\n}\n\nfunction getAuthHeaders(apiToken?: string): Record<string, string> | undefined {\n if (!apiToken) {\n return undefined;\n }\n\n return { Authorization: `Bearer ${apiToken}` };\n}\n\nexport async function markrGetInfoChains({\n apiBaseUrl,\n apiToken,\n fetch: customFetch,\n}: ApiOptions): Promise<SupportedChainsResponse> {\n const endpoint = '/info/chains';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, { headers: getAuthHeaders(apiToken), fetch: customFetch });\n\n const parsed = SupportedChainsResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(`Invalid response from Markr \"${endpoint}\" endpoint.`, parsed.error.issues);\n }\n\n return parsed.data;\n}\n\nexport async function markrGetTokenList(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n chainId: number,\n): Promise<TokenListResponse> {\n const endpoint = `/tokens/${chainId}/list`;\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, { headers: getAuthHeaders(apiToken), fetch: customFetch });\n\n const parsed = TokenListResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(`Invalid response from Markr \"${endpoint}\" endpoint.`, parsed.error.issues);\n }\n\n return parsed.data;\n}\n\nexport interface MarkrStreamQuoteParams {\n amount: string;\n appId: string;\n chainId: number | Caip2ChainId;\n destinationChainId?: number | Caip2ChainId;\n slippage?: number;\n tokenIn: string;\n tokenInDecimals: number;\n tokenOut: string;\n tokenOutDecimals: number;\n /**\n * Required ONLY for cross-chain swaps involving EVM.\n *\n * EVM -> EVM (different chains)\n * EVM -> Solana\n * Solana -> EVM\n */\n userEvmAddress?: EvmAddress;\n /**\n * Required ONLY for cross-chain swaps involving Solana.\n *\n * Solana -> EVM\n * EVM -> Solana\n * Solana -> Solana\n */\n userSolanaAddress?: SolanaAddress;\n}\n\nexport interface MarkrStreamQuoteOptions {\n onDone: () => void;\n onError: (error: Error) => void;\n onQuote: (data: QuoteResponseData) => void;\n signal?: AbortSignal;\n}\n\nexport async function markrStreamQuote(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n params: MarkrStreamQuoteParams,\n { onDone, onError, onQuote, signal }: MarkrStreamQuoteOptions,\n): Promise<ReadonlyArray<QuoteResponseData>> {\n const endpoint = '/quote';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const quotes: QuoteResponseData[] = [];\n\n try {\n for await (const event of fetchEventStream(url, {\n body: JSON.stringify(params),\n fetch: customFetch,\n headers: getAuthHeaders(apiToken),\n signal,\n })) {\n const parsedData = QuoteResponseSchema.safeParse(JSON.parse(event.data));\n\n if (!parsedData.success) {\n onError(\n new ResponseValidationError(\n `Invalid quote data received from Markr \"${endpoint}\" endpoint.`,\n parsedData.error.issues,\n ),\n );\n continue;\n }\n\n if (!('done' in parsedData.data)) {\n onQuote(parsedData.data);\n quotes.push(parsedData.data);\n } else {\n onDone();\n }\n }\n } catch (error) {\n if (error instanceof Error) {\n onError(error);\n } else {\n onError(new Error('An unknown error occurred during the quote stream.', { cause: error }));\n }\n\n onDone();\n }\n\n return quotes;\n}\n\nexport interface MarkrGetSwapParams {\n appId: string;\n /** Input amount in token's smallest unit */\n amountIn: string;\n /** Return raw transaction data alongside wrapped data */\n debug?: boolean;\n /** Minimum expected output amount */\n minAmountOut: string;\n /** Input token contract address */\n tokenIn: string;\n /** Output token contract address */\n tokenOut: string;\n /** Solana wallet public key (required for Solana swaps) */\n userPublicKey?: string;\n /** Quote UUID from previous quote request */\n uuid: string;\n}\n\nexport async function markrSwap(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n params: MarkrGetSwapParams,\n): Promise<SwapResponse> {\n const endpoint = '/swap';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, {\n body: JSON.stringify(params),\n fetch: customFetch,\n headers: getAuthHeaders(apiToken),\n method: 'POST',\n });\n\n const parsed = SwapResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(`Invalid response from Markr \"${endpoint}\" endpoint.`, parsed.error.issues);\n }\n\n return parsed.data;\n}\n\nexport async function markrGetPartnerInfo({\n apiBaseUrl,\n apiToken,\n fetch: customFetch,\n}: ApiOptions): Promise<PartnerInfoResponse> {\n const endpoint = '/info/partner';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, { headers: getAuthHeaders(apiToken), fetch: customFetch });\n\n const parsed = PartnerInfoResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(`Invalid response from Markr \"${endpoint}\" endpoint.`, parsed.error.issues);\n }\n\n return parsed.data;\n}\n\nexport async function markrGetCrossChainStatus(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n sourceTxHash: string,\n { signal }: { signal?: AbortSignal } = {},\n): Promise<CrossChainStatusResponse> {\n const endpoint = `/cross-chain/status/${sourceTxHash}`;\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, {\n headers: getAuthHeaders(apiToken),\n fetch: customFetch,\n retries: 10,\n retryOn(ctx) {\n const method = (ctx.init.method ?? 'GET').toUpperCase();\n\n if (!['GET', 'HEAD', 'OPTIONS', 'TRACE'].includes(method)) {\n return false;\n }\n\n if (ctx.error) {\n if (ctx.error instanceof AbortedError) {\n return false;\n }\n\n return true;\n }\n\n const status = ctx.response?.status ?? 0;\n\n // Retry on 404 requests since the endpoint may not return a result immediately after the source transaction is executed.\n if (status === 404 || status === 408 || status === 425 || status === 429 || (status >= 500 && status <= 599)) {\n return true;\n }\n\n return false;\n },\n signal,\n });\n\n const parsed = CrossChainStatusResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(\n `Invalid response from Markr \"${endpoint}\" endpoint.`,\n parsed.error.issues,\n rawJson,\n );\n }\n\n return parsed.data;\n}\n\n/**\n * Returns the spender address (wrapped contract) that needs to be approved\n * for token swaps on the specified chain.\n *\n * Not applicable for Solana.\n */\nexport async function markrGetSpenderAddress(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n {\n chainId,\n crossChainSwap,\n quoteId,\n }: {\n chainId: number;\n crossChainSwap?: boolean;\n quoteId: string;\n },\n): Promise<SpenderAddressResponse> {\n const endpoint = `/spender-address?chainId=${chainId}&cross=${crossChainSwap ? 'true' : 'false'}&uuid=${quoteId}`;\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, { headers: getAuthHeaders(apiToken), fetch: customFetch });\n\n const parsed = SpenderAddressResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(`Invalid response from Markr \"${endpoint}\" endpoint.`, parsed.error.issues);\n }\n\n return parsed.data;\n}\n\n// ---------------------------------------------------------------------------\n// Hyperliquid 2-phase withdrawal: nonce-mapping authorize\n// ---------------------------------------------------------------------------\n\n/**\n * Legacy (`protocolVersion: 1`) authorize body — Markr rebuilds the upstream\n * Relay request from a fixed schema. Kept for back-compat; new integrations\n * should use {@link MarkrPostAuthorizeParamsV2}.\n *\n * **Caveat:** when Relay adds a required field (as it did with `depositor` in\n * v2), the rebuilt body silently omits it and the order is orphaned upstream.\n */\nexport interface MarkrPostAuthorizeParamsV1 {\n /** Quote uuid — lets Markr correlate the authorize call with the cached Relay step. */\n readonly uuid: string;\n /** User's EIP-712 signature over the `RelayNonceMapping` typed data (`0x{r}{s}{v}`, 65 bytes hex). */\n readonly signature: `0x${string}`;\n /** EVM address of the signer. */\n readonly wallet: EvmAddress;\n /** Always `1337` (Hyperliquid). */\n readonly walletChainId: 1337;\n /** EIP-712 domain.chainId used in the signature — the wallet's active chain. */\n readonly signatureChainId: number;\n /**\n * Shared nonce — JSON number, matching Relay's upstream `/authorize` contract\n * and HL `/exchange`. HL nonces are millisecond timestamps, well under\n * `Number.MAX_SAFE_INTEGER`. Sending it as a string makes Relay reject the\n * payload (strict type validation), so we coerce here at the wire boundary.\n */\n readonly nonce: number;\n /** Relay request id (`0x{64hex}`). */\n readonly id: `0x${string}`;\n}\n\n/**\n * Pass-through (`protocolVersion: 2`) authorize body. The SDK forwards Relay's\n * `authorize.body` envelope from the `/swap` response **verbatim**. Markr only\n * normalizes `nonce` to a JSON number and defaults `referrer`; every other\n * field is untouched, so additive Relay schema changes don't break SDKs.\n */\nexport interface MarkrPostAuthorizeParamsV2 {\n /** Quote uuid — lets Markr correlate the authorize call with the cached Relay step. */\n readonly uuid: string;\n /** User's EIP-712 signature over `swap.sign.value` using `swap.sign.domain` / `swap.sign.types`. */\n readonly signature: `0x${string}`;\n /** Relay's `/authorize` POST body, taken from `swap.authorize.body`. Forwarded verbatim. */\n readonly body: Record<string, unknown>;\n}\n\nexport type MarkrPostAuthorizeParams = MarkrPostAuthorizeParamsV1 | MarkrPostAuthorizeParamsV2;\n\n/**\n * Submit the user's signed nonce-mapping to Markr's `/authorize` endpoint.\n *\n * Markr proxies the request to Relay's upstream `/authorize`. Returns on any\n * 2xx; throws `HttpError` otherwise.\n */\nexport async function markrPostAuthorize(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n params: MarkrPostAuthorizeParams,\n { signal }: { signal?: AbortSignal } = {},\n): Promise<void> {\n const endpoint = '/authorize';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n // `fetchJson` throws `HttpError` on non-2xx; the schema additionally rejects\n // upstream error envelopes that come back with a 200 (Relay sometimes does\n // this when authorize is rejected after HTTP success).\n const rawJson = await fetchJson(url, {\n body: JSON.stringify(params),\n fetch: customFetch,\n headers: getAuthHeaders(apiToken),\n method: 'POST',\n signal,\n });\n\n const parsed = MarkrAuthorizeResponseSchema.safeParse(rawJson);\n if (!parsed.success) {\n throw new ResponseValidationError(`Invalid response from Markr \"${endpoint}\" endpoint.`, parsed.error.issues);\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{AbortedError as e,ResponseValidationError as t}from"../../errors.js";import{CrossChainStatusResponseSchema as n,
|
|
1
|
+
import{AbortedError as e,ResponseValidationError as t}from"../../errors.js";import{CrossChainStatusResponseSchema as n,MarkrAuthorizeResponseSchema as r,PartnerInfoResponseSchema as i,QuoteResponseSchema as a,SpenderAddressResponseSchema as o,SupportedChainsResponseSchema as s,SwapResponseSchema as c,TokenListResponseSchema as l}from"./_schema.js";import{combineUrlPathnames as u,fetchEventStream as d,fetchJson as f}from"../fetch-utilities.js";function p(e){if(e)return{Authorization:`Bearer ${e}`}}async function m({apiBaseUrl:e,apiToken:n,fetch:r}){let i=`/info/chains`,a=await f(u(e,i),{headers:p(n),fetch:r}),o=s.safeParse(a);if(!o.success)throw new t(`Invalid response from Markr "${i}" endpoint.`,o.error.issues);return o.data}async function h({apiBaseUrl:e,apiToken:n,fetch:r},i){let a=`/tokens/${i}/list`,o=await f(u(e,a),{headers:p(n),fetch:r}),s=l.safeParse(o);if(!s.success)throw new t(`Invalid response from Markr "${a}" endpoint.`,s.error.issues);return s.data}async function g({apiBaseUrl:e,apiToken:n,fetch:r},i,{onDone:o,onError:s,onQuote:c,signal:l}){let f=`/quote`,m=u(e,f),h=[];try{for await(let e of d(m,{body:JSON.stringify(i),fetch:r,headers:p(n),signal:l})){let n=a.safeParse(JSON.parse(e.data));if(!n.success){s(new t(`Invalid quote data received from Markr "${f}" endpoint.`,n.error.issues));continue}`done`in n.data?o():(c(n.data),h.push(n.data))}}catch(e){e instanceof Error?s(e):s(Error(`An unknown error occurred during the quote stream.`,{cause:e})),o()}return h}async function _({apiBaseUrl:e,apiToken:n,fetch:r},i){let a=`/swap`,o=await f(u(e,a),{body:JSON.stringify(i),fetch:r,headers:p(n),method:`POST`}),s=c.safeParse(o);if(!s.success)throw new t(`Invalid response from Markr "${a}" endpoint.`,s.error.issues);return s.data}async function v({apiBaseUrl:e,apiToken:n,fetch:r}){let a=`/info/partner`,o=await f(u(e,a),{headers:p(n),fetch:r}),s=i.safeParse(o);if(!s.success)throw new t(`Invalid response from Markr "${a}" endpoint.`,s.error.issues);return s.data}async function y({apiBaseUrl:r,apiToken:i,fetch:a},o,{signal:s}={}){let c=`/cross-chain/status/${o}`,l=await f(u(r,c),{headers:p(i),fetch:a,retries:10,retryOn(t){let n=(t.init.method??`GET`).toUpperCase();if(![`GET`,`HEAD`,`OPTIONS`,`TRACE`].includes(n))return!1;if(t.error)return!(t.error instanceof e);let r=t.response?.status??0;return r===404||r===408||r===425||r===429||r>=500&&r<=599},signal:s}),d=n.safeParse(l);if(!d.success)throw new t(`Invalid response from Markr "${c}" endpoint.`,d.error.issues,l);return d.data}async function b({apiBaseUrl:e,apiToken:n,fetch:r},{chainId:i,crossChainSwap:a,quoteId:s}){let c=`/spender-address?chainId=${i}&cross=${a?`true`:`false`}&uuid=${s}`,l=await f(u(e,c),{headers:p(n),fetch:r}),d=o.safeParse(l);if(!d.success)throw new t(`Invalid response from Markr "${c}" endpoint.`,d.error.issues);return d.data}async function x({apiBaseUrl:e,apiToken:n,fetch:i},a,{signal:o}={}){let s=`/authorize`,c=await f(u(e,s),{body:JSON.stringify(a),fetch:i,headers:p(n),method:`POST`,signal:o}),l=r.safeParse(c);if(!l.success)throw new t(`Invalid response from Markr "${s}" endpoint.`,l.error.issues)}export{y as markrGetCrossChainStatus,m as markrGetInfoChains,v as markrGetPartnerInfo,b as markrGetSpenderAddress,h as markrGetTokenList,x as markrPostAuthorize,g as markrStreamQuote,_ as markrSwap};
|
|
2
2
|
//# sourceMappingURL=_api.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_api.js","names":[],"sources":["../../../src/transfer-service/markr/_api.ts"],"sourcesContent":["import type { Address as SolanaAddress } from '@solana/kit';\nimport type { Address as EvmAddress } from 'viem';\nimport { AbortedError, ResponseValidationError } from '../../errors';\nimport { combineUrlPathnames, fetchEventStream, fetchJson } from '../fetch-utilities';\nimport {\n CrossChainStatusResponseSchema,\n PartnerInfoResponseSchema,\n QuoteResponseSchema,\n SpenderAddressResponseSchema,\n SupportedChainsResponseSchema,\n SwapResponseSchema,\n TokenListResponseSchema,\n type CrossChainStatusResponse,\n type PartnerInfoResponse,\n type QuoteResponseData,\n type SpenderAddressResponse,\n type SupportedChainsResponse,\n type SwapResponse,\n type TokenListResponse,\n} from './_schema';\nimport type { Caip2ChainId } from '../../types/caip';\nimport type { Fetch } from '../../types/utility-types';\n\nexport interface ApiOptions {\n apiBaseUrl: URL;\n apiToken?: string;\n fetch?: Fetch;\n}\n\nfunction getAuthHeaders(apiToken?: string): Record<string, string> | undefined {\n if (!apiToken) {\n return undefined;\n }\n\n return { Authorization: `Bearer ${apiToken}` };\n}\n\nexport async function markrGetInfoChains({\n apiBaseUrl,\n apiToken,\n fetch: customFetch,\n}: ApiOptions): Promise<SupportedChainsResponse> {\n const endpoint = '/info/chains';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, { headers: getAuthHeaders(apiToken), fetch: customFetch });\n\n const parsed = SupportedChainsResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(`Invalid response from Markr \"${endpoint}\" endpoint.`, parsed.error.issues);\n }\n\n return parsed.data;\n}\n\nexport async function markrGetTokenList(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n chainId: number,\n): Promise<TokenListResponse> {\n const endpoint = `/tokens/${chainId}/list`;\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, { headers: getAuthHeaders(apiToken), fetch: customFetch });\n\n const parsed = TokenListResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(`Invalid response from Markr \"${endpoint}\" endpoint.`, parsed.error.issues);\n }\n\n return parsed.data;\n}\n\nexport interface MarkrStreamQuoteParams {\n amount: string;\n appId: string;\n chainId: number | Caip2ChainId;\n destinationChainId?: number | Caip2ChainId;\n slippage?: number;\n tokenIn: string;\n tokenInDecimals: number;\n tokenOut: string;\n tokenOutDecimals: number;\n /**\n * Required ONLY for cross-chain swaps involving EVM.\n *\n * EVM -> EVM (different chains)\n * EVM -> Solana\n * Solana -> EVM\n */\n userEvmAddress?: EvmAddress;\n /**\n * Required ONLY for cross-chain swaps involving Solana.\n *\n * Solana -> EVM\n * EVM -> Solana\n * Solana -> Solana\n */\n userSolanaAddress?: SolanaAddress;\n}\n\nexport interface MarkrStreamQuoteOptions {\n onDone: () => void;\n onError: (error: Error) => void;\n onQuote: (data: QuoteResponseData) => void;\n signal?: AbortSignal;\n}\n\nexport async function markrStreamQuote(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n params: MarkrStreamQuoteParams,\n { onDone, onError, onQuote, signal }: MarkrStreamQuoteOptions,\n): Promise<ReadonlyArray<QuoteResponseData>> {\n const endpoint = '/quote';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const quotes: QuoteResponseData[] = [];\n\n try {\n for await (const event of fetchEventStream(url, {\n body: JSON.stringify(params),\n fetch: customFetch,\n headers: getAuthHeaders(apiToken),\n signal,\n })) {\n const parsedData = QuoteResponseSchema.safeParse(JSON.parse(event.data));\n\n if (!parsedData.success) {\n onError(\n new ResponseValidationError(\n `Invalid quote data received from Markr \"${endpoint}\" endpoint.`,\n parsedData.error.issues,\n ),\n );\n continue;\n }\n\n if (!('done' in parsedData.data)) {\n onQuote(parsedData.data);\n quotes.push(parsedData.data);\n } else {\n onDone();\n }\n }\n } catch (error) {\n if (error instanceof Error) {\n onError(error);\n } else {\n onError(new Error('An unknown error occurred during the quote stream.', { cause: error }));\n }\n\n onDone();\n }\n\n return quotes;\n}\n\nexport interface MarkrGetSwapParams {\n appId: string;\n /** Input amount in token's smallest unit */\n amountIn: string;\n /** Return raw transaction data alongside wrapped data */\n debug?: boolean;\n /** Minimum expected output amount */\n minAmountOut: string;\n /** Input token contract address */\n tokenIn: string;\n /** Output token contract address */\n tokenOut: string;\n /** Solana wallet public key (required for Solana swaps) */\n userPublicKey?: string;\n /** Quote UUID from previous quote request */\n uuid: string;\n}\n\nexport async function markrSwap(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n params: MarkrGetSwapParams,\n): Promise<SwapResponse> {\n const endpoint = '/swap';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, {\n body: JSON.stringify(params),\n fetch: customFetch,\n headers: getAuthHeaders(apiToken),\n method: 'POST',\n });\n\n const parsed = SwapResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(`Invalid response from Markr \"${endpoint}\" endpoint.`, parsed.error.issues);\n }\n\n return parsed.data;\n}\n\nexport async function markrGetPartnerInfo({\n apiBaseUrl,\n apiToken,\n fetch: customFetch,\n}: ApiOptions): Promise<PartnerInfoResponse> {\n const endpoint = '/info/partner';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, { headers: getAuthHeaders(apiToken), fetch: customFetch });\n\n const parsed = PartnerInfoResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(`Invalid response from Markr \"${endpoint}\" endpoint.`, parsed.error.issues);\n }\n\n return parsed.data;\n}\n\nexport async function markrGetCrossChainStatus(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n sourceTxHash: string,\n { signal }: { signal?: AbortSignal } = {},\n): Promise<CrossChainStatusResponse> {\n const endpoint = `/cross-chain/status/${sourceTxHash}`;\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, {\n headers: getAuthHeaders(apiToken),\n fetch: customFetch,\n retries: 10,\n retryOn(ctx) {\n const method = (ctx.init.method ?? 'GET').toUpperCase();\n\n if (!['GET', 'HEAD', 'OPTIONS', 'TRACE'].includes(method)) {\n return false;\n }\n\n if (ctx.error) {\n if (ctx.error instanceof AbortedError) {\n return false;\n }\n\n return true;\n }\n\n const status = ctx.response?.status ?? 0;\n\n // Retry on 404 requests since the endpoint may not return a result immediately after the source transaction is executed.\n if (status === 404 || status === 408 || status === 425 || status === 429 || (status >= 500 && status <= 599)) {\n return true;\n }\n\n return false;\n },\n signal,\n });\n\n const parsed = CrossChainStatusResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(\n `Invalid response from Markr \"${endpoint}\" endpoint.`,\n parsed.error.issues,\n rawJson,\n );\n }\n\n return parsed.data;\n}\n\n/**\n * Returns the spender address (wrapped contract) that needs to be approved\n * for token swaps on the specified chain.\n *\n * Not applicable for Solana.\n */\nexport async function markrGetSpenderAddress(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n {\n chainId,\n crossChainSwap,\n quoteId,\n }: {\n chainId: number;\n crossChainSwap?: boolean;\n quoteId: string;\n },\n): Promise<SpenderAddressResponse> {\n const endpoint = `/spender-address?chainId=${chainId}&cross=${crossChainSwap ? 'true' : 'false'}&uuid=${quoteId}`;\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, { headers: getAuthHeaders(apiToken), fetch: customFetch });\n\n const parsed = SpenderAddressResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(`Invalid response from Markr \"${endpoint}\" endpoint.`, parsed.error.issues);\n }\n\n return parsed.data;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"_api.js","names":[],"sources":["../../../src/transfer-service/markr/_api.ts"],"sourcesContent":["import type { Address as SolanaAddress } from '@solana/kit';\nimport type { Address as EvmAddress } from 'viem';\nimport { AbortedError, ResponseValidationError } from '../../errors';\nimport { combineUrlPathnames, fetchEventStream, fetchJson } from '../fetch-utilities';\nimport {\n CrossChainStatusResponseSchema,\n MarkrAuthorizeResponseSchema,\n PartnerInfoResponseSchema,\n QuoteResponseSchema,\n SpenderAddressResponseSchema,\n SupportedChainsResponseSchema,\n SwapResponseSchema,\n TokenListResponseSchema,\n type CrossChainStatusResponse,\n type PartnerInfoResponse,\n type QuoteResponseData,\n type SpenderAddressResponse,\n type SupportedChainsResponse,\n type SwapResponse,\n type TokenListResponse,\n} from './_schema';\nimport type { Caip2ChainId } from '../../types/caip';\nimport type { Fetch } from '../../types/utility-types';\n\nexport interface ApiOptions {\n apiBaseUrl: URL;\n apiToken?: string;\n fetch?: Fetch;\n}\n\nfunction getAuthHeaders(apiToken?: string): Record<string, string> | undefined {\n if (!apiToken) {\n return undefined;\n }\n\n return { Authorization: `Bearer ${apiToken}` };\n}\n\nexport async function markrGetInfoChains({\n apiBaseUrl,\n apiToken,\n fetch: customFetch,\n}: ApiOptions): Promise<SupportedChainsResponse> {\n const endpoint = '/info/chains';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, { headers: getAuthHeaders(apiToken), fetch: customFetch });\n\n const parsed = SupportedChainsResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(`Invalid response from Markr \"${endpoint}\" endpoint.`, parsed.error.issues);\n }\n\n return parsed.data;\n}\n\nexport async function markrGetTokenList(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n chainId: number,\n): Promise<TokenListResponse> {\n const endpoint = `/tokens/${chainId}/list`;\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, { headers: getAuthHeaders(apiToken), fetch: customFetch });\n\n const parsed = TokenListResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(`Invalid response from Markr \"${endpoint}\" endpoint.`, parsed.error.issues);\n }\n\n return parsed.data;\n}\n\nexport interface MarkrStreamQuoteParams {\n amount: string;\n appId: string;\n chainId: number | Caip2ChainId;\n destinationChainId?: number | Caip2ChainId;\n slippage?: number;\n tokenIn: string;\n tokenInDecimals: number;\n tokenOut: string;\n tokenOutDecimals: number;\n /**\n * Required ONLY for cross-chain swaps involving EVM.\n *\n * EVM -> EVM (different chains)\n * EVM -> Solana\n * Solana -> EVM\n */\n userEvmAddress?: EvmAddress;\n /**\n * Required ONLY for cross-chain swaps involving Solana.\n *\n * Solana -> EVM\n * EVM -> Solana\n * Solana -> Solana\n */\n userSolanaAddress?: SolanaAddress;\n}\n\nexport interface MarkrStreamQuoteOptions {\n onDone: () => void;\n onError: (error: Error) => void;\n onQuote: (data: QuoteResponseData) => void;\n signal?: AbortSignal;\n}\n\nexport async function markrStreamQuote(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n params: MarkrStreamQuoteParams,\n { onDone, onError, onQuote, signal }: MarkrStreamQuoteOptions,\n): Promise<ReadonlyArray<QuoteResponseData>> {\n const endpoint = '/quote';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const quotes: QuoteResponseData[] = [];\n\n try {\n for await (const event of fetchEventStream(url, {\n body: JSON.stringify(params),\n fetch: customFetch,\n headers: getAuthHeaders(apiToken),\n signal,\n })) {\n const parsedData = QuoteResponseSchema.safeParse(JSON.parse(event.data));\n\n if (!parsedData.success) {\n onError(\n new ResponseValidationError(\n `Invalid quote data received from Markr \"${endpoint}\" endpoint.`,\n parsedData.error.issues,\n ),\n );\n continue;\n }\n\n if (!('done' in parsedData.data)) {\n onQuote(parsedData.data);\n quotes.push(parsedData.data);\n } else {\n onDone();\n }\n }\n } catch (error) {\n if (error instanceof Error) {\n onError(error);\n } else {\n onError(new Error('An unknown error occurred during the quote stream.', { cause: error }));\n }\n\n onDone();\n }\n\n return quotes;\n}\n\nexport interface MarkrGetSwapParams {\n appId: string;\n /** Input amount in token's smallest unit */\n amountIn: string;\n /** Return raw transaction data alongside wrapped data */\n debug?: boolean;\n /** Minimum expected output amount */\n minAmountOut: string;\n /** Input token contract address */\n tokenIn: string;\n /** Output token contract address */\n tokenOut: string;\n /** Solana wallet public key (required for Solana swaps) */\n userPublicKey?: string;\n /** Quote UUID from previous quote request */\n uuid: string;\n}\n\nexport async function markrSwap(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n params: MarkrGetSwapParams,\n): Promise<SwapResponse> {\n const endpoint = '/swap';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, {\n body: JSON.stringify(params),\n fetch: customFetch,\n headers: getAuthHeaders(apiToken),\n method: 'POST',\n });\n\n const parsed = SwapResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(`Invalid response from Markr \"${endpoint}\" endpoint.`, parsed.error.issues);\n }\n\n return parsed.data;\n}\n\nexport async function markrGetPartnerInfo({\n apiBaseUrl,\n apiToken,\n fetch: customFetch,\n}: ApiOptions): Promise<PartnerInfoResponse> {\n const endpoint = '/info/partner';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, { headers: getAuthHeaders(apiToken), fetch: customFetch });\n\n const parsed = PartnerInfoResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(`Invalid response from Markr \"${endpoint}\" endpoint.`, parsed.error.issues);\n }\n\n return parsed.data;\n}\n\nexport async function markrGetCrossChainStatus(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n sourceTxHash: string,\n { signal }: { signal?: AbortSignal } = {},\n): Promise<CrossChainStatusResponse> {\n const endpoint = `/cross-chain/status/${sourceTxHash}`;\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, {\n headers: getAuthHeaders(apiToken),\n fetch: customFetch,\n retries: 10,\n retryOn(ctx) {\n const method = (ctx.init.method ?? 'GET').toUpperCase();\n\n if (!['GET', 'HEAD', 'OPTIONS', 'TRACE'].includes(method)) {\n return false;\n }\n\n if (ctx.error) {\n if (ctx.error instanceof AbortedError) {\n return false;\n }\n\n return true;\n }\n\n const status = ctx.response?.status ?? 0;\n\n // Retry on 404 requests since the endpoint may not return a result immediately after the source transaction is executed.\n if (status === 404 || status === 408 || status === 425 || status === 429 || (status >= 500 && status <= 599)) {\n return true;\n }\n\n return false;\n },\n signal,\n });\n\n const parsed = CrossChainStatusResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(\n `Invalid response from Markr \"${endpoint}\" endpoint.`,\n parsed.error.issues,\n rawJson,\n );\n }\n\n return parsed.data;\n}\n\n/**\n * Returns the spender address (wrapped contract) that needs to be approved\n * for token swaps on the specified chain.\n *\n * Not applicable for Solana.\n */\nexport async function markrGetSpenderAddress(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n {\n chainId,\n crossChainSwap,\n quoteId,\n }: {\n chainId: number;\n crossChainSwap?: boolean;\n quoteId: string;\n },\n): Promise<SpenderAddressResponse> {\n const endpoint = `/spender-address?chainId=${chainId}&cross=${crossChainSwap ? 'true' : 'false'}&uuid=${quoteId}`;\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n const rawJson = await fetchJson(url, { headers: getAuthHeaders(apiToken), fetch: customFetch });\n\n const parsed = SpenderAddressResponseSchema.safeParse(rawJson);\n\n if (!parsed.success) {\n throw new ResponseValidationError(`Invalid response from Markr \"${endpoint}\" endpoint.`, parsed.error.issues);\n }\n\n return parsed.data;\n}\n\n// ---------------------------------------------------------------------------\n// Hyperliquid 2-phase withdrawal: nonce-mapping authorize\n// ---------------------------------------------------------------------------\n\n/**\n * Legacy (`protocolVersion: 1`) authorize body — Markr rebuilds the upstream\n * Relay request from a fixed schema. Kept for back-compat; new integrations\n * should use {@link MarkrPostAuthorizeParamsV2}.\n *\n * **Caveat:** when Relay adds a required field (as it did with `depositor` in\n * v2), the rebuilt body silently omits it and the order is orphaned upstream.\n */\nexport interface MarkrPostAuthorizeParamsV1 {\n /** Quote uuid — lets Markr correlate the authorize call with the cached Relay step. */\n readonly uuid: string;\n /** User's EIP-712 signature over the `RelayNonceMapping` typed data (`0x{r}{s}{v}`, 65 bytes hex). */\n readonly signature: `0x${string}`;\n /** EVM address of the signer. */\n readonly wallet: EvmAddress;\n /** Always `1337` (Hyperliquid). */\n readonly walletChainId: 1337;\n /** EIP-712 domain.chainId used in the signature — the wallet's active chain. */\n readonly signatureChainId: number;\n /**\n * Shared nonce — JSON number, matching Relay's upstream `/authorize` contract\n * and HL `/exchange`. HL nonces are millisecond timestamps, well under\n * `Number.MAX_SAFE_INTEGER`. Sending it as a string makes Relay reject the\n * payload (strict type validation), so we coerce here at the wire boundary.\n */\n readonly nonce: number;\n /** Relay request id (`0x{64hex}`). */\n readonly id: `0x${string}`;\n}\n\n/**\n * Pass-through (`protocolVersion: 2`) authorize body. The SDK forwards Relay's\n * `authorize.body` envelope from the `/swap` response **verbatim**. Markr only\n * normalizes `nonce` to a JSON number and defaults `referrer`; every other\n * field is untouched, so additive Relay schema changes don't break SDKs.\n */\nexport interface MarkrPostAuthorizeParamsV2 {\n /** Quote uuid — lets Markr correlate the authorize call with the cached Relay step. */\n readonly uuid: string;\n /** User's EIP-712 signature over `swap.sign.value` using `swap.sign.domain` / `swap.sign.types`. */\n readonly signature: `0x${string}`;\n /** Relay's `/authorize` POST body, taken from `swap.authorize.body`. Forwarded verbatim. */\n readonly body: Record<string, unknown>;\n}\n\nexport type MarkrPostAuthorizeParams = MarkrPostAuthorizeParamsV1 | MarkrPostAuthorizeParamsV2;\n\n/**\n * Submit the user's signed nonce-mapping to Markr's `/authorize` endpoint.\n *\n * Markr proxies the request to Relay's upstream `/authorize`. Returns on any\n * 2xx; throws `HttpError` otherwise.\n */\nexport async function markrPostAuthorize(\n { apiBaseUrl, apiToken, fetch: customFetch }: ApiOptions,\n params: MarkrPostAuthorizeParams,\n { signal }: { signal?: AbortSignal } = {},\n): Promise<void> {\n const endpoint = '/authorize';\n const url = combineUrlPathnames(apiBaseUrl, endpoint);\n\n // `fetchJson` throws `HttpError` on non-2xx; the schema additionally rejects\n // upstream error envelopes that come back with a 200 (Relay sometimes does\n // this when authorize is rejected after HTTP success).\n const rawJson = await fetchJson(url, {\n body: JSON.stringify(params),\n fetch: customFetch,\n headers: getAuthHeaders(apiToken),\n method: 'POST',\n signal,\n });\n\n const parsed = MarkrAuthorizeResponseSchema.safeParse(rawJson);\n if (!parsed.success) {\n throw new ResponseValidationError(`Invalid response from Markr \"${endpoint}\" endpoint.`, parsed.error.issues);\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
require(`../../../_virtual/_rolldown/runtime.cjs`);const e=require(`../../../errors.cjs`),t=require(`../../../utils/caip.cjs`),n=require(`../../_utils.cjs`),r=require(`../../../_utils/chain.cjs`),i=require(`../../_evm-gas.cjs`),a=require(`../../_evm-errors.cjs`),o=require(`../_api.cjs`),s=require(`../_utils.cjs`),c=require(`../_type-guards.cjs`),l=require(`../../../utils/solana.cjs`);let u=require(`viem`),d=require(`@solana/kit`);function f(t){return async(n,i)=>{if(r.isEvmNamespace(n.sourceChain.chainId))return await p(n,i,t);if(r.isSolanaNamespace(n.sourceChain.chainId))return await m(n,i,t);throw new e.InvalidParamsError(e.ErrorReason.INVALID_PARAMS,`Unsupported source chain namespace for estimateNativeFee: ${n.sourceChain.chainId}`)}}async function p(r,l,{apiOptions:d,appId:f}){if(!(0,u.isAddress)(r.fromAddress))throw new e.InvalidParamsError(e.ErrorReason.INVALID_PARAMS,`Invalid fromAddress: ${r.fromAddress}`);let p=n.getEvmClientForChain({chain:r.sourceChain}),m=s.assetToAddressString(r.assetIn,r.sourceChain.chainId);if(!(0,u.isAddress)(m))throw new e.SdkError(e.ErrorReason.INVALID_PARAMS,e.ErrorCode.INVALID_PARAMS,{details:`assetIn address is not a valid EVM address. Can not call estimateGas.`});let g=s.isTokenAddressNative(m),_=r.sourceChain.chainId.toLowerCase()!==r.targetChain.chainId.toLowerCase(),v=r.amountIn+s.getAdditiveSourceAssetFeeAmount(r),y=0n;if(!g){let n,{address:i}=await o.markrGetSpenderAddress(d,{chainId:t.caip2ToEip155ChainId(r.sourceChain.chainId),crossChainSwap:_,quoteId:r.id});try{n=await p.readContract({address:m,abi:u.erc20Abi,functionName:`allowance`,args:[r.fromAddress,i]})}catch(t){throw new e.SdkError(`Error during allowance check`,e.ErrorCode.VIEM_ERROR,{cause:t,details:`Failed to read ERC20 allowance for Markr spender.`})}n<v&&(y=await a.estimateGasWithRevert(p,{account:r.fromAddress,to:m,data:(0,u.encodeFunctionData)({abi:u.erc20Abi,functionName:`approve`,args:[i,v]})},u.erc20Abi,`Failed to estimate gas for ERC20 approval transaction.`))}let b=y>0n,x=s.calculateMarkrMinimumAmountOut({amountOut:r.amountOut,assetOut:r.assetOut,slippageBps:r.slippageBps}),S=await o.markrSwap(d,{amountIn:r.amountIn.toString(),appId:f,minAmountOut:x.toString(),tokenIn:m,tokenOut:s.assetToAddressString(r.assetOut,r.targetChain.chainId),uuid:r.id});if(!c.isEvmSwapResponse(S))throw new e.SdkError(e.ErrorReason.CHAIN_NOT_SUPPORTED,e.ErrorCode.INVALID_PARAMS,{details:`Received non-EVM swap response from Markr. Expected EVM transaction data for gas estimation, but got response
|
|
1
|
+
require(`../../../_virtual/_rolldown/runtime.cjs`);const e=require(`../../../errors.cjs`),t=require(`../../../utils/caip.cjs`),n=require(`../../_utils.cjs`),r=require(`../../../_utils/chain.cjs`),i=require(`../../_evm-gas.cjs`),a=require(`../../_evm-errors.cjs`),o=require(`../_api.cjs`),s=require(`../_utils.cjs`),c=require(`../_type-guards.cjs`),l=require(`../../../utils/solana.cjs`);let u=require(`viem`),d=require(`@solana/kit`);function f(t){return async(n,i)=>{if(r.isEvmNamespace(n.sourceChain.chainId))return await p(n,i,t);if(r.isSolanaNamespace(n.sourceChain.chainId))return await m(n,i,t);throw new e.InvalidParamsError(e.ErrorReason.INVALID_PARAMS,`Unsupported source chain namespace for estimateNativeFee: ${n.sourceChain.chainId}`)}}async function p(r,l,{apiOptions:d,appId:f}){if(!(0,u.isAddress)(r.fromAddress))throw new e.InvalidParamsError(e.ErrorReason.INVALID_PARAMS,`Invalid fromAddress: ${r.fromAddress}`);let p=n.getEvmClientForChain({chain:r.sourceChain}),m=s.assetToAddressString(r.assetIn,r.sourceChain.chainId);if(!(0,u.isAddress)(m))throw new e.SdkError(e.ErrorReason.INVALID_PARAMS,e.ErrorCode.INVALID_PARAMS,{details:`assetIn address is not a valid EVM address. Can not call estimateGas.`});let g=s.isTokenAddressNative(m),_=r.sourceChain.chainId.toLowerCase()!==r.targetChain.chainId.toLowerCase(),v=r.amountIn+s.getAdditiveSourceAssetFeeAmount(r),y=0n;if(!g){let n,{address:i}=await o.markrGetSpenderAddress(d,{chainId:t.caip2ToEip155ChainId(r.sourceChain.chainId),crossChainSwap:_,quoteId:r.id});try{n=await p.readContract({address:m,abi:u.erc20Abi,functionName:`allowance`,args:[r.fromAddress,i]})}catch(t){throw new e.SdkError(`Error during allowance check`,e.ErrorCode.VIEM_ERROR,{cause:t,details:`Failed to read ERC20 allowance for Markr spender.`})}n<v&&(y=await a.estimateGasWithRevert(p,{account:r.fromAddress,to:m,data:(0,u.encodeFunctionData)({abi:u.erc20Abi,functionName:`approve`,args:[i,v]})},u.erc20Abi,`Failed to estimate gas for ERC20 approval transaction.`))}let b=y>0n,x=s.calculateMarkrMinimumAmountOut({amountOut:r.amountOut,assetOut:r.assetOut,slippageBps:r.slippageBps}),S=await o.markrSwap(d,{amountIn:r.amountIn.toString(),appId:f,minAmountOut:x.toString(),tokenIn:m,tokenOut:s.assetToAddressString(r.assetOut,r.targetChain.chainId),uuid:r.id});if(!c.isEvmSwapResponse(S)){let t=c.isSolanaSwapResponse(S)?S.chainType:c.isHyperliquidWithdrawSwapResponse(S)?S.type:`unknown`;throw new e.SdkError(e.ErrorReason.CHAIN_NOT_SUPPORTED,e.ErrorCode.INVALID_PARAMS,{details:`Received non-EVM swap response from Markr. Expected EVM transaction data for gas estimation, but got response variant "${t}".`})}let C=0n;C=b?r.gasEstimate?r.gasEstimate:r.fees.filter(e=>e.type===`gas`&&e.chainId===r.sourceChain.chainId).reduce((e,t)=>e+t.amount,0n)||700000n:await h({crossChain:_,fromAddress:r.fromAddress,sourceClient:p,swap:S});let w=await i.estimateEvmFeesPerGas(p,r.sourceChain,l?.overrides?.feeRateTier),T=l?.overrides?.maxFeePerGas??w.maxFeePerGas,E=l?.overrides?.maxPriorityFeePerGas??w.maxPriorityFeePerGas,D=(C+y)*T;return{asset:r.sourceChain.networkToken,totalFee:n.applyFeeUnitsBpsMargin(D,l?.feeUnitsMarginBps),totalFeeWithoutMargin:D,meta:{approvalFee:b?y*T:void 0,maxFeePerGas:T,maxPriorityFeePerGas:E}}}async function m(t,r,{apiOptions:i,appId:a}){let u=s.assetToAddressString(t.assetIn,t.sourceChain.chainId);if(!(0,d.isAddress)(u))throw new e.SdkError(e.ErrorReason.INVALID_PARAMS,e.ErrorCode.INVALID_PARAMS,{details:`assetIn address is not a valid Solana address. Can not call estimateGas.`});let f=s.getAdditiveSourceNativeAssetFeeAmount(t),p=t.amountIn+f,m=s.calculateMarkrMinimumAmountOut({amountOut:t.amountOut,assetOut:t.assetOut,slippageBps:t.slippageBps}),h=await o.markrSwap(i,{amountIn:t.amountIn.toString(),appId:a,minAmountOut:m.toString(),tokenIn:u,tokenOut:s.assetToAddressString(t.assetOut,t.targetChain.chainId),userPublicKey:t.fromAddress,uuid:t.id});if(!c.isSolanaSwapResponse(h))throw new e.SdkError(e.ErrorReason.CHAIN_NOT_SUPPORTED,e.ErrorCode.INVALID_PARAMS,{details:`Received non-Solana swap response from Markr. Expected Solana transaction data for fee estimation.`});let{baseFeeLamports:g,priorityFeeLamports:_,...v}=await l.estimateSolanaFee(n.getSolanaRpcForChain({chain:t.sourceChain}),h.swapTransaction),y=v.feePayerBalanceDiffLamports-p;return{asset:t.sourceChain.networkToken,totalFee:n.applyFeeUnitsBpsMargin(y,r?.feeUnitsMarginBps),totalFeeWithoutMargin:y,meta:{...v,baseFee:g,priorityFee:_}}}async function h({crossChain:e,feeUnitsMarginBps:t,fromAddress:r,sourceClient:i,swap:o}){let c=await s.getMarkrSwapWrapperAbi(e);return n.applyFeeUnitsBpsMargin(await a.estimateGasWithRevert(i,{account:r,to:o.to,data:o.data,value:o.value},c,`Failed to estimate gas for Markr swap transaction.`),t)}exports._estimateGasFromSwapResponse=h,exports.estimateNativeFeeFactory=f;
|
|
2
2
|
//# sourceMappingURL=estimate-native-fee.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"estimate-native-fee.cjs","names":["isEvmNamespace","isSolanaNamespace","InvalidParamsError","ErrorReason","getEvmClientForChain","assetToAddressString","SdkError","ErrorCode","isTokenAddressNative","getAdditiveSourceAssetFeeAmount","markrGetSpenderAddress","caip2ToEip155ChainId","erc20Abi","estimateGasWithRevert","calculateMarkrMinimumAmountOut","markrSwap","isEvmSwapResponse","estimateEvmFeesPerGas","applyFeeUnitsBpsMargin","getAdditiveSourceNativeAssetFeeAmount","isSolanaSwapResponse","estimateSolanaFee","getSolanaRpcForChain","getMarkrSwapWrapperAbi"],"sources":["../../../../src/transfer-service/markr/_handlers/estimate-native-fee.ts"],"sourcesContent":["import { isAddress as isSolanaAddress } from '@solana/kit';\nimport { encodeFunctionData, erc20Abi, type Address as EvmAddress, isAddress as isEvmAddress } from 'viem';\nimport { ErrorCode, ErrorReason, InvalidParamsError, SdkError } from '../../../errors';\nimport type { EstimateNativeFeeOptions, NativeFeeEstimate, TransferService } from '../../../types/service';\nimport { applyFeeUnitsBpsMargin, getEvmClientForChain, getSolanaRpcForChain } from '../../_utils';\nimport { estimateEvmFeesPerGas } from '../../_evm-gas';\nimport { estimateGasWithRevert } from '../../_evm-errors';\nimport { markrGetSpenderAddress, markrSwap, type ApiOptions } from '../_api';\nimport {\n assetToAddressString,\n calculateMarkrMinimumAmountOut,\n getAdditiveSourceAssetFeeAmount,\n getAdditiveSourceNativeAssetFeeAmount,\n getMarkrSwapWrapperAbi,\n isTokenAddressNative,\n} from '../_utils';\nimport type { WrappedSwapTransactionResponse } from '../_schema';\nimport { isEvmSwapResponse, isSolanaSwapResponse } from '../_type-guards';\nimport { caip2ToEip155ChainId } from '../../../utils/caip';\nimport type { Quote } from '../../../types/quote';\nimport { isEvmNamespace, isSolanaNamespace } from '../../../_utils/chain';\nimport { estimateSolanaFee } from '../../../utils/solana';\n\n/**\n * This is just a fallback value used in the case that an\n * allowance approval is needed, and Markr didn't return us any gas estimate data.\n *\n * Just based on my review of some quotes, I think this is a reasonable estimate\n * left on the higher end.\n *\n * Assume this could need to change.\n */\nconst EVM_SWAP_FALLBACK_GAS_ESTIMATE = 700_000n;\n\nexport interface EstimateNativeFeeFactoryConfig {\n apiOptions: ApiOptions;\n appId: string;\n}\n\nexport function estimateNativeFeeFactory(config: EstimateNativeFeeFactoryConfig): TransferService['estimateNativeFee'] {\n return async (quote, options) => {\n // Either the source chain is EVM or Solana, we need to call different functions that\n // calculate the native fee depending on the chain kind.\n\n if (isEvmNamespace(quote.sourceChain.chainId)) {\n return await _estimateNativeFeeEvm(quote, options, config);\n }\n\n if (isSolanaNamespace(quote.sourceChain.chainId)) {\n return await _estimateNativeFeeSolana(quote, options, config);\n }\n\n throw new InvalidParamsError(\n ErrorReason.INVALID_PARAMS,\n `Unsupported source chain namespace for estimateNativeFee: ${quote.sourceChain.chainId}`,\n );\n };\n}\n\n/** @internal */\nexport async function _estimateNativeFeeEvm(\n quote: Quote,\n options: EstimateNativeFeeOptions | undefined,\n { apiOptions, appId }: EstimateNativeFeeFactoryConfig,\n): Promise<NativeFeeEstimate> {\n if (!isEvmAddress(quote.fromAddress)) {\n throw new InvalidParamsError(ErrorReason.INVALID_PARAMS, `Invalid fromAddress: ${quote.fromAddress}`);\n }\n\n const sourceClient = getEvmClientForChain({ chain: quote.sourceChain });\n\n const assetInAddressString = assetToAddressString(quote.assetIn, quote.sourceChain.chainId);\n\n if (!isEvmAddress(assetInAddressString)) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: `assetIn address is not a valid EVM address. Can not call estimateGas.`,\n });\n }\n\n const isAssetInNative = isTokenAddressNative(assetInAddressString);\n const isCrossChainSwap = quote.sourceChain.chainId.toLowerCase() !== quote.targetChain.chainId.toLowerCase();\n const allowanceAmount = quote.amountIn + getAdditiveSourceAssetFeeAmount(quote);\n\n let allowanceApprovalGas = 0n;\n if (!isAssetInNative) {\n // Check if approval is needed, and if so, calculate the gas cost for the approval.\n let allowance: bigint;\n\n const { address: spenderAddress } = await markrGetSpenderAddress(apiOptions, {\n chainId: caip2ToEip155ChainId(quote.sourceChain.chainId),\n crossChainSwap: isCrossChainSwap,\n quoteId: quote.id,\n });\n\n try {\n allowance = await sourceClient.readContract({\n address: assetInAddressString,\n abi: erc20Abi,\n functionName: 'allowance',\n args: [quote.fromAddress, spenderAddress],\n });\n } catch (error) {\n throw new SdkError('Error during allowance check', ErrorCode.VIEM_ERROR, {\n cause: error,\n details: 'Failed to read ERC20 allowance for Markr spender.',\n });\n }\n\n const approvalNeeded = allowance < allowanceAmount;\n\n if (approvalNeeded) {\n allowanceApprovalGas = await estimateGasWithRevert(\n sourceClient,\n {\n account: quote.fromAddress,\n to: assetInAddressString,\n data: encodeFunctionData({\n abi: erc20Abi,\n functionName: 'approve',\n args: [spenderAddress, allowanceAmount],\n }),\n },\n erc20Abi,\n 'Failed to estimate gas for ERC20 approval transaction.',\n );\n }\n }\n\n const allowanceApprovalIsNeeded = allowanceApprovalGas > 0n;\n\n const minAmountOut = calculateMarkrMinimumAmountOut({\n amountOut: quote.amountOut,\n assetOut: quote.assetOut,\n slippageBps: quote.slippageBps,\n });\n\n const swap = await markrSwap(apiOptions, {\n amountIn: quote.amountIn.toString(),\n appId,\n minAmountOut: minAmountOut.toString(),\n tokenIn: assetInAddressString,\n tokenOut: assetToAddressString(quote.assetOut, quote.targetChain.chainId),\n uuid: quote.id,\n });\n\n if (!isEvmSwapResponse(swap)) {\n // Should hopefully never happen.\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: `Received non-EVM swap response from Markr. Expected EVM transaction data for gas estimation, but got response with chainType ${swap.chainType}.`,\n });\n }\n\n let gas = 0n;\n\n // If an approval is needed, we can't call `eth_estimateGas` on the\n // swap transaction because no actual allowance approval was performed yet,\n // so a gas estimation would fail due to needing allowance.\n //\n // So if an allowance approval is needed, we fall back to some other logic for\n // estimating the swap tx gas.\n if (allowanceApprovalIsNeeded) {\n // Attempt to use the Markr provided `gasEstimate` first if available.\n // Otherwise we attempt to get the gas from the `fees` component that are applicable\n // to the source chain. If neither of those are available, we fall back to a hardcoded value.\n if (quote.gasEstimate) {\n gas = quote.gasEstimate;\n } else {\n const sourceGasFee = quote.fees\n .filter((fee) => fee.type === 'gas' && fee.chainId === quote.sourceChain.chainId)\n .reduce((acc, fee) => acc + fee.amount, 0n);\n\n gas = sourceGasFee || EVM_SWAP_FALLBACK_GAS_ESTIMATE;\n }\n } else {\n gas = await _estimateGasFromSwapResponse({\n crossChain: isCrossChainSwap,\n fromAddress: quote.fromAddress,\n sourceClient,\n swap,\n });\n }\n\n const fees = await estimateEvmFeesPerGas(sourceClient, quote.sourceChain, options?.overrides?.feeRateTier);\n\n const maxFeePerGas = options?.overrides?.maxFeePerGas ?? fees.maxFeePerGas;\n const maxPriorityFeePerGas = options?.overrides?.maxPriorityFeePerGas ?? fees.maxPriorityFeePerGas;\n\n const totalFeeWithoutMargin = (gas + allowanceApprovalGas) * maxFeePerGas;\n\n return {\n asset: quote.sourceChain.networkToken,\n totalFee: applyFeeUnitsBpsMargin(totalFeeWithoutMargin, options?.feeUnitsMarginBps),\n totalFeeWithoutMargin,\n meta: {\n approvalFee: allowanceApprovalIsNeeded ? allowanceApprovalGas * maxFeePerGas : undefined,\n maxFeePerGas,\n maxPriorityFeePerGas,\n },\n };\n}\n\n/** @internal */\nexport async function _estimateNativeFeeSolana(\n quote: Quote,\n options: EstimateNativeFeeOptions | undefined,\n { apiOptions, appId }: EstimateNativeFeeFactoryConfig,\n): Promise<NativeFeeEstimate> {\n const assetInAddressString = assetToAddressString(quote.assetIn, quote.sourceChain.chainId);\n\n if (!isSolanaAddress(assetInAddressString)) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: `assetIn address is not a valid Solana address. Can not call estimateGas.`,\n });\n }\n\n const additiveNativeFee: bigint = getAdditiveSourceNativeAssetFeeAmount(quote);\n const totalInput: bigint = quote.amountIn + additiveNativeFee;\n\n const minAmountOut = calculateMarkrMinimumAmountOut({\n amountOut: quote.amountOut,\n assetOut: quote.assetOut,\n slippageBps: quote.slippageBps,\n });\n\n const swap = await markrSwap(apiOptions, {\n amountIn: quote.amountIn.toString(),\n appId,\n minAmountOut: minAmountOut.toString(),\n tokenIn: assetInAddressString,\n tokenOut: assetToAddressString(quote.assetOut, quote.targetChain.chainId),\n userPublicKey: quote.fromAddress,\n uuid: quote.id,\n });\n\n if (!isSolanaSwapResponse(swap)) {\n // Should hopefully never happen.\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: 'Received non-Solana swap response from Markr. Expected Solana transaction data for fee estimation.',\n });\n }\n\n const rpc = getSolanaRpcForChain({ chain: quote.sourceChain });\n\n const { baseFeeLamports, priorityFeeLamports, ...rest } = await estimateSolanaFee(rpc, swap.swapTransaction);\n\n const totalFeeWithoutMargin = rest.feePayerBalanceDiffLamports - totalInput;\n\n return {\n asset: quote.sourceChain.networkToken,\n totalFee: applyFeeUnitsBpsMargin(totalFeeWithoutMargin, options?.feeUnitsMarginBps),\n totalFeeWithoutMargin,\n meta: {\n ...rest,\n baseFee: baseFeeLamports,\n priorityFee: priorityFeeLamports,\n },\n };\n}\n\n/** @internal */\nexport async function _estimateGasFromSwapResponse({\n crossChain,\n feeUnitsMarginBps,\n fromAddress,\n sourceClient,\n swap,\n}: {\n crossChain: boolean;\n feeUnitsMarginBps?: number;\n fromAddress: EvmAddress;\n sourceClient: ReturnType<typeof getEvmClientForChain>;\n swap: WrappedSwapTransactionResponse;\n}): Promise<bigint> {\n const markrAbi = await getMarkrSwapWrapperAbi(crossChain);\n\n // Use raw estimateGas — the calldata is pre-encoded and may target any\n // contract (Markr wrapper, deBridge, etc.). The ABI is only used for\n // best-effort error decoding on the failure path.\n const gasEstimate = await estimateGasWithRevert(\n sourceClient,\n {\n account: fromAddress,\n to: swap.to,\n data: swap.data,\n value: swap.value,\n },\n markrAbi,\n 'Failed to estimate gas for Markr swap transaction.',\n );\n\n return applyFeeUnitsBpsMargin(gasEstimate, feeUnitsMarginBps);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"estimate-native-fee.cjs","names":["isEvmNamespace","isSolanaNamespace","InvalidParamsError","ErrorReason","getEvmClientForChain","assetToAddressString","SdkError","ErrorCode","isTokenAddressNative","getAdditiveSourceAssetFeeAmount","markrGetSpenderAddress","caip2ToEip155ChainId","erc20Abi","estimateGasWithRevert","calculateMarkrMinimumAmountOut","markrSwap","isEvmSwapResponse","isSolanaSwapResponse","isHyperliquidWithdrawSwapResponse","estimateEvmFeesPerGas","applyFeeUnitsBpsMargin","getAdditiveSourceNativeAssetFeeAmount","estimateSolanaFee","getSolanaRpcForChain","getMarkrSwapWrapperAbi"],"sources":["../../../../src/transfer-service/markr/_handlers/estimate-native-fee.ts"],"sourcesContent":["import { isAddress as isSolanaAddress } from '@solana/kit';\nimport { encodeFunctionData, erc20Abi, type Address as EvmAddress, isAddress as isEvmAddress } from 'viem';\nimport { ErrorCode, ErrorReason, InvalidParamsError, SdkError } from '../../../errors';\nimport type { EstimateNativeFeeOptions, NativeFeeEstimate, TransferService } from '../../../types/service';\nimport { applyFeeUnitsBpsMargin, getEvmClientForChain, getSolanaRpcForChain } from '../../_utils';\nimport { estimateEvmFeesPerGas } from '../../_evm-gas';\nimport { estimateGasWithRevert } from '../../_evm-errors';\nimport { markrGetSpenderAddress, markrSwap, type ApiOptions } from '../_api';\nimport {\n assetToAddressString,\n calculateMarkrMinimumAmountOut,\n getAdditiveSourceAssetFeeAmount,\n getAdditiveSourceNativeAssetFeeAmount,\n getMarkrSwapWrapperAbi,\n isTokenAddressNative,\n} from '../_utils';\nimport type { WrappedSwapTransactionResponse } from '../_schema';\nimport { isEvmSwapResponse, isHyperliquidWithdrawSwapResponse, isSolanaSwapResponse } from '../_type-guards';\nimport { caip2ToEip155ChainId } from '../../../utils/caip';\nimport type { Quote } from '../../../types/quote';\nimport { isEvmNamespace, isSolanaNamespace } from '../../../_utils/chain';\nimport { estimateSolanaFee } from '../../../utils/solana';\n\n/**\n * This is just a fallback value used in the case that an\n * allowance approval is needed, and Markr didn't return us any gas estimate data.\n *\n * Just based on my review of some quotes, I think this is a reasonable estimate\n * left on the higher end.\n *\n * Assume this could need to change.\n */\nconst EVM_SWAP_FALLBACK_GAS_ESTIMATE = 700_000n;\n\nexport interface EstimateNativeFeeFactoryConfig {\n apiOptions: ApiOptions;\n appId: string;\n}\n\nexport function estimateNativeFeeFactory(config: EstimateNativeFeeFactoryConfig): TransferService['estimateNativeFee'] {\n return async (quote, options) => {\n // Either the source chain is EVM or Solana, we need to call different functions that\n // calculate the native fee depending on the chain kind.\n\n if (isEvmNamespace(quote.sourceChain.chainId)) {\n return await _estimateNativeFeeEvm(quote, options, config);\n }\n\n if (isSolanaNamespace(quote.sourceChain.chainId)) {\n return await _estimateNativeFeeSolana(quote, options, config);\n }\n\n throw new InvalidParamsError(\n ErrorReason.INVALID_PARAMS,\n `Unsupported source chain namespace for estimateNativeFee: ${quote.sourceChain.chainId}`,\n );\n };\n}\n\n/** @internal */\nexport async function _estimateNativeFeeEvm(\n quote: Quote,\n options: EstimateNativeFeeOptions | undefined,\n { apiOptions, appId }: EstimateNativeFeeFactoryConfig,\n): Promise<NativeFeeEstimate> {\n if (!isEvmAddress(quote.fromAddress)) {\n throw new InvalidParamsError(ErrorReason.INVALID_PARAMS, `Invalid fromAddress: ${quote.fromAddress}`);\n }\n\n const sourceClient = getEvmClientForChain({ chain: quote.sourceChain });\n\n const assetInAddressString = assetToAddressString(quote.assetIn, quote.sourceChain.chainId);\n\n if (!isEvmAddress(assetInAddressString)) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: `assetIn address is not a valid EVM address. Can not call estimateGas.`,\n });\n }\n\n const isAssetInNative = isTokenAddressNative(assetInAddressString);\n const isCrossChainSwap = quote.sourceChain.chainId.toLowerCase() !== quote.targetChain.chainId.toLowerCase();\n const allowanceAmount = quote.amountIn + getAdditiveSourceAssetFeeAmount(quote);\n\n let allowanceApprovalGas = 0n;\n if (!isAssetInNative) {\n // Check if approval is needed, and if so, calculate the gas cost for the approval.\n let allowance: bigint;\n\n const { address: spenderAddress } = await markrGetSpenderAddress(apiOptions, {\n chainId: caip2ToEip155ChainId(quote.sourceChain.chainId),\n crossChainSwap: isCrossChainSwap,\n quoteId: quote.id,\n });\n\n try {\n allowance = await sourceClient.readContract({\n address: assetInAddressString,\n abi: erc20Abi,\n functionName: 'allowance',\n args: [quote.fromAddress, spenderAddress],\n });\n } catch (error) {\n throw new SdkError('Error during allowance check', ErrorCode.VIEM_ERROR, {\n cause: error,\n details: 'Failed to read ERC20 allowance for Markr spender.',\n });\n }\n\n const approvalNeeded = allowance < allowanceAmount;\n\n if (approvalNeeded) {\n allowanceApprovalGas = await estimateGasWithRevert(\n sourceClient,\n {\n account: quote.fromAddress,\n to: assetInAddressString,\n data: encodeFunctionData({\n abi: erc20Abi,\n functionName: 'approve',\n args: [spenderAddress, allowanceAmount],\n }),\n },\n erc20Abi,\n 'Failed to estimate gas for ERC20 approval transaction.',\n );\n }\n }\n\n const allowanceApprovalIsNeeded = allowanceApprovalGas > 0n;\n\n const minAmountOut = calculateMarkrMinimumAmountOut({\n amountOut: quote.amountOut,\n assetOut: quote.assetOut,\n slippageBps: quote.slippageBps,\n });\n\n const swap = await markrSwap(apiOptions, {\n amountIn: quote.amountIn.toString(),\n appId,\n minAmountOut: minAmountOut.toString(),\n tokenIn: assetInAddressString,\n tokenOut: assetToAddressString(quote.assetOut, quote.targetChain.chainId),\n uuid: quote.id,\n });\n\n if (!isEvmSwapResponse(swap)) {\n // Should hopefully never happen.\n const variantTag = isSolanaSwapResponse(swap)\n ? swap.chainType\n : isHyperliquidWithdrawSwapResponse(swap)\n ? swap.type\n : 'unknown';\n\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: `Received non-EVM swap response from Markr. Expected EVM transaction data for gas estimation, but got response variant \"${variantTag}\".`,\n });\n }\n\n let gas = 0n;\n\n // If an approval is needed, we can't call `eth_estimateGas` on the\n // swap transaction because no actual allowance approval was performed yet,\n // so a gas estimation would fail due to needing allowance.\n //\n // So if an allowance approval is needed, we fall back to some other logic for\n // estimating the swap tx gas.\n if (allowanceApprovalIsNeeded) {\n // Attempt to use the Markr provided `gasEstimate` first if available.\n // Otherwise we attempt to get the gas from the `fees` component that are applicable\n // to the source chain. If neither of those are available, we fall back to a hardcoded value.\n if (quote.gasEstimate) {\n gas = quote.gasEstimate;\n } else {\n const sourceGasFee = quote.fees\n .filter((fee) => fee.type === 'gas' && fee.chainId === quote.sourceChain.chainId)\n .reduce((acc, fee) => acc + fee.amount, 0n);\n\n gas = sourceGasFee || EVM_SWAP_FALLBACK_GAS_ESTIMATE;\n }\n } else {\n gas = await _estimateGasFromSwapResponse({\n crossChain: isCrossChainSwap,\n fromAddress: quote.fromAddress,\n sourceClient,\n swap,\n });\n }\n\n const fees = await estimateEvmFeesPerGas(sourceClient, quote.sourceChain, options?.overrides?.feeRateTier);\n\n const maxFeePerGas = options?.overrides?.maxFeePerGas ?? fees.maxFeePerGas;\n const maxPriorityFeePerGas = options?.overrides?.maxPriorityFeePerGas ?? fees.maxPriorityFeePerGas;\n\n const totalFeeWithoutMargin = (gas + allowanceApprovalGas) * maxFeePerGas;\n\n return {\n asset: quote.sourceChain.networkToken,\n totalFee: applyFeeUnitsBpsMargin(totalFeeWithoutMargin, options?.feeUnitsMarginBps),\n totalFeeWithoutMargin,\n meta: {\n approvalFee: allowanceApprovalIsNeeded ? allowanceApprovalGas * maxFeePerGas : undefined,\n maxFeePerGas,\n maxPriorityFeePerGas,\n },\n };\n}\n\n/** @internal */\nexport async function _estimateNativeFeeSolana(\n quote: Quote,\n options: EstimateNativeFeeOptions | undefined,\n { apiOptions, appId }: EstimateNativeFeeFactoryConfig,\n): Promise<NativeFeeEstimate> {\n const assetInAddressString = assetToAddressString(quote.assetIn, quote.sourceChain.chainId);\n\n if (!isSolanaAddress(assetInAddressString)) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: `assetIn address is not a valid Solana address. Can not call estimateGas.`,\n });\n }\n\n const additiveNativeFee: bigint = getAdditiveSourceNativeAssetFeeAmount(quote);\n const totalInput: bigint = quote.amountIn + additiveNativeFee;\n\n const minAmountOut = calculateMarkrMinimumAmountOut({\n amountOut: quote.amountOut,\n assetOut: quote.assetOut,\n slippageBps: quote.slippageBps,\n });\n\n const swap = await markrSwap(apiOptions, {\n amountIn: quote.amountIn.toString(),\n appId,\n minAmountOut: minAmountOut.toString(),\n tokenIn: assetInAddressString,\n tokenOut: assetToAddressString(quote.assetOut, quote.targetChain.chainId),\n userPublicKey: quote.fromAddress,\n uuid: quote.id,\n });\n\n if (!isSolanaSwapResponse(swap)) {\n // Should hopefully never happen.\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: 'Received non-Solana swap response from Markr. Expected Solana transaction data for fee estimation.',\n });\n }\n\n const rpc = getSolanaRpcForChain({ chain: quote.sourceChain });\n\n const { baseFeeLamports, priorityFeeLamports, ...rest } = await estimateSolanaFee(rpc, swap.swapTransaction);\n\n const totalFeeWithoutMargin = rest.feePayerBalanceDiffLamports - totalInput;\n\n return {\n asset: quote.sourceChain.networkToken,\n totalFee: applyFeeUnitsBpsMargin(totalFeeWithoutMargin, options?.feeUnitsMarginBps),\n totalFeeWithoutMargin,\n meta: {\n ...rest,\n baseFee: baseFeeLamports,\n priorityFee: priorityFeeLamports,\n },\n };\n}\n\n/** @internal */\nexport async function _estimateGasFromSwapResponse({\n crossChain,\n feeUnitsMarginBps,\n fromAddress,\n sourceClient,\n swap,\n}: {\n crossChain: boolean;\n feeUnitsMarginBps?: number;\n fromAddress: EvmAddress;\n sourceClient: ReturnType<typeof getEvmClientForChain>;\n swap: WrappedSwapTransactionResponse;\n}): Promise<bigint> {\n const markrAbi = await getMarkrSwapWrapperAbi(crossChain);\n\n // Use raw estimateGas — the calldata is pre-encoded and may target any\n // contract (Markr wrapper, deBridge, etc.). The ABI is only used for\n // best-effort error decoding on the failure path.\n const gasEstimate = await estimateGasWithRevert(\n sourceClient,\n {\n account: fromAddress,\n to: swap.to,\n data: swap.data,\n value: swap.value,\n },\n markrAbi,\n 'Failed to estimate gas for Markr swap transaction.',\n );\n\n return applyFeeUnitsBpsMargin(gasEstimate, feeUnitsMarginBps);\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{ErrorCode as e,ErrorReason as t,InvalidParamsError as n,SdkError as r}from"../../../errors.js";import{caip2ToEip155ChainId as i}from"../../../utils/caip.js";import{applyFeeUnitsBpsMargin as a,getEvmClientForChain as o,getSolanaRpcForChain as s}from"../../_utils.js";import{isEvmNamespace as c,isSolanaNamespace as l}from"../../../_utils/chain.js";import{estimateEvmFeesPerGas as u}from"../../_evm-gas.js";import{estimateGasWithRevert as d}from"../../_evm-errors.js";import{markrGetSpenderAddress as f,markrSwap as p}from"../_api.js";import{assetToAddressString as m,calculateMarkrMinimumAmountOut as h,getAdditiveSourceAssetFeeAmount as g,getAdditiveSourceNativeAssetFeeAmount as _,getMarkrSwapWrapperAbi as v,isTokenAddressNative as y}from"../_utils.js";import{isEvmSwapResponse as b,isSolanaSwapResponse as
|
|
1
|
+
import{ErrorCode as e,ErrorReason as t,InvalidParamsError as n,SdkError as r}from"../../../errors.js";import{caip2ToEip155ChainId as i}from"../../../utils/caip.js";import{applyFeeUnitsBpsMargin as a,getEvmClientForChain as o,getSolanaRpcForChain as s}from"../../_utils.js";import{isEvmNamespace as c,isSolanaNamespace as l}from"../../../_utils/chain.js";import{estimateEvmFeesPerGas as u}from"../../_evm-gas.js";import{estimateGasWithRevert as d}from"../../_evm-errors.js";import{markrGetSpenderAddress as f,markrSwap as p}from"../_api.js";import{assetToAddressString as m,calculateMarkrMinimumAmountOut as h,getAdditiveSourceAssetFeeAmount as g,getAdditiveSourceNativeAssetFeeAmount as _,getMarkrSwapWrapperAbi as v,isTokenAddressNative as y}from"../_utils.js";import{isEvmSwapResponse as b,isHyperliquidWithdrawSwapResponse as x,isSolanaSwapResponse as S}from"../_type-guards.js";import{estimateSolanaFee as C}from"../../../utils/solana.js";import{encodeFunctionData as w,erc20Abi as T,isAddress as E}from"viem";import{isAddress as D}from"@solana/kit";function O(e){return async(r,i)=>{if(c(r.sourceChain.chainId))return await k(r,i,e);if(l(r.sourceChain.chainId))return await A(r,i,e);throw new n(t.INVALID_PARAMS,`Unsupported source chain namespace for estimateNativeFee: ${r.sourceChain.chainId}`)}}async function k(s,c,{apiOptions:l,appId:_}){if(!E(s.fromAddress))throw new n(t.INVALID_PARAMS,`Invalid fromAddress: ${s.fromAddress}`);let v=o({chain:s.sourceChain}),C=m(s.assetIn,s.sourceChain.chainId);if(!E(C))throw new r(t.INVALID_PARAMS,e.INVALID_PARAMS,{details:`assetIn address is not a valid EVM address. Can not call estimateGas.`});let D=y(C),O=s.sourceChain.chainId.toLowerCase()!==s.targetChain.chainId.toLowerCase(),k=s.amountIn+g(s),A=0n;if(!D){let t,{address:n}=await f(l,{chainId:i(s.sourceChain.chainId),crossChainSwap:O,quoteId:s.id});try{t=await v.readContract({address:C,abi:T,functionName:`allowance`,args:[s.fromAddress,n]})}catch(t){throw new r(`Error during allowance check`,e.VIEM_ERROR,{cause:t,details:`Failed to read ERC20 allowance for Markr spender.`})}t<k&&(A=await d(v,{account:s.fromAddress,to:C,data:w({abi:T,functionName:`approve`,args:[n,k]})},T,`Failed to estimate gas for ERC20 approval transaction.`))}let M=A>0n,N=h({amountOut:s.amountOut,assetOut:s.assetOut,slippageBps:s.slippageBps}),P=await p(l,{amountIn:s.amountIn.toString(),appId:_,minAmountOut:N.toString(),tokenIn:C,tokenOut:m(s.assetOut,s.targetChain.chainId),uuid:s.id});if(!b(P)){let n=S(P)?P.chainType:x(P)?P.type:`unknown`;throw new r(t.CHAIN_NOT_SUPPORTED,e.INVALID_PARAMS,{details:`Received non-EVM swap response from Markr. Expected EVM transaction data for gas estimation, but got response variant "${n}".`})}let F=0n;F=M?s.gasEstimate?s.gasEstimate:s.fees.filter(e=>e.type===`gas`&&e.chainId===s.sourceChain.chainId).reduce((e,t)=>e+t.amount,0n)||700000n:await j({crossChain:O,fromAddress:s.fromAddress,sourceClient:v,swap:P});let I=await u(v,s.sourceChain,c?.overrides?.feeRateTier),L=c?.overrides?.maxFeePerGas??I.maxFeePerGas,R=c?.overrides?.maxPriorityFeePerGas??I.maxPriorityFeePerGas,z=(F+A)*L;return{asset:s.sourceChain.networkToken,totalFee:a(z,c?.feeUnitsMarginBps),totalFeeWithoutMargin:z,meta:{approvalFee:M?A*L:void 0,maxFeePerGas:L,maxPriorityFeePerGas:R}}}async function A(n,i,{apiOptions:o,appId:c}){let l=m(n.assetIn,n.sourceChain.chainId);if(!D(l))throw new r(t.INVALID_PARAMS,e.INVALID_PARAMS,{details:`assetIn address is not a valid Solana address. Can not call estimateGas.`});let u=_(n),d=n.amountIn+u,f=h({amountOut:n.amountOut,assetOut:n.assetOut,slippageBps:n.slippageBps}),g=await p(o,{amountIn:n.amountIn.toString(),appId:c,minAmountOut:f.toString(),tokenIn:l,tokenOut:m(n.assetOut,n.targetChain.chainId),userPublicKey:n.fromAddress,uuid:n.id});if(!S(g))throw new r(t.CHAIN_NOT_SUPPORTED,e.INVALID_PARAMS,{details:`Received non-Solana swap response from Markr. Expected Solana transaction data for fee estimation.`});let{baseFeeLamports:v,priorityFeeLamports:y,...b}=await C(s({chain:n.sourceChain}),g.swapTransaction),x=b.feePayerBalanceDiffLamports-d;return{asset:n.sourceChain.networkToken,totalFee:a(x,i?.feeUnitsMarginBps),totalFeeWithoutMargin:x,meta:{...b,baseFee:v,priorityFee:y}}}async function j({crossChain:e,feeUnitsMarginBps:t,fromAddress:n,sourceClient:r,swap:i}){let o=await v(e);return a(await d(r,{account:n,to:i.to,data:i.data,value:i.value},o,`Failed to estimate gas for Markr swap transaction.`),t)}export{j as _estimateGasFromSwapResponse,O as estimateNativeFeeFactory};
|
|
2
2
|
//# sourceMappingURL=estimate-native-fee.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"estimate-native-fee.js","names":["isEvmAddress","isSolanaAddress"],"sources":["../../../../src/transfer-service/markr/_handlers/estimate-native-fee.ts"],"sourcesContent":["import { isAddress as isSolanaAddress } from '@solana/kit';\nimport { encodeFunctionData, erc20Abi, type Address as EvmAddress, isAddress as isEvmAddress } from 'viem';\nimport { ErrorCode, ErrorReason, InvalidParamsError, SdkError } from '../../../errors';\nimport type { EstimateNativeFeeOptions, NativeFeeEstimate, TransferService } from '../../../types/service';\nimport { applyFeeUnitsBpsMargin, getEvmClientForChain, getSolanaRpcForChain } from '../../_utils';\nimport { estimateEvmFeesPerGas } from '../../_evm-gas';\nimport { estimateGasWithRevert } from '../../_evm-errors';\nimport { markrGetSpenderAddress, markrSwap, type ApiOptions } from '../_api';\nimport {\n assetToAddressString,\n calculateMarkrMinimumAmountOut,\n getAdditiveSourceAssetFeeAmount,\n getAdditiveSourceNativeAssetFeeAmount,\n getMarkrSwapWrapperAbi,\n isTokenAddressNative,\n} from '../_utils';\nimport type { WrappedSwapTransactionResponse } from '../_schema';\nimport { isEvmSwapResponse, isSolanaSwapResponse } from '../_type-guards';\nimport { caip2ToEip155ChainId } from '../../../utils/caip';\nimport type { Quote } from '../../../types/quote';\nimport { isEvmNamespace, isSolanaNamespace } from '../../../_utils/chain';\nimport { estimateSolanaFee } from '../../../utils/solana';\n\n/**\n * This is just a fallback value used in the case that an\n * allowance approval is needed, and Markr didn't return us any gas estimate data.\n *\n * Just based on my review of some quotes, I think this is a reasonable estimate\n * left on the higher end.\n *\n * Assume this could need to change.\n */\nconst EVM_SWAP_FALLBACK_GAS_ESTIMATE = 700_000n;\n\nexport interface EstimateNativeFeeFactoryConfig {\n apiOptions: ApiOptions;\n appId: string;\n}\n\nexport function estimateNativeFeeFactory(config: EstimateNativeFeeFactoryConfig): TransferService['estimateNativeFee'] {\n return async (quote, options) => {\n // Either the source chain is EVM or Solana, we need to call different functions that\n // calculate the native fee depending on the chain kind.\n\n if (isEvmNamespace(quote.sourceChain.chainId)) {\n return await _estimateNativeFeeEvm(quote, options, config);\n }\n\n if (isSolanaNamespace(quote.sourceChain.chainId)) {\n return await _estimateNativeFeeSolana(quote, options, config);\n }\n\n throw new InvalidParamsError(\n ErrorReason.INVALID_PARAMS,\n `Unsupported source chain namespace for estimateNativeFee: ${quote.sourceChain.chainId}`,\n );\n };\n}\n\n/** @internal */\nexport async function _estimateNativeFeeEvm(\n quote: Quote,\n options: EstimateNativeFeeOptions | undefined,\n { apiOptions, appId }: EstimateNativeFeeFactoryConfig,\n): Promise<NativeFeeEstimate> {\n if (!isEvmAddress(quote.fromAddress)) {\n throw new InvalidParamsError(ErrorReason.INVALID_PARAMS, `Invalid fromAddress: ${quote.fromAddress}`);\n }\n\n const sourceClient = getEvmClientForChain({ chain: quote.sourceChain });\n\n const assetInAddressString = assetToAddressString(quote.assetIn, quote.sourceChain.chainId);\n\n if (!isEvmAddress(assetInAddressString)) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: `assetIn address is not a valid EVM address. Can not call estimateGas.`,\n });\n }\n\n const isAssetInNative = isTokenAddressNative(assetInAddressString);\n const isCrossChainSwap = quote.sourceChain.chainId.toLowerCase() !== quote.targetChain.chainId.toLowerCase();\n const allowanceAmount = quote.amountIn + getAdditiveSourceAssetFeeAmount(quote);\n\n let allowanceApprovalGas = 0n;\n if (!isAssetInNative) {\n // Check if approval is needed, and if so, calculate the gas cost for the approval.\n let allowance: bigint;\n\n const { address: spenderAddress } = await markrGetSpenderAddress(apiOptions, {\n chainId: caip2ToEip155ChainId(quote.sourceChain.chainId),\n crossChainSwap: isCrossChainSwap,\n quoteId: quote.id,\n });\n\n try {\n allowance = await sourceClient.readContract({\n address: assetInAddressString,\n abi: erc20Abi,\n functionName: 'allowance',\n args: [quote.fromAddress, spenderAddress],\n });\n } catch (error) {\n throw new SdkError('Error during allowance check', ErrorCode.VIEM_ERROR, {\n cause: error,\n details: 'Failed to read ERC20 allowance for Markr spender.',\n });\n }\n\n const approvalNeeded = allowance < allowanceAmount;\n\n if (approvalNeeded) {\n allowanceApprovalGas = await estimateGasWithRevert(\n sourceClient,\n {\n account: quote.fromAddress,\n to: assetInAddressString,\n data: encodeFunctionData({\n abi: erc20Abi,\n functionName: 'approve',\n args: [spenderAddress, allowanceAmount],\n }),\n },\n erc20Abi,\n 'Failed to estimate gas for ERC20 approval transaction.',\n );\n }\n }\n\n const allowanceApprovalIsNeeded = allowanceApprovalGas > 0n;\n\n const minAmountOut = calculateMarkrMinimumAmountOut({\n amountOut: quote.amountOut,\n assetOut: quote.assetOut,\n slippageBps: quote.slippageBps,\n });\n\n const swap = await markrSwap(apiOptions, {\n amountIn: quote.amountIn.toString(),\n appId,\n minAmountOut: minAmountOut.toString(),\n tokenIn: assetInAddressString,\n tokenOut: assetToAddressString(quote.assetOut, quote.targetChain.chainId),\n uuid: quote.id,\n });\n\n if (!isEvmSwapResponse(swap)) {\n // Should hopefully never happen.\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: `Received non-EVM swap response from Markr. Expected EVM transaction data for gas estimation, but got response with chainType ${swap.chainType}.`,\n });\n }\n\n let gas = 0n;\n\n // If an approval is needed, we can't call `eth_estimateGas` on the\n // swap transaction because no actual allowance approval was performed yet,\n // so a gas estimation would fail due to needing allowance.\n //\n // So if an allowance approval is needed, we fall back to some other logic for\n // estimating the swap tx gas.\n if (allowanceApprovalIsNeeded) {\n // Attempt to use the Markr provided `gasEstimate` first if available.\n // Otherwise we attempt to get the gas from the `fees` component that are applicable\n // to the source chain. If neither of those are available, we fall back to a hardcoded value.\n if (quote.gasEstimate) {\n gas = quote.gasEstimate;\n } else {\n const sourceGasFee = quote.fees\n .filter((fee) => fee.type === 'gas' && fee.chainId === quote.sourceChain.chainId)\n .reduce((acc, fee) => acc + fee.amount, 0n);\n\n gas = sourceGasFee || EVM_SWAP_FALLBACK_GAS_ESTIMATE;\n }\n } else {\n gas = await _estimateGasFromSwapResponse({\n crossChain: isCrossChainSwap,\n fromAddress: quote.fromAddress,\n sourceClient,\n swap,\n });\n }\n\n const fees = await estimateEvmFeesPerGas(sourceClient, quote.sourceChain, options?.overrides?.feeRateTier);\n\n const maxFeePerGas = options?.overrides?.maxFeePerGas ?? fees.maxFeePerGas;\n const maxPriorityFeePerGas = options?.overrides?.maxPriorityFeePerGas ?? fees.maxPriorityFeePerGas;\n\n const totalFeeWithoutMargin = (gas + allowanceApprovalGas) * maxFeePerGas;\n\n return {\n asset: quote.sourceChain.networkToken,\n totalFee: applyFeeUnitsBpsMargin(totalFeeWithoutMargin, options?.feeUnitsMarginBps),\n totalFeeWithoutMargin,\n meta: {\n approvalFee: allowanceApprovalIsNeeded ? allowanceApprovalGas * maxFeePerGas : undefined,\n maxFeePerGas,\n maxPriorityFeePerGas,\n },\n };\n}\n\n/** @internal */\nexport async function _estimateNativeFeeSolana(\n quote: Quote,\n options: EstimateNativeFeeOptions | undefined,\n { apiOptions, appId }: EstimateNativeFeeFactoryConfig,\n): Promise<NativeFeeEstimate> {\n const assetInAddressString = assetToAddressString(quote.assetIn, quote.sourceChain.chainId);\n\n if (!isSolanaAddress(assetInAddressString)) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: `assetIn address is not a valid Solana address. Can not call estimateGas.`,\n });\n }\n\n const additiveNativeFee: bigint = getAdditiveSourceNativeAssetFeeAmount(quote);\n const totalInput: bigint = quote.amountIn + additiveNativeFee;\n\n const minAmountOut = calculateMarkrMinimumAmountOut({\n amountOut: quote.amountOut,\n assetOut: quote.assetOut,\n slippageBps: quote.slippageBps,\n });\n\n const swap = await markrSwap(apiOptions, {\n amountIn: quote.amountIn.toString(),\n appId,\n minAmountOut: minAmountOut.toString(),\n tokenIn: assetInAddressString,\n tokenOut: assetToAddressString(quote.assetOut, quote.targetChain.chainId),\n userPublicKey: quote.fromAddress,\n uuid: quote.id,\n });\n\n if (!isSolanaSwapResponse(swap)) {\n // Should hopefully never happen.\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: 'Received non-Solana swap response from Markr. Expected Solana transaction data for fee estimation.',\n });\n }\n\n const rpc = getSolanaRpcForChain({ chain: quote.sourceChain });\n\n const { baseFeeLamports, priorityFeeLamports, ...rest } = await estimateSolanaFee(rpc, swap.swapTransaction);\n\n const totalFeeWithoutMargin = rest.feePayerBalanceDiffLamports - totalInput;\n\n return {\n asset: quote.sourceChain.networkToken,\n totalFee: applyFeeUnitsBpsMargin(totalFeeWithoutMargin, options?.feeUnitsMarginBps),\n totalFeeWithoutMargin,\n meta: {\n ...rest,\n baseFee: baseFeeLamports,\n priorityFee: priorityFeeLamports,\n },\n };\n}\n\n/** @internal */\nexport async function _estimateGasFromSwapResponse({\n crossChain,\n feeUnitsMarginBps,\n fromAddress,\n sourceClient,\n swap,\n}: {\n crossChain: boolean;\n feeUnitsMarginBps?: number;\n fromAddress: EvmAddress;\n sourceClient: ReturnType<typeof getEvmClientForChain>;\n swap: WrappedSwapTransactionResponse;\n}): Promise<bigint> {\n const markrAbi = await getMarkrSwapWrapperAbi(crossChain);\n\n // Use raw estimateGas — the calldata is pre-encoded and may target any\n // contract (Markr wrapper, deBridge, etc.). The ABI is only used for\n // best-effort error decoding on the failure path.\n const gasEstimate = await estimateGasWithRevert(\n sourceClient,\n {\n account: fromAddress,\n to: swap.to,\n data: swap.data,\n value: swap.value,\n },\n markrAbi,\n 'Failed to estimate gas for Markr swap transaction.',\n );\n\n return applyFeeUnitsBpsMargin(gasEstimate, feeUnitsMarginBps);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"estimate-native-fee.js","names":["isEvmAddress","isSolanaAddress"],"sources":["../../../../src/transfer-service/markr/_handlers/estimate-native-fee.ts"],"sourcesContent":["import { isAddress as isSolanaAddress } from '@solana/kit';\nimport { encodeFunctionData, erc20Abi, type Address as EvmAddress, isAddress as isEvmAddress } from 'viem';\nimport { ErrorCode, ErrorReason, InvalidParamsError, SdkError } from '../../../errors';\nimport type { EstimateNativeFeeOptions, NativeFeeEstimate, TransferService } from '../../../types/service';\nimport { applyFeeUnitsBpsMargin, getEvmClientForChain, getSolanaRpcForChain } from '../../_utils';\nimport { estimateEvmFeesPerGas } from '../../_evm-gas';\nimport { estimateGasWithRevert } from '../../_evm-errors';\nimport { markrGetSpenderAddress, markrSwap, type ApiOptions } from '../_api';\nimport {\n assetToAddressString,\n calculateMarkrMinimumAmountOut,\n getAdditiveSourceAssetFeeAmount,\n getAdditiveSourceNativeAssetFeeAmount,\n getMarkrSwapWrapperAbi,\n isTokenAddressNative,\n} from '../_utils';\nimport type { WrappedSwapTransactionResponse } from '../_schema';\nimport { isEvmSwapResponse, isHyperliquidWithdrawSwapResponse, isSolanaSwapResponse } from '../_type-guards';\nimport { caip2ToEip155ChainId } from '../../../utils/caip';\nimport type { Quote } from '../../../types/quote';\nimport { isEvmNamespace, isSolanaNamespace } from '../../../_utils/chain';\nimport { estimateSolanaFee } from '../../../utils/solana';\n\n/**\n * This is just a fallback value used in the case that an\n * allowance approval is needed, and Markr didn't return us any gas estimate data.\n *\n * Just based on my review of some quotes, I think this is a reasonable estimate\n * left on the higher end.\n *\n * Assume this could need to change.\n */\nconst EVM_SWAP_FALLBACK_GAS_ESTIMATE = 700_000n;\n\nexport interface EstimateNativeFeeFactoryConfig {\n apiOptions: ApiOptions;\n appId: string;\n}\n\nexport function estimateNativeFeeFactory(config: EstimateNativeFeeFactoryConfig): TransferService['estimateNativeFee'] {\n return async (quote, options) => {\n // Either the source chain is EVM or Solana, we need to call different functions that\n // calculate the native fee depending on the chain kind.\n\n if (isEvmNamespace(quote.sourceChain.chainId)) {\n return await _estimateNativeFeeEvm(quote, options, config);\n }\n\n if (isSolanaNamespace(quote.sourceChain.chainId)) {\n return await _estimateNativeFeeSolana(quote, options, config);\n }\n\n throw new InvalidParamsError(\n ErrorReason.INVALID_PARAMS,\n `Unsupported source chain namespace for estimateNativeFee: ${quote.sourceChain.chainId}`,\n );\n };\n}\n\n/** @internal */\nexport async function _estimateNativeFeeEvm(\n quote: Quote,\n options: EstimateNativeFeeOptions | undefined,\n { apiOptions, appId }: EstimateNativeFeeFactoryConfig,\n): Promise<NativeFeeEstimate> {\n if (!isEvmAddress(quote.fromAddress)) {\n throw new InvalidParamsError(ErrorReason.INVALID_PARAMS, `Invalid fromAddress: ${quote.fromAddress}`);\n }\n\n const sourceClient = getEvmClientForChain({ chain: quote.sourceChain });\n\n const assetInAddressString = assetToAddressString(quote.assetIn, quote.sourceChain.chainId);\n\n if (!isEvmAddress(assetInAddressString)) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: `assetIn address is not a valid EVM address. Can not call estimateGas.`,\n });\n }\n\n const isAssetInNative = isTokenAddressNative(assetInAddressString);\n const isCrossChainSwap = quote.sourceChain.chainId.toLowerCase() !== quote.targetChain.chainId.toLowerCase();\n const allowanceAmount = quote.amountIn + getAdditiveSourceAssetFeeAmount(quote);\n\n let allowanceApprovalGas = 0n;\n if (!isAssetInNative) {\n // Check if approval is needed, and if so, calculate the gas cost for the approval.\n let allowance: bigint;\n\n const { address: spenderAddress } = await markrGetSpenderAddress(apiOptions, {\n chainId: caip2ToEip155ChainId(quote.sourceChain.chainId),\n crossChainSwap: isCrossChainSwap,\n quoteId: quote.id,\n });\n\n try {\n allowance = await sourceClient.readContract({\n address: assetInAddressString,\n abi: erc20Abi,\n functionName: 'allowance',\n args: [quote.fromAddress, spenderAddress],\n });\n } catch (error) {\n throw new SdkError('Error during allowance check', ErrorCode.VIEM_ERROR, {\n cause: error,\n details: 'Failed to read ERC20 allowance for Markr spender.',\n });\n }\n\n const approvalNeeded = allowance < allowanceAmount;\n\n if (approvalNeeded) {\n allowanceApprovalGas = await estimateGasWithRevert(\n sourceClient,\n {\n account: quote.fromAddress,\n to: assetInAddressString,\n data: encodeFunctionData({\n abi: erc20Abi,\n functionName: 'approve',\n args: [spenderAddress, allowanceAmount],\n }),\n },\n erc20Abi,\n 'Failed to estimate gas for ERC20 approval transaction.',\n );\n }\n }\n\n const allowanceApprovalIsNeeded = allowanceApprovalGas > 0n;\n\n const minAmountOut = calculateMarkrMinimumAmountOut({\n amountOut: quote.amountOut,\n assetOut: quote.assetOut,\n slippageBps: quote.slippageBps,\n });\n\n const swap = await markrSwap(apiOptions, {\n amountIn: quote.amountIn.toString(),\n appId,\n minAmountOut: minAmountOut.toString(),\n tokenIn: assetInAddressString,\n tokenOut: assetToAddressString(quote.assetOut, quote.targetChain.chainId),\n uuid: quote.id,\n });\n\n if (!isEvmSwapResponse(swap)) {\n // Should hopefully never happen.\n const variantTag = isSolanaSwapResponse(swap)\n ? swap.chainType\n : isHyperliquidWithdrawSwapResponse(swap)\n ? swap.type\n : 'unknown';\n\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: `Received non-EVM swap response from Markr. Expected EVM transaction data for gas estimation, but got response variant \"${variantTag}\".`,\n });\n }\n\n let gas = 0n;\n\n // If an approval is needed, we can't call `eth_estimateGas` on the\n // swap transaction because no actual allowance approval was performed yet,\n // so a gas estimation would fail due to needing allowance.\n //\n // So if an allowance approval is needed, we fall back to some other logic for\n // estimating the swap tx gas.\n if (allowanceApprovalIsNeeded) {\n // Attempt to use the Markr provided `gasEstimate` first if available.\n // Otherwise we attempt to get the gas from the `fees` component that are applicable\n // to the source chain. If neither of those are available, we fall back to a hardcoded value.\n if (quote.gasEstimate) {\n gas = quote.gasEstimate;\n } else {\n const sourceGasFee = quote.fees\n .filter((fee) => fee.type === 'gas' && fee.chainId === quote.sourceChain.chainId)\n .reduce((acc, fee) => acc + fee.amount, 0n);\n\n gas = sourceGasFee || EVM_SWAP_FALLBACK_GAS_ESTIMATE;\n }\n } else {\n gas = await _estimateGasFromSwapResponse({\n crossChain: isCrossChainSwap,\n fromAddress: quote.fromAddress,\n sourceClient,\n swap,\n });\n }\n\n const fees = await estimateEvmFeesPerGas(sourceClient, quote.sourceChain, options?.overrides?.feeRateTier);\n\n const maxFeePerGas = options?.overrides?.maxFeePerGas ?? fees.maxFeePerGas;\n const maxPriorityFeePerGas = options?.overrides?.maxPriorityFeePerGas ?? fees.maxPriorityFeePerGas;\n\n const totalFeeWithoutMargin = (gas + allowanceApprovalGas) * maxFeePerGas;\n\n return {\n asset: quote.sourceChain.networkToken,\n totalFee: applyFeeUnitsBpsMargin(totalFeeWithoutMargin, options?.feeUnitsMarginBps),\n totalFeeWithoutMargin,\n meta: {\n approvalFee: allowanceApprovalIsNeeded ? allowanceApprovalGas * maxFeePerGas : undefined,\n maxFeePerGas,\n maxPriorityFeePerGas,\n },\n };\n}\n\n/** @internal */\nexport async function _estimateNativeFeeSolana(\n quote: Quote,\n options: EstimateNativeFeeOptions | undefined,\n { apiOptions, appId }: EstimateNativeFeeFactoryConfig,\n): Promise<NativeFeeEstimate> {\n const assetInAddressString = assetToAddressString(quote.assetIn, quote.sourceChain.chainId);\n\n if (!isSolanaAddress(assetInAddressString)) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: `assetIn address is not a valid Solana address. Can not call estimateGas.`,\n });\n }\n\n const additiveNativeFee: bigint = getAdditiveSourceNativeAssetFeeAmount(quote);\n const totalInput: bigint = quote.amountIn + additiveNativeFee;\n\n const minAmountOut = calculateMarkrMinimumAmountOut({\n amountOut: quote.amountOut,\n assetOut: quote.assetOut,\n slippageBps: quote.slippageBps,\n });\n\n const swap = await markrSwap(apiOptions, {\n amountIn: quote.amountIn.toString(),\n appId,\n minAmountOut: minAmountOut.toString(),\n tokenIn: assetInAddressString,\n tokenOut: assetToAddressString(quote.assetOut, quote.targetChain.chainId),\n userPublicKey: quote.fromAddress,\n uuid: quote.id,\n });\n\n if (!isSolanaSwapResponse(swap)) {\n // Should hopefully never happen.\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: 'Received non-Solana swap response from Markr. Expected Solana transaction data for fee estimation.',\n });\n }\n\n const rpc = getSolanaRpcForChain({ chain: quote.sourceChain });\n\n const { baseFeeLamports, priorityFeeLamports, ...rest } = await estimateSolanaFee(rpc, swap.swapTransaction);\n\n const totalFeeWithoutMargin = rest.feePayerBalanceDiffLamports - totalInput;\n\n return {\n asset: quote.sourceChain.networkToken,\n totalFee: applyFeeUnitsBpsMargin(totalFeeWithoutMargin, options?.feeUnitsMarginBps),\n totalFeeWithoutMargin,\n meta: {\n ...rest,\n baseFee: baseFeeLamports,\n priorityFee: priorityFeeLamports,\n },\n };\n}\n\n/** @internal */\nexport async function _estimateGasFromSwapResponse({\n crossChain,\n feeUnitsMarginBps,\n fromAddress,\n sourceClient,\n swap,\n}: {\n crossChain: boolean;\n feeUnitsMarginBps?: number;\n fromAddress: EvmAddress;\n sourceClient: ReturnType<typeof getEvmClientForChain>;\n swap: WrappedSwapTransactionResponse;\n}): Promise<bigint> {\n const markrAbi = await getMarkrSwapWrapperAbi(crossChain);\n\n // Use raw estimateGas — the calldata is pre-encoded and may target any\n // contract (Markr wrapper, deBridge, etc.). The ABI is only used for\n // best-effort error decoding on the failure path.\n const gasEstimate = await estimateGasWithRevert(\n sourceClient,\n {\n account: fromAddress,\n to: swap.to,\n data: swap.data,\n value: swap.value,\n },\n markrAbi,\n 'Failed to estimate gas for Markr swap transaction.',\n );\n\n return applyFeeUnitsBpsMargin(gasEstimate, feeUnitsMarginBps);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
require(`../../../_virtual/_rolldown/runtime.cjs`);const e=require(`../../../constants.cjs`),t=require(`../../../errors.cjs`),n=require(`../../../utils/caip.cjs`),r=require(`../../_utils.cjs`),i=require(`../_api.cjs`),a=require(`../_hyperliquid-eip712.cjs`),o=require(`../_hyperliquid.cjs`);let s=require(`viem`);function c(e){if(!/^\d+$/.test(e))throw new t.SdkError(`Hyperliquid swap response contained a malformed nonce.`,t.ErrorCode.INVALID_PARAMS);return BigInt(e)}function l(e,n,r){if(e.value.id!==n.id)throw new t.SdkError(`Relay sign.value.id does not match swap.id.`,t.ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION,{details:`expected ${n.id}, got ${String(e.value.id)}`});if(String(e.value.nonce)!==n.nonce)throw new t.SdkError(`Relay sign.value.nonce does not match swap.nonce.`,t.ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION,{details:`expected ${n.nonce}, got ${String(e.value.nonce)}`});if(e.value.wallet.toLowerCase()!==r.toLowerCase())throw new t.SdkError(`Relay sign.value.wallet does not match the signer address.`,t.ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION,{details:`expected ${r}, got ${e.value.wallet}`})}async function u({apiOptions:u,environment:f,evmSigner:p,quote:m,swap:h,onStepChange:g}){if(!a.SUPPORTED_HL_PROTOCOL_VERSIONS.includes(h.protocolVersion))throw new t.SdkError(`Unsupported Hyperliquid withdrawal protocol version. Please update @avalabs/fusion-sdk.`,t.ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION,{details:`Markr returned protocolVersion=${h.protocolVersion}; SDK supports [${a.SUPPORTED_HL_PROTOCOL_VERSIONS.join(`, `)}]`});if(!p.signTypedData)throw new t.SdkError(`Hyperliquid withdrawal requires an EIP-712 typed-data signer.`,t.ErrorCode.SIGNING_FAILED,{details:`EvmSigner.signTypedData is undefined. Wire it via wagmi useSignTypedData.`});if(!(0,s.isAddress)(m.fromAddress))throw new t.SdkError(t.ErrorReason.INVALID_PARAMS,t.ErrorCode.INVALID_PARAMS,{details:`fromAddress is not a valid EVM address.`});let _=m.fromAddress,v=n.caip2ToEip155ChainId(m.targetChain.chainId),y=c(h.nonce),b=h.protocolVersion>=2,x={currentSignature:1,currentSignatureReason:e.TransferSignatureReason.HyperliquidAuthorize,quote:m,requiredSignatures:2};g?.(x);let S;if(b){if(!h.sign||!h.authorize)throw new t.SdkError("Hyperliquid v2 swap response is missing `sign` or `authorize` envelope.",t.ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION,{details:`Required pass-through fields are absent — the upstream may have downgraded to v1.`});l(h.sign,h,_),S={domain:h.sign.domain,types:h.sign.types,primaryType:h.sign.primaryType,message:h.sign.value}}else S=a.buildRelayNonceMappingTypedData({signatureChainId:v,wallet:_,orderId:h.id,nonce:y});let C=await p.signTypedData({typedData:S,address:_,chainId:v},x);try{b&&h.authorize?await i.markrPostAuthorize(u,{uuid:m.id,signature:C,body:h.authorize.body}):await i.markrPostAuthorize(u,{uuid:m.id,signature:C,wallet:_,walletChainId:1337,signatureChainId:v,nonce:Number(y),id:h.id})}catch(e){if(e instanceof t.HttpError)return r.makeFailedTransferFromQuote(m,{environment:f,errorCode:t.ErrorCode.HL_EXCHANGE_REJECTED,errorReason:`Markr /authorize failed (HTTP ${e.status}). Please retry.`});throw e}let w=Number(h.parameters.amount),T=await o.hyperliquidGetWithdrawable({apiBaseUrl:o.hyperliquidApiBaseUrlForEnvironment(f),user:_});if(Number.isFinite(T)&&T<w)return r.makeFailedTransferFromQuote(m,{environment:f,errorCode:t.ErrorCode.HL_WITHDRAWABLE_DROPPED,errorReason:`Withdrawable balance changed during withdrawal (requested ${w}, available ${T}). Please retry with a smaller amount.`});let E={currentSignature:2,currentSignatureReason:e.TransferSignatureReason.HyperliquidSendAsset,quote:m,requiredSignatures:2};g?.(E);let D={signatureChainId:v,hyperliquidChain:h.parameters.hyperliquidChain,destination:h.parameters.destination,token:h.parameters.token,amount:h.parameters.amount,sourceDex:h.parameters.sourceDex,destinationDex:h.parameters.destinationDex,fromSubAccount:h.parameters.fromSubAccount,nonce:y},O=a.buildHyperliquidSendAssetTypedData(D),k=await p.signTypedData({typedData:O,address:_,chainId:v},E),A=a.buildHyperliquidExchangeAction(D),j=await o.hyperliquidExchangePost({apiBaseUrl:o.hyperliquidApiBaseUrlForEnvironment(f),signature:k,nonce:y,action:A});if(j.status!==`ok`)return r.makeFailedTransferFromQuote(m,{environment:f,errorCode:t.ErrorCode.HL_EXCHANGE_REJECTED,errorReason:d(j.response)});let M=Date.now();return{amountIn:m.amountIn,amountOut:m.amountOut,environment:f,fees:m.fees,fromAddress:m.fromAddress,id:m.id,partnerFeeBps:m.partnerFeeBps,sourceAsset:m.assetIn,sourceChain:m.sourceChain,status:`source-completed`,targetAsset:m.assetOut,targetChain:m.targetChain,toAddress:m.toAddress,type:m.serviceType,source:{confirmationCount:2,requiredConfirmationCount:2,startedAtMs:M,txHash:h.requestId}}}function d(e){return typeof e==`string`?`Hyperliquid /exchange rejected the transfer: ${e}`:e&&typeof e==`object`&&`error`in e&&typeof e.error==`string`?`Hyperliquid /exchange rejected the transfer: ${e.error}`:t.ErrorReason.UNKNOWN}exports._executeHyperliquidWithdraw=u;
|
|
2
|
+
//# sourceMappingURL=transfer-asset-hyperliquid-withdraw.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transfer-asset-hyperliquid-withdraw.cjs","names":["SdkError","ErrorCode","SUPPORTED_HL_PROTOCOL_VERSIONS","ErrorReason","caip2ToEip155ChainId","TransferSignatureReason","buildRelayNonceMappingTypedData","markrPostAuthorize","HttpError","makeFailedTransferFromQuote","hyperliquidGetWithdrawable","hyperliquidApiBaseUrlForEnvironment","buildHyperliquidSendAssetTypedData","buildHyperliquidExchangeAction","hyperliquidExchangePost"],"sources":["../../../../src/transfer-service/markr/_handlers/transfer-asset-hyperliquid-withdraw.ts"],"sourcesContent":["/**\n * Hyperliquid 2-phase withdrawal handler.\n *\n * Mirrors the existing `approve + swap` shape — two sequential signatures with\n * an inter-phase gate — but with EIP-712 typed-data signatures and HTTP POSTs\n * instead of serialized EVM txs.\n *\n * 1. AUTHORIZE — sign `RelayNonceMapping`, POST to Markr `/authorize`.\n * 2. PRE-FLIGHT — re-fetch `withdrawable`, abort if it dropped.\n * 3. SEND ASSET — sign `HyperliquidTransaction:SendAsset`, POST to\n * `api.hyperliquid.xyz/exchange` directly (no Markr in this path).\n */\nimport { isAddress } from 'viem';\nimport type { Environment } from '../../../constants';\nimport { TransferSignatureReason } from '../../../constants';\nimport { ErrorCode, ErrorReason, HttpError, SdkError } from '../../../errors';\nimport type { TransferService } from '../../../types/service';\nimport type { EvmSigner, EvmTypedData } from '../../../types/signer';\nimport type { Transfer, TransferStepDetails } from '../../../types/transfer';\nimport { caip2ToEip155ChainId } from '../../../utils/caip';\nimport { makeFailedTransferFromQuote } from '../../_utils';\nimport { markrPostAuthorize, type ApiOptions } from '../_api';\nimport type { HyperliquidWithdrawSwapResponse, RelayEip712SignEnvelope } from '../_schema';\nimport {\n type BuildHyperliquidSendAssetParams,\n buildHyperliquidExchangeAction,\n buildHyperliquidSendAssetTypedData,\n buildRelayNonceMappingTypedData,\n SUPPORTED_HL_PROTOCOL_VERSIONS,\n} from '../_hyperliquid-eip712';\nimport {\n hyperliquidApiBaseUrlForEnvironment,\n hyperliquidExchangePost,\n hyperliquidGetWithdrawable,\n} from '../_hyperliquid';\n\nexport interface HyperliquidWithdrawTransferParams {\n readonly apiOptions: ApiOptions;\n readonly environment: Environment;\n readonly evmSigner: EvmSigner;\n readonly quote: Parameters<TransferService['transferAsset']>[0]['quote'];\n readonly swap: HyperliquidWithdrawSwapResponse;\n readonly onStepChange?: Parameters<TransferService['transferAsset']>[0]['onStepChange'];\n}\n\n/**\n * Convert the Markr-supplied `nonce` (decimal string) into a `bigint`.\n *\n * We don't trust the wire value blindly — if Markr ever changes the encoding\n * we want a deterministic error, not a silent bad signature.\n */\nfunction parseNonce(raw: string): bigint {\n if (!/^\\d+$/.test(raw)) {\n throw new SdkError('Hyperliquid swap response contained a malformed nonce.', ErrorCode.INVALID_PARAMS);\n }\n\n return BigInt(raw);\n}\n\n/**\n * Cross-checks before signing Relay's pass-through `sign` envelope. Guards\n * against a downstream bug surfacing as a signed message bound to a different\n * order or a different wallet.\n */\nfunction assertSignEnvelopeMatchesSwap(\n sign: RelayEip712SignEnvelope,\n swap: HyperliquidWithdrawSwapResponse,\n wallet: string,\n): void {\n if (sign.value.id !== swap.id) {\n throw new SdkError('Relay sign.value.id does not match swap.id.', ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION, {\n details: `expected ${swap.id}, got ${String(sign.value.id)}`,\n });\n }\n if (String(sign.value.nonce) !== swap.nonce) {\n throw new SdkError('Relay sign.value.nonce does not match swap.nonce.', ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION, {\n details: `expected ${swap.nonce}, got ${String(sign.value.nonce)}`,\n });\n }\n if (sign.value.wallet.toLowerCase() !== wallet.toLowerCase()) {\n throw new SdkError(\n 'Relay sign.value.wallet does not match the signer address.',\n ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION,\n { details: `expected ${wallet}, got ${sign.value.wallet}` },\n );\n }\n}\n\nexport async function _executeHyperliquidWithdraw({\n apiOptions,\n environment,\n evmSigner,\n quote,\n swap,\n onStepChange,\n}: HyperliquidWithdrawTransferParams): Promise<Transfer> {\n // 0. Version guard — fail loud if Markr/Relay/HL bump the EIP-712 schemas.\n if (!SUPPORTED_HL_PROTOCOL_VERSIONS.includes(swap.protocolVersion as 1 | 2)) {\n throw new SdkError(\n 'Unsupported Hyperliquid withdrawal protocol version. Please update @avalabs/fusion-sdk.',\n ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION,\n {\n details: `Markr returned protocolVersion=${\n swap.protocolVersion\n }; SDK supports [${SUPPORTED_HL_PROTOCOL_VERSIONS.join(', ')}]`,\n },\n );\n }\n\n if (!evmSigner.signTypedData) {\n throw new SdkError('Hyperliquid withdrawal requires an EIP-712 typed-data signer.', ErrorCode.SIGNING_FAILED, {\n details: 'EvmSigner.signTypedData is undefined. Wire it via wagmi useSignTypedData.',\n });\n }\n\n if (!isAddress(quote.fromAddress)) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: 'fromAddress is not a valid EVM address.',\n });\n }\n\n const wallet = quote.fromAddress;\n const targetChainId = caip2ToEip155ChainId(quote.targetChain.chainId);\n const nonce = parseNonce(swap.nonce);\n const isV2 = swap.protocolVersion >= 2;\n\n // 1. AUTHORIZE — sign nonce-mapping locally, POST to Markr.\n const authorizeStep: TransferStepDetails = {\n currentSignature: 1,\n currentSignatureReason: TransferSignatureReason.HyperliquidAuthorize,\n quote,\n requiredSignatures: 2,\n };\n onStepChange?.(authorizeStep);\n\n // v2: sign Relay's `sign` envelope verbatim — no schema knowledge required.\n // v1 (legacy): rebuild the `RelayNonceMapping` typed data from pinned types.\n // Either path produces the same authorize signature for matching orders;\n // the v2 path additionally tolerates Relay-side schema additions (e.g.\n // `depositor`) without an SDK release.\n let authorizeTypedData: EvmTypedData;\n if (isV2) {\n if (!swap.sign || !swap.authorize) {\n throw new SdkError(\n 'Hyperliquid v2 swap response is missing `sign` or `authorize` envelope.',\n ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION,\n { details: 'Required pass-through fields are absent — the upstream may have downgraded to v1.' },\n );\n }\n assertSignEnvelopeMatchesSwap(swap.sign, swap, wallet);\n authorizeTypedData = {\n domain: swap.sign.domain,\n types: swap.sign.types,\n primaryType: swap.sign.primaryType,\n message: swap.sign.value,\n };\n } else {\n // `swap.id` (the order id) is what Relay binds the nonce-mapping to —\n // distinct from `swap.requestId` (the cross-chain tracking id). Mixing them\n // up produces a \"valid-looking\" signature for an order Relay doesn't know\n // about, so the on-chain sendAsset gets orphaned.\n authorizeTypedData = buildRelayNonceMappingTypedData({\n signatureChainId: targetChainId,\n wallet,\n orderId: swap.id,\n nonce,\n });\n }\n\n const authorizeSignature = await evmSigner.signTypedData(\n { typedData: authorizeTypedData, address: wallet, chainId: targetChainId },\n authorizeStep,\n );\n\n try {\n if (isV2 && swap.authorize) {\n await markrPostAuthorize(apiOptions, {\n uuid: quote.id,\n signature: authorizeSignature,\n // Forward Relay's `/authorize` body verbatim — Markr only normalizes\n // `nonce` to a JSON number and defaults `referrer`. Additive Relay\n // fields (e.g. `depositor`) flow through automatically.\n body: swap.authorize.body,\n });\n } else {\n await markrPostAuthorize(apiOptions, {\n uuid: quote.id,\n signature: authorizeSignature,\n wallet,\n walletChainId: 1337,\n signatureChainId: targetChainId,\n /**\n * Relay's upstream `/authorize` validates `nonce` strictly as a JSON\n * number. Sending it as a string makes the request fail upstream. HL\n * nonces are millisecond timestamps, comfortably under\n * `Number.MAX_SAFE_INTEGER`, so the Number coercion is lossless.\n */\n nonce: Number(nonce),\n // Order id (not the tracking requestId).\n id: swap.id,\n });\n }\n } catch (error) {\n // Surface as a failed transfer so the UI can prompt a retry rather than\n // crashing the panel. The user paid no on-chain cost yet.\n if (error instanceof HttpError) {\n return makeFailedTransferFromQuote(quote, {\n environment,\n errorCode: ErrorCode.HL_EXCHANGE_REJECTED,\n errorReason: `Markr /authorize failed (HTTP ${error.status}). Please retry.`,\n });\n }\n throw error;\n }\n\n // 2. PRE-FLIGHT — re-check withdrawable. Guards the second wallet prompt\n // from being asked for a balance the user no longer has (e.g. they opened\n // a position between the two signatures on another device). Relay's\n // pre-formatted `amount` is at the spot token's display precision, so it\n // parses cleanly via `Number(...)` — no need to re-format from the bigint.\n const requestedAmount = Number(swap.parameters.amount);\n const liveWithdrawable = await hyperliquidGetWithdrawable({\n apiBaseUrl: hyperliquidApiBaseUrlForEnvironment(environment),\n user: wallet,\n });\n\n if (Number.isFinite(liveWithdrawable) && liveWithdrawable < requestedAmount) {\n return makeFailedTransferFromQuote(quote, {\n environment,\n errorCode: ErrorCode.HL_WITHDRAWABLE_DROPPED,\n errorReason: `Withdrawable balance changed during withdrawal (requested ${requestedAmount}, available ${liveWithdrawable}). Please retry with a smaller amount.`,\n });\n }\n\n // 3. SEND ASSET — sign HL transaction locally, POST to api.hyperliquid.xyz/exchange.\n // We sign and submit Relay's `parameters` *verbatim* — Relay's order matcher\n // recovers the on-chain `sendAsset` by hashing these exact strings, so any\n // local re-formatting (especially of `amount`) would orphan the transfer.\n const sendAssetStep: TransferStepDetails = {\n currentSignature: 2,\n currentSignatureReason: TransferSignatureReason.HyperliquidSendAsset,\n quote,\n requiredSignatures: 2,\n };\n onStepChange?.(sendAssetStep);\n\n const sendAssetParams = {\n signatureChainId: targetChainId,\n hyperliquidChain: swap.parameters.hyperliquidChain,\n destination: swap.parameters.destination,\n token: swap.parameters.token,\n amount: swap.parameters.amount,\n sourceDex: swap.parameters.sourceDex,\n destinationDex: swap.parameters.destinationDex,\n fromSubAccount: swap.parameters.fromSubAccount,\n nonce,\n } satisfies BuildHyperliquidSendAssetParams;\n\n const sendAssetTypedData = buildHyperliquidSendAssetTypedData(sendAssetParams);\n const sendAssetSignature = await evmSigner.signTypedData(\n { typedData: sendAssetTypedData, address: wallet, chainId: targetChainId },\n sendAssetStep,\n );\n\n const exchangeAction = buildHyperliquidExchangeAction(sendAssetParams);\n\n const exchangeResponse = await hyperliquidExchangePost({\n apiBaseUrl: hyperliquidApiBaseUrlForEnvironment(environment),\n signature: sendAssetSignature,\n nonce,\n action: exchangeAction,\n });\n\n if (exchangeResponse.status !== 'ok') {\n return makeFailedTransferFromQuote(quote, {\n environment,\n errorCode: ErrorCode.HL_EXCHANGE_REJECTED,\n errorReason: extractHlExchangeError(exchangeResponse.response),\n });\n }\n\n const startedAtMs = Date.now();\n\n // HL `/exchange` answering `status: ok` is our source-side finality signal —\n // HL's matcher debits the user atomically with that ack (no separate on-chain\n // tx hash to wait on; HL doesn't expose one for `sendAsset`). Hand the tracker\n // a `source-completed` transfer keyed on Relay's `requestId` so it skips the\n // EVM finality gate and goes straight to Markr `/cross-chain/status` polling.\n return {\n amountIn: quote.amountIn,\n amountOut: quote.amountOut,\n environment,\n fees: quote.fees,\n fromAddress: quote.fromAddress,\n id: quote.id,\n partnerFeeBps: quote.partnerFeeBps,\n sourceAsset: quote.assetIn,\n sourceChain: quote.sourceChain,\n status: 'source-completed',\n targetAsset: quote.assetOut,\n targetChain: quote.targetChain,\n toAddress: quote.toAddress,\n type: quote.serviceType,\n source: {\n confirmationCount: 2,\n requiredConfirmationCount: 2,\n startedAtMs,\n // `requestId` doubles as the SDK's sourceTxHash for status polling.\n txHash: swap.requestId,\n },\n };\n}\n\nfunction extractHlExchangeError(response: unknown): string {\n if (typeof response === 'string') {\n return `Hyperliquid /exchange rejected the transfer: ${response}`;\n }\n\n if (response && typeof response === 'object' && 'error' in response && typeof response.error === 'string') {\n return `Hyperliquid /exchange rejected the transfer: ${response.error}`;\n }\n\n return ErrorReason.UNKNOWN;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{TransferSignatureReason as e}from"../../../constants.js";import{ErrorCode as t,ErrorReason as n,HttpError as r,SdkError as i}from"../../../errors.js";import{caip2ToEip155ChainId as a}from"../../../utils/caip.js";import{makeFailedTransferFromQuote as o}from"../../_utils.js";import{markrPostAuthorize as s}from"../_api.js";import{SUPPORTED_HL_PROTOCOL_VERSIONS as c,buildHyperliquidExchangeAction as l,buildHyperliquidSendAssetTypedData as u,buildRelayNonceMappingTypedData as d}from"../_hyperliquid-eip712.js";import{hyperliquidApiBaseUrlForEnvironment as f,hyperliquidExchangePost as p,hyperliquidGetWithdrawable as m}from"../_hyperliquid.js";import{isAddress as h}from"viem";function g(e){if(!/^\d+$/.test(e))throw new i(`Hyperliquid swap response contained a malformed nonce.`,t.INVALID_PARAMS);return BigInt(e)}function _(e,n,r){if(e.value.id!==n.id)throw new i(`Relay sign.value.id does not match swap.id.`,t.HL_UNSUPPORTED_PROTOCOL_VERSION,{details:`expected ${n.id}, got ${String(e.value.id)}`});if(String(e.value.nonce)!==n.nonce)throw new i(`Relay sign.value.nonce does not match swap.nonce.`,t.HL_UNSUPPORTED_PROTOCOL_VERSION,{details:`expected ${n.nonce}, got ${String(e.value.nonce)}`});if(e.value.wallet.toLowerCase()!==r.toLowerCase())throw new i(`Relay sign.value.wallet does not match the signer address.`,t.HL_UNSUPPORTED_PROTOCOL_VERSION,{details:`expected ${r}, got ${e.value.wallet}`})}async function v({apiOptions:v,environment:b,evmSigner:x,quote:S,swap:C,onStepChange:w}){if(!c.includes(C.protocolVersion))throw new i(`Unsupported Hyperliquid withdrawal protocol version. Please update @avalabs/fusion-sdk.`,t.HL_UNSUPPORTED_PROTOCOL_VERSION,{details:`Markr returned protocolVersion=${C.protocolVersion}; SDK supports [${c.join(`, `)}]`});if(!x.signTypedData)throw new i(`Hyperliquid withdrawal requires an EIP-712 typed-data signer.`,t.SIGNING_FAILED,{details:`EvmSigner.signTypedData is undefined. Wire it via wagmi useSignTypedData.`});if(!h(S.fromAddress))throw new i(n.INVALID_PARAMS,t.INVALID_PARAMS,{details:`fromAddress is not a valid EVM address.`});let T=S.fromAddress,E=a(S.targetChain.chainId),D=g(C.nonce),O=C.protocolVersion>=2,k={currentSignature:1,currentSignatureReason:e.HyperliquidAuthorize,quote:S,requiredSignatures:2};w?.(k);let A;if(O){if(!C.sign||!C.authorize)throw new i("Hyperliquid v2 swap response is missing `sign` or `authorize` envelope.",t.HL_UNSUPPORTED_PROTOCOL_VERSION,{details:`Required pass-through fields are absent — the upstream may have downgraded to v1.`});_(C.sign,C,T),A={domain:C.sign.domain,types:C.sign.types,primaryType:C.sign.primaryType,message:C.sign.value}}else A=d({signatureChainId:E,wallet:T,orderId:C.id,nonce:D});let j=await x.signTypedData({typedData:A,address:T,chainId:E},k);try{O&&C.authorize?await s(v,{uuid:S.id,signature:j,body:C.authorize.body}):await s(v,{uuid:S.id,signature:j,wallet:T,walletChainId:1337,signatureChainId:E,nonce:Number(D),id:C.id})}catch(e){if(e instanceof r)return o(S,{environment:b,errorCode:t.HL_EXCHANGE_REJECTED,errorReason:`Markr /authorize failed (HTTP ${e.status}). Please retry.`});throw e}let M=Number(C.parameters.amount),N=await m({apiBaseUrl:f(b),user:T});if(Number.isFinite(N)&&N<M)return o(S,{environment:b,errorCode:t.HL_WITHDRAWABLE_DROPPED,errorReason:`Withdrawable balance changed during withdrawal (requested ${M}, available ${N}). Please retry with a smaller amount.`});let P={currentSignature:2,currentSignatureReason:e.HyperliquidSendAsset,quote:S,requiredSignatures:2};w?.(P);let F={signatureChainId:E,hyperliquidChain:C.parameters.hyperliquidChain,destination:C.parameters.destination,token:C.parameters.token,amount:C.parameters.amount,sourceDex:C.parameters.sourceDex,destinationDex:C.parameters.destinationDex,fromSubAccount:C.parameters.fromSubAccount,nonce:D},I=u(F),L=await x.signTypedData({typedData:I,address:T,chainId:E},P),R=l(F),z=await p({apiBaseUrl:f(b),signature:L,nonce:D,action:R});if(z.status!==`ok`)return o(S,{environment:b,errorCode:t.HL_EXCHANGE_REJECTED,errorReason:y(z.response)});let B=Date.now();return{amountIn:S.amountIn,amountOut:S.amountOut,environment:b,fees:S.fees,fromAddress:S.fromAddress,id:S.id,partnerFeeBps:S.partnerFeeBps,sourceAsset:S.assetIn,sourceChain:S.sourceChain,status:`source-completed`,targetAsset:S.assetOut,targetChain:S.targetChain,toAddress:S.toAddress,type:S.serviceType,source:{confirmationCount:2,requiredConfirmationCount:2,startedAtMs:B,txHash:C.requestId}}}function y(e){return typeof e==`string`?`Hyperliquid /exchange rejected the transfer: ${e}`:e&&typeof e==`object`&&`error`in e&&typeof e.error==`string`?`Hyperliquid /exchange rejected the transfer: ${e.error}`:n.UNKNOWN}export{v as _executeHyperliquidWithdraw};
|
|
2
|
+
//# sourceMappingURL=transfer-asset-hyperliquid-withdraw.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transfer-asset-hyperliquid-withdraw.js","names":[],"sources":["../../../../src/transfer-service/markr/_handlers/transfer-asset-hyperliquid-withdraw.ts"],"sourcesContent":["/**\n * Hyperliquid 2-phase withdrawal handler.\n *\n * Mirrors the existing `approve + swap` shape — two sequential signatures with\n * an inter-phase gate — but with EIP-712 typed-data signatures and HTTP POSTs\n * instead of serialized EVM txs.\n *\n * 1. AUTHORIZE — sign `RelayNonceMapping`, POST to Markr `/authorize`.\n * 2. PRE-FLIGHT — re-fetch `withdrawable`, abort if it dropped.\n * 3. SEND ASSET — sign `HyperliquidTransaction:SendAsset`, POST to\n * `api.hyperliquid.xyz/exchange` directly (no Markr in this path).\n */\nimport { isAddress } from 'viem';\nimport type { Environment } from '../../../constants';\nimport { TransferSignatureReason } from '../../../constants';\nimport { ErrorCode, ErrorReason, HttpError, SdkError } from '../../../errors';\nimport type { TransferService } from '../../../types/service';\nimport type { EvmSigner, EvmTypedData } from '../../../types/signer';\nimport type { Transfer, TransferStepDetails } from '../../../types/transfer';\nimport { caip2ToEip155ChainId } from '../../../utils/caip';\nimport { makeFailedTransferFromQuote } from '../../_utils';\nimport { markrPostAuthorize, type ApiOptions } from '../_api';\nimport type { HyperliquidWithdrawSwapResponse, RelayEip712SignEnvelope } from '../_schema';\nimport {\n type BuildHyperliquidSendAssetParams,\n buildHyperliquidExchangeAction,\n buildHyperliquidSendAssetTypedData,\n buildRelayNonceMappingTypedData,\n SUPPORTED_HL_PROTOCOL_VERSIONS,\n} from '../_hyperliquid-eip712';\nimport {\n hyperliquidApiBaseUrlForEnvironment,\n hyperliquidExchangePost,\n hyperliquidGetWithdrawable,\n} from '../_hyperliquid';\n\nexport interface HyperliquidWithdrawTransferParams {\n readonly apiOptions: ApiOptions;\n readonly environment: Environment;\n readonly evmSigner: EvmSigner;\n readonly quote: Parameters<TransferService['transferAsset']>[0]['quote'];\n readonly swap: HyperliquidWithdrawSwapResponse;\n readonly onStepChange?: Parameters<TransferService['transferAsset']>[0]['onStepChange'];\n}\n\n/**\n * Convert the Markr-supplied `nonce` (decimal string) into a `bigint`.\n *\n * We don't trust the wire value blindly — if Markr ever changes the encoding\n * we want a deterministic error, not a silent bad signature.\n */\nfunction parseNonce(raw: string): bigint {\n if (!/^\\d+$/.test(raw)) {\n throw new SdkError('Hyperliquid swap response contained a malformed nonce.', ErrorCode.INVALID_PARAMS);\n }\n\n return BigInt(raw);\n}\n\n/**\n * Cross-checks before signing Relay's pass-through `sign` envelope. Guards\n * against a downstream bug surfacing as a signed message bound to a different\n * order or a different wallet.\n */\nfunction assertSignEnvelopeMatchesSwap(\n sign: RelayEip712SignEnvelope,\n swap: HyperliquidWithdrawSwapResponse,\n wallet: string,\n): void {\n if (sign.value.id !== swap.id) {\n throw new SdkError('Relay sign.value.id does not match swap.id.', ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION, {\n details: `expected ${swap.id}, got ${String(sign.value.id)}`,\n });\n }\n if (String(sign.value.nonce) !== swap.nonce) {\n throw new SdkError('Relay sign.value.nonce does not match swap.nonce.', ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION, {\n details: `expected ${swap.nonce}, got ${String(sign.value.nonce)}`,\n });\n }\n if (sign.value.wallet.toLowerCase() !== wallet.toLowerCase()) {\n throw new SdkError(\n 'Relay sign.value.wallet does not match the signer address.',\n ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION,\n { details: `expected ${wallet}, got ${sign.value.wallet}` },\n );\n }\n}\n\nexport async function _executeHyperliquidWithdraw({\n apiOptions,\n environment,\n evmSigner,\n quote,\n swap,\n onStepChange,\n}: HyperliquidWithdrawTransferParams): Promise<Transfer> {\n // 0. Version guard — fail loud if Markr/Relay/HL bump the EIP-712 schemas.\n if (!SUPPORTED_HL_PROTOCOL_VERSIONS.includes(swap.protocolVersion as 1 | 2)) {\n throw new SdkError(\n 'Unsupported Hyperliquid withdrawal protocol version. Please update @avalabs/fusion-sdk.',\n ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION,\n {\n details: `Markr returned protocolVersion=${\n swap.protocolVersion\n }; SDK supports [${SUPPORTED_HL_PROTOCOL_VERSIONS.join(', ')}]`,\n },\n );\n }\n\n if (!evmSigner.signTypedData) {\n throw new SdkError('Hyperliquid withdrawal requires an EIP-712 typed-data signer.', ErrorCode.SIGNING_FAILED, {\n details: 'EvmSigner.signTypedData is undefined. Wire it via wagmi useSignTypedData.',\n });\n }\n\n if (!isAddress(quote.fromAddress)) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: 'fromAddress is not a valid EVM address.',\n });\n }\n\n const wallet = quote.fromAddress;\n const targetChainId = caip2ToEip155ChainId(quote.targetChain.chainId);\n const nonce = parseNonce(swap.nonce);\n const isV2 = swap.protocolVersion >= 2;\n\n // 1. AUTHORIZE — sign nonce-mapping locally, POST to Markr.\n const authorizeStep: TransferStepDetails = {\n currentSignature: 1,\n currentSignatureReason: TransferSignatureReason.HyperliquidAuthorize,\n quote,\n requiredSignatures: 2,\n };\n onStepChange?.(authorizeStep);\n\n // v2: sign Relay's `sign` envelope verbatim — no schema knowledge required.\n // v1 (legacy): rebuild the `RelayNonceMapping` typed data from pinned types.\n // Either path produces the same authorize signature for matching orders;\n // the v2 path additionally tolerates Relay-side schema additions (e.g.\n // `depositor`) without an SDK release.\n let authorizeTypedData: EvmTypedData;\n if (isV2) {\n if (!swap.sign || !swap.authorize) {\n throw new SdkError(\n 'Hyperliquid v2 swap response is missing `sign` or `authorize` envelope.',\n ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION,\n { details: 'Required pass-through fields are absent — the upstream may have downgraded to v1.' },\n );\n }\n assertSignEnvelopeMatchesSwap(swap.sign, swap, wallet);\n authorizeTypedData = {\n domain: swap.sign.domain,\n types: swap.sign.types,\n primaryType: swap.sign.primaryType,\n message: swap.sign.value,\n };\n } else {\n // `swap.id` (the order id) is what Relay binds the nonce-mapping to —\n // distinct from `swap.requestId` (the cross-chain tracking id). Mixing them\n // up produces a \"valid-looking\" signature for an order Relay doesn't know\n // about, so the on-chain sendAsset gets orphaned.\n authorizeTypedData = buildRelayNonceMappingTypedData({\n signatureChainId: targetChainId,\n wallet,\n orderId: swap.id,\n nonce,\n });\n }\n\n const authorizeSignature = await evmSigner.signTypedData(\n { typedData: authorizeTypedData, address: wallet, chainId: targetChainId },\n authorizeStep,\n );\n\n try {\n if (isV2 && swap.authorize) {\n await markrPostAuthorize(apiOptions, {\n uuid: quote.id,\n signature: authorizeSignature,\n // Forward Relay's `/authorize` body verbatim — Markr only normalizes\n // `nonce` to a JSON number and defaults `referrer`. Additive Relay\n // fields (e.g. `depositor`) flow through automatically.\n body: swap.authorize.body,\n });\n } else {\n await markrPostAuthorize(apiOptions, {\n uuid: quote.id,\n signature: authorizeSignature,\n wallet,\n walletChainId: 1337,\n signatureChainId: targetChainId,\n /**\n * Relay's upstream `/authorize` validates `nonce` strictly as a JSON\n * number. Sending it as a string makes the request fail upstream. HL\n * nonces are millisecond timestamps, comfortably under\n * `Number.MAX_SAFE_INTEGER`, so the Number coercion is lossless.\n */\n nonce: Number(nonce),\n // Order id (not the tracking requestId).\n id: swap.id,\n });\n }\n } catch (error) {\n // Surface as a failed transfer so the UI can prompt a retry rather than\n // crashing the panel. The user paid no on-chain cost yet.\n if (error instanceof HttpError) {\n return makeFailedTransferFromQuote(quote, {\n environment,\n errorCode: ErrorCode.HL_EXCHANGE_REJECTED,\n errorReason: `Markr /authorize failed (HTTP ${error.status}). Please retry.`,\n });\n }\n throw error;\n }\n\n // 2. PRE-FLIGHT — re-check withdrawable. Guards the second wallet prompt\n // from being asked for a balance the user no longer has (e.g. they opened\n // a position between the two signatures on another device). Relay's\n // pre-formatted `amount` is at the spot token's display precision, so it\n // parses cleanly via `Number(...)` — no need to re-format from the bigint.\n const requestedAmount = Number(swap.parameters.amount);\n const liveWithdrawable = await hyperliquidGetWithdrawable({\n apiBaseUrl: hyperliquidApiBaseUrlForEnvironment(environment),\n user: wallet,\n });\n\n if (Number.isFinite(liveWithdrawable) && liveWithdrawable < requestedAmount) {\n return makeFailedTransferFromQuote(quote, {\n environment,\n errorCode: ErrorCode.HL_WITHDRAWABLE_DROPPED,\n errorReason: `Withdrawable balance changed during withdrawal (requested ${requestedAmount}, available ${liveWithdrawable}). Please retry with a smaller amount.`,\n });\n }\n\n // 3. SEND ASSET — sign HL transaction locally, POST to api.hyperliquid.xyz/exchange.\n // We sign and submit Relay's `parameters` *verbatim* — Relay's order matcher\n // recovers the on-chain `sendAsset` by hashing these exact strings, so any\n // local re-formatting (especially of `amount`) would orphan the transfer.\n const sendAssetStep: TransferStepDetails = {\n currentSignature: 2,\n currentSignatureReason: TransferSignatureReason.HyperliquidSendAsset,\n quote,\n requiredSignatures: 2,\n };\n onStepChange?.(sendAssetStep);\n\n const sendAssetParams = {\n signatureChainId: targetChainId,\n hyperliquidChain: swap.parameters.hyperliquidChain,\n destination: swap.parameters.destination,\n token: swap.parameters.token,\n amount: swap.parameters.amount,\n sourceDex: swap.parameters.sourceDex,\n destinationDex: swap.parameters.destinationDex,\n fromSubAccount: swap.parameters.fromSubAccount,\n nonce,\n } satisfies BuildHyperliquidSendAssetParams;\n\n const sendAssetTypedData = buildHyperliquidSendAssetTypedData(sendAssetParams);\n const sendAssetSignature = await evmSigner.signTypedData(\n { typedData: sendAssetTypedData, address: wallet, chainId: targetChainId },\n sendAssetStep,\n );\n\n const exchangeAction = buildHyperliquidExchangeAction(sendAssetParams);\n\n const exchangeResponse = await hyperliquidExchangePost({\n apiBaseUrl: hyperliquidApiBaseUrlForEnvironment(environment),\n signature: sendAssetSignature,\n nonce,\n action: exchangeAction,\n });\n\n if (exchangeResponse.status !== 'ok') {\n return makeFailedTransferFromQuote(quote, {\n environment,\n errorCode: ErrorCode.HL_EXCHANGE_REJECTED,\n errorReason: extractHlExchangeError(exchangeResponse.response),\n });\n }\n\n const startedAtMs = Date.now();\n\n // HL `/exchange` answering `status: ok` is our source-side finality signal —\n // HL's matcher debits the user atomically with that ack (no separate on-chain\n // tx hash to wait on; HL doesn't expose one for `sendAsset`). Hand the tracker\n // a `source-completed` transfer keyed on Relay's `requestId` so it skips the\n // EVM finality gate and goes straight to Markr `/cross-chain/status` polling.\n return {\n amountIn: quote.amountIn,\n amountOut: quote.amountOut,\n environment,\n fees: quote.fees,\n fromAddress: quote.fromAddress,\n id: quote.id,\n partnerFeeBps: quote.partnerFeeBps,\n sourceAsset: quote.assetIn,\n sourceChain: quote.sourceChain,\n status: 'source-completed',\n targetAsset: quote.assetOut,\n targetChain: quote.targetChain,\n toAddress: quote.toAddress,\n type: quote.serviceType,\n source: {\n confirmationCount: 2,\n requiredConfirmationCount: 2,\n startedAtMs,\n // `requestId` doubles as the SDK's sourceTxHash for status polling.\n txHash: swap.requestId,\n },\n };\n}\n\nfunction extractHlExchangeError(response: unknown): string {\n if (typeof response === 'string') {\n return `Hyperliquid /exchange rejected the transfer: ${response}`;\n }\n\n if (response && typeof response === 'object' && 'error' in response && typeof response.error === 'string') {\n return `Hyperliquid /exchange rejected the transfer: ${response.error}`;\n }\n\n return ErrorReason.UNKNOWN;\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
require(`../../../_virtual/_rolldown/runtime.cjs`);const e=require(`../../../constants.cjs`),t=require(`../../../errors.cjs`),n=require(`../../../utils/caip.cjs`),r=require(`../../_utils.cjs`),i=require(`../../../_utils/chain.cjs`),a=require(`../../_evm-errors.cjs`),o=require(`../_api.cjs`),s=require(`../../../utils/sol-address.cjs`),c=require(`../_utils.cjs`),l=require(`../_type-guards.cjs`),u=require(`../../../utils/solana.cjs`),d=require(`./estimate-native-fee.cjs`);let
|
|
1
|
+
require(`../../../_virtual/_rolldown/runtime.cjs`);const e=require(`../../../constants.cjs`),t=require(`../../../errors.cjs`),n=require(`../../../utils/caip.cjs`),r=require(`../../_utils.cjs`),i=require(`../../../_utils/chain.cjs`),a=require(`../../_evm-errors.cjs`),o=require(`../_api.cjs`),s=require(`../../../utils/sol-address.cjs`),c=require(`../_utils.cjs`),l=require(`../_type-guards.cjs`),u=require(`../../../utils/solana.cjs`),d=require(`./estimate-native-fee.cjs`),f=require(`./transfer-asset-hyperliquid-withdraw.cjs`);let p=require(`viem`);function m({apiOptions:n,appId:r,environment:a,evmSigner:o,solanaSigner:s}){return async({quote:c,gasSettings:l,fallbackToDefaultOnBatchFailure:u,onStepChange:d})=>{if(c.serviceType!==e.ServiceType.MARKR)throw new t.SdkError(t.ErrorReason.INCORRECT_PROVIDER_PROVIDED,t.ErrorCode.INVALID_PARAMS);let f=Math.floor(Date.now()/1e3);if(c.expiresAt<=f)throw new t.SdkError(t.ErrorReason.QUOTE_EXPIRED,t.ErrorCode.INVALID_PARAMS);let p=c.sourceChain.chainId;if(p===e.HyperliquidChainIds.MAINNET)return h({apiOptions:n,appId:r,environment:a,evmSigner:o,quote:c,onStepChange:d});if(i.isSolanaNamespace(p))return _({apiOptions:n,appId:r,environment:a,solanaSigner:s,quote:c,onStepChange:d});if(i.isEvmNamespace(p))return g({apiOptions:n,appId:r,environment:a,evmSigner:o,quote:c,gasSettings:l,fallbackToDefaultOnBatchFailure:u,onStepChange:d});throw new t.SdkError(t.ErrorReason.CHAIN_NOT_SUPPORTED,t.ErrorCode.INVALID_PARAMS,{details:`Unsupported source chain namespace: ${p}`})}}async function h({apiOptions:e,appId:n,environment:r,evmSigner:i,quote:a,onStepChange:s}){let u=c.calculateMarkrMinimumAmountOut({amountOut:a.amountOut,assetOut:a.assetOut,slippageBps:a.slippageBps}),d=c.assetToAddressString(a.assetIn,a.sourceChain.chainId),p=c.assetToAddressString(a.assetOut,a.targetChain.chainId),m=await o.markrSwap(e,{amountIn:a.amountIn.toString(),appId:n,minAmountOut:u.toString(),tokenIn:d,tokenOut:p,uuid:a.id});if(!l.isHyperliquidWithdrawSwapResponse(m))throw new t.SdkError(t.ErrorReason.CHAIN_NOT_SUPPORTED,t.ErrorCode.INVALID_PARAMS,{details:"Expected `hyperliquid-withdraw` swap response from Markr for Hyperliquid source chain, but received a different variant."});return f._executeHyperliquidWithdraw({apiOptions:e,environment:r,evmSigner:i,quote:a,swap:m,onStepChange:s})}async function g({apiOptions:i,appId:s,environment:u,evmSigner:f,quote:m,gasSettings:h,fallbackToDefaultOnBatchFailure:g,onStepChange:_}){let y=m.fromAddress;if(!(0,p.isAddress)(y))throw new t.SdkError(t.ErrorReason.INVALID_PARAMS,t.ErrorCode.INVALID_PARAMS,{details:`fromAddress is not a valid EVM address.`});let b=c.calculateMarkrMinimumAmountOut({amountOut:m.amountOut,assetOut:m.assetOut,slippageBps:m.slippageBps}),x=r.getEvmClientForChain({chain:m.sourceChain}),S=c.assetToAddressString(m.assetIn,m.sourceChain.chainId),C=c.assetToAddressString(m.assetOut,m.targetChain.chainId);if(!(0,p.isAddress)(S))throw new t.SdkError(t.ErrorReason.INVALID_PARAMS,t.ErrorCode.INVALID_PARAMS,{details:`assetIn address is not a valid EVM address.`});let w=S,T=c.isTokenAddressNative(w),E=m.sourceChain.chainId.toLowerCase()!==m.targetChain.chainId.toLowerCase(),D=m.amountIn+c.getAdditiveSourceAssetFeeAmount(m),{address:O}=await o.markrGetSpenderAddress(i,{chainId:n.caip2ToEip155ChainId(m.sourceChain.chainId),crossChainSwap:E,quoteId:m.id});if(!T&&!O)throw new t.SdkError(t.ErrorReason.CHAIN_NOT_SUPPORTED,t.ErrorCode.INVALID_PARAMS,{details:`Missing Markr spender address for source chain ${m.sourceChain.chainId}.`});let k=!1,A;if(!T&&O&&await x.readContract({address:w,abi:p.erc20Abi,functionName:`allowance`,args:[y,O]})<D){k=!0;let e=(0,p.encodeFunctionData)({abi:p.erc20Abi,functionName:`approve`,args:[O,D]}),t=r.applyFeeUnitsBpsMargin(await a.estimateGasWithRevert(x,{account:y,to:w,data:e},p.erc20Abi,`Failed to estimate gas for ERC20 approval transaction.`),h?.estimateGasMarginBps);A={chainId:n.caip2ToEip155HexChainId(m.sourceChain.chainId),data:e,from:y,gas:t,to:w,value:0n,...h?.maxFeePerGas===void 0?null:{maxFeePerGas:h.maxFeePerGas,maxPriorityFeePerGas:h.maxPriorityFeePerGas}}}let j=v({approvalRequest:A,requiresApprovalSignature:k,signBatch:f.signBatch,sourceChainId:m.sourceChain.chainId,targetChainId:m.targetChain.chainId}),M=await o.markrSwap(i,{amountIn:m.amountIn.toString(),appId:s,minAmountOut:b.toString(),tokenIn:S,tokenOut:C,uuid:m.id});if(!l.isEvmSwapResponse(M))throw new t.SdkError(t.ErrorReason.CHAIN_NOT_SUPPORTED,t.ErrorCode.INVALID_PARAMS,{details:`Received non-EVM swap response from Markr, but only EVM swaps are supported for this source chain.`});let N=(t,n=Date.now())=>{let r=m.sourceChain.chainId===m.targetChain.chainId;return{amountIn:m.amountIn,amountOut:m.amountOut,environment:u,fees:m.fees,fromAddress:m.fromAddress,id:m.id,partnerFeeBps:m.partnerFeeBps,sourceAsset:m.assetIn,sourceChain:m.sourceChain,status:`source-pending`,targetAsset:m.assetOut,targetChain:m.targetChain,toAddress:m.toAddress,type:e.ServiceType.MARKR,source:{confirmationCount:0,requiredConfirmationCount:r?1:2,startedAtMs:n,txHash:t}}};if(j&&A){let r={currentSignature:1,currentSignatureReason:e.TransferSignatureReason.TokensTransfer,quote:m,requiredSignatures:1},i=f.signBatch;if(!i)throw new t.SdkError(`One-click batch signer is not available.`,t.ErrorCode.SIGNING_FAILED);let a={chainId:n.caip2ToEip155HexChainId(m.sourceChain.chainId),data:M.data,from:y,gas:void 0,to:M.to,value:M.value,...h?.maxFeePerGas===void 0?null:{maxFeePerGas:h.maxFeePerGas,maxPriorityFeePerGas:h.maxPriorityFeePerGas}};_?.(r);try{let e=(await i([A,a],async e=>x.sendRawTransaction({serializedTransaction:e}),r)).at(-1);if(!e)throw new t.SdkError(`One-click batch signing returned no transaction hashes.`,t.ErrorCode.SIGNING_FAILED);return N(e)}catch(e){if(!g)throw e}}if(k&&A){let n={currentSignature:1,currentSignatureReason:e.TransferSignatureReason.AllowanceApproval,quote:m,requiredSignatures:2};_?.(n);let i=await f.sign(A,async e=>x.sendRawTransaction({serializedTransaction:e}),n);if((await x.waitForTransactionReceipt({hash:i})).status===`reverted`)return r.makeFailedTransferFromQuote(m,{environment:u,errorCode:t.ErrorCode.TRANSACTION_REVERTED,errorReason:`ERC20 approval transaction was reverted`})}let P=await d._estimateGasFromSwapResponse({crossChain:E,fromAddress:y,feeUnitsMarginBps:h?.estimateGasMarginBps,sourceClient:x,swap:M}),F={chainId:n.caip2ToEip155HexChainId(m.sourceChain.chainId),data:M.data,from:y,gas:P,to:M.to,value:M.value,...h?.maxFeePerGas===void 0?null:{maxFeePerGas:h.maxFeePerGas,maxPriorityFeePerGas:h.maxPriorityFeePerGas}},I={currentSignature:k?2:1,currentSignatureReason:e.TransferSignatureReason.TokensTransfer,quote:m,requiredSignatures:k?2:1};_?.(I);let L=Date.now();return N(await f.sign(F,async e=>x.sendRawTransaction({serializedTransaction:e}),I),L)}async function _({apiOptions:n,appId:i,environment:a,solanaSigner:d,quote:f,onStepChange:p}){if(!d)throw new t.SdkError(t.ErrorReason.INVALID_PARAMS,t.ErrorCode.INVALID_PARAMS,{details:`solanaSigner is required for Solana transfers but was not provided.`});if(!s.isSolAddress(f.fromAddress))throw new t.SdkError(t.ErrorReason.INVALID_PARAMS,t.ErrorCode.INVALID_PARAMS,{details:`fromAddress is not a valid Solana address.`});let m=c.calculateMarkrMinimumAmountOut({amountOut:f.amountOut,assetOut:f.assetOut,slippageBps:f.slippageBps}),h=c.assetToAddressString(f.assetIn,f.sourceChain.chainId),g=c.assetToAddressString(f.assetOut,f.targetChain.chainId),_=await o.markrSwap(n,{amountIn:f.amountIn.toString(),appId:i,minAmountOut:m.toString(),tokenIn:h,tokenOut:g,userPublicKey:f.fromAddress,uuid:f.id});if(!l.isSolanaSwapResponse(_))throw new t.SdkError(t.ErrorReason.CHAIN_NOT_SUPPORTED,t.ErrorCode.INVALID_PARAMS,{details:`Received non-SVM swap response from Markr, but only SVM swaps are supported for this source chain.`});console.debug(`[Fusion SDK] Original swap transaction (base64):`,_.swapTransaction);let v=await u.refreshSolanaSwapTransactionBlockhash(_.swapTransaction,r.getSolanaRpcForChain({chain:f.sourceChain}));console.debug(`[Fusion SDK] Refreshed swap transaction with new blockhash (base64):`,v);let y={currentSignature:1,currentSignatureReason:e.TransferSignatureReason.TokensTransfer,quote:f,requiredSignatures:1};p?.(y);let b=Date.now(),x=await d.signAndSend({account:f.fromAddress,serializedTx:v},y),S=f.sourceChain.chainId.toLowerCase()!==f.targetChain.chainId.toLowerCase();return{amountIn:f.amountIn,amountOut:f.amountOut,environment:a,fees:f.fees,fromAddress:f.fromAddress,id:f.id,partnerFeeBps:f.partnerFeeBps,sourceAsset:f.assetIn,sourceChain:f.sourceChain,status:`source-pending`,targetAsset:f.assetOut,targetChain:f.targetChain,toAddress:f.toAddress,type:e.ServiceType.MARKR,source:{confirmationCount:0,requiredConfirmationCount:S?2:32,startedAtMs:b,txHash:x}}}function v({approvalRequest:e,requiresApprovalSignature:t,signBatch:n,sourceChainId:r,targetChainId:a}){return i.isEvmNamespace(r)&&i.isEvmNamespace(a)&&r===a&&t&&e!==void 0&&typeof n==`function`}exports.transferAssetFactory=m;
|
|
2
2
|
//# sourceMappingURL=transfer-asset.cjs.map
|