@avalabs/fusion-sdk 0.16.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 +34 -2
- package/dist/constants.d.ts +34 -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 +8 -6
- package/dist/mod.d.ts +8 -6
- package/dist/mod.js +1 -1
- package/dist/transfer-manager.cjs +1 -1
- package/dist/transfer-manager.cjs.map +1 -1
- package/dist/transfer-manager.js +1 -1
- package/dist/transfer-manager.js.map +1 -1
- package/dist/transfer-service/_evm-gas.cjs +1 -1
- package/dist/transfer-service/_evm-gas.js +1 -1
- package/dist/transfer-service/_utils.cjs +1 -1
- package/dist/transfer-service/_utils.cjs.map +1 -1
- package/dist/transfer-service/_utils.js +1 -1
- package/dist/transfer-service/_utils.js.map +1 -1
- package/dist/transfer-service/avalanche-cct/_handlers/analyze-support.cjs +2 -0
- package/dist/transfer-service/avalanche-cct/_handlers/analyze-support.cjs.map +1 -0
- package/dist/transfer-service/avalanche-cct/_handlers/analyze-support.js +2 -0
- package/dist/transfer-service/avalanche-cct/_handlers/analyze-support.js.map +1 -0
- package/dist/transfer-service/avalanche-cct/_handlers/estimate-native-fee.cjs +2 -0
- package/dist/transfer-service/avalanche-cct/_handlers/estimate-native-fee.cjs.map +1 -0
- package/dist/transfer-service/avalanche-cct/_handlers/estimate-native-fee.js +2 -0
- package/dist/transfer-service/avalanche-cct/_handlers/estimate-native-fee.js.map +1 -0
- package/dist/transfer-service/avalanche-cct/_handlers/get-bridgeable-assets.cjs +2 -0
- package/dist/transfer-service/avalanche-cct/_handlers/get-bridgeable-assets.cjs.map +1 -0
- package/dist/transfer-service/avalanche-cct/_handlers/get-bridgeable-assets.js +2 -0
- package/dist/transfer-service/avalanche-cct/_handlers/get-bridgeable-assets.js.map +1 -0
- package/dist/transfer-service/avalanche-cct/_handlers/get-minimum-transfer-amount.cjs +2 -0
- package/dist/transfer-service/avalanche-cct/_handlers/get-minimum-transfer-amount.cjs.map +1 -0
- package/dist/transfer-service/avalanche-cct/_handlers/get-minimum-transfer-amount.js +2 -0
- package/dist/transfer-service/avalanche-cct/_handlers/get-minimum-transfer-amount.js.map +1 -0
- package/dist/transfer-service/avalanche-cct/_handlers/get-supported-chains.cjs +2 -0
- package/dist/transfer-service/avalanche-cct/_handlers/get-supported-chains.cjs.map +1 -0
- package/dist/transfer-service/avalanche-cct/_handlers/get-supported-chains.js +2 -0
- package/dist/transfer-service/avalanche-cct/_handlers/get-supported-chains.js.map +1 -0
- package/dist/transfer-service/avalanche-cct/_handlers/stream-quotes.cjs +2 -0
- package/dist/transfer-service/avalanche-cct/_handlers/stream-quotes.cjs.map +1 -0
- package/dist/transfer-service/avalanche-cct/_handlers/stream-quotes.js +2 -0
- package/dist/transfer-service/avalanche-cct/_handlers/stream-quotes.js.map +1 -0
- package/dist/transfer-service/avalanche-cct/_handlers/track-transfer.cjs +2 -0
- package/dist/transfer-service/avalanche-cct/_handlers/track-transfer.cjs.map +1 -0
- package/dist/transfer-service/avalanche-cct/_handlers/track-transfer.js +2 -0
- package/dist/transfer-service/avalanche-cct/_handlers/track-transfer.js.map +1 -0
- package/dist/transfer-service/avalanche-cct/_handlers/transfer-asset.cjs +2 -0
- package/dist/transfer-service/avalanche-cct/_handlers/transfer-asset.cjs.map +1 -0
- package/dist/transfer-service/avalanche-cct/_handlers/transfer-asset.js +2 -0
- package/dist/transfer-service/avalanche-cct/_handlers/transfer-asset.js.map +1 -0
- package/dist/transfer-service/avalanche-cct/_type-guards.cjs +2 -0
- package/dist/transfer-service/avalanche-cct/_type-guards.cjs.map +1 -0
- package/dist/transfer-service/avalanche-cct/_type-guards.js +2 -0
- package/dist/transfer-service/avalanche-cct/_type-guards.js.map +1 -0
- package/dist/transfer-service/avalanche-cct/_utils/addresses.cjs +2 -0
- package/dist/transfer-service/avalanche-cct/_utils/addresses.cjs.map +1 -0
- package/dist/transfer-service/avalanche-cct/_utils/addresses.js +2 -0
- package/dist/transfer-service/avalanche-cct/_utils/addresses.js.map +1 -0
- package/dist/transfer-service/avalanche-cct/_utils/fees.cjs +2 -0
- package/dist/transfer-service/avalanche-cct/_utils/fees.cjs.map +1 -0
- package/dist/transfer-service/avalanche-cct/_utils/fees.js +2 -0
- package/dist/transfer-service/avalanche-cct/_utils/fees.js.map +1 -0
- package/dist/transfer-service/avalanche-cct/_utils/p-chain.cjs +2 -0
- package/dist/transfer-service/avalanche-cct/_utils/p-chain.cjs.map +1 -0
- package/dist/transfer-service/avalanche-cct/_utils/p-chain.js +2 -0
- package/dist/transfer-service/avalanche-cct/_utils/p-chain.js.map +1 -0
- package/dist/transfer-service/avalanche-cct/_utils/polling.cjs +2 -0
- package/dist/transfer-service/avalanche-cct/_utils/polling.cjs.map +1 -0
- package/dist/transfer-service/avalanche-cct/_utils/polling.js +2 -0
- package/dist/transfer-service/avalanche-cct/_utils/polling.js.map +1 -0
- package/dist/transfer-service/avalanche-cct/_utils/transactions.cjs +2 -0
- package/dist/transfer-service/avalanche-cct/_utils/transactions.cjs.map +1 -0
- package/dist/transfer-service/avalanche-cct/_utils/transactions.js +2 -0
- package/dist/transfer-service/avalanche-cct/_utils/transactions.js.map +1 -0
- package/dist/transfer-service/avalanche-cct/_utils.cjs +2 -0
- package/dist/transfer-service/avalanche-cct/_utils.cjs.map +1 -0
- package/dist/transfer-service/avalanche-cct/_utils.js +2 -0
- package/dist/transfer-service/avalanche-cct/_utils.js.map +1 -0
- package/dist/transfer-service/avalanche-cct/avalanche-cct-service.cjs +2 -0
- package/dist/transfer-service/avalanche-cct/avalanche-cct-service.cjs.map +1 -0
- package/dist/transfer-service/avalanche-cct/avalanche-cct-service.js +2 -0
- package/dist/transfer-service/avalanche-cct/avalanche-cct-service.js.map +1 -0
- package/dist/transfer-service/avalanche-cct/constants.cjs +2 -0
- package/dist/transfer-service/avalanche-cct/constants.cjs.map +1 -0
- package/dist/transfer-service/avalanche-cct/constants.js +2 -0
- package/dist/transfer-service/avalanche-cct/constants.js.map +1 -0
- package/dist/transfer-service/avalanche-cct/types.d.cts +33 -0
- package/dist/transfer-service/avalanche-cct/types.d.ts +33 -0
- package/dist/transfer-service/avalanche-evm/_handlers/get-bridgeable-assets.cjs +1 -1
- package/dist/transfer-service/avalanche-evm/_handlers/get-bridgeable-assets.cjs.map +1 -1
- package/dist/transfer-service/avalanche-evm/_handlers/get-bridgeable-assets.js +1 -1
- package/dist/transfer-service/avalanche-evm/_handlers/get-bridgeable-assets.js.map +1 -1
- package/dist/transfer-service/avalanche-evm/_handlers/transfer-asset.cjs +1 -1
- package/dist/transfer-service/avalanche-evm/_handlers/transfer-asset.js +1 -1
- package/dist/transfer-service/avalanche-evm/_utils/wrap.cjs +1 -1
- package/dist/transfer-service/avalanche-evm/_utils/wrap.js +1 -1
- package/dist/transfer-service/fetch-utilities.cjs +1 -1
- package/dist/transfer-service/fetch-utilities.cjs.map +1 -1
- package/dist/transfer-service/fetch-utilities.js +1 -1
- package/dist/transfer-service/fetch-utilities.js.map +1 -1
- package/dist/transfer-service/lombard/btc-to-btcb/_handlers/get-bridgeable-assets.cjs +1 -1
- package/dist/transfer-service/lombard/btc-to-btcb/_handlers/get-bridgeable-assets.cjs.map +1 -1
- package/dist/transfer-service/lombard/btc-to-btcb/_handlers/get-bridgeable-assets.js +1 -1
- package/dist/transfer-service/lombard/btc-to-btcb/_handlers/get-bridgeable-assets.js.map +1 -1
- package/dist/transfer-service/lombard/btcb-to-btc/_handlers/get-bridgeable-assets.cjs +1 -1
- package/dist/transfer-service/lombard/btcb-to-btc/_handlers/get-bridgeable-assets.cjs.map +1 -1
- package/dist/transfer-service/lombard/btcb-to-btc/_handlers/get-bridgeable-assets.js +1 -1
- package/dist/transfer-service/lombard/btcb-to-btc/_handlers/get-bridgeable-assets.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/get-bridgeable-assets.cjs +1 -1
- package/dist/transfer-service/markr/_handlers/get-bridgeable-assets.cjs.map +1 -1
- package/dist/transfer-service/markr/_handlers/get-bridgeable-assets.js +1 -1
- package/dist/transfer-service/markr/_handlers/get-bridgeable-assets.js.map +1 -1
- package/dist/transfer-service/markr/_handlers/track-transfer.cjs +1 -1
- package/dist/transfer-service/markr/_handlers/track-transfer.js +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/transfer-service/wrap-unwrap/_handlers/get-bridgeable-assets.cjs +1 -1
- package/dist/transfer-service/wrap-unwrap/_handlers/get-bridgeable-assets.cjs.map +1 -1
- package/dist/transfer-service/wrap-unwrap/_handlers/get-bridgeable-assets.js +1 -1
- package/dist/transfer-service/wrap-unwrap/_handlers/get-bridgeable-assets.js.map +1 -1
- package/dist/transfer-service/wrap-unwrap/_handlers/transfer-asset.cjs +1 -1
- package/dist/transfer-service/wrap-unwrap/_handlers/transfer-asset.js +1 -1
- package/dist/types/asset.d.cts +18 -2
- package/dist/types/asset.d.ts +18 -2
- package/dist/types/service.d.cts +106 -7
- package/dist/types/service.d.ts +106 -7
- package/dist/types/signer.d.cts +37 -1
- package/dist/types/signer.d.ts +37 -1
- package/dist/types/transfer-manager.d.cts +19 -10
- package/dist/types/transfer-manager.d.ts +19 -10
- package/dist/types/utility-types.d.cts +8 -1
- package/dist/types/utility-types.d.ts +8 -1
- package/dist/utils/asset-id.cjs +1 -1
- package/dist/utils/asset-id.cjs.map +1 -1
- package/dist/utils/asset-id.js +1 -1
- package/dist/utils/asset-id.js.map +1 -1
- package/dist/utils/bridgeable-assets.cjs +2 -0
- package/dist/utils/bridgeable-assets.cjs.map +1 -0
- package/dist/utils/bridgeable-assets.d.cts +16 -0
- package/dist/utils/bridgeable-assets.d.ts +16 -0
- package/dist/utils/bridgeable-assets.js +2 -0
- package/dist/utils/bridgeable-assets.js.map +1 -0
- package/package.json +5 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transfer-asset.cjs","names":["ServiceType","SdkError","ErrorReason","ErrorCode","isSolanaNamespace","isEvmNamespace","calculateMarkrMinimumAmountOut","getEvmClientForChain","assetToAddressString","isTokenAddressNative","getAdditiveSourceAssetFeeAmount","markrGetSpenderAddress","caip2ToEip155ChainId","erc20Abi","applyFeeUnitsBpsMargin","estimateGasWithRevert","caip2ToEip155HexChainId","markrSwap","isEvmSwapResponse","TransferSignatureReason","makeFailedTransferFromQuote","_estimateGasFromSwapResponse","isSolAddress","isSolanaSwapResponse","refreshSolanaSwapTransactionBlockhash","getSolanaRpcForChain"],"sources":["../../../../src/transfer-service/markr/_handlers/transfer-asset.ts"],"sourcesContent":["import { encodeFunctionData, erc20Abi, isAddress } from 'viem';\nimport { isEvmNamespace, isSolanaNamespace } from '../../../_utils/chain';\nimport { ServiceType, TransferSignatureReason } from '../../../constants';\nimport type { Environment } from '../../../constants';\nimport { ErrorCode, ErrorReason, SdkError } from '../../../errors';\nimport type { TransferService } from '../../../types/service';\nimport type { EvmSigner, EvmTransactionRequest, SolanaSigner } from '../../../types/signer';\nimport type { Transfer, TransferStepDetails } from '../../../types/transfer';\nimport { caip2ToEip155ChainId, caip2ToEip155HexChainId } from '../../../utils/caip';\nimport { isSolAddress } from '../../../utils/sol-address';\nimport {\n applyFeeUnitsBpsMargin,\n getEvmClientForChain,\n getSolanaRpcForChain,\n makeFailedTransferFromQuote,\n} from '../../_utils';\nimport { estimateGasWithRevert } from '../../_evm-errors';\nimport { markrGetSpenderAddress, markrSwap, type ApiOptions } from '../_api';\nimport {\n assetToAddressString,\n calculateMarkrMinimumAmountOut,\n getAdditiveSourceAssetFeeAmount,\n isTokenAddressNative,\n} from '../_utils';\nimport { isEvmSwapResponse, isSolanaSwapResponse } from '../_type-guards';\nimport { SOLANA_REQUIRED_CONFIRMATIONS } from '../constants';\nimport { _estimateGasFromSwapResponse } from './estimate-native-fee';\nimport type { Caip2ChainId } from '../../../types/caip';\nimport { refreshSolanaSwapTransactionBlockhash } from '../../../utils/solana';\n\nexport interface TransferAssetFactoryConfig {\n apiOptions: ApiOptions;\n appId: string;\n environment: Environment;\n evmSigner: EvmSigner;\n solanaSigner?: SolanaSigner;\n}\n\nexport function transferAssetFactory({\n apiOptions,\n appId,\n environment,\n evmSigner,\n solanaSigner,\n}: TransferAssetFactoryConfig): TransferService['transferAsset'] {\n return async ({ quote, gasSettings, fallbackToDefaultOnBatchFailure, onStepChange }) => {\n if (quote.serviceType !== ServiceType.MARKR) {\n throw new SdkError(ErrorReason.INCORRECT_PROVIDER_PROVIDED, ErrorCode.INVALID_PARAMS);\n }\n\n const now = Math.floor(Date.now() / 1_000);\n\n if (quote.expiresAt <= now) {\n throw new SdkError(ErrorReason.QUOTE_EXPIRED, ErrorCode.INVALID_PARAMS);\n }\n\n const sourceChainId = quote.sourceChain.chainId;\n\n if (isSolanaNamespace(sourceChainId)) {\n return _executeSvmTransfer({ apiOptions, appId, environment, solanaSigner, quote, onStepChange });\n }\n\n if (isEvmNamespace(sourceChainId)) {\n return _executeEvmTransfer({\n apiOptions,\n appId,\n environment,\n evmSigner,\n quote,\n gasSettings,\n fallbackToDefaultOnBatchFailure,\n onStepChange,\n });\n }\n\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: `Unsupported source chain namespace: ${sourceChainId}`,\n });\n };\n}\n\n// ---------------------------------------------------------------------------\n// EVM transfer path\n// ---------------------------------------------------------------------------\n\ninterface EvmTransferParams {\n apiOptions: ApiOptions;\n appId: string;\n environment: Environment;\n evmSigner: EvmSigner;\n quote: Parameters<TransferService['transferAsset']>[0]['quote'];\n gasSettings?: Parameters<TransferService['transferAsset']>[0]['gasSettings'];\n fallbackToDefaultOnBatchFailure?: Parameters<TransferService['transferAsset']>[0]['fallbackToDefaultOnBatchFailure'];\n onStepChange?: Parameters<TransferService['transferAsset']>[0]['onStepChange'];\n}\n\nasync function _executeEvmTransfer({\n apiOptions,\n appId,\n environment,\n evmSigner,\n quote,\n gasSettings,\n fallbackToDefaultOnBatchFailure,\n onStepChange,\n}: EvmTransferParams): Promise<Transfer> {\n const fromAddress = quote.fromAddress;\n\n if (!isAddress(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 minAmountOut = calculateMarkrMinimumAmountOut({\n amountOut: quote.amountOut,\n assetOut: quote.assetOut,\n slippageBps: quote.slippageBps,\n });\n\n const sourceClient = getEvmClientForChain({ chain: quote.sourceChain });\n\n const tokenInAddressString = assetToAddressString(quote.assetIn, quote.sourceChain.chainId);\n const tokenOutAddressString = assetToAddressString(quote.assetOut, quote.targetChain.chainId);\n\n if (!isAddress(tokenInAddressString)) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: 'assetIn address is not a valid EVM address.',\n });\n }\n\n const tokenInAddress = tokenInAddressString;\n const isTokenInNative = isTokenAddressNative(tokenInAddress);\n const isCrossChainSwap = quote.sourceChain.chainId.toLowerCase() !== quote.targetChain.chainId.toLowerCase();\n const allowanceAmount = quote.amountIn + getAdditiveSourceAssetFeeAmount(quote);\n\n const { address: spenderAddress } = await markrGetSpenderAddress(apiOptions, {\n chainId: caip2ToEip155ChainId(quote.sourceChain.chainId),\n crossChainSwap: isCrossChainSwap,\n quoteId: quote.id,\n });\n\n if (!isTokenInNative && !spenderAddress) {\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: `Missing Markr spender address for source chain ${quote.sourceChain.chainId}.`,\n });\n }\n\n let requiresApprovalSignature = false;\n let approvalRequest: EvmTransactionRequest | undefined;\n\n if (!isTokenInNative && spenderAddress) {\n const allowance = await sourceClient.readContract({\n address: tokenInAddress,\n abi: erc20Abi,\n functionName: 'allowance',\n args: [fromAddress, spenderAddress],\n });\n\n if (allowance < allowanceAmount) {\n requiresApprovalSignature = true;\n\n const approvalData = encodeFunctionData({\n abi: erc20Abi,\n functionName: 'approve',\n args: [spenderAddress, allowanceAmount],\n });\n\n const approvalGasEstimate = await estimateGasWithRevert(\n sourceClient,\n {\n account: fromAddress,\n to: tokenInAddress,\n data: approvalData,\n },\n erc20Abi,\n 'Failed to estimate gas for ERC20 approval transaction.',\n );\n\n const approvalGasWithMargin = applyFeeUnitsBpsMargin(approvalGasEstimate, gasSettings?.estimateGasMarginBps);\n\n approvalRequest = {\n chainId: caip2ToEip155HexChainId(quote.sourceChain.chainId),\n data: approvalData,\n from: fromAddress,\n gas: approvalGasWithMargin,\n to: tokenInAddress,\n value: 0n,\n ...(gasSettings?.maxFeePerGas !== undefined\n ? {\n maxFeePerGas: gasSettings.maxFeePerGas,\n maxPriorityFeePerGas: gasSettings.maxPriorityFeePerGas,\n }\n : null),\n };\n }\n }\n\n const maybeOneClickBatch = isOneClickBatchEligible({\n approvalRequest,\n requiresApprovalSignature,\n signBatch: evmSigner.signBatch,\n sourceChainId: quote.sourceChain.chainId,\n targetChainId: quote.targetChain.chainId,\n });\n\n const swap = await markrSwap(apiOptions, {\n amountIn: quote.amountIn.toString(),\n appId,\n minAmountOut: minAmountOut.toString(),\n tokenIn: tokenInAddressString,\n tokenOut: tokenOutAddressString,\n uuid: quote.id,\n });\n\n if (!isEvmSwapResponse(swap)) {\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: 'Received non-EVM swap response from Markr, but only EVM swaps are supported for this source chain.',\n });\n }\n\n const makePendingTransfer = (txHash: `0x${string}`, startedAtMs: number = Date.now()): Transfer => {\n const isSameChainTransfer = quote.sourceChain.chainId === quote.targetChain.chainId;\n\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-pending',\n targetAsset: quote.assetOut,\n targetChain: quote.targetChain,\n toAddress: quote.toAddress,\n type: ServiceType.MARKR,\n source: {\n confirmationCount: 0,\n requiredConfirmationCount: isSameChainTransfer ? 1 : 2,\n startedAtMs,\n txHash,\n },\n };\n };\n\n if (maybeOneClickBatch && approvalRequest) {\n const batchStep: TransferStepDetails = {\n currentSignature: 1,\n currentSignatureReason: TransferSignatureReason.TokensTransfer,\n quote,\n requiredSignatures: 1,\n };\n\n const signBatch = evmSigner.signBatch;\n\n if (!signBatch) {\n throw new SdkError('One-click batch signer is not available.', ErrorCode.SIGNING_FAILED);\n }\n\n const swapRequest: EvmTransactionRequest = {\n chainId: caip2ToEip155HexChainId(quote.sourceChain.chainId),\n data: swap.data,\n from: fromAddress,\n // Gas is purposely left undefined here.\n // We can't estimate the gas because we aren't executing the\n // approval tx. It's up to the wallet to correctly handle gas estimation.\n gas: undefined,\n to: swap.to,\n value: swap.value,\n ...(gasSettings?.maxFeePerGas !== undefined\n ? {\n maxFeePerGas: gasSettings.maxFeePerGas,\n maxPriorityFeePerGas: gasSettings.maxPriorityFeePerGas,\n }\n : null),\n };\n\n onStepChange?.(batchStep);\n\n try {\n const txHashes = await signBatch(\n [approvalRequest, swapRequest],\n async (signedTxHash) => sourceClient.sendRawTransaction({ serializedTransaction: signedTxHash }),\n batchStep,\n );\n\n const swapTxHash = txHashes.at(-1);\n\n if (!swapTxHash) {\n throw new SdkError('One-click batch signing returned no transaction hashes.', ErrorCode.SIGNING_FAILED);\n }\n\n return makePendingTransfer(swapTxHash);\n } catch (error) {\n if (!fallbackToDefaultOnBatchFailure) {\n throw error;\n }\n }\n }\n\n if (requiresApprovalSignature && approvalRequest) {\n const approvalStep: TransferStepDetails = {\n currentSignature: 1,\n currentSignatureReason: TransferSignatureReason.AllowanceApproval,\n quote,\n requiredSignatures: 2,\n };\n\n onStepChange?.(approvalStep);\n\n const approvalTxHash = await evmSigner.sign(\n approvalRequest,\n async (signedTxHash) => sourceClient.sendRawTransaction({ serializedTransaction: signedTxHash }),\n approvalStep,\n );\n\n const approvalReceipt = await sourceClient.waitForTransactionReceipt({ hash: approvalTxHash });\n\n if (approvalReceipt.status === 'reverted') {\n return makeFailedTransferFromQuote(quote, {\n environment,\n errorCode: ErrorCode.TRANSACTION_REVERTED,\n errorReason: 'ERC20 approval transaction was reverted',\n });\n }\n }\n\n const swapGasWithMargin = await _estimateGasFromSwapResponse({\n crossChain: isCrossChainSwap,\n fromAddress,\n feeUnitsMarginBps: gasSettings?.estimateGasMarginBps,\n sourceClient,\n swap,\n });\n\n const swapRequest: EvmTransactionRequest = {\n chainId: caip2ToEip155HexChainId(quote.sourceChain.chainId),\n data: swap.data,\n from: fromAddress,\n gas: swapGasWithMargin,\n to: swap.to,\n value: swap.value,\n ...(gasSettings?.maxFeePerGas !== undefined\n ? {\n maxFeePerGas: gasSettings.maxFeePerGas,\n maxPriorityFeePerGas: gasSettings.maxPriorityFeePerGas,\n }\n : null),\n };\n\n const step: TransferStepDetails = {\n currentSignature: requiresApprovalSignature ? 2 : 1,\n currentSignatureReason: TransferSignatureReason.TokensTransfer,\n quote,\n requiredSignatures: requiresApprovalSignature ? 2 : 1,\n };\n\n onStepChange?.(step);\n\n const startedAtMs = Date.now();\n\n const txHash = await evmSigner.sign(\n swapRequest,\n async (signedTxHash) => sourceClient.sendRawTransaction({ serializedTransaction: signedTxHash }),\n step,\n );\n\n return makePendingTransfer(txHash, startedAtMs);\n}\n\n// ---------------------------------------------------------------------------\n// SVM (Solana) transfer path\n// ---------------------------------------------------------------------------\n\ninterface SvmTransferParams {\n apiOptions: ApiOptions;\n appId: string;\n environment: Environment;\n solanaSigner?: SolanaSigner;\n quote: Parameters<TransferService['transferAsset']>[0]['quote'];\n onStepChange?: Parameters<TransferService['transferAsset']>[0]['onStepChange'];\n}\n\nasync function _executeSvmTransfer({\n apiOptions,\n appId,\n environment,\n solanaSigner,\n quote,\n onStepChange,\n}: SvmTransferParams): Promise<Transfer> {\n if (!solanaSigner) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: 'solanaSigner is required for Solana transfers but was not provided.',\n });\n }\n\n if (!isSolAddress(quote.fromAddress)) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: 'fromAddress is not a valid Solana address.',\n });\n }\n\n const minAmountOut = calculateMarkrMinimumAmountOut({\n amountOut: quote.amountOut,\n assetOut: quote.assetOut,\n slippageBps: quote.slippageBps,\n });\n\n const tokenInAddressString = assetToAddressString(quote.assetIn, quote.sourceChain.chainId);\n const tokenOutAddressString = assetToAddressString(quote.assetOut, quote.targetChain.chainId);\n\n const swap = await markrSwap(apiOptions, {\n amountIn: quote.amountIn.toString(),\n appId,\n minAmountOut: minAmountOut.toString(),\n tokenIn: tokenInAddressString,\n tokenOut: tokenOutAddressString,\n userPublicKey: quote.fromAddress,\n uuid: quote.id,\n });\n\n if (!isSolanaSwapResponse(swap)) {\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: 'Received non-SVM swap response from Markr, but only SVM swaps are supported for this source chain.',\n });\n }\n\n // TODO: Cleanup\n console.debug('[Fusion SDK] Original swap transaction (base64):', swap.swapTransaction);\n\n const swapTransactionBase64 = await refreshSolanaSwapTransactionBlockhash(\n swap.swapTransaction,\n getSolanaRpcForChain({ chain: quote.sourceChain }),\n );\n\n // TODO: Cleanup\n console.debug('[Fusion SDK] Refreshed swap transaction with new blockhash (base64):', swapTransactionBase64);\n\n const step: TransferStepDetails = {\n currentSignature: 1,\n currentSignatureReason: TransferSignatureReason.TokensTransfer,\n quote,\n requiredSignatures: 1,\n };\n\n onStepChange?.(step);\n\n const startedAtMs = Date.now();\n\n const txHash = await solanaSigner.signAndSend(\n {\n account: quote.fromAddress,\n serializedTx: swapTransactionBase64,\n },\n step,\n );\n\n const isCrossChainSwap = quote.sourceChain.chainId.toLowerCase() !== quote.targetChain.chainId.toLowerCase();\n\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-pending',\n targetAsset: quote.assetOut,\n targetChain: quote.targetChain,\n toAddress: quote.toAddress,\n type: ServiceType.MARKR,\n source: {\n confirmationCount: 0,\n requiredConfirmationCount: isCrossChainSwap ? 2 : SOLANA_REQUIRED_CONFIRMATIONS,\n startedAtMs,\n txHash,\n },\n };\n}\n\n/**\n * Determines whether one-click swap eligibility criteria is met.\n *\n * Only same-chain EVM swaps are currently eligible.\n */\nfunction isOneClickBatchEligible({\n approvalRequest,\n requiresApprovalSignature,\n signBatch,\n sourceChainId,\n targetChainId,\n}: {\n approvalRequest?: EvmTransactionRequest;\n requiresApprovalSignature: boolean;\n signBatch?: EvmSigner['signBatch'];\n sourceChainId: Caip2ChainId;\n targetChainId: Caip2ChainId;\n}): boolean {\n return (\n isEvmNamespace(sourceChainId) &&\n isEvmNamespace(targetChainId) &&\n sourceChainId === targetChainId &&\n requiresApprovalSignature &&\n approvalRequest !== undefined &&\n typeof signBatch === 'function'\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"transfer-asset.cjs","names":["ServiceType","SdkError","ErrorReason","ErrorCode","HyperliquidChainIds","isSolanaNamespace","isEvmNamespace","calculateMarkrMinimumAmountOut","assetToAddressString","markrSwap","isHyperliquidWithdrawSwapResponse","_executeHyperliquidWithdraw","getEvmClientForChain","isTokenAddressNative","getAdditiveSourceAssetFeeAmount","markrGetSpenderAddress","caip2ToEip155ChainId","erc20Abi","applyFeeUnitsBpsMargin","estimateGasWithRevert","caip2ToEip155HexChainId","isEvmSwapResponse","TransferSignatureReason","makeFailedTransferFromQuote","_estimateGasFromSwapResponse","isSolAddress","isSolanaSwapResponse","refreshSolanaSwapTransactionBlockhash","getSolanaRpcForChain"],"sources":["../../../../src/transfer-service/markr/_handlers/transfer-asset.ts"],"sourcesContent":["import { encodeFunctionData, erc20Abi, isAddress } from 'viem';\nimport { isEvmNamespace, isSolanaNamespace } from '../../../_utils/chain';\nimport { HyperliquidChainIds, ServiceType, TransferSignatureReason } from '../../../constants';\nimport type { Environment } from '../../../constants';\nimport { ErrorCode, ErrorReason, SdkError } from '../../../errors';\nimport type { TransferService } from '../../../types/service';\nimport type { EvmSigner, EvmTransactionRequest, SolanaSigner } from '../../../types/signer';\nimport type { Transfer, TransferStepDetails } from '../../../types/transfer';\nimport { caip2ToEip155ChainId, caip2ToEip155HexChainId } from '../../../utils/caip';\nimport { isSolAddress } from '../../../utils/sol-address';\nimport {\n applyFeeUnitsBpsMargin,\n getEvmClientForChain,\n getSolanaRpcForChain,\n makeFailedTransferFromQuote,\n} from '../../_utils';\nimport { estimateGasWithRevert } from '../../_evm-errors';\nimport { markrGetSpenderAddress, markrSwap, type ApiOptions } from '../_api';\nimport {\n assetToAddressString,\n calculateMarkrMinimumAmountOut,\n getAdditiveSourceAssetFeeAmount,\n isTokenAddressNative,\n} from '../_utils';\nimport { isEvmSwapResponse, isHyperliquidWithdrawSwapResponse, isSolanaSwapResponse } from '../_type-guards';\nimport { SOLANA_REQUIRED_CONFIRMATIONS } from '../constants';\nimport { _estimateGasFromSwapResponse } from './estimate-native-fee';\nimport { _executeHyperliquidWithdraw } from './transfer-asset-hyperliquid-withdraw';\nimport type { Caip2ChainId } from '../../../types/caip';\nimport { refreshSolanaSwapTransactionBlockhash } from '../../../utils/solana';\n\nexport interface TransferAssetFactoryConfig {\n apiOptions: ApiOptions;\n appId: string;\n environment: Environment;\n evmSigner: EvmSigner;\n solanaSigner?: SolanaSigner;\n}\n\nexport function transferAssetFactory({\n apiOptions,\n appId,\n environment,\n evmSigner,\n solanaSigner,\n}: TransferAssetFactoryConfig): TransferService['transferAsset'] {\n return async ({ quote, gasSettings, fallbackToDefaultOnBatchFailure, onStepChange }) => {\n if (quote.serviceType !== ServiceType.MARKR) {\n throw new SdkError(ErrorReason.INCORRECT_PROVIDER_PROVIDED, ErrorCode.INVALID_PARAMS);\n }\n\n const now = Math.floor(Date.now() / 1_000);\n\n if (quote.expiresAt <= now) {\n throw new SdkError(ErrorReason.QUOTE_EXPIRED, ErrorCode.INVALID_PARAMS);\n }\n\n const sourceChainId = quote.sourceChain.chainId;\n\n if (sourceChainId === HyperliquidChainIds.MAINNET) {\n return _executeHyperliquidWithdrawTransfer({\n apiOptions,\n appId,\n environment,\n evmSigner,\n quote,\n onStepChange,\n });\n }\n\n if (isSolanaNamespace(sourceChainId)) {\n return _executeSvmTransfer({ apiOptions, appId, environment, solanaSigner, quote, onStepChange });\n }\n\n if (isEvmNamespace(sourceChainId)) {\n return _executeEvmTransfer({\n apiOptions,\n appId,\n environment,\n evmSigner,\n quote,\n gasSettings,\n fallbackToDefaultOnBatchFailure,\n onStepChange,\n });\n }\n\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: `Unsupported source chain namespace: ${sourceChainId}`,\n });\n };\n}\n\n// ---------------------------------------------------------------------------\n// Hyperliquid withdrawal path — wires `markrSwap` → HL handler\n// ---------------------------------------------------------------------------\n\ninterface HyperliquidWithdrawTransferParams {\n apiOptions: ApiOptions;\n appId: string;\n environment: Environment;\n evmSigner: EvmSigner;\n quote: Parameters<TransferService['transferAsset']>[0]['quote'];\n onStepChange?: Parameters<TransferService['transferAsset']>[0]['onStepChange'];\n}\n\nasync function _executeHyperliquidWithdrawTransfer({\n apiOptions,\n appId,\n environment,\n evmSigner,\n quote,\n onStepChange,\n}: HyperliquidWithdrawTransferParams): Promise<Transfer> {\n const minAmountOut = calculateMarkrMinimumAmountOut({\n amountOut: quote.amountOut,\n assetOut: quote.assetOut,\n slippageBps: quote.slippageBps,\n });\n\n const tokenInAddressString = assetToAddressString(quote.assetIn, quote.sourceChain.chainId);\n const tokenOutAddressString = assetToAddressString(quote.assetOut, quote.targetChain.chainId);\n\n const swap = await markrSwap(apiOptions, {\n amountIn: quote.amountIn.toString(),\n appId,\n minAmountOut: minAmountOut.toString(),\n tokenIn: tokenInAddressString,\n tokenOut: tokenOutAddressString,\n uuid: quote.id,\n });\n\n if (!isHyperliquidWithdrawSwapResponse(swap)) {\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details:\n 'Expected `hyperliquid-withdraw` swap response from Markr for Hyperliquid source chain, but received a different variant.',\n });\n }\n\n return _executeHyperliquidWithdraw({\n apiOptions,\n environment,\n evmSigner,\n quote,\n swap,\n onStepChange,\n });\n}\n\n// ---------------------------------------------------------------------------\n// EVM transfer path\n// ---------------------------------------------------------------------------\n\ninterface EvmTransferParams {\n apiOptions: ApiOptions;\n appId: string;\n environment: Environment;\n evmSigner: EvmSigner;\n quote: Parameters<TransferService['transferAsset']>[0]['quote'];\n gasSettings?: Parameters<TransferService['transferAsset']>[0]['gasSettings'];\n fallbackToDefaultOnBatchFailure?: Parameters<TransferService['transferAsset']>[0]['fallbackToDefaultOnBatchFailure'];\n onStepChange?: Parameters<TransferService['transferAsset']>[0]['onStepChange'];\n}\n\nasync function _executeEvmTransfer({\n apiOptions,\n appId,\n environment,\n evmSigner,\n quote,\n gasSettings,\n fallbackToDefaultOnBatchFailure,\n onStepChange,\n}: EvmTransferParams): Promise<Transfer> {\n const fromAddress = quote.fromAddress;\n\n if (!isAddress(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 minAmountOut = calculateMarkrMinimumAmountOut({\n amountOut: quote.amountOut,\n assetOut: quote.assetOut,\n slippageBps: quote.slippageBps,\n });\n\n const sourceClient = getEvmClientForChain({ chain: quote.sourceChain });\n\n const tokenInAddressString = assetToAddressString(quote.assetIn, quote.sourceChain.chainId);\n const tokenOutAddressString = assetToAddressString(quote.assetOut, quote.targetChain.chainId);\n\n if (!isAddress(tokenInAddressString)) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: 'assetIn address is not a valid EVM address.',\n });\n }\n\n const tokenInAddress = tokenInAddressString;\n const isTokenInNative = isTokenAddressNative(tokenInAddress);\n const isCrossChainSwap = quote.sourceChain.chainId.toLowerCase() !== quote.targetChain.chainId.toLowerCase();\n const allowanceAmount = quote.amountIn + getAdditiveSourceAssetFeeAmount(quote);\n\n const { address: spenderAddress } = await markrGetSpenderAddress(apiOptions, {\n chainId: caip2ToEip155ChainId(quote.sourceChain.chainId),\n crossChainSwap: isCrossChainSwap,\n quoteId: quote.id,\n });\n\n if (!isTokenInNative && !spenderAddress) {\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: `Missing Markr spender address for source chain ${quote.sourceChain.chainId}.`,\n });\n }\n\n let requiresApprovalSignature = false;\n let approvalRequest: EvmTransactionRequest | undefined;\n\n if (!isTokenInNative && spenderAddress) {\n const allowance = await sourceClient.readContract({\n address: tokenInAddress,\n abi: erc20Abi,\n functionName: 'allowance',\n args: [fromAddress, spenderAddress],\n });\n\n if (allowance < allowanceAmount) {\n requiresApprovalSignature = true;\n\n const approvalData = encodeFunctionData({\n abi: erc20Abi,\n functionName: 'approve',\n args: [spenderAddress, allowanceAmount],\n });\n\n const approvalGasEstimate = await estimateGasWithRevert(\n sourceClient,\n {\n account: fromAddress,\n to: tokenInAddress,\n data: approvalData,\n },\n erc20Abi,\n 'Failed to estimate gas for ERC20 approval transaction.',\n );\n\n const approvalGasWithMargin = applyFeeUnitsBpsMargin(approvalGasEstimate, gasSettings?.estimateGasMarginBps);\n\n approvalRequest = {\n chainId: caip2ToEip155HexChainId(quote.sourceChain.chainId),\n data: approvalData,\n from: fromAddress,\n gas: approvalGasWithMargin,\n to: tokenInAddress,\n value: 0n,\n ...(gasSettings?.maxFeePerGas !== undefined\n ? {\n maxFeePerGas: gasSettings.maxFeePerGas,\n maxPriorityFeePerGas: gasSettings.maxPriorityFeePerGas,\n }\n : null),\n };\n }\n }\n\n const maybeOneClickBatch = isOneClickBatchEligible({\n approvalRequest,\n requiresApprovalSignature,\n signBatch: evmSigner.signBatch,\n sourceChainId: quote.sourceChain.chainId,\n targetChainId: quote.targetChain.chainId,\n });\n\n const swap = await markrSwap(apiOptions, {\n amountIn: quote.amountIn.toString(),\n appId,\n minAmountOut: minAmountOut.toString(),\n tokenIn: tokenInAddressString,\n tokenOut: tokenOutAddressString,\n uuid: quote.id,\n });\n\n if (!isEvmSwapResponse(swap)) {\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: 'Received non-EVM swap response from Markr, but only EVM swaps are supported for this source chain.',\n });\n }\n\n const makePendingTransfer = (txHash: `0x${string}`, startedAtMs: number = Date.now()): Transfer => {\n const isSameChainTransfer = quote.sourceChain.chainId === quote.targetChain.chainId;\n\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-pending',\n targetAsset: quote.assetOut,\n targetChain: quote.targetChain,\n toAddress: quote.toAddress,\n type: ServiceType.MARKR,\n source: {\n confirmationCount: 0,\n requiredConfirmationCount: isSameChainTransfer ? 1 : 2,\n startedAtMs,\n txHash,\n },\n };\n };\n\n if (maybeOneClickBatch && approvalRequest) {\n const batchStep: TransferStepDetails = {\n currentSignature: 1,\n currentSignatureReason: TransferSignatureReason.TokensTransfer,\n quote,\n requiredSignatures: 1,\n };\n\n const signBatch = evmSigner.signBatch;\n\n if (!signBatch) {\n throw new SdkError('One-click batch signer is not available.', ErrorCode.SIGNING_FAILED);\n }\n\n const swapRequest: EvmTransactionRequest = {\n chainId: caip2ToEip155HexChainId(quote.sourceChain.chainId),\n data: swap.data,\n from: fromAddress,\n // Gas is purposely left undefined here.\n // We can't estimate the gas because we aren't executing the\n // approval tx. It's up to the wallet to correctly handle gas estimation.\n gas: undefined,\n to: swap.to,\n value: swap.value,\n ...(gasSettings?.maxFeePerGas !== undefined\n ? {\n maxFeePerGas: gasSettings.maxFeePerGas,\n maxPriorityFeePerGas: gasSettings.maxPriorityFeePerGas,\n }\n : null),\n };\n\n onStepChange?.(batchStep);\n\n try {\n const txHashes = await signBatch(\n [approvalRequest, swapRequest],\n async (signedTxHash) => sourceClient.sendRawTransaction({ serializedTransaction: signedTxHash }),\n batchStep,\n );\n\n const swapTxHash = txHashes.at(-1);\n\n if (!swapTxHash) {\n throw new SdkError('One-click batch signing returned no transaction hashes.', ErrorCode.SIGNING_FAILED);\n }\n\n return makePendingTransfer(swapTxHash);\n } catch (error) {\n if (!fallbackToDefaultOnBatchFailure) {\n throw error;\n }\n }\n }\n\n if (requiresApprovalSignature && approvalRequest) {\n const approvalStep: TransferStepDetails = {\n currentSignature: 1,\n currentSignatureReason: TransferSignatureReason.AllowanceApproval,\n quote,\n requiredSignatures: 2,\n };\n\n onStepChange?.(approvalStep);\n\n const approvalTxHash = await evmSigner.sign(\n approvalRequest,\n async (signedTxHash) => sourceClient.sendRawTransaction({ serializedTransaction: signedTxHash }),\n approvalStep,\n );\n\n const approvalReceipt = await sourceClient.waitForTransactionReceipt({ hash: approvalTxHash });\n\n if (approvalReceipt.status === 'reverted') {\n return makeFailedTransferFromQuote(quote, {\n environment,\n errorCode: ErrorCode.TRANSACTION_REVERTED,\n errorReason: 'ERC20 approval transaction was reverted',\n });\n }\n }\n\n const swapGasWithMargin = await _estimateGasFromSwapResponse({\n crossChain: isCrossChainSwap,\n fromAddress,\n feeUnitsMarginBps: gasSettings?.estimateGasMarginBps,\n sourceClient,\n swap,\n });\n\n const swapRequest: EvmTransactionRequest = {\n chainId: caip2ToEip155HexChainId(quote.sourceChain.chainId),\n data: swap.data,\n from: fromAddress,\n gas: swapGasWithMargin,\n to: swap.to,\n value: swap.value,\n ...(gasSettings?.maxFeePerGas !== undefined\n ? {\n maxFeePerGas: gasSettings.maxFeePerGas,\n maxPriorityFeePerGas: gasSettings.maxPriorityFeePerGas,\n }\n : null),\n };\n\n const step: TransferStepDetails = {\n currentSignature: requiresApprovalSignature ? 2 : 1,\n currentSignatureReason: TransferSignatureReason.TokensTransfer,\n quote,\n requiredSignatures: requiresApprovalSignature ? 2 : 1,\n };\n\n onStepChange?.(step);\n\n const startedAtMs = Date.now();\n\n const txHash = await evmSigner.sign(\n swapRequest,\n async (signedTxHash) => sourceClient.sendRawTransaction({ serializedTransaction: signedTxHash }),\n step,\n );\n\n return makePendingTransfer(txHash, startedAtMs);\n}\n\n// ---------------------------------------------------------------------------\n// SVM (Solana) transfer path\n// ---------------------------------------------------------------------------\n\ninterface SvmTransferParams {\n apiOptions: ApiOptions;\n appId: string;\n environment: Environment;\n solanaSigner?: SolanaSigner;\n quote: Parameters<TransferService['transferAsset']>[0]['quote'];\n onStepChange?: Parameters<TransferService['transferAsset']>[0]['onStepChange'];\n}\n\nasync function _executeSvmTransfer({\n apiOptions,\n appId,\n environment,\n solanaSigner,\n quote,\n onStepChange,\n}: SvmTransferParams): Promise<Transfer> {\n if (!solanaSigner) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: 'solanaSigner is required for Solana transfers but was not provided.',\n });\n }\n\n if (!isSolAddress(quote.fromAddress)) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: 'fromAddress is not a valid Solana address.',\n });\n }\n\n const minAmountOut = calculateMarkrMinimumAmountOut({\n amountOut: quote.amountOut,\n assetOut: quote.assetOut,\n slippageBps: quote.slippageBps,\n });\n\n const tokenInAddressString = assetToAddressString(quote.assetIn, quote.sourceChain.chainId);\n const tokenOutAddressString = assetToAddressString(quote.assetOut, quote.targetChain.chainId);\n\n const swap = await markrSwap(apiOptions, {\n amountIn: quote.amountIn.toString(),\n appId,\n minAmountOut: minAmountOut.toString(),\n tokenIn: tokenInAddressString,\n tokenOut: tokenOutAddressString,\n userPublicKey: quote.fromAddress,\n uuid: quote.id,\n });\n\n if (!isSolanaSwapResponse(swap)) {\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: 'Received non-SVM swap response from Markr, but only SVM swaps are supported for this source chain.',\n });\n }\n\n // TODO: Cleanup\n console.debug('[Fusion SDK] Original swap transaction (base64):', swap.swapTransaction);\n\n const swapTransactionBase64 = await refreshSolanaSwapTransactionBlockhash(\n swap.swapTransaction,\n getSolanaRpcForChain({ chain: quote.sourceChain }),\n );\n\n // TODO: Cleanup\n console.debug('[Fusion SDK] Refreshed swap transaction with new blockhash (base64):', swapTransactionBase64);\n\n const step: TransferStepDetails = {\n currentSignature: 1,\n currentSignatureReason: TransferSignatureReason.TokensTransfer,\n quote,\n requiredSignatures: 1,\n };\n\n onStepChange?.(step);\n\n const startedAtMs = Date.now();\n\n const txHash = await solanaSigner.signAndSend(\n {\n account: quote.fromAddress,\n serializedTx: swapTransactionBase64,\n },\n step,\n );\n\n const isCrossChainSwap = quote.sourceChain.chainId.toLowerCase() !== quote.targetChain.chainId.toLowerCase();\n\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-pending',\n targetAsset: quote.assetOut,\n targetChain: quote.targetChain,\n toAddress: quote.toAddress,\n type: ServiceType.MARKR,\n source: {\n confirmationCount: 0,\n requiredConfirmationCount: isCrossChainSwap ? 2 : SOLANA_REQUIRED_CONFIRMATIONS,\n startedAtMs,\n txHash,\n },\n };\n}\n\n/**\n * Determines whether one-click swap eligibility criteria is met.\n *\n * Only same-chain EVM swaps are currently eligible.\n */\nfunction isOneClickBatchEligible({\n approvalRequest,\n requiresApprovalSignature,\n signBatch,\n sourceChainId,\n targetChainId,\n}: {\n approvalRequest?: EvmTransactionRequest;\n requiresApprovalSignature: boolean;\n signBatch?: EvmSigner['signBatch'];\n sourceChainId: Caip2ChainId;\n targetChainId: Caip2ChainId;\n}): boolean {\n return (\n isEvmNamespace(sourceChainId) &&\n isEvmNamespace(targetChainId) &&\n sourceChainId === targetChainId &&\n requiresApprovalSignature &&\n approvalRequest !== undefined &&\n typeof signBatch === 'function'\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{HyperliquidChainIds as e,ServiceType as t,TransferSignatureReason as n}from"../../../constants.js";import{ErrorCode as r,ErrorReason as i,SdkError as a}from"../../../errors.js";import{caip2ToEip155ChainId as o,caip2ToEip155HexChainId as s}from"../../../utils/caip.js";import{applyFeeUnitsBpsMargin as c,getEvmClientForChain as l,getSolanaRpcForChain as u,makeFailedTransferFromQuote as d}from"../../_utils.js";import{isEvmNamespace as f,isSolanaNamespace as p}from"../../../_utils/chain.js";import{estimateGasWithRevert as m}from"../../_evm-errors.js";import{markrGetSpenderAddress as h,markrSwap as g}from"../_api.js";import{isSolAddress as _}from"../../../utils/sol-address.js";import{assetToAddressString as v,calculateMarkrMinimumAmountOut as y,getAdditiveSourceAssetFeeAmount as b,isTokenAddressNative as x}from"../_utils.js";import{isEvmSwapResponse as S,isHyperliquidWithdrawSwapResponse as C,isSolanaSwapResponse as w}from"../_type-guards.js";import{refreshSolanaSwapTransactionBlockhash as T}from"../../../utils/solana.js";import{_estimateGasFromSwapResponse as E}from"./estimate-native-fee.js";import{_executeHyperliquidWithdraw as D}from"./transfer-asset-hyperliquid-withdraw.js";import{encodeFunctionData as O,erc20Abi as k,isAddress as A}from"viem";function j({apiOptions:n,appId:o,environment:s,evmSigner:c,solanaSigner:l}){return async({quote:u,gasSettings:d,fallbackToDefaultOnBatchFailure:m,onStepChange:h})=>{if(u.serviceType!==t.MARKR)throw new a(i.INCORRECT_PROVIDER_PROVIDED,r.INVALID_PARAMS);let g=Math.floor(Date.now()/1e3);if(u.expiresAt<=g)throw new a(i.QUOTE_EXPIRED,r.INVALID_PARAMS);let _=u.sourceChain.chainId;if(_===e.MAINNET)return M({apiOptions:n,appId:o,environment:s,evmSigner:c,quote:u,onStepChange:h});if(p(_))return P({apiOptions:n,appId:o,environment:s,solanaSigner:l,quote:u,onStepChange:h});if(f(_))return N({apiOptions:n,appId:o,environment:s,evmSigner:c,quote:u,gasSettings:d,fallbackToDefaultOnBatchFailure:m,onStepChange:h});throw new a(i.CHAIN_NOT_SUPPORTED,r.INVALID_PARAMS,{details:`Unsupported source chain namespace: ${_}`})}}async function M({apiOptions:e,appId:t,environment:n,evmSigner:o,quote:s,onStepChange:c}){let l=y({amountOut:s.amountOut,assetOut:s.assetOut,slippageBps:s.slippageBps}),u=v(s.assetIn,s.sourceChain.chainId),d=v(s.assetOut,s.targetChain.chainId),f=await g(e,{amountIn:s.amountIn.toString(),appId:t,minAmountOut:l.toString(),tokenIn:u,tokenOut:d,uuid:s.id});if(!C(f))throw new a(i.CHAIN_NOT_SUPPORTED,r.INVALID_PARAMS,{details:"Expected `hyperliquid-withdraw` swap response from Markr for Hyperliquid source chain, but received a different variant."});return D({apiOptions:e,environment:n,evmSigner:o,quote:s,swap:f,onStepChange:c})}async function N({apiOptions:e,appId:u,environment:f,evmSigner:p,quote:_,gasSettings:C,fallbackToDefaultOnBatchFailure:w,onStepChange:T}){let D=_.fromAddress;if(!A(D))throw new a(i.INVALID_PARAMS,r.INVALID_PARAMS,{details:`fromAddress is not a valid EVM address.`});let j=y({amountOut:_.amountOut,assetOut:_.assetOut,slippageBps:_.slippageBps}),M=l({chain:_.sourceChain}),N=v(_.assetIn,_.sourceChain.chainId),P=v(_.assetOut,_.targetChain.chainId);if(!A(N))throw new a(i.INVALID_PARAMS,r.INVALID_PARAMS,{details:`assetIn address is not a valid EVM address.`});let I=N,L=x(I),R=_.sourceChain.chainId.toLowerCase()!==_.targetChain.chainId.toLowerCase(),z=_.amountIn+b(_),{address:B}=await h(e,{chainId:o(_.sourceChain.chainId),crossChainSwap:R,quoteId:_.id});if(!L&&!B)throw new a(i.CHAIN_NOT_SUPPORTED,r.INVALID_PARAMS,{details:`Missing Markr spender address for source chain ${_.sourceChain.chainId}.`});let V=!1,H;if(!L&&B&&await M.readContract({address:I,abi:k,functionName:`allowance`,args:[D,B]})<z){V=!0;let e=O({abi:k,functionName:`approve`,args:[B,z]}),t=c(await m(M,{account:D,to:I,data:e},k,`Failed to estimate gas for ERC20 approval transaction.`),C?.estimateGasMarginBps);H={chainId:s(_.sourceChain.chainId),data:e,from:D,gas:t,to:I,value:0n,...C?.maxFeePerGas===void 0?null:{maxFeePerGas:C.maxFeePerGas,maxPriorityFeePerGas:C.maxPriorityFeePerGas}}}let U=F({approvalRequest:H,requiresApprovalSignature:V,signBatch:p.signBatch,sourceChainId:_.sourceChain.chainId,targetChainId:_.targetChain.chainId}),W=await g(e,{amountIn:_.amountIn.toString(),appId:u,minAmountOut:j.toString(),tokenIn:N,tokenOut:P,uuid:_.id});if(!S(W))throw new a(i.CHAIN_NOT_SUPPORTED,r.INVALID_PARAMS,{details:`Received non-EVM swap response from Markr, but only EVM swaps are supported for this source chain.`});let G=(e,n=Date.now())=>{let r=_.sourceChain.chainId===_.targetChain.chainId;return{amountIn:_.amountIn,amountOut:_.amountOut,environment:f,fees:_.fees,fromAddress:_.fromAddress,id:_.id,partnerFeeBps:_.partnerFeeBps,sourceAsset:_.assetIn,sourceChain:_.sourceChain,status:`source-pending`,targetAsset:_.assetOut,targetChain:_.targetChain,toAddress:_.toAddress,type:t.MARKR,source:{confirmationCount:0,requiredConfirmationCount:r?1:2,startedAtMs:n,txHash:e}}};if(U&&H){let e={currentSignature:1,currentSignatureReason:n.TokensTransfer,quote:_,requiredSignatures:1},t=p.signBatch;if(!t)throw new a(`One-click batch signer is not available.`,r.SIGNING_FAILED);let i={chainId:s(_.sourceChain.chainId),data:W.data,from:D,gas:void 0,to:W.to,value:W.value,...C?.maxFeePerGas===void 0?null:{maxFeePerGas:C.maxFeePerGas,maxPriorityFeePerGas:C.maxPriorityFeePerGas}};T?.(e);try{let n=(await t([H,i],async e=>M.sendRawTransaction({serializedTransaction:e}),e)).at(-1);if(!n)throw new a(`One-click batch signing returned no transaction hashes.`,r.SIGNING_FAILED);return G(n)}catch(e){if(!w)throw e}}if(V&&H){let e={currentSignature:1,currentSignatureReason:n.AllowanceApproval,quote:_,requiredSignatures:2};T?.(e);let t=await p.sign(H,async e=>M.sendRawTransaction({serializedTransaction:e}),e);if((await M.waitForTransactionReceipt({hash:t})).status===`reverted`)return d(_,{environment:f,errorCode:r.TRANSACTION_REVERTED,errorReason:`ERC20 approval transaction was reverted`})}let K=await E({crossChain:R,fromAddress:D,feeUnitsMarginBps:C?.estimateGasMarginBps,sourceClient:M,swap:W}),q={chainId:s(_.sourceChain.chainId),data:W.data,from:D,gas:K,to:W.to,value:W.value,...C?.maxFeePerGas===void 0?null:{maxFeePerGas:C.maxFeePerGas,maxPriorityFeePerGas:C.maxPriorityFeePerGas}},J={currentSignature:V?2:1,currentSignatureReason:n.TokensTransfer,quote:_,requiredSignatures:V?2:1};T?.(J);let Y=Date.now();return G(await p.sign(q,async e=>M.sendRawTransaction({serializedTransaction:e}),J),Y)}async function P({apiOptions:e,appId:o,environment:s,solanaSigner:c,quote:l,onStepChange:d}){if(!c)throw new a(i.INVALID_PARAMS,r.INVALID_PARAMS,{details:`solanaSigner is required for Solana transfers but was not provided.`});if(!_(l.fromAddress))throw new a(i.INVALID_PARAMS,r.INVALID_PARAMS,{details:`fromAddress is not a valid Solana address.`});let f=y({amountOut:l.amountOut,assetOut:l.assetOut,slippageBps:l.slippageBps}),p=v(l.assetIn,l.sourceChain.chainId),m=v(l.assetOut,l.targetChain.chainId),h=await g(e,{amountIn:l.amountIn.toString(),appId:o,minAmountOut:f.toString(),tokenIn:p,tokenOut:m,userPublicKey:l.fromAddress,uuid:l.id});if(!w(h))throw new a(i.CHAIN_NOT_SUPPORTED,r.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):`,h.swapTransaction);let b=await T(h.swapTransaction,u({chain:l.sourceChain}));console.debug(`[Fusion SDK] Refreshed swap transaction with new blockhash (base64):`,b);let x={currentSignature:1,currentSignatureReason:n.TokensTransfer,quote:l,requiredSignatures:1};d?.(x);let S=Date.now(),C=await c.signAndSend({account:l.fromAddress,serializedTx:b},x),E=l.sourceChain.chainId.toLowerCase()!==l.targetChain.chainId.toLowerCase();return{amountIn:l.amountIn,amountOut:l.amountOut,environment:s,fees:l.fees,fromAddress:l.fromAddress,id:l.id,partnerFeeBps:l.partnerFeeBps,sourceAsset:l.assetIn,sourceChain:l.sourceChain,status:`source-pending`,targetAsset:l.assetOut,targetChain:l.targetChain,toAddress:l.toAddress,type:t.MARKR,source:{confirmationCount:0,requiredConfirmationCount:E?2:32,startedAtMs:S,txHash:C}}}function F({approvalRequest:e,requiresApprovalSignature:t,signBatch:n,sourceChainId:r,targetChainId:i}){return f(r)&&f(i)&&r===i&&t&&e!==void 0&&typeof n==`function`}export{j as transferAssetFactory};
|
|
2
2
|
//# sourceMappingURL=transfer-asset.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transfer-asset.js","names":[],"sources":["../../../../src/transfer-service/markr/_handlers/transfer-asset.ts"],"sourcesContent":["import { encodeFunctionData, erc20Abi, isAddress } from 'viem';\nimport { isEvmNamespace, isSolanaNamespace } from '../../../_utils/chain';\nimport { ServiceType, TransferSignatureReason } from '../../../constants';\nimport type { Environment } from '../../../constants';\nimport { ErrorCode, ErrorReason, SdkError } from '../../../errors';\nimport type { TransferService } from '../../../types/service';\nimport type { EvmSigner, EvmTransactionRequest, SolanaSigner } from '../../../types/signer';\nimport type { Transfer, TransferStepDetails } from '../../../types/transfer';\nimport { caip2ToEip155ChainId, caip2ToEip155HexChainId } from '../../../utils/caip';\nimport { isSolAddress } from '../../../utils/sol-address';\nimport {\n applyFeeUnitsBpsMargin,\n getEvmClientForChain,\n getSolanaRpcForChain,\n makeFailedTransferFromQuote,\n} from '../../_utils';\nimport { estimateGasWithRevert } from '../../_evm-errors';\nimport { markrGetSpenderAddress, markrSwap, type ApiOptions } from '../_api';\nimport {\n assetToAddressString,\n calculateMarkrMinimumAmountOut,\n getAdditiveSourceAssetFeeAmount,\n isTokenAddressNative,\n} from '../_utils';\nimport { isEvmSwapResponse, isSolanaSwapResponse } from '../_type-guards';\nimport { SOLANA_REQUIRED_CONFIRMATIONS } from '../constants';\nimport { _estimateGasFromSwapResponse } from './estimate-native-fee';\nimport type { Caip2ChainId } from '../../../types/caip';\nimport { refreshSolanaSwapTransactionBlockhash } from '../../../utils/solana';\n\nexport interface TransferAssetFactoryConfig {\n apiOptions: ApiOptions;\n appId: string;\n environment: Environment;\n evmSigner: EvmSigner;\n solanaSigner?: SolanaSigner;\n}\n\nexport function transferAssetFactory({\n apiOptions,\n appId,\n environment,\n evmSigner,\n solanaSigner,\n}: TransferAssetFactoryConfig): TransferService['transferAsset'] {\n return async ({ quote, gasSettings, fallbackToDefaultOnBatchFailure, onStepChange }) => {\n if (quote.serviceType !== ServiceType.MARKR) {\n throw new SdkError(ErrorReason.INCORRECT_PROVIDER_PROVIDED, ErrorCode.INVALID_PARAMS);\n }\n\n const now = Math.floor(Date.now() / 1_000);\n\n if (quote.expiresAt <= now) {\n throw new SdkError(ErrorReason.QUOTE_EXPIRED, ErrorCode.INVALID_PARAMS);\n }\n\n const sourceChainId = quote.sourceChain.chainId;\n\n if (isSolanaNamespace(sourceChainId)) {\n return _executeSvmTransfer({ apiOptions, appId, environment, solanaSigner, quote, onStepChange });\n }\n\n if (isEvmNamespace(sourceChainId)) {\n return _executeEvmTransfer({\n apiOptions,\n appId,\n environment,\n evmSigner,\n quote,\n gasSettings,\n fallbackToDefaultOnBatchFailure,\n onStepChange,\n });\n }\n\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: `Unsupported source chain namespace: ${sourceChainId}`,\n });\n };\n}\n\n// ---------------------------------------------------------------------------\n// EVM transfer path\n// ---------------------------------------------------------------------------\n\ninterface EvmTransferParams {\n apiOptions: ApiOptions;\n appId: string;\n environment: Environment;\n evmSigner: EvmSigner;\n quote: Parameters<TransferService['transferAsset']>[0]['quote'];\n gasSettings?: Parameters<TransferService['transferAsset']>[0]['gasSettings'];\n fallbackToDefaultOnBatchFailure?: Parameters<TransferService['transferAsset']>[0]['fallbackToDefaultOnBatchFailure'];\n onStepChange?: Parameters<TransferService['transferAsset']>[0]['onStepChange'];\n}\n\nasync function _executeEvmTransfer({\n apiOptions,\n appId,\n environment,\n evmSigner,\n quote,\n gasSettings,\n fallbackToDefaultOnBatchFailure,\n onStepChange,\n}: EvmTransferParams): Promise<Transfer> {\n const fromAddress = quote.fromAddress;\n\n if (!isAddress(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 minAmountOut = calculateMarkrMinimumAmountOut({\n amountOut: quote.amountOut,\n assetOut: quote.assetOut,\n slippageBps: quote.slippageBps,\n });\n\n const sourceClient = getEvmClientForChain({ chain: quote.sourceChain });\n\n const tokenInAddressString = assetToAddressString(quote.assetIn, quote.sourceChain.chainId);\n const tokenOutAddressString = assetToAddressString(quote.assetOut, quote.targetChain.chainId);\n\n if (!isAddress(tokenInAddressString)) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: 'assetIn address is not a valid EVM address.',\n });\n }\n\n const tokenInAddress = tokenInAddressString;\n const isTokenInNative = isTokenAddressNative(tokenInAddress);\n const isCrossChainSwap = quote.sourceChain.chainId.toLowerCase() !== quote.targetChain.chainId.toLowerCase();\n const allowanceAmount = quote.amountIn + getAdditiveSourceAssetFeeAmount(quote);\n\n const { address: spenderAddress } = await markrGetSpenderAddress(apiOptions, {\n chainId: caip2ToEip155ChainId(quote.sourceChain.chainId),\n crossChainSwap: isCrossChainSwap,\n quoteId: quote.id,\n });\n\n if (!isTokenInNative && !spenderAddress) {\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: `Missing Markr spender address for source chain ${quote.sourceChain.chainId}.`,\n });\n }\n\n let requiresApprovalSignature = false;\n let approvalRequest: EvmTransactionRequest | undefined;\n\n if (!isTokenInNative && spenderAddress) {\n const allowance = await sourceClient.readContract({\n address: tokenInAddress,\n abi: erc20Abi,\n functionName: 'allowance',\n args: [fromAddress, spenderAddress],\n });\n\n if (allowance < allowanceAmount) {\n requiresApprovalSignature = true;\n\n const approvalData = encodeFunctionData({\n abi: erc20Abi,\n functionName: 'approve',\n args: [spenderAddress, allowanceAmount],\n });\n\n const approvalGasEstimate = await estimateGasWithRevert(\n sourceClient,\n {\n account: fromAddress,\n to: tokenInAddress,\n data: approvalData,\n },\n erc20Abi,\n 'Failed to estimate gas for ERC20 approval transaction.',\n );\n\n const approvalGasWithMargin = applyFeeUnitsBpsMargin(approvalGasEstimate, gasSettings?.estimateGasMarginBps);\n\n approvalRequest = {\n chainId: caip2ToEip155HexChainId(quote.sourceChain.chainId),\n data: approvalData,\n from: fromAddress,\n gas: approvalGasWithMargin,\n to: tokenInAddress,\n value: 0n,\n ...(gasSettings?.maxFeePerGas !== undefined\n ? {\n maxFeePerGas: gasSettings.maxFeePerGas,\n maxPriorityFeePerGas: gasSettings.maxPriorityFeePerGas,\n }\n : null),\n };\n }\n }\n\n const maybeOneClickBatch = isOneClickBatchEligible({\n approvalRequest,\n requiresApprovalSignature,\n signBatch: evmSigner.signBatch,\n sourceChainId: quote.sourceChain.chainId,\n targetChainId: quote.targetChain.chainId,\n });\n\n const swap = await markrSwap(apiOptions, {\n amountIn: quote.amountIn.toString(),\n appId,\n minAmountOut: minAmountOut.toString(),\n tokenIn: tokenInAddressString,\n tokenOut: tokenOutAddressString,\n uuid: quote.id,\n });\n\n if (!isEvmSwapResponse(swap)) {\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: 'Received non-EVM swap response from Markr, but only EVM swaps are supported for this source chain.',\n });\n }\n\n const makePendingTransfer = (txHash: `0x${string}`, startedAtMs: number = Date.now()): Transfer => {\n const isSameChainTransfer = quote.sourceChain.chainId === quote.targetChain.chainId;\n\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-pending',\n targetAsset: quote.assetOut,\n targetChain: quote.targetChain,\n toAddress: quote.toAddress,\n type: ServiceType.MARKR,\n source: {\n confirmationCount: 0,\n requiredConfirmationCount: isSameChainTransfer ? 1 : 2,\n startedAtMs,\n txHash,\n },\n };\n };\n\n if (maybeOneClickBatch && approvalRequest) {\n const batchStep: TransferStepDetails = {\n currentSignature: 1,\n currentSignatureReason: TransferSignatureReason.TokensTransfer,\n quote,\n requiredSignatures: 1,\n };\n\n const signBatch = evmSigner.signBatch;\n\n if (!signBatch) {\n throw new SdkError('One-click batch signer is not available.', ErrorCode.SIGNING_FAILED);\n }\n\n const swapRequest: EvmTransactionRequest = {\n chainId: caip2ToEip155HexChainId(quote.sourceChain.chainId),\n data: swap.data,\n from: fromAddress,\n // Gas is purposely left undefined here.\n // We can't estimate the gas because we aren't executing the\n // approval tx. It's up to the wallet to correctly handle gas estimation.\n gas: undefined,\n to: swap.to,\n value: swap.value,\n ...(gasSettings?.maxFeePerGas !== undefined\n ? {\n maxFeePerGas: gasSettings.maxFeePerGas,\n maxPriorityFeePerGas: gasSettings.maxPriorityFeePerGas,\n }\n : null),\n };\n\n onStepChange?.(batchStep);\n\n try {\n const txHashes = await signBatch(\n [approvalRequest, swapRequest],\n async (signedTxHash) => sourceClient.sendRawTransaction({ serializedTransaction: signedTxHash }),\n batchStep,\n );\n\n const swapTxHash = txHashes.at(-1);\n\n if (!swapTxHash) {\n throw new SdkError('One-click batch signing returned no transaction hashes.', ErrorCode.SIGNING_FAILED);\n }\n\n return makePendingTransfer(swapTxHash);\n } catch (error) {\n if (!fallbackToDefaultOnBatchFailure) {\n throw error;\n }\n }\n }\n\n if (requiresApprovalSignature && approvalRequest) {\n const approvalStep: TransferStepDetails = {\n currentSignature: 1,\n currentSignatureReason: TransferSignatureReason.AllowanceApproval,\n quote,\n requiredSignatures: 2,\n };\n\n onStepChange?.(approvalStep);\n\n const approvalTxHash = await evmSigner.sign(\n approvalRequest,\n async (signedTxHash) => sourceClient.sendRawTransaction({ serializedTransaction: signedTxHash }),\n approvalStep,\n );\n\n const approvalReceipt = await sourceClient.waitForTransactionReceipt({ hash: approvalTxHash });\n\n if (approvalReceipt.status === 'reverted') {\n return makeFailedTransferFromQuote(quote, {\n environment,\n errorCode: ErrorCode.TRANSACTION_REVERTED,\n errorReason: 'ERC20 approval transaction was reverted',\n });\n }\n }\n\n const swapGasWithMargin = await _estimateGasFromSwapResponse({\n crossChain: isCrossChainSwap,\n fromAddress,\n feeUnitsMarginBps: gasSettings?.estimateGasMarginBps,\n sourceClient,\n swap,\n });\n\n const swapRequest: EvmTransactionRequest = {\n chainId: caip2ToEip155HexChainId(quote.sourceChain.chainId),\n data: swap.data,\n from: fromAddress,\n gas: swapGasWithMargin,\n to: swap.to,\n value: swap.value,\n ...(gasSettings?.maxFeePerGas !== undefined\n ? {\n maxFeePerGas: gasSettings.maxFeePerGas,\n maxPriorityFeePerGas: gasSettings.maxPriorityFeePerGas,\n }\n : null),\n };\n\n const step: TransferStepDetails = {\n currentSignature: requiresApprovalSignature ? 2 : 1,\n currentSignatureReason: TransferSignatureReason.TokensTransfer,\n quote,\n requiredSignatures: requiresApprovalSignature ? 2 : 1,\n };\n\n onStepChange?.(step);\n\n const startedAtMs = Date.now();\n\n const txHash = await evmSigner.sign(\n swapRequest,\n async (signedTxHash) => sourceClient.sendRawTransaction({ serializedTransaction: signedTxHash }),\n step,\n );\n\n return makePendingTransfer(txHash, startedAtMs);\n}\n\n// ---------------------------------------------------------------------------\n// SVM (Solana) transfer path\n// ---------------------------------------------------------------------------\n\ninterface SvmTransferParams {\n apiOptions: ApiOptions;\n appId: string;\n environment: Environment;\n solanaSigner?: SolanaSigner;\n quote: Parameters<TransferService['transferAsset']>[0]['quote'];\n onStepChange?: Parameters<TransferService['transferAsset']>[0]['onStepChange'];\n}\n\nasync function _executeSvmTransfer({\n apiOptions,\n appId,\n environment,\n solanaSigner,\n quote,\n onStepChange,\n}: SvmTransferParams): Promise<Transfer> {\n if (!solanaSigner) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: 'solanaSigner is required for Solana transfers but was not provided.',\n });\n }\n\n if (!isSolAddress(quote.fromAddress)) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: 'fromAddress is not a valid Solana address.',\n });\n }\n\n const minAmountOut = calculateMarkrMinimumAmountOut({\n amountOut: quote.amountOut,\n assetOut: quote.assetOut,\n slippageBps: quote.slippageBps,\n });\n\n const tokenInAddressString = assetToAddressString(quote.assetIn, quote.sourceChain.chainId);\n const tokenOutAddressString = assetToAddressString(quote.assetOut, quote.targetChain.chainId);\n\n const swap = await markrSwap(apiOptions, {\n amountIn: quote.amountIn.toString(),\n appId,\n minAmountOut: minAmountOut.toString(),\n tokenIn: tokenInAddressString,\n tokenOut: tokenOutAddressString,\n userPublicKey: quote.fromAddress,\n uuid: quote.id,\n });\n\n if (!isSolanaSwapResponse(swap)) {\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: 'Received non-SVM swap response from Markr, but only SVM swaps are supported for this source chain.',\n });\n }\n\n // TODO: Cleanup\n console.debug('[Fusion SDK] Original swap transaction (base64):', swap.swapTransaction);\n\n const swapTransactionBase64 = await refreshSolanaSwapTransactionBlockhash(\n swap.swapTransaction,\n getSolanaRpcForChain({ chain: quote.sourceChain }),\n );\n\n // TODO: Cleanup\n console.debug('[Fusion SDK] Refreshed swap transaction with new blockhash (base64):', swapTransactionBase64);\n\n const step: TransferStepDetails = {\n currentSignature: 1,\n currentSignatureReason: TransferSignatureReason.TokensTransfer,\n quote,\n requiredSignatures: 1,\n };\n\n onStepChange?.(step);\n\n const startedAtMs = Date.now();\n\n const txHash = await solanaSigner.signAndSend(\n {\n account: quote.fromAddress,\n serializedTx: swapTransactionBase64,\n },\n step,\n );\n\n const isCrossChainSwap = quote.sourceChain.chainId.toLowerCase() !== quote.targetChain.chainId.toLowerCase();\n\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-pending',\n targetAsset: quote.assetOut,\n targetChain: quote.targetChain,\n toAddress: quote.toAddress,\n type: ServiceType.MARKR,\n source: {\n confirmationCount: 0,\n requiredConfirmationCount: isCrossChainSwap ? 2 : SOLANA_REQUIRED_CONFIRMATIONS,\n startedAtMs,\n txHash,\n },\n };\n}\n\n/**\n * Determines whether one-click swap eligibility criteria is met.\n *\n * Only same-chain EVM swaps are currently eligible.\n */\nfunction isOneClickBatchEligible({\n approvalRequest,\n requiresApprovalSignature,\n signBatch,\n sourceChainId,\n targetChainId,\n}: {\n approvalRequest?: EvmTransactionRequest;\n requiresApprovalSignature: boolean;\n signBatch?: EvmSigner['signBatch'];\n sourceChainId: Caip2ChainId;\n targetChainId: Caip2ChainId;\n}): boolean {\n return (\n isEvmNamespace(sourceChainId) &&\n isEvmNamespace(targetChainId) &&\n sourceChainId === targetChainId &&\n requiresApprovalSignature &&\n approvalRequest !== undefined &&\n typeof signBatch === 'function'\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"transfer-asset.js","names":[],"sources":["../../../../src/transfer-service/markr/_handlers/transfer-asset.ts"],"sourcesContent":["import { encodeFunctionData, erc20Abi, isAddress } from 'viem';\nimport { isEvmNamespace, isSolanaNamespace } from '../../../_utils/chain';\nimport { HyperliquidChainIds, ServiceType, TransferSignatureReason } from '../../../constants';\nimport type { Environment } from '../../../constants';\nimport { ErrorCode, ErrorReason, SdkError } from '../../../errors';\nimport type { TransferService } from '../../../types/service';\nimport type { EvmSigner, EvmTransactionRequest, SolanaSigner } from '../../../types/signer';\nimport type { Transfer, TransferStepDetails } from '../../../types/transfer';\nimport { caip2ToEip155ChainId, caip2ToEip155HexChainId } from '../../../utils/caip';\nimport { isSolAddress } from '../../../utils/sol-address';\nimport {\n applyFeeUnitsBpsMargin,\n getEvmClientForChain,\n getSolanaRpcForChain,\n makeFailedTransferFromQuote,\n} from '../../_utils';\nimport { estimateGasWithRevert } from '../../_evm-errors';\nimport { markrGetSpenderAddress, markrSwap, type ApiOptions } from '../_api';\nimport {\n assetToAddressString,\n calculateMarkrMinimumAmountOut,\n getAdditiveSourceAssetFeeAmount,\n isTokenAddressNative,\n} from '../_utils';\nimport { isEvmSwapResponse, isHyperliquidWithdrawSwapResponse, isSolanaSwapResponse } from '../_type-guards';\nimport { SOLANA_REQUIRED_CONFIRMATIONS } from '../constants';\nimport { _estimateGasFromSwapResponse } from './estimate-native-fee';\nimport { _executeHyperliquidWithdraw } from './transfer-asset-hyperliquid-withdraw';\nimport type { Caip2ChainId } from '../../../types/caip';\nimport { refreshSolanaSwapTransactionBlockhash } from '../../../utils/solana';\n\nexport interface TransferAssetFactoryConfig {\n apiOptions: ApiOptions;\n appId: string;\n environment: Environment;\n evmSigner: EvmSigner;\n solanaSigner?: SolanaSigner;\n}\n\nexport function transferAssetFactory({\n apiOptions,\n appId,\n environment,\n evmSigner,\n solanaSigner,\n}: TransferAssetFactoryConfig): TransferService['transferAsset'] {\n return async ({ quote, gasSettings, fallbackToDefaultOnBatchFailure, onStepChange }) => {\n if (quote.serviceType !== ServiceType.MARKR) {\n throw new SdkError(ErrorReason.INCORRECT_PROVIDER_PROVIDED, ErrorCode.INVALID_PARAMS);\n }\n\n const now = Math.floor(Date.now() / 1_000);\n\n if (quote.expiresAt <= now) {\n throw new SdkError(ErrorReason.QUOTE_EXPIRED, ErrorCode.INVALID_PARAMS);\n }\n\n const sourceChainId = quote.sourceChain.chainId;\n\n if (sourceChainId === HyperliquidChainIds.MAINNET) {\n return _executeHyperliquidWithdrawTransfer({\n apiOptions,\n appId,\n environment,\n evmSigner,\n quote,\n onStepChange,\n });\n }\n\n if (isSolanaNamespace(sourceChainId)) {\n return _executeSvmTransfer({ apiOptions, appId, environment, solanaSigner, quote, onStepChange });\n }\n\n if (isEvmNamespace(sourceChainId)) {\n return _executeEvmTransfer({\n apiOptions,\n appId,\n environment,\n evmSigner,\n quote,\n gasSettings,\n fallbackToDefaultOnBatchFailure,\n onStepChange,\n });\n }\n\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: `Unsupported source chain namespace: ${sourceChainId}`,\n });\n };\n}\n\n// ---------------------------------------------------------------------------\n// Hyperliquid withdrawal path — wires `markrSwap` → HL handler\n// ---------------------------------------------------------------------------\n\ninterface HyperliquidWithdrawTransferParams {\n apiOptions: ApiOptions;\n appId: string;\n environment: Environment;\n evmSigner: EvmSigner;\n quote: Parameters<TransferService['transferAsset']>[0]['quote'];\n onStepChange?: Parameters<TransferService['transferAsset']>[0]['onStepChange'];\n}\n\nasync function _executeHyperliquidWithdrawTransfer({\n apiOptions,\n appId,\n environment,\n evmSigner,\n quote,\n onStepChange,\n}: HyperliquidWithdrawTransferParams): Promise<Transfer> {\n const minAmountOut = calculateMarkrMinimumAmountOut({\n amountOut: quote.amountOut,\n assetOut: quote.assetOut,\n slippageBps: quote.slippageBps,\n });\n\n const tokenInAddressString = assetToAddressString(quote.assetIn, quote.sourceChain.chainId);\n const tokenOutAddressString = assetToAddressString(quote.assetOut, quote.targetChain.chainId);\n\n const swap = await markrSwap(apiOptions, {\n amountIn: quote.amountIn.toString(),\n appId,\n minAmountOut: minAmountOut.toString(),\n tokenIn: tokenInAddressString,\n tokenOut: tokenOutAddressString,\n uuid: quote.id,\n });\n\n if (!isHyperliquidWithdrawSwapResponse(swap)) {\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details:\n 'Expected `hyperliquid-withdraw` swap response from Markr for Hyperliquid source chain, but received a different variant.',\n });\n }\n\n return _executeHyperliquidWithdraw({\n apiOptions,\n environment,\n evmSigner,\n quote,\n swap,\n onStepChange,\n });\n}\n\n// ---------------------------------------------------------------------------\n// EVM transfer path\n// ---------------------------------------------------------------------------\n\ninterface EvmTransferParams {\n apiOptions: ApiOptions;\n appId: string;\n environment: Environment;\n evmSigner: EvmSigner;\n quote: Parameters<TransferService['transferAsset']>[0]['quote'];\n gasSettings?: Parameters<TransferService['transferAsset']>[0]['gasSettings'];\n fallbackToDefaultOnBatchFailure?: Parameters<TransferService['transferAsset']>[0]['fallbackToDefaultOnBatchFailure'];\n onStepChange?: Parameters<TransferService['transferAsset']>[0]['onStepChange'];\n}\n\nasync function _executeEvmTransfer({\n apiOptions,\n appId,\n environment,\n evmSigner,\n quote,\n gasSettings,\n fallbackToDefaultOnBatchFailure,\n onStepChange,\n}: EvmTransferParams): Promise<Transfer> {\n const fromAddress = quote.fromAddress;\n\n if (!isAddress(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 minAmountOut = calculateMarkrMinimumAmountOut({\n amountOut: quote.amountOut,\n assetOut: quote.assetOut,\n slippageBps: quote.slippageBps,\n });\n\n const sourceClient = getEvmClientForChain({ chain: quote.sourceChain });\n\n const tokenInAddressString = assetToAddressString(quote.assetIn, quote.sourceChain.chainId);\n const tokenOutAddressString = assetToAddressString(quote.assetOut, quote.targetChain.chainId);\n\n if (!isAddress(tokenInAddressString)) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: 'assetIn address is not a valid EVM address.',\n });\n }\n\n const tokenInAddress = tokenInAddressString;\n const isTokenInNative = isTokenAddressNative(tokenInAddress);\n const isCrossChainSwap = quote.sourceChain.chainId.toLowerCase() !== quote.targetChain.chainId.toLowerCase();\n const allowanceAmount = quote.amountIn + getAdditiveSourceAssetFeeAmount(quote);\n\n const { address: spenderAddress } = await markrGetSpenderAddress(apiOptions, {\n chainId: caip2ToEip155ChainId(quote.sourceChain.chainId),\n crossChainSwap: isCrossChainSwap,\n quoteId: quote.id,\n });\n\n if (!isTokenInNative && !spenderAddress) {\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: `Missing Markr spender address for source chain ${quote.sourceChain.chainId}.`,\n });\n }\n\n let requiresApprovalSignature = false;\n let approvalRequest: EvmTransactionRequest | undefined;\n\n if (!isTokenInNative && spenderAddress) {\n const allowance = await sourceClient.readContract({\n address: tokenInAddress,\n abi: erc20Abi,\n functionName: 'allowance',\n args: [fromAddress, spenderAddress],\n });\n\n if (allowance < allowanceAmount) {\n requiresApprovalSignature = true;\n\n const approvalData = encodeFunctionData({\n abi: erc20Abi,\n functionName: 'approve',\n args: [spenderAddress, allowanceAmount],\n });\n\n const approvalGasEstimate = await estimateGasWithRevert(\n sourceClient,\n {\n account: fromAddress,\n to: tokenInAddress,\n data: approvalData,\n },\n erc20Abi,\n 'Failed to estimate gas for ERC20 approval transaction.',\n );\n\n const approvalGasWithMargin = applyFeeUnitsBpsMargin(approvalGasEstimate, gasSettings?.estimateGasMarginBps);\n\n approvalRequest = {\n chainId: caip2ToEip155HexChainId(quote.sourceChain.chainId),\n data: approvalData,\n from: fromAddress,\n gas: approvalGasWithMargin,\n to: tokenInAddress,\n value: 0n,\n ...(gasSettings?.maxFeePerGas !== undefined\n ? {\n maxFeePerGas: gasSettings.maxFeePerGas,\n maxPriorityFeePerGas: gasSettings.maxPriorityFeePerGas,\n }\n : null),\n };\n }\n }\n\n const maybeOneClickBatch = isOneClickBatchEligible({\n approvalRequest,\n requiresApprovalSignature,\n signBatch: evmSigner.signBatch,\n sourceChainId: quote.sourceChain.chainId,\n targetChainId: quote.targetChain.chainId,\n });\n\n const swap = await markrSwap(apiOptions, {\n amountIn: quote.amountIn.toString(),\n appId,\n minAmountOut: minAmountOut.toString(),\n tokenIn: tokenInAddressString,\n tokenOut: tokenOutAddressString,\n uuid: quote.id,\n });\n\n if (!isEvmSwapResponse(swap)) {\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: 'Received non-EVM swap response from Markr, but only EVM swaps are supported for this source chain.',\n });\n }\n\n const makePendingTransfer = (txHash: `0x${string}`, startedAtMs: number = Date.now()): Transfer => {\n const isSameChainTransfer = quote.sourceChain.chainId === quote.targetChain.chainId;\n\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-pending',\n targetAsset: quote.assetOut,\n targetChain: quote.targetChain,\n toAddress: quote.toAddress,\n type: ServiceType.MARKR,\n source: {\n confirmationCount: 0,\n requiredConfirmationCount: isSameChainTransfer ? 1 : 2,\n startedAtMs,\n txHash,\n },\n };\n };\n\n if (maybeOneClickBatch && approvalRequest) {\n const batchStep: TransferStepDetails = {\n currentSignature: 1,\n currentSignatureReason: TransferSignatureReason.TokensTransfer,\n quote,\n requiredSignatures: 1,\n };\n\n const signBatch = evmSigner.signBatch;\n\n if (!signBatch) {\n throw new SdkError('One-click batch signer is not available.', ErrorCode.SIGNING_FAILED);\n }\n\n const swapRequest: EvmTransactionRequest = {\n chainId: caip2ToEip155HexChainId(quote.sourceChain.chainId),\n data: swap.data,\n from: fromAddress,\n // Gas is purposely left undefined here.\n // We can't estimate the gas because we aren't executing the\n // approval tx. It's up to the wallet to correctly handle gas estimation.\n gas: undefined,\n to: swap.to,\n value: swap.value,\n ...(gasSettings?.maxFeePerGas !== undefined\n ? {\n maxFeePerGas: gasSettings.maxFeePerGas,\n maxPriorityFeePerGas: gasSettings.maxPriorityFeePerGas,\n }\n : null),\n };\n\n onStepChange?.(batchStep);\n\n try {\n const txHashes = await signBatch(\n [approvalRequest, swapRequest],\n async (signedTxHash) => sourceClient.sendRawTransaction({ serializedTransaction: signedTxHash }),\n batchStep,\n );\n\n const swapTxHash = txHashes.at(-1);\n\n if (!swapTxHash) {\n throw new SdkError('One-click batch signing returned no transaction hashes.', ErrorCode.SIGNING_FAILED);\n }\n\n return makePendingTransfer(swapTxHash);\n } catch (error) {\n if (!fallbackToDefaultOnBatchFailure) {\n throw error;\n }\n }\n }\n\n if (requiresApprovalSignature && approvalRequest) {\n const approvalStep: TransferStepDetails = {\n currentSignature: 1,\n currentSignatureReason: TransferSignatureReason.AllowanceApproval,\n quote,\n requiredSignatures: 2,\n };\n\n onStepChange?.(approvalStep);\n\n const approvalTxHash = await evmSigner.sign(\n approvalRequest,\n async (signedTxHash) => sourceClient.sendRawTransaction({ serializedTransaction: signedTxHash }),\n approvalStep,\n );\n\n const approvalReceipt = await sourceClient.waitForTransactionReceipt({ hash: approvalTxHash });\n\n if (approvalReceipt.status === 'reverted') {\n return makeFailedTransferFromQuote(quote, {\n environment,\n errorCode: ErrorCode.TRANSACTION_REVERTED,\n errorReason: 'ERC20 approval transaction was reverted',\n });\n }\n }\n\n const swapGasWithMargin = await _estimateGasFromSwapResponse({\n crossChain: isCrossChainSwap,\n fromAddress,\n feeUnitsMarginBps: gasSettings?.estimateGasMarginBps,\n sourceClient,\n swap,\n });\n\n const swapRequest: EvmTransactionRequest = {\n chainId: caip2ToEip155HexChainId(quote.sourceChain.chainId),\n data: swap.data,\n from: fromAddress,\n gas: swapGasWithMargin,\n to: swap.to,\n value: swap.value,\n ...(gasSettings?.maxFeePerGas !== undefined\n ? {\n maxFeePerGas: gasSettings.maxFeePerGas,\n maxPriorityFeePerGas: gasSettings.maxPriorityFeePerGas,\n }\n : null),\n };\n\n const step: TransferStepDetails = {\n currentSignature: requiresApprovalSignature ? 2 : 1,\n currentSignatureReason: TransferSignatureReason.TokensTransfer,\n quote,\n requiredSignatures: requiresApprovalSignature ? 2 : 1,\n };\n\n onStepChange?.(step);\n\n const startedAtMs = Date.now();\n\n const txHash = await evmSigner.sign(\n swapRequest,\n async (signedTxHash) => sourceClient.sendRawTransaction({ serializedTransaction: signedTxHash }),\n step,\n );\n\n return makePendingTransfer(txHash, startedAtMs);\n}\n\n// ---------------------------------------------------------------------------\n// SVM (Solana) transfer path\n// ---------------------------------------------------------------------------\n\ninterface SvmTransferParams {\n apiOptions: ApiOptions;\n appId: string;\n environment: Environment;\n solanaSigner?: SolanaSigner;\n quote: Parameters<TransferService['transferAsset']>[0]['quote'];\n onStepChange?: Parameters<TransferService['transferAsset']>[0]['onStepChange'];\n}\n\nasync function _executeSvmTransfer({\n apiOptions,\n appId,\n environment,\n solanaSigner,\n quote,\n onStepChange,\n}: SvmTransferParams): Promise<Transfer> {\n if (!solanaSigner) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: 'solanaSigner is required for Solana transfers but was not provided.',\n });\n }\n\n if (!isSolAddress(quote.fromAddress)) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: 'fromAddress is not a valid Solana address.',\n });\n }\n\n const minAmountOut = calculateMarkrMinimumAmountOut({\n amountOut: quote.amountOut,\n assetOut: quote.assetOut,\n slippageBps: quote.slippageBps,\n });\n\n const tokenInAddressString = assetToAddressString(quote.assetIn, quote.sourceChain.chainId);\n const tokenOutAddressString = assetToAddressString(quote.assetOut, quote.targetChain.chainId);\n\n const swap = await markrSwap(apiOptions, {\n amountIn: quote.amountIn.toString(),\n appId,\n minAmountOut: minAmountOut.toString(),\n tokenIn: tokenInAddressString,\n tokenOut: tokenOutAddressString,\n userPublicKey: quote.fromAddress,\n uuid: quote.id,\n });\n\n if (!isSolanaSwapResponse(swap)) {\n throw new SdkError(ErrorReason.CHAIN_NOT_SUPPORTED, ErrorCode.INVALID_PARAMS, {\n details: 'Received non-SVM swap response from Markr, but only SVM swaps are supported for this source chain.',\n });\n }\n\n // TODO: Cleanup\n console.debug('[Fusion SDK] Original swap transaction (base64):', swap.swapTransaction);\n\n const swapTransactionBase64 = await refreshSolanaSwapTransactionBlockhash(\n swap.swapTransaction,\n getSolanaRpcForChain({ chain: quote.sourceChain }),\n );\n\n // TODO: Cleanup\n console.debug('[Fusion SDK] Refreshed swap transaction with new blockhash (base64):', swapTransactionBase64);\n\n const step: TransferStepDetails = {\n currentSignature: 1,\n currentSignatureReason: TransferSignatureReason.TokensTransfer,\n quote,\n requiredSignatures: 1,\n };\n\n onStepChange?.(step);\n\n const startedAtMs = Date.now();\n\n const txHash = await solanaSigner.signAndSend(\n {\n account: quote.fromAddress,\n serializedTx: swapTransactionBase64,\n },\n step,\n );\n\n const isCrossChainSwap = quote.sourceChain.chainId.toLowerCase() !== quote.targetChain.chainId.toLowerCase();\n\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-pending',\n targetAsset: quote.assetOut,\n targetChain: quote.targetChain,\n toAddress: quote.toAddress,\n type: ServiceType.MARKR,\n source: {\n confirmationCount: 0,\n requiredConfirmationCount: isCrossChainSwap ? 2 : SOLANA_REQUIRED_CONFIRMATIONS,\n startedAtMs,\n txHash,\n },\n };\n}\n\n/**\n * Determines whether one-click swap eligibility criteria is met.\n *\n * Only same-chain EVM swaps are currently eligible.\n */\nfunction isOneClickBatchEligible({\n approvalRequest,\n requiresApprovalSignature,\n signBatch,\n sourceChainId,\n targetChainId,\n}: {\n approvalRequest?: EvmTransactionRequest;\n requiresApprovalSignature: boolean;\n signBatch?: EvmSigner['signBatch'];\n sourceChainId: Caip2ChainId;\n targetChainId: Caip2ChainId;\n}): boolean {\n return (\n isEvmNamespace(sourceChainId) &&\n isEvmNamespace(targetChainId) &&\n sourceChainId === targetChainId &&\n requiresApprovalSignature &&\n approvalRequest !== undefined &&\n typeof signBatch === 'function'\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=[1,2],t=`0x0000000000000000000000000000000000000000`,n={NonceMapping:[{name:`chainId`,type:`string`},{name:`wallet`,type:`address`},{name:`id`,type:`bytes32`},{name:`nonce`,type:`uint256`}]};function r({signatureChainId:e,wallet:r,orderId:i,nonce:a}){return{domain:{name:`RelayNonceMapping`,version:`1`,chainId:e,verifyingContract:t},types:n,primaryType:`NonceMapping`,message:{chainId:`hyperliquid`,wallet:r,id:i,nonce:a}}}const i={"HyperliquidTransaction:SendAsset":[{name:`hyperliquidChain`,type:`string`},{name:`destination`,type:`string`},{name:`sourceDex`,type:`string`},{name:`destinationDex`,type:`string`},{name:`token`,type:`string`},{name:`amount`,type:`string`},{name:`fromSubAccount`,type:`string`},{name:`nonce`,type:`uint64`}]};function a({signatureChainId:e,hyperliquidChain:n,destination:r,token:a,amount:o,sourceDex:s,destinationDex:c,fromSubAccount:l,nonce:u}){return{domain:{name:`HyperliquidSignTransaction`,version:`1`,chainId:e,verifyingContract:t},types:i,primaryType:`HyperliquidTransaction:SendAsset`,message:{hyperliquidChain:n,destination:r,sourceDex:s,destinationDex:c,token:a,amount:o,fromSubAccount:l,nonce:u}}}function o(e){return{type:`sendAsset`,signatureChainId:`0x${e.signatureChainId.toString(16)}`,hyperliquidChain:e.hyperliquidChain,destination:e.destination,sourceDex:e.sourceDex,destinationDex:e.destinationDex,token:e.token,amount:e.amount,fromSubAccount:e.fromSubAccount,nonce:Number(e.nonce)}}exports.SUPPORTED_HL_PROTOCOL_VERSIONS=e,exports.buildHyperliquidExchangeAction=o,exports.buildHyperliquidSendAssetTypedData=a,exports.buildRelayNonceMappingTypedData=r;
|
|
2
|
+
//# sourceMappingURL=_hyperliquid-eip712.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_hyperliquid-eip712.cjs","names":[],"sources":["../../../src/transfer-service/markr/_hyperliquid-eip712.ts"],"sourcesContent":["/**\n * Version-pinned EIP-712 constants for Hyperliquid's 2-phase withdrawal flow.\n *\n * These shapes are public, stable protocol contracts owned by:\n * - Relay (`RelayNonceMapping`): https://docs.relay.link/references/api/api_guides/hyperliquid-support\n * - Hyperliquid (`HyperliquidTransaction:SendAsset`): https://hyperliquid.gitbook.io/hyperliquid-docs\n *\n * We pin them in the SDK like we pin `erc20Abi` for the approve flow — Markr\n * only sends us the dynamic fields (nonce, requestId, destination); the SDK\n * constructs both typed-data structures locally and signs them.\n *\n * If either protocol bumps its schema, the swap response's `protocolVersion`\n * field bumps, the SDK throws `UNSUPPORTED_PROTOCOL_VERSION` until the\n * application updates to a fusion-sdk release that pins the new version.\n */\nimport type { Address as EvmAddress } from 'viem';\nimport type { EvmTypedData } from '../../types/signer';\n\n/**\n * v1 — legacy. SDK reconstructs Relay's `RelayNonceMapping` schema locally.\n * Kept for backwards-compat while Markr maintains the legacy `/authorize`\n * body rebuild path. Will be removed once Relay drops the v1 contract.\n *\n * v2 — pass-through. Relay's `sign` and `authorize` envelopes flow verbatim\n * from `/swap` through Markr to the wallet/`POST /authorize`. New Relay\n * fields (e.g. `depositor`) propagate without an SDK release.\n */\nexport const SUPPORTED_HL_PROTOCOL_VERSIONS = [1, 2] as const;\nexport type SupportedHlProtocolVersion = (typeof SUPPORTED_HL_PROTOCOL_VERSIONS)[number];\n\nexport const HL_WITHDRAW_VERIFYING_CONTRACT: EvmAddress = '0x0000000000000000000000000000000000000000';\n\n// ---------------------------------------------------------------------------\n// Step 1: Relay nonce-mapping (binds a unique HL nonce to a Relay requestId)\n// ---------------------------------------------------------------------------\n\nconst RELAY_NONCE_MAPPING_DOMAIN_NAME = 'RelayNonceMapping' as const;\nconst RELAY_NONCE_MAPPING_VERSION = '1' as const;\n\nconst RELAY_NONCE_MAPPING_TYPES = {\n NonceMapping: [\n { name: 'chainId', type: 'string' },\n { name: 'wallet', type: 'address' },\n { name: 'id', type: 'bytes32' },\n { name: 'nonce', type: 'uint256' },\n ],\n} as const;\n\nexport interface BuildRelayNonceMappingParams {\n /** EIP-712 domain chainId; should match the wallet's active EVM chain. */\n readonly signatureChainId: number;\n /** Signer's EVM address. */\n readonly wallet: EvmAddress;\n /**\n * Relay order id (`0x{64hex}`) — the bytes32 that Relay's `/authorize`\n * matches against to bind a nonce → order. **Not** the cross-chain tracking\n * `requestId` from `steps[*].requestId`; those are different bytes32s.\n */\n readonly orderId: `0x${string}`;\n /** Shared nonce (uint256) used for both authorize and sendAsset signatures. */\n readonly nonce: bigint;\n}\n\nexport function buildRelayNonceMappingTypedData({\n signatureChainId,\n wallet,\n orderId,\n nonce,\n}: BuildRelayNonceMappingParams): EvmTypedData {\n return {\n domain: {\n name: RELAY_NONCE_MAPPING_DOMAIN_NAME,\n version: RELAY_NONCE_MAPPING_VERSION,\n chainId: signatureChainId,\n verifyingContract: HL_WITHDRAW_VERIFYING_CONTRACT,\n },\n types: RELAY_NONCE_MAPPING_TYPES,\n primaryType: 'NonceMapping',\n message: {\n chainId: 'hyperliquid',\n wallet,\n id: orderId,\n nonce,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Step 2: Hyperliquid SendAsset (the actual cross-chain transfer)\n// ---------------------------------------------------------------------------\n\nconst HL_SIGN_TX_DOMAIN_NAME = 'HyperliquidSignTransaction' as const;\nconst HL_SIGN_TX_VERSION = '1' as const;\n\nconst HL_SEND_ASSET_PRIMARY_TYPE = 'HyperliquidTransaction:SendAsset' as const;\n\nconst HL_SEND_ASSET_TYPES = {\n 'HyperliquidTransaction:SendAsset': [\n { name: 'hyperliquidChain', type: 'string' },\n { name: 'destination', type: 'string' },\n { name: 'sourceDex', type: 'string' },\n { name: 'destinationDex', type: 'string' },\n { name: 'token', type: 'string' },\n { name: 'amount', type: 'string' },\n { name: 'fromSubAccount', type: 'string' },\n { name: 'nonce', type: 'uint64' },\n ],\n} as const;\n\n/** Kept for backwards-compat at the import site. Relay's wire value is the\n * source of truth and we accept it as a generic string in the builders below. */\nexport type HyperliquidChain = 'Mainnet' | 'Testnet';\n\nexport interface BuildHyperliquidSendAssetParams {\n readonly signatureChainId: number;\n /** `\"Mainnet\"` / `\"Testnet\"` (or whatever Relay emits for new HL envs). */\n readonly hyperliquidChain: string;\n /** Relay's HL custodian address (from `/swap` response). */\n readonly destination: string;\n /** HL `{symbol}:{evmContract}` (e.g. `USDC:0x6d1e…054`). Relay-supplied verbatim. */\n readonly token: string;\n /** Human-readable decimal amount string at the spot token's display precision,\n * pre-formatted by Relay (e.g. `\"10.000000\"`). MUST be signed/submitted as-is. */\n readonly amount: string;\n /** Relay-supplied; empty string today but may carry a HIP-3 DEX name. */\n readonly sourceDex: string;\n /** Relay-supplied; empty string today. */\n readonly destinationDex: string;\n /** Relay-supplied; empty string for non-sub-account flows. */\n readonly fromSubAccount: string;\n readonly nonce: bigint;\n}\n\nexport function buildHyperliquidSendAssetTypedData({\n signatureChainId,\n hyperliquidChain,\n destination,\n token,\n amount,\n sourceDex,\n destinationDex,\n fromSubAccount,\n nonce,\n}: BuildHyperliquidSendAssetParams): EvmTypedData {\n return {\n domain: {\n name: HL_SIGN_TX_DOMAIN_NAME,\n version: HL_SIGN_TX_VERSION,\n chainId: signatureChainId,\n verifyingContract: HL_WITHDRAW_VERIFYING_CONTRACT,\n },\n types: HL_SEND_ASSET_TYPES,\n primaryType: HL_SEND_ASSET_PRIMARY_TYPE,\n message: {\n hyperliquidChain,\n destination,\n sourceDex,\n destinationDex,\n token,\n amount,\n fromSubAccount,\n nonce,\n },\n };\n}\n\n/**\n * Build the `action` object posted to `https://api.hyperliquid.xyz/exchange`.\n *\n * Hyperliquid's exchange API requires the same fields as the typed-data\n * `message` plus `type` and `signatureChainId` (hex). Keeping this in the\n * same file as the typed-data builder so they stay in lock-step on schema\n * changes.\n */\nexport interface BuildHyperliquidExchangeActionParams extends BuildHyperliquidSendAssetParams {}\n\nexport function buildHyperliquidExchangeAction(params: BuildHyperliquidExchangeActionParams): Record<string, unknown> {\n return {\n type: 'sendAsset',\n signatureChainId: `0x${params.signatureChainId.toString(16)}`,\n hyperliquidChain: params.hyperliquidChain,\n destination: params.destination,\n sourceDex: params.sourceDex,\n destinationDex: params.destinationDex,\n token: params.token,\n amount: params.amount,\n fromSubAccount: params.fromSubAccount,\n /**\n * HL's `/exchange` wire format expects `nonce` as a JSON number (millisecond\n * timestamp; fits in Number.MAX_SAFE_INTEGER comfortably). Keeping it as a\n * bigint here would make `JSON.stringify` throw \"Do not know how to\n * serialize a BigInt\" inside `hyperliquidExchangePost`.\n */\n nonce: Number(params.nonce),\n };\n}\n"],"mappings":"AA2BA,MAAa,EAAiC,CAAC,EAAG,EAAE,CAGvC,EAA6C,6CASpD,EAA4B,CAChC,aAAc,CACZ,CAAE,KAAM,UAAW,KAAM,SAAU,CACnC,CAAE,KAAM,SAAU,KAAM,UAAW,CACnC,CAAE,KAAM,KAAM,KAAM,UAAW,CAC/B,CAAE,KAAM,QAAS,KAAM,UAAW,CACnC,CACF,CAiBD,SAAgB,EAAgC,CAC9C,mBACA,SACA,UACA,SAC6C,CAC7C,MAAO,CACL,OAAQ,CACN,KAAM,oBACN,QAAS,IACT,QAAS,EACT,kBAAmB,EACpB,CACD,MAAO,EACP,YAAa,eACb,QAAS,CACP,QAAS,cACT,SACA,GAAI,EACJ,QACD,CACF,CAOH,MAKM,EAAsB,CAC1B,mCAAoC,CAClC,CAAE,KAAM,mBAAoB,KAAM,SAAU,CAC5C,CAAE,KAAM,cAAe,KAAM,SAAU,CACvC,CAAE,KAAM,YAAa,KAAM,SAAU,CACrC,CAAE,KAAM,iBAAkB,KAAM,SAAU,CAC1C,CAAE,KAAM,QAAS,KAAM,SAAU,CACjC,CAAE,KAAM,SAAU,KAAM,SAAU,CAClC,CAAE,KAAM,iBAAkB,KAAM,SAAU,CAC1C,CAAE,KAAM,QAAS,KAAM,SAAU,CAClC,CACF,CA0BD,SAAgB,EAAmC,CACjD,mBACA,mBACA,cACA,QACA,SACA,YACA,iBACA,iBACA,SACgD,CAChD,MAAO,CACL,OAAQ,CACN,KAAM,6BACN,QAAS,IACT,QAAS,EACT,kBAAmB,EACpB,CACD,MAAO,EACP,YAAa,mCACb,QAAS,CACP,mBACA,cACA,YACA,iBACA,QACA,SACA,iBACA,QACD,CACF,CAaH,SAAgB,EAA+B,EAAuE,CACpH,MAAO,CACL,KAAM,YACN,iBAAkB,KAAK,EAAO,iBAAiB,SAAS,GAAG,GAC3D,iBAAkB,EAAO,iBACzB,YAAa,EAAO,YACpB,UAAW,EAAO,UAClB,eAAgB,EAAO,eACvB,MAAO,EAAO,MACd,OAAQ,EAAO,OACf,eAAgB,EAAO,eAOvB,MAAO,OAAO,EAAO,MAAM,CAC5B"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=[1,2],t=`0x0000000000000000000000000000000000000000`,n={NonceMapping:[{name:`chainId`,type:`string`},{name:`wallet`,type:`address`},{name:`id`,type:`bytes32`},{name:`nonce`,type:`uint256`}]};function r({signatureChainId:e,wallet:r,orderId:i,nonce:a}){return{domain:{name:`RelayNonceMapping`,version:`1`,chainId:e,verifyingContract:t},types:n,primaryType:`NonceMapping`,message:{chainId:`hyperliquid`,wallet:r,id:i,nonce:a}}}const i={"HyperliquidTransaction:SendAsset":[{name:`hyperliquidChain`,type:`string`},{name:`destination`,type:`string`},{name:`sourceDex`,type:`string`},{name:`destinationDex`,type:`string`},{name:`token`,type:`string`},{name:`amount`,type:`string`},{name:`fromSubAccount`,type:`string`},{name:`nonce`,type:`uint64`}]};function a({signatureChainId:e,hyperliquidChain:n,destination:r,token:a,amount:o,sourceDex:s,destinationDex:c,fromSubAccount:l,nonce:u}){return{domain:{name:`HyperliquidSignTransaction`,version:`1`,chainId:e,verifyingContract:t},types:i,primaryType:`HyperliquidTransaction:SendAsset`,message:{hyperliquidChain:n,destination:r,sourceDex:s,destinationDex:c,token:a,amount:o,fromSubAccount:l,nonce:u}}}function o(e){return{type:`sendAsset`,signatureChainId:`0x${e.signatureChainId.toString(16)}`,hyperliquidChain:e.hyperliquidChain,destination:e.destination,sourceDex:e.sourceDex,destinationDex:e.destinationDex,token:e.token,amount:e.amount,fromSubAccount:e.fromSubAccount,nonce:Number(e.nonce)}}export{e as SUPPORTED_HL_PROTOCOL_VERSIONS,o as buildHyperliquidExchangeAction,a as buildHyperliquidSendAssetTypedData,r as buildRelayNonceMappingTypedData};
|
|
2
|
+
//# sourceMappingURL=_hyperliquid-eip712.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_hyperliquid-eip712.js","names":[],"sources":["../../../src/transfer-service/markr/_hyperliquid-eip712.ts"],"sourcesContent":["/**\n * Version-pinned EIP-712 constants for Hyperliquid's 2-phase withdrawal flow.\n *\n * These shapes are public, stable protocol contracts owned by:\n * - Relay (`RelayNonceMapping`): https://docs.relay.link/references/api/api_guides/hyperliquid-support\n * - Hyperliquid (`HyperliquidTransaction:SendAsset`): https://hyperliquid.gitbook.io/hyperliquid-docs\n *\n * We pin them in the SDK like we pin `erc20Abi` for the approve flow — Markr\n * only sends us the dynamic fields (nonce, requestId, destination); the SDK\n * constructs both typed-data structures locally and signs them.\n *\n * If either protocol bumps its schema, the swap response's `protocolVersion`\n * field bumps, the SDK throws `UNSUPPORTED_PROTOCOL_VERSION` until the\n * application updates to a fusion-sdk release that pins the new version.\n */\nimport type { Address as EvmAddress } from 'viem';\nimport type { EvmTypedData } from '../../types/signer';\n\n/**\n * v1 — legacy. SDK reconstructs Relay's `RelayNonceMapping` schema locally.\n * Kept for backwards-compat while Markr maintains the legacy `/authorize`\n * body rebuild path. Will be removed once Relay drops the v1 contract.\n *\n * v2 — pass-through. Relay's `sign` and `authorize` envelopes flow verbatim\n * from `/swap` through Markr to the wallet/`POST /authorize`. New Relay\n * fields (e.g. `depositor`) propagate without an SDK release.\n */\nexport const SUPPORTED_HL_PROTOCOL_VERSIONS = [1, 2] as const;\nexport type SupportedHlProtocolVersion = (typeof SUPPORTED_HL_PROTOCOL_VERSIONS)[number];\n\nexport const HL_WITHDRAW_VERIFYING_CONTRACT: EvmAddress = '0x0000000000000000000000000000000000000000';\n\n// ---------------------------------------------------------------------------\n// Step 1: Relay nonce-mapping (binds a unique HL nonce to a Relay requestId)\n// ---------------------------------------------------------------------------\n\nconst RELAY_NONCE_MAPPING_DOMAIN_NAME = 'RelayNonceMapping' as const;\nconst RELAY_NONCE_MAPPING_VERSION = '1' as const;\n\nconst RELAY_NONCE_MAPPING_TYPES = {\n NonceMapping: [\n { name: 'chainId', type: 'string' },\n { name: 'wallet', type: 'address' },\n { name: 'id', type: 'bytes32' },\n { name: 'nonce', type: 'uint256' },\n ],\n} as const;\n\nexport interface BuildRelayNonceMappingParams {\n /** EIP-712 domain chainId; should match the wallet's active EVM chain. */\n readonly signatureChainId: number;\n /** Signer's EVM address. */\n readonly wallet: EvmAddress;\n /**\n * Relay order id (`0x{64hex}`) — the bytes32 that Relay's `/authorize`\n * matches against to bind a nonce → order. **Not** the cross-chain tracking\n * `requestId` from `steps[*].requestId`; those are different bytes32s.\n */\n readonly orderId: `0x${string}`;\n /** Shared nonce (uint256) used for both authorize and sendAsset signatures. */\n readonly nonce: bigint;\n}\n\nexport function buildRelayNonceMappingTypedData({\n signatureChainId,\n wallet,\n orderId,\n nonce,\n}: BuildRelayNonceMappingParams): EvmTypedData {\n return {\n domain: {\n name: RELAY_NONCE_MAPPING_DOMAIN_NAME,\n version: RELAY_NONCE_MAPPING_VERSION,\n chainId: signatureChainId,\n verifyingContract: HL_WITHDRAW_VERIFYING_CONTRACT,\n },\n types: RELAY_NONCE_MAPPING_TYPES,\n primaryType: 'NonceMapping',\n message: {\n chainId: 'hyperliquid',\n wallet,\n id: orderId,\n nonce,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Step 2: Hyperliquid SendAsset (the actual cross-chain transfer)\n// ---------------------------------------------------------------------------\n\nconst HL_SIGN_TX_DOMAIN_NAME = 'HyperliquidSignTransaction' as const;\nconst HL_SIGN_TX_VERSION = '1' as const;\n\nconst HL_SEND_ASSET_PRIMARY_TYPE = 'HyperliquidTransaction:SendAsset' as const;\n\nconst HL_SEND_ASSET_TYPES = {\n 'HyperliquidTransaction:SendAsset': [\n { name: 'hyperliquidChain', type: 'string' },\n { name: 'destination', type: 'string' },\n { name: 'sourceDex', type: 'string' },\n { name: 'destinationDex', type: 'string' },\n { name: 'token', type: 'string' },\n { name: 'amount', type: 'string' },\n { name: 'fromSubAccount', type: 'string' },\n { name: 'nonce', type: 'uint64' },\n ],\n} as const;\n\n/** Kept for backwards-compat at the import site. Relay's wire value is the\n * source of truth and we accept it as a generic string in the builders below. */\nexport type HyperliquidChain = 'Mainnet' | 'Testnet';\n\nexport interface BuildHyperliquidSendAssetParams {\n readonly signatureChainId: number;\n /** `\"Mainnet\"` / `\"Testnet\"` (or whatever Relay emits for new HL envs). */\n readonly hyperliquidChain: string;\n /** Relay's HL custodian address (from `/swap` response). */\n readonly destination: string;\n /** HL `{symbol}:{evmContract}` (e.g. `USDC:0x6d1e…054`). Relay-supplied verbatim. */\n readonly token: string;\n /** Human-readable decimal amount string at the spot token's display precision,\n * pre-formatted by Relay (e.g. `\"10.000000\"`). MUST be signed/submitted as-is. */\n readonly amount: string;\n /** Relay-supplied; empty string today but may carry a HIP-3 DEX name. */\n readonly sourceDex: string;\n /** Relay-supplied; empty string today. */\n readonly destinationDex: string;\n /** Relay-supplied; empty string for non-sub-account flows. */\n readonly fromSubAccount: string;\n readonly nonce: bigint;\n}\n\nexport function buildHyperliquidSendAssetTypedData({\n signatureChainId,\n hyperliquidChain,\n destination,\n token,\n amount,\n sourceDex,\n destinationDex,\n fromSubAccount,\n nonce,\n}: BuildHyperliquidSendAssetParams): EvmTypedData {\n return {\n domain: {\n name: HL_SIGN_TX_DOMAIN_NAME,\n version: HL_SIGN_TX_VERSION,\n chainId: signatureChainId,\n verifyingContract: HL_WITHDRAW_VERIFYING_CONTRACT,\n },\n types: HL_SEND_ASSET_TYPES,\n primaryType: HL_SEND_ASSET_PRIMARY_TYPE,\n message: {\n hyperliquidChain,\n destination,\n sourceDex,\n destinationDex,\n token,\n amount,\n fromSubAccount,\n nonce,\n },\n };\n}\n\n/**\n * Build the `action` object posted to `https://api.hyperliquid.xyz/exchange`.\n *\n * Hyperliquid's exchange API requires the same fields as the typed-data\n * `message` plus `type` and `signatureChainId` (hex). Keeping this in the\n * same file as the typed-data builder so they stay in lock-step on schema\n * changes.\n */\nexport interface BuildHyperliquidExchangeActionParams extends BuildHyperliquidSendAssetParams {}\n\nexport function buildHyperliquidExchangeAction(params: BuildHyperliquidExchangeActionParams): Record<string, unknown> {\n return {\n type: 'sendAsset',\n signatureChainId: `0x${params.signatureChainId.toString(16)}`,\n hyperliquidChain: params.hyperliquidChain,\n destination: params.destination,\n sourceDex: params.sourceDex,\n destinationDex: params.destinationDex,\n token: params.token,\n amount: params.amount,\n fromSubAccount: params.fromSubAccount,\n /**\n * HL's `/exchange` wire format expects `nonce` as a JSON number (millisecond\n * timestamp; fits in Number.MAX_SAFE_INTEGER comfortably). Keeping it as a\n * bigint here would make `JSON.stringify` throw \"Do not know how to\n * serialize a BigInt\" inside `hyperliquidExchangePost`.\n */\n nonce: Number(params.nonce),\n };\n}\n"],"mappings":"AA2BA,MAAa,EAAiC,CAAC,EAAG,EAAE,CAGvC,EAA6C,6CASpD,EAA4B,CAChC,aAAc,CACZ,CAAE,KAAM,UAAW,KAAM,SAAU,CACnC,CAAE,KAAM,SAAU,KAAM,UAAW,CACnC,CAAE,KAAM,KAAM,KAAM,UAAW,CAC/B,CAAE,KAAM,QAAS,KAAM,UAAW,CACnC,CACF,CAiBD,SAAgB,EAAgC,CAC9C,mBACA,SACA,UACA,SAC6C,CAC7C,MAAO,CACL,OAAQ,CACN,KAAM,oBACN,QAAS,IACT,QAAS,EACT,kBAAmB,EACpB,CACD,MAAO,EACP,YAAa,eACb,QAAS,CACP,QAAS,cACT,SACA,GAAI,EACJ,QACD,CACF,CAOH,MAKM,EAAsB,CAC1B,mCAAoC,CAClC,CAAE,KAAM,mBAAoB,KAAM,SAAU,CAC5C,CAAE,KAAM,cAAe,KAAM,SAAU,CACvC,CAAE,KAAM,YAAa,KAAM,SAAU,CACrC,CAAE,KAAM,iBAAkB,KAAM,SAAU,CAC1C,CAAE,KAAM,QAAS,KAAM,SAAU,CACjC,CAAE,KAAM,SAAU,KAAM,SAAU,CAClC,CAAE,KAAM,iBAAkB,KAAM,SAAU,CAC1C,CAAE,KAAM,QAAS,KAAM,SAAU,CAClC,CACF,CA0BD,SAAgB,EAAmC,CACjD,mBACA,mBACA,cACA,QACA,SACA,YACA,iBACA,iBACA,SACgD,CAChD,MAAO,CACL,OAAQ,CACN,KAAM,6BACN,QAAS,IACT,QAAS,EACT,kBAAmB,EACpB,CACD,MAAO,EACP,YAAa,mCACb,QAAS,CACP,mBACA,cACA,YACA,iBACA,QACA,SACA,iBACA,QACD,CACF,CAaH,SAAgB,EAA+B,EAAuE,CACpH,MAAO,CACL,KAAM,YACN,iBAAkB,KAAK,EAAO,iBAAiB,SAAS,GAAG,GAC3D,iBAAkB,EAAO,iBACzB,YAAa,EAAO,YACpB,UAAW,EAAO,UAClB,eAAgB,EAAO,eACvB,MAAO,EAAO,MACd,OAAQ,EAAO,OACf,eAAgB,EAAO,eAOvB,MAAO,OAAO,EAAO,MAAM,CAC5B"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
require(`../../_virtual/_rolldown/runtime.cjs`);let e=require(`zod`);const t=e.z.object({withdrawable:e.z.union([e.z.string(),e.z.number()]).optional()}),n=e.z.object({balances:e.z.array(e.z.object({coin:e.z.string(),total:e.z.string(),hold:e.z.string()}))}),r=new Set([`unifiedAccount`,`portfolioMargin`,`dexAbstraction`,`disabled`,`default`]),i=e.z.string();function a(e){return r.has(e)?e:`default`}const o=e.z.object({status:e.z.enum([`ok`,`err`]),response:e.z.unknown().optional()});exports.HyperliquidClearinghouseStateSchema=t,exports.HyperliquidExchangeResponseSchema=o,exports.HyperliquidSpotClearinghouseStateSchema=n,exports.HyperliquidUserAbstractionSchema=i,exports.coerceHyperliquidUserAbstractionMode=a;
|
|
2
|
+
//# sourceMappingURL=_hyperliquid-schema.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_hyperliquid-schema.cjs","names":["z"],"sources":["../../../src/transfer-service/markr/_hyperliquid-schema.ts"],"sourcesContent":["/**\n * Zod schemas for the Hyperliquid HTTP responses we consume directly\n * (i.e. not via Markr).\n *\n * Kept in a sibling file rather than co-located with `_hyperliquid.ts` so\n * the project's \"wire schemas live next to each other\" convention holds:\n * - `_schema.ts` — Markr API responses\n * - `_hyperliquid-schema.ts` — Hyperliquid `/info` + `/exchange` responses\n * - `_hyperliquid-eip712.ts` — EIP-712 typed-data builders\n * - `_hyperliquid.ts` — HTTP helpers that wire the above together\n */\nimport { z } from 'zod';\n\n/**\n * Subset of Hyperliquid's `clearinghouseState` we actually consume.\n *\n * The real response carries dozens of fields (positions, margin summary, …)\n * but the pre-flight only needs `withdrawable`. Zod strips unknown keys by\n * default, so this schema validates the field we read without rejecting the\n * rest of the wire shape.\n */\nexport const HyperliquidClearinghouseStateSchema: z.ZodType<{ withdrawable?: string | number }> = z.object({\n withdrawable: z.union([z.string(), z.number()]).optional(),\n});\n\n/**\n * Subset of `spotClearinghouseState` — only the `balances` array, and within\n * each balance only the fields the pre-flight needs (`coin`, `total`, `hold`).\n *\n * Used by the unified-account branch of the pre-flight check: HL stores the\n * unified ledger in spot, so perp `withdrawable` alone *understates* the\n * actual withdrawable amount for `unifiedAccount` users.\n */\nexport const HyperliquidSpotClearinghouseStateSchema: z.ZodType<{\n balances: Array<{ coin: string; total: string; hold: string }>;\n}> = z.object({\n balances: z.array(\n z.object({\n coin: z.string(),\n total: z.string(),\n hold: z.string(),\n }),\n ),\n});\n\n/**\n * Hyperliquid account abstraction modes. Anything outside the known set is\n * coerced to `'default'` by {@link coerceHyperliquidUserAbstractionMode} so\n * the pre-flight stays conservative (no spot added).\n *\n * Mirrors `perps-sdk`'s `UserAbstractionMode`; redeclared here to keep\n * fusion-sdk free of a perps-sdk dependency for these tiny helpers.\n */\nexport type HyperliquidUserAbstractionMode =\n | 'unifiedAccount'\n | 'portfolioMargin'\n | 'dexAbstraction'\n | 'disabled'\n | 'default';\n\nconst KNOWN_ABSTRACTION_MODES = new Set<HyperliquidUserAbstractionMode>([\n 'unifiedAccount',\n 'portfolioMargin',\n 'dexAbstraction',\n 'disabled',\n 'default',\n]);\n\n/**\n * Hyperliquid's `userAbstraction` info endpoint returns a bare JSON string.\n * The schema just asserts the wire is a string; mapping unknown / future\n * values to `'default'` happens in {@link coerceHyperliquidUserAbstractionMode}\n * so the pyrotechnics around zod's `.transform` typing don't leak into the\n * exported schema (kept simple for `--isolatedDeclarations`).\n */\nexport const HyperliquidUserAbstractionSchema: z.ZodType<string> = z.string();\n\n/** Coerce a raw HL `userAbstraction` string to a known mode (unknown → `'default'`). */\nexport function coerceHyperliquidUserAbstractionMode(raw: string): HyperliquidUserAbstractionMode {\n return KNOWN_ABSTRACTION_MODES.has(raw as HyperliquidUserAbstractionMode)\n ? (raw as HyperliquidUserAbstractionMode)\n : 'default';\n}\n\nexport interface HyperliquidExchangeResponse {\n readonly status: 'ok' | 'err';\n readonly response?: unknown;\n}\n\n/**\n * HL `/exchange` always answers with `{ status: \"ok\" | \"err\", response?: … }`.\n * `response` is variant-shaped (action-specific), so we keep it as\n * `z.unknown()` and let callers inspect it on the `err` branch.\n */\nexport const HyperliquidExchangeResponseSchema: z.ZodType<HyperliquidExchangeResponse> = z.object({\n status: z.enum(['ok', 'err']),\n response: z.unknown().optional(),\n});\n"],"mappings":"qEAqBA,MAAa,EAAqFA,EAAAA,EAAE,OAAO,CACzG,aAAcA,EAAAA,EAAE,MAAM,CAACA,EAAAA,EAAE,QAAQ,CAAEA,EAAAA,EAAE,QAAQ,CAAC,CAAC,CAAC,UAAU,CAC3D,CAAC,CAUW,EAERA,EAAAA,EAAE,OAAO,CACZ,SAAUA,EAAAA,EAAE,MACVA,EAAAA,EAAE,OAAO,CACP,KAAMA,EAAAA,EAAE,QAAQ,CAChB,MAAOA,EAAAA,EAAE,QAAQ,CACjB,KAAMA,EAAAA,EAAE,QAAQ,CACjB,CAAC,CACH,CACF,CAAC,CAiBI,EAA0B,IAAI,IAAoC,CACtE,iBACA,kBACA,iBACA,WACA,UACD,CAAC,CASW,EAAsDA,EAAAA,EAAE,QAAQ,CAG7E,SAAgB,EAAqC,EAA6C,CAChG,OAAO,EAAwB,IAAI,EAAsC,CACpE,EACD,UAaN,MAAa,EAA4EA,EAAAA,EAAE,OAAO,CAChG,OAAQA,EAAAA,EAAE,KAAK,CAAC,KAAM,MAAM,CAAC,CAC7B,SAAUA,EAAAA,EAAE,SAAS,CAAC,UAAU,CACjC,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{z as e}from"zod";const t=e.object({withdrawable:e.union([e.string(),e.number()]).optional()}),n=e.object({balances:e.array(e.object({coin:e.string(),total:e.string(),hold:e.string()}))}),r=new Set([`unifiedAccount`,`portfolioMargin`,`dexAbstraction`,`disabled`,`default`]),i=e.string();function a(e){return r.has(e)?e:`default`}const o=e.object({status:e.enum([`ok`,`err`]),response:e.unknown().optional()});export{t as HyperliquidClearinghouseStateSchema,o as HyperliquidExchangeResponseSchema,n as HyperliquidSpotClearinghouseStateSchema,i as HyperliquidUserAbstractionSchema,a as coerceHyperliquidUserAbstractionMode};
|
|
2
|
+
//# sourceMappingURL=_hyperliquid-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_hyperliquid-schema.js","names":[],"sources":["../../../src/transfer-service/markr/_hyperliquid-schema.ts"],"sourcesContent":["/**\n * Zod schemas for the Hyperliquid HTTP responses we consume directly\n * (i.e. not via Markr).\n *\n * Kept in a sibling file rather than co-located with `_hyperliquid.ts` so\n * the project's \"wire schemas live next to each other\" convention holds:\n * - `_schema.ts` — Markr API responses\n * - `_hyperliquid-schema.ts` — Hyperliquid `/info` + `/exchange` responses\n * - `_hyperliquid-eip712.ts` — EIP-712 typed-data builders\n * - `_hyperliquid.ts` — HTTP helpers that wire the above together\n */\nimport { z } from 'zod';\n\n/**\n * Subset of Hyperliquid's `clearinghouseState` we actually consume.\n *\n * The real response carries dozens of fields (positions, margin summary, …)\n * but the pre-flight only needs `withdrawable`. Zod strips unknown keys by\n * default, so this schema validates the field we read without rejecting the\n * rest of the wire shape.\n */\nexport const HyperliquidClearinghouseStateSchema: z.ZodType<{ withdrawable?: string | number }> = z.object({\n withdrawable: z.union([z.string(), z.number()]).optional(),\n});\n\n/**\n * Subset of `spotClearinghouseState` — only the `balances` array, and within\n * each balance only the fields the pre-flight needs (`coin`, `total`, `hold`).\n *\n * Used by the unified-account branch of the pre-flight check: HL stores the\n * unified ledger in spot, so perp `withdrawable` alone *understates* the\n * actual withdrawable amount for `unifiedAccount` users.\n */\nexport const HyperliquidSpotClearinghouseStateSchema: z.ZodType<{\n balances: Array<{ coin: string; total: string; hold: string }>;\n}> = z.object({\n balances: z.array(\n z.object({\n coin: z.string(),\n total: z.string(),\n hold: z.string(),\n }),\n ),\n});\n\n/**\n * Hyperliquid account abstraction modes. Anything outside the known set is\n * coerced to `'default'` by {@link coerceHyperliquidUserAbstractionMode} so\n * the pre-flight stays conservative (no spot added).\n *\n * Mirrors `perps-sdk`'s `UserAbstractionMode`; redeclared here to keep\n * fusion-sdk free of a perps-sdk dependency for these tiny helpers.\n */\nexport type HyperliquidUserAbstractionMode =\n | 'unifiedAccount'\n | 'portfolioMargin'\n | 'dexAbstraction'\n | 'disabled'\n | 'default';\n\nconst KNOWN_ABSTRACTION_MODES = new Set<HyperliquidUserAbstractionMode>([\n 'unifiedAccount',\n 'portfolioMargin',\n 'dexAbstraction',\n 'disabled',\n 'default',\n]);\n\n/**\n * Hyperliquid's `userAbstraction` info endpoint returns a bare JSON string.\n * The schema just asserts the wire is a string; mapping unknown / future\n * values to `'default'` happens in {@link coerceHyperliquidUserAbstractionMode}\n * so the pyrotechnics around zod's `.transform` typing don't leak into the\n * exported schema (kept simple for `--isolatedDeclarations`).\n */\nexport const HyperliquidUserAbstractionSchema: z.ZodType<string> = z.string();\n\n/** Coerce a raw HL `userAbstraction` string to a known mode (unknown → `'default'`). */\nexport function coerceHyperliquidUserAbstractionMode(raw: string): HyperliquidUserAbstractionMode {\n return KNOWN_ABSTRACTION_MODES.has(raw as HyperliquidUserAbstractionMode)\n ? (raw as HyperliquidUserAbstractionMode)\n : 'default';\n}\n\nexport interface HyperliquidExchangeResponse {\n readonly status: 'ok' | 'err';\n readonly response?: unknown;\n}\n\n/**\n * HL `/exchange` always answers with `{ status: \"ok\" | \"err\", response?: … }`.\n * `response` is variant-shaped (action-specific), so we keep it as\n * `z.unknown()` and let callers inspect it on the `err` branch.\n */\nexport const HyperliquidExchangeResponseSchema: z.ZodType<HyperliquidExchangeResponse> = z.object({\n status: z.enum(['ok', 'err']),\n response: z.unknown().optional(),\n});\n"],"mappings":"wBAqBA,MAAa,EAAqF,EAAE,OAAO,CACzG,aAAc,EAAE,MAAM,CAAC,EAAE,QAAQ,CAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,UAAU,CAC3D,CAAC,CAUW,EAER,EAAE,OAAO,CACZ,SAAU,EAAE,MACV,EAAE,OAAO,CACP,KAAM,EAAE,QAAQ,CAChB,MAAO,EAAE,QAAQ,CACjB,KAAM,EAAE,QAAQ,CACjB,CAAC,CACH,CACF,CAAC,CAiBI,EAA0B,IAAI,IAAoC,CACtE,iBACA,kBACA,iBACA,WACA,UACD,CAAC,CASW,EAAsD,EAAE,QAAQ,CAG7E,SAAgB,EAAqC,EAA6C,CAChG,OAAO,EAAwB,IAAI,EAAsC,CACpE,EACD,UAaN,MAAa,EAA4E,EAAE,OAAO,CAChG,OAAQ,EAAE,KAAK,CAAC,KAAM,MAAM,CAAC,CAC7B,SAAU,EAAE,SAAS,CAAC,UAAU,CACjC,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
require(`../../_virtual/_rolldown/runtime.cjs`);const e=require(`../../constants.cjs`),t=require(`../../errors.cjs`),n=require(`../fetch-utilities.cjs`),r=require(`./_hyperliquid-schema.cjs`);let i=require(`viem`);const a={mainnet:`https://api.hyperliquid.xyz`,testnet:`https://api.hyperliquid-testnet.xyz`};function o(t){return t===e.Environment.PROD?a.mainnet:a.testnet}async function s(e,t,r,i){return n.fetchJson(`${e}/info`,{body:JSON.stringify(t),fetch:r,method:`POST`,signal:i})}function c(e){let t=r.HyperliquidClearinghouseStateSchema.safeParse(e);if(!t.success||t.data.withdrawable===void 0)return 0;let n=t.data.withdrawable,i=typeof n==`number`?n:Number.parseFloat(n);return Number.isFinite(i)?i:0}function l(e){let t=r.HyperliquidSpotClearinghouseStateSchema.safeParse(e);if(!t.success)return 0;let n=t.data.balances.find(e=>e.coin.toUpperCase()===`USDC`);if(n===void 0)return 0;let i=Number.parseFloat(n.total),a=Number.parseFloat(n.hold);return!Number.isFinite(i)||!Number.isFinite(a)?0:Math.max(0,i-a)}function u(e){let t=r.HyperliquidUserAbstractionSchema.safeParse(e);return t.success?r.coerceHyperliquidUserAbstractionMode(t.data):`default`}async function d({apiBaseUrl:e,user:t,fetch:n,signal:r}){let i=t.toLowerCase(),[a,o,d]=await Promise.allSettled([s(e,{type:`clearinghouseState`,user:i},n,r),s(e,{type:`spotClearinghouseState`,user:i},n,r),s(e,{type:`userAbstraction`,user:i},n,r)]),f=a.status===`fulfilled`?c(a.value):0;return(d.status===`fulfilled`?u(d.value):`default`)===`unifiedAccount`?f+(o.status===`fulfilled`?l(o.value):0):f}async function f({apiBaseUrl:e,signature:a,nonce:o,action:s,fetch:c,signal:l}){let{r:u,s:d,v:f}=(0,i.parseSignature)(a),p=await n.fetchJson(`${e}/exchange`,{body:JSON.stringify({action:s,nonce:Number(o),signature:{r:u,s:d,v:Number(f??0n)}},(e,t)=>typeof t==`bigint`?Number(t):t),fetch:c,method:`POST`,signal:l}),m=r.HyperliquidExchangeResponseSchema.safeParse(p);if(!m.success)throw new t.HttpError(`Hyperliquid /exchange returned an unexpected response shape.`,new Response(JSON.stringify(p),{status:502}),p);return m.data}exports.hyperliquidApiBaseUrlForEnvironment=o,exports.hyperliquidExchangePost=f,exports.hyperliquidGetWithdrawable=d;
|
|
2
|
+
//# sourceMappingURL=_hyperliquid.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_hyperliquid.cjs","names":["Environment","fetchJson","HyperliquidClearinghouseStateSchema","HyperliquidSpotClearinghouseStateSchema","HyperliquidUserAbstractionSchema","coerceHyperliquidUserAbstractionMode","HyperliquidExchangeResponseSchema","HttpError"],"sources":["../../../src/transfer-service/markr/_hyperliquid.ts"],"sourcesContent":["/**\n * Minimal Hyperliquid HTTP primitives for the 2-phase withdrawal flow.\n *\n * Kept inline in fusion-sdk (rather than depending on `perps-sdk`) because we\n * only need three tiny operations:\n * - `hyperliquidGetWithdrawable` — pre-flight balance check between signatures\n * - `hyperliquidExchangePost` — submit the signed sendAsset action\n * - `formatHlToken` / `formatHlAmount` — encode the wire shapes Hyperliquid expects\n *\n * Anything more substantial belongs in perps-sdk; injecting a heavy sibling\n * SDK into fusion-sdk just to share these helpers would be a non-trivial\n * dependency lift for marginal code reuse.\n */\nimport { parseSignature, type Hex } from 'viem';\nimport { fetchJson } from '../fetch-utilities';\nimport { HttpError } from '../../errors';\nimport { Environment } from '../../constants';\nimport type { Fetch } from '../../types/utility-types';\nimport type { HyperliquidChain } from './_hyperliquid-eip712';\nimport {\n HyperliquidClearinghouseStateSchema,\n HyperliquidExchangeResponseSchema,\n HyperliquidSpotClearinghouseStateSchema,\n HyperliquidUserAbstractionSchema,\n coerceHyperliquidUserAbstractionMode,\n type HyperliquidExchangeResponse,\n type HyperliquidUserAbstractionMode,\n} from './_hyperliquid-schema';\n\nexport type { HyperliquidExchangeResponse } from './_hyperliquid-schema';\n\nconst HL_API_BASE_URL = {\n mainnet: 'https://api.hyperliquid.xyz',\n testnet: 'https://api.hyperliquid-testnet.xyz',\n} as const;\n\nexport function hyperliquidApiBaseUrlForEnvironment(environment: Environment): string {\n // `Environment.PROD` → mainnet; `Environment.DEV`/`Environment.TEST` → testnet.\n // Matches how perps-sdk routes via `MAINNET_API_URL` / `TESTNET_API_URL` constants.\n return environment === Environment.PROD ? HL_API_BASE_URL.mainnet : HL_API_BASE_URL.testnet;\n}\n\nexport function hlChainForEnvironment(environment: Environment): HyperliquidChain {\n return environment === Environment.PROD ? 'Mainnet' : 'Testnet';\n}\n\n// ---------------------------------------------------------------------------\n// Info: clearinghouseState + spotClearinghouseState + userAbstraction\n// ---------------------------------------------------------------------------\n\nexport interface HyperliquidGetWithdrawableOptions {\n /** Hyperliquid API base URL — usually derived via {@link hyperliquidApiBaseUrlForEnvironment}. */\n readonly apiBaseUrl: string;\n /** User wallet address (lowercase per HL convention; we normalize here). */\n readonly user: string;\n readonly fetch?: Fetch;\n readonly signal?: AbortSignal;\n}\n\nasync function postInfo(\n apiBaseUrl: string,\n body: Record<string, unknown>,\n customFetch?: Fetch,\n signal?: AbortSignal,\n): Promise<unknown> {\n return fetchJson(`${apiBaseUrl}/info`, {\n body: JSON.stringify(body),\n fetch: customFetch,\n method: 'POST',\n signal,\n });\n}\n\nfunction parsePerpWithdrawable(raw: unknown): number {\n const parsed = HyperliquidClearinghouseStateSchema.safeParse(raw);\n if (!parsed.success || parsed.data.withdrawable === undefined) {\n return 0;\n }\n const v = parsed.data.withdrawable;\n const n = typeof v === 'number' ? v : Number.parseFloat(v);\n return Number.isFinite(n) ? n : 0;\n}\n\n/** Mirrors perps-sdk's `spotUsdcAvailableFromSpotState`: `total - hold` for USDC. */\nfunction parseSpotUsdcAvailable(raw: unknown): number {\n const parsed = HyperliquidSpotClearinghouseStateSchema.safeParse(raw);\n if (!parsed.success) {\n return 0;\n }\n const row = parsed.data.balances.find((b) => b.coin.toUpperCase() === 'USDC');\n if (row === undefined) {\n return 0;\n }\n const total = Number.parseFloat(row.total);\n const hold = Number.parseFloat(row.hold);\n if (!Number.isFinite(total) || !Number.isFinite(hold)) {\n return 0;\n }\n return Math.max(0, total - hold);\n}\n\nfunction parseUserAbstractionMode(raw: unknown): HyperliquidUserAbstractionMode {\n const parsed = HyperliquidUserAbstractionSchema.safeParse(raw);\n return parsed.success ? coerceHyperliquidUserAbstractionMode(parsed.data) : 'default';\n}\n\n/**\n * Fetch the user's effective withdrawable USD balance from Hyperliquid.\n *\n * Account-mode aware:\n *\n * - Standard / `dexAbstraction` / `disabled` / `default` /\n * `portfolioMargin` — only the perp `clearinghouseState.withdrawable`\n * field counts (spot is a separate pool).\n * - **`unifiedAccount`** — HL keeps the unified ledger in\n * `spotClearinghouseState`; perp queries are \"meaningless\" per HL docs.\n * We add `(spot.total − spot.hold)` for USDC to the perp value.\n *\n * Three `/info` POSTs run in parallel:\n * 1. `clearinghouseState` — perp withdrawable\n * 2. `spotClearinghouseState` — spot total / hold for USDC\n * 3. `userAbstraction` — abstraction mode\n *\n * Defensive on purpose: any individual sub-fetch failure resolves to a\n * conservative default (perp `0` / spot `0` / mode `'default'`) so the\n * caller surfaces `HL_WITHDRAWABLE_DROPPED` rather than crashing between\n * the two wallet prompts.\n */\nexport async function hyperliquidGetWithdrawable({\n apiBaseUrl,\n user,\n fetch: customFetch,\n signal,\n}: HyperliquidGetWithdrawableOptions): Promise<number> {\n const lowerUser = user.toLowerCase();\n const [perpRes, spotRes, modeRes] = await Promise.allSettled([\n postInfo(apiBaseUrl, { type: 'clearinghouseState', user: lowerUser }, customFetch, signal),\n postInfo(apiBaseUrl, { type: 'spotClearinghouseState', user: lowerUser }, customFetch, signal),\n postInfo(apiBaseUrl, { type: 'userAbstraction', user: lowerUser }, customFetch, signal),\n ]);\n\n const perp = perpRes.status === 'fulfilled' ? parsePerpWithdrawable(perpRes.value) : 0;\n const mode = modeRes.status === 'fulfilled' ? parseUserAbstractionMode(modeRes.value) : 'default';\n\n // Spot only counts for unified accounts. For every other mode the spot\n // ledger is a separate pool the user would have to `usdClassTransfer`\n // before HL will accept it as withdrawable from perp.\n if (mode !== 'unifiedAccount') {\n return perp;\n }\n const spotUsdc = spotRes.status === 'fulfilled' ? parseSpotUsdcAvailable(spotRes.value) : 0;\n return perp + spotUsdc;\n}\n\n// ---------------------------------------------------------------------------\n// Exchange: POST signed sendAsset action\n// ---------------------------------------------------------------------------\n\nexport interface HyperliquidExchangePostOptions {\n readonly apiBaseUrl: string;\n /** EIP-712 signature returned by the wallet (`0x{r}{s}{v}`, 65 bytes). */\n readonly signature: Hex;\n readonly nonce: bigint;\n /** Action payload (exactly the value object signed in step 2). */\n readonly action: Record<string, unknown>;\n readonly fetch?: Fetch;\n readonly signal?: AbortSignal;\n}\n\n/**\n * Submit a signed Hyperliquid `sendAsset` action to `/exchange`.\n *\n * Hyperliquid expects:\n * - `signature` split into `{ r, s, v: number }` — NOT bigint, NOT raw hex.\n * - `nonce` as a JS number/bigint at the top level alongside `action`.\n * - `action` identical to the EIP-712 message (plus `type` + `signatureChainId`).\n *\n * Success criterion: HTTP 200 AND body `status === \"ok\"`.\n */\nexport async function hyperliquidExchangePost({\n apiBaseUrl,\n signature,\n nonce,\n action,\n fetch: customFetch,\n signal,\n}: HyperliquidExchangePostOptions): Promise<HyperliquidExchangeResponse> {\n const { r, s, v } = parseSignature(signature);\n\n const url = `${apiBaseUrl}/exchange`;\n\n const rawJson = await fetchJson(url, {\n /**\n * HL's `/exchange` wants `nonce` as a JSON Number (millisecond timestamp,\n * always safely under `Number.MAX_SAFE_INTEGER`). The replacer is a\n * belt-and-braces guard — `JSON.stringify` of a bigint throws \"Do not know\n * how to serialize a BigInt\", so we coerce any stray bigint to a Number\n * rather than fail at the wire boundary.\n */\n body: JSON.stringify(\n {\n action,\n nonce: Number(nonce),\n signature: {\n r,\n s,\n v: Number(v ?? 0n),\n },\n },\n (_key, value) => (typeof value === 'bigint' ? Number(value) : value),\n ),\n fetch: customFetch,\n method: 'POST',\n signal,\n });\n\n const parsed = HyperliquidExchangeResponseSchema.safeParse(rawJson);\n if (!parsed.success) {\n // HL is reliable about returning { status } — anything else is malformed.\n throw new HttpError(\n `Hyperliquid /exchange returned an unexpected response shape.`,\n new Response(JSON.stringify(rawJson), { status: 502 }),\n rawJson,\n );\n }\n\n return parsed.data;\n}\n\n// ---------------------------------------------------------------------------\n// Token / amount formatting helpers\n// ---------------------------------------------------------------------------\n\n// HL spot USDC token id — same on mainnet and testnet. Kept here as a constant\n// since the rest of the SDK doesn't otherwise care about HL token registries.\nexport const HL_SPOT_USDC_ADDRESS = '0x6d1e7cde53ba9467b783cb7c530ce054' as const;\n\n/**\n * Format a token for Hyperliquid's `sendAsset` action.\n *\n * HL wire format is `\"{symbol}:{evmContract}\"`. For perps USDC withdrawals\n * (the only case we currently support) this is fixed to `USDC:` + the spot\n * USDC token id; HL maps that to the perps balance source-side.\n */\nexport function formatHlToken(symbol: string, tokenAddress: string = HL_SPOT_USDC_ADDRESS): string {\n return `${symbol}:${tokenAddress}`;\n}\n\n/**\n * Format a `bigint` amount in smallest units → HL's human-readable decimal\n * string. HL truncates trailing zeros internally but a fixed decimal width\n * keeps signatures deterministic across replays.\n */\nexport function formatHlAmount(amount: bigint, decimals: number): string {\n if (decimals === 0) {\n return amount.toString();\n }\n\n const abs = amount < 0n ? -amount : amount;\n const divisor = 10n ** BigInt(decimals);\n const whole = abs / divisor;\n const fraction = abs % divisor;\n\n const fractionStr = fraction.toString().padStart(decimals, '0');\n const sign = amount < 0n ? '-' : '';\n\n return `${sign}${whole}.${fractionStr}`;\n}\n"],"mappings":"sNA+BA,MAAM,EAAkB,CACtB,QAAS,8BACT,QAAS,sCACV,CAED,SAAgB,EAAoC,EAAkC,CAGpF,OAAO,IAAgBA,EAAAA,YAAY,KAAO,EAAgB,QAAU,EAAgB,QAoBtF,eAAe,EACb,EACA,EACA,EACA,EACkB,CAClB,OAAOC,EAAAA,UAAU,GAAG,EAAW,OAAQ,CACrC,KAAM,KAAK,UAAU,EAAK,CAC1B,MAAO,EACP,OAAQ,OACR,SACD,CAAC,CAGJ,SAAS,EAAsB,EAAsB,CACnD,IAAM,EAASC,EAAAA,oCAAoC,UAAU,EAAI,CACjE,GAAI,CAAC,EAAO,SAAW,EAAO,KAAK,eAAiB,IAAA,GAClD,MAAO,GAET,IAAM,EAAI,EAAO,KAAK,aAChB,EAAI,OAAO,GAAM,SAAW,EAAI,OAAO,WAAW,EAAE,CAC1D,OAAO,OAAO,SAAS,EAAE,CAAG,EAAI,EAIlC,SAAS,EAAuB,EAAsB,CACpD,IAAM,EAASC,EAAAA,wCAAwC,UAAU,EAAI,CACrE,GAAI,CAAC,EAAO,QACV,MAAO,GAET,IAAM,EAAM,EAAO,KAAK,SAAS,KAAM,GAAM,EAAE,KAAK,aAAa,GAAK,OAAO,CAC7E,GAAI,IAAQ,IAAA,GACV,MAAO,GAET,IAAM,EAAQ,OAAO,WAAW,EAAI,MAAM,CACpC,EAAO,OAAO,WAAW,EAAI,KAAK,CAIxC,MAHI,CAAC,OAAO,SAAS,EAAM,EAAI,CAAC,OAAO,SAAS,EAAK,CAC5C,EAEF,KAAK,IAAI,EAAG,EAAQ,EAAK,CAGlC,SAAS,EAAyB,EAA8C,CAC9E,IAAM,EAASC,EAAAA,iCAAiC,UAAU,EAAI,CAC9D,OAAO,EAAO,QAAUC,EAAAA,qCAAqC,EAAO,KAAK,CAAG,UAyB9E,eAAsB,EAA2B,CAC/C,aACA,OACA,MAAO,EACP,UACqD,CACrD,IAAM,EAAY,EAAK,aAAa,CAC9B,CAAC,EAAS,EAAS,GAAW,MAAM,QAAQ,WAAW,CAC3D,EAAS,EAAY,CAAE,KAAM,qBAAsB,KAAM,EAAW,CAAE,EAAa,EAAO,CAC1F,EAAS,EAAY,CAAE,KAAM,yBAA0B,KAAM,EAAW,CAAE,EAAa,EAAO,CAC9F,EAAS,EAAY,CAAE,KAAM,kBAAmB,KAAM,EAAW,CAAE,EAAa,EAAO,CACxF,CAAC,CAEI,EAAO,EAAQ,SAAW,YAAc,EAAsB,EAAQ,MAAM,CAAG,EAUrF,OATa,EAAQ,SAAW,YAAc,EAAyB,EAAQ,MAAM,CAAG,aAK3E,iBAIN,GADU,EAAQ,SAAW,YAAc,EAAuB,EAAQ,MAAM,CAAG,GAFjF,EA+BX,eAAsB,EAAwB,CAC5C,aACA,YACA,QACA,SACA,MAAO,EACP,UACuE,CACvE,GAAM,CAAE,IAAG,IAAG,MAAA,EAAA,EAAA,gBAAqB,EAAU,CAIvC,EAAU,MAAMJ,EAAAA,UAFV,GAAG,EAAW,WAEW,CAQnC,KAAM,KAAK,UACT,CACE,SACA,MAAO,OAAO,EAAM,CACpB,UAAW,CACT,IACA,IACA,EAAG,OAAO,GAAK,GAAG,CACnB,CACF,EACA,EAAM,IAAW,OAAO,GAAU,SAAW,OAAO,EAAM,CAAG,EAC/D,CACD,MAAO,EACP,OAAQ,OACR,SACD,CAAC,CAEI,EAASK,EAAAA,kCAAkC,UAAU,EAAQ,CACnE,GAAI,CAAC,EAAO,QAEV,MAAM,IAAIC,EAAAA,UACR,+DACA,IAAI,SAAS,KAAK,UAAU,EAAQ,CAAE,CAAE,OAAQ,IAAK,CAAC,CACtD,EACD,CAGH,OAAO,EAAO"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{Environment as e}from"../../constants.js";import{HttpError as t}from"../../errors.js";import{fetchJson as n}from"../fetch-utilities.js";import{HyperliquidClearinghouseStateSchema as r,HyperliquidExchangeResponseSchema as i,HyperliquidSpotClearinghouseStateSchema as a,HyperliquidUserAbstractionSchema as o,coerceHyperliquidUserAbstractionMode as s}from"./_hyperliquid-schema.js";import{parseSignature as c}from"viem";const l={mainnet:`https://api.hyperliquid.xyz`,testnet:`https://api.hyperliquid-testnet.xyz`};function u(t){return t===e.PROD?l.mainnet:l.testnet}async function d(e,t,r,i){return n(`${e}/info`,{body:JSON.stringify(t),fetch:r,method:`POST`,signal:i})}function f(e){let t=r.safeParse(e);if(!t.success||t.data.withdrawable===void 0)return 0;let n=t.data.withdrawable,i=typeof n==`number`?n:Number.parseFloat(n);return Number.isFinite(i)?i:0}function p(e){let t=a.safeParse(e);if(!t.success)return 0;let n=t.data.balances.find(e=>e.coin.toUpperCase()===`USDC`);if(n===void 0)return 0;let r=Number.parseFloat(n.total),i=Number.parseFloat(n.hold);return!Number.isFinite(r)||!Number.isFinite(i)?0:Math.max(0,r-i)}function m(e){let t=o.safeParse(e);return t.success?s(t.data):`default`}async function h({apiBaseUrl:e,user:t,fetch:n,signal:r}){let i=t.toLowerCase(),[a,o,s]=await Promise.allSettled([d(e,{type:`clearinghouseState`,user:i},n,r),d(e,{type:`spotClearinghouseState`,user:i},n,r),d(e,{type:`userAbstraction`,user:i},n,r)]),c=a.status===`fulfilled`?f(a.value):0;return(s.status===`fulfilled`?m(s.value):`default`)===`unifiedAccount`?c+(o.status===`fulfilled`?p(o.value):0):c}async function g({apiBaseUrl:e,signature:r,nonce:a,action:o,fetch:s,signal:l}){let{r:u,s:d,v:f}=c(r),p=await n(`${e}/exchange`,{body:JSON.stringify({action:o,nonce:Number(a),signature:{r:u,s:d,v:Number(f??0n)}},(e,t)=>typeof t==`bigint`?Number(t):t),fetch:s,method:`POST`,signal:l}),m=i.safeParse(p);if(!m.success)throw new t(`Hyperliquid /exchange returned an unexpected response shape.`,new Response(JSON.stringify(p),{status:502}),p);return m.data}export{u as hyperliquidApiBaseUrlForEnvironment,g as hyperliquidExchangePost,h as hyperliquidGetWithdrawable};
|
|
2
|
+
//# sourceMappingURL=_hyperliquid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_hyperliquid.js","names":[],"sources":["../../../src/transfer-service/markr/_hyperliquid.ts"],"sourcesContent":["/**\n * Minimal Hyperliquid HTTP primitives for the 2-phase withdrawal flow.\n *\n * Kept inline in fusion-sdk (rather than depending on `perps-sdk`) because we\n * only need three tiny operations:\n * - `hyperliquidGetWithdrawable` — pre-flight balance check between signatures\n * - `hyperliquidExchangePost` — submit the signed sendAsset action\n * - `formatHlToken` / `formatHlAmount` — encode the wire shapes Hyperliquid expects\n *\n * Anything more substantial belongs in perps-sdk; injecting a heavy sibling\n * SDK into fusion-sdk just to share these helpers would be a non-trivial\n * dependency lift for marginal code reuse.\n */\nimport { parseSignature, type Hex } from 'viem';\nimport { fetchJson } from '../fetch-utilities';\nimport { HttpError } from '../../errors';\nimport { Environment } from '../../constants';\nimport type { Fetch } from '../../types/utility-types';\nimport type { HyperliquidChain } from './_hyperliquid-eip712';\nimport {\n HyperliquidClearinghouseStateSchema,\n HyperliquidExchangeResponseSchema,\n HyperliquidSpotClearinghouseStateSchema,\n HyperliquidUserAbstractionSchema,\n coerceHyperliquidUserAbstractionMode,\n type HyperliquidExchangeResponse,\n type HyperliquidUserAbstractionMode,\n} from './_hyperliquid-schema';\n\nexport type { HyperliquidExchangeResponse } from './_hyperliquid-schema';\n\nconst HL_API_BASE_URL = {\n mainnet: 'https://api.hyperliquid.xyz',\n testnet: 'https://api.hyperliquid-testnet.xyz',\n} as const;\n\nexport function hyperliquidApiBaseUrlForEnvironment(environment: Environment): string {\n // `Environment.PROD` → mainnet; `Environment.DEV`/`Environment.TEST` → testnet.\n // Matches how perps-sdk routes via `MAINNET_API_URL` / `TESTNET_API_URL` constants.\n return environment === Environment.PROD ? HL_API_BASE_URL.mainnet : HL_API_BASE_URL.testnet;\n}\n\nexport function hlChainForEnvironment(environment: Environment): HyperliquidChain {\n return environment === Environment.PROD ? 'Mainnet' : 'Testnet';\n}\n\n// ---------------------------------------------------------------------------\n// Info: clearinghouseState + spotClearinghouseState + userAbstraction\n// ---------------------------------------------------------------------------\n\nexport interface HyperliquidGetWithdrawableOptions {\n /** Hyperliquid API base URL — usually derived via {@link hyperliquidApiBaseUrlForEnvironment}. */\n readonly apiBaseUrl: string;\n /** User wallet address (lowercase per HL convention; we normalize here). */\n readonly user: string;\n readonly fetch?: Fetch;\n readonly signal?: AbortSignal;\n}\n\nasync function postInfo(\n apiBaseUrl: string,\n body: Record<string, unknown>,\n customFetch?: Fetch,\n signal?: AbortSignal,\n): Promise<unknown> {\n return fetchJson(`${apiBaseUrl}/info`, {\n body: JSON.stringify(body),\n fetch: customFetch,\n method: 'POST',\n signal,\n });\n}\n\nfunction parsePerpWithdrawable(raw: unknown): number {\n const parsed = HyperliquidClearinghouseStateSchema.safeParse(raw);\n if (!parsed.success || parsed.data.withdrawable === undefined) {\n return 0;\n }\n const v = parsed.data.withdrawable;\n const n = typeof v === 'number' ? v : Number.parseFloat(v);\n return Number.isFinite(n) ? n : 0;\n}\n\n/** Mirrors perps-sdk's `spotUsdcAvailableFromSpotState`: `total - hold` for USDC. */\nfunction parseSpotUsdcAvailable(raw: unknown): number {\n const parsed = HyperliquidSpotClearinghouseStateSchema.safeParse(raw);\n if (!parsed.success) {\n return 0;\n }\n const row = parsed.data.balances.find((b) => b.coin.toUpperCase() === 'USDC');\n if (row === undefined) {\n return 0;\n }\n const total = Number.parseFloat(row.total);\n const hold = Number.parseFloat(row.hold);\n if (!Number.isFinite(total) || !Number.isFinite(hold)) {\n return 0;\n }\n return Math.max(0, total - hold);\n}\n\nfunction parseUserAbstractionMode(raw: unknown): HyperliquidUserAbstractionMode {\n const parsed = HyperliquidUserAbstractionSchema.safeParse(raw);\n return parsed.success ? coerceHyperliquidUserAbstractionMode(parsed.data) : 'default';\n}\n\n/**\n * Fetch the user's effective withdrawable USD balance from Hyperliquid.\n *\n * Account-mode aware:\n *\n * - Standard / `dexAbstraction` / `disabled` / `default` /\n * `portfolioMargin` — only the perp `clearinghouseState.withdrawable`\n * field counts (spot is a separate pool).\n * - **`unifiedAccount`** — HL keeps the unified ledger in\n * `spotClearinghouseState`; perp queries are \"meaningless\" per HL docs.\n * We add `(spot.total − spot.hold)` for USDC to the perp value.\n *\n * Three `/info` POSTs run in parallel:\n * 1. `clearinghouseState` — perp withdrawable\n * 2. `spotClearinghouseState` — spot total / hold for USDC\n * 3. `userAbstraction` — abstraction mode\n *\n * Defensive on purpose: any individual sub-fetch failure resolves to a\n * conservative default (perp `0` / spot `0` / mode `'default'`) so the\n * caller surfaces `HL_WITHDRAWABLE_DROPPED` rather than crashing between\n * the two wallet prompts.\n */\nexport async function hyperliquidGetWithdrawable({\n apiBaseUrl,\n user,\n fetch: customFetch,\n signal,\n}: HyperliquidGetWithdrawableOptions): Promise<number> {\n const lowerUser = user.toLowerCase();\n const [perpRes, spotRes, modeRes] = await Promise.allSettled([\n postInfo(apiBaseUrl, { type: 'clearinghouseState', user: lowerUser }, customFetch, signal),\n postInfo(apiBaseUrl, { type: 'spotClearinghouseState', user: lowerUser }, customFetch, signal),\n postInfo(apiBaseUrl, { type: 'userAbstraction', user: lowerUser }, customFetch, signal),\n ]);\n\n const perp = perpRes.status === 'fulfilled' ? parsePerpWithdrawable(perpRes.value) : 0;\n const mode = modeRes.status === 'fulfilled' ? parseUserAbstractionMode(modeRes.value) : 'default';\n\n // Spot only counts for unified accounts. For every other mode the spot\n // ledger is a separate pool the user would have to `usdClassTransfer`\n // before HL will accept it as withdrawable from perp.\n if (mode !== 'unifiedAccount') {\n return perp;\n }\n const spotUsdc = spotRes.status === 'fulfilled' ? parseSpotUsdcAvailable(spotRes.value) : 0;\n return perp + spotUsdc;\n}\n\n// ---------------------------------------------------------------------------\n// Exchange: POST signed sendAsset action\n// ---------------------------------------------------------------------------\n\nexport interface HyperliquidExchangePostOptions {\n readonly apiBaseUrl: string;\n /** EIP-712 signature returned by the wallet (`0x{r}{s}{v}`, 65 bytes). */\n readonly signature: Hex;\n readonly nonce: bigint;\n /** Action payload (exactly the value object signed in step 2). */\n readonly action: Record<string, unknown>;\n readonly fetch?: Fetch;\n readonly signal?: AbortSignal;\n}\n\n/**\n * Submit a signed Hyperliquid `sendAsset` action to `/exchange`.\n *\n * Hyperliquid expects:\n * - `signature` split into `{ r, s, v: number }` — NOT bigint, NOT raw hex.\n * - `nonce` as a JS number/bigint at the top level alongside `action`.\n * - `action` identical to the EIP-712 message (plus `type` + `signatureChainId`).\n *\n * Success criterion: HTTP 200 AND body `status === \"ok\"`.\n */\nexport async function hyperliquidExchangePost({\n apiBaseUrl,\n signature,\n nonce,\n action,\n fetch: customFetch,\n signal,\n}: HyperliquidExchangePostOptions): Promise<HyperliquidExchangeResponse> {\n const { r, s, v } = parseSignature(signature);\n\n const url = `${apiBaseUrl}/exchange`;\n\n const rawJson = await fetchJson(url, {\n /**\n * HL's `/exchange` wants `nonce` as a JSON Number (millisecond timestamp,\n * always safely under `Number.MAX_SAFE_INTEGER`). The replacer is a\n * belt-and-braces guard — `JSON.stringify` of a bigint throws \"Do not know\n * how to serialize a BigInt\", so we coerce any stray bigint to a Number\n * rather than fail at the wire boundary.\n */\n body: JSON.stringify(\n {\n action,\n nonce: Number(nonce),\n signature: {\n r,\n s,\n v: Number(v ?? 0n),\n },\n },\n (_key, value) => (typeof value === 'bigint' ? Number(value) : value),\n ),\n fetch: customFetch,\n method: 'POST',\n signal,\n });\n\n const parsed = HyperliquidExchangeResponseSchema.safeParse(rawJson);\n if (!parsed.success) {\n // HL is reliable about returning { status } — anything else is malformed.\n throw new HttpError(\n `Hyperliquid /exchange returned an unexpected response shape.`,\n new Response(JSON.stringify(rawJson), { status: 502 }),\n rawJson,\n );\n }\n\n return parsed.data;\n}\n\n// ---------------------------------------------------------------------------\n// Token / amount formatting helpers\n// ---------------------------------------------------------------------------\n\n// HL spot USDC token id — same on mainnet and testnet. Kept here as a constant\n// since the rest of the SDK doesn't otherwise care about HL token registries.\nexport const HL_SPOT_USDC_ADDRESS = '0x6d1e7cde53ba9467b783cb7c530ce054' as const;\n\n/**\n * Format a token for Hyperliquid's `sendAsset` action.\n *\n * HL wire format is `\"{symbol}:{evmContract}\"`. For perps USDC withdrawals\n * (the only case we currently support) this is fixed to `USDC:` + the spot\n * USDC token id; HL maps that to the perps balance source-side.\n */\nexport function formatHlToken(symbol: string, tokenAddress: string = HL_SPOT_USDC_ADDRESS): string {\n return `${symbol}:${tokenAddress}`;\n}\n\n/**\n * Format a `bigint` amount in smallest units → HL's human-readable decimal\n * string. HL truncates trailing zeros internally but a fixed decimal width\n * keeps signatures deterministic across replays.\n */\nexport function formatHlAmount(amount: bigint, decimals: number): string {\n if (decimals === 0) {\n return amount.toString();\n }\n\n const abs = amount < 0n ? -amount : amount;\n const divisor = 10n ** BigInt(decimals);\n const whole = abs / divisor;\n const fraction = abs % divisor;\n\n const fractionStr = fraction.toString().padStart(decimals, '0');\n const sign = amount < 0n ? '-' : '';\n\n return `${sign}${whole}.${fractionStr}`;\n}\n"],"mappings":"waA+BA,MAAM,EAAkB,CACtB,QAAS,8BACT,QAAS,sCACV,CAED,SAAgB,EAAoC,EAAkC,CAGpF,OAAO,IAAgB,EAAY,KAAO,EAAgB,QAAU,EAAgB,QAoBtF,eAAe,EACb,EACA,EACA,EACA,EACkB,CAClB,OAAO,EAAU,GAAG,EAAW,OAAQ,CACrC,KAAM,KAAK,UAAU,EAAK,CAC1B,MAAO,EACP,OAAQ,OACR,SACD,CAAC,CAGJ,SAAS,EAAsB,EAAsB,CACnD,IAAM,EAAS,EAAoC,UAAU,EAAI,CACjE,GAAI,CAAC,EAAO,SAAW,EAAO,KAAK,eAAiB,IAAA,GAClD,MAAO,GAET,IAAM,EAAI,EAAO,KAAK,aAChB,EAAI,OAAO,GAAM,SAAW,EAAI,OAAO,WAAW,EAAE,CAC1D,OAAO,OAAO,SAAS,EAAE,CAAG,EAAI,EAIlC,SAAS,EAAuB,EAAsB,CACpD,IAAM,EAAS,EAAwC,UAAU,EAAI,CACrE,GAAI,CAAC,EAAO,QACV,MAAO,GAET,IAAM,EAAM,EAAO,KAAK,SAAS,KAAM,GAAM,EAAE,KAAK,aAAa,GAAK,OAAO,CAC7E,GAAI,IAAQ,IAAA,GACV,MAAO,GAET,IAAM,EAAQ,OAAO,WAAW,EAAI,MAAM,CACpC,EAAO,OAAO,WAAW,EAAI,KAAK,CAIxC,MAHI,CAAC,OAAO,SAAS,EAAM,EAAI,CAAC,OAAO,SAAS,EAAK,CAC5C,EAEF,KAAK,IAAI,EAAG,EAAQ,EAAK,CAGlC,SAAS,EAAyB,EAA8C,CAC9E,IAAM,EAAS,EAAiC,UAAU,EAAI,CAC9D,OAAO,EAAO,QAAU,EAAqC,EAAO,KAAK,CAAG,UAyB9E,eAAsB,EAA2B,CAC/C,aACA,OACA,MAAO,EACP,UACqD,CACrD,IAAM,EAAY,EAAK,aAAa,CAC9B,CAAC,EAAS,EAAS,GAAW,MAAM,QAAQ,WAAW,CAC3D,EAAS,EAAY,CAAE,KAAM,qBAAsB,KAAM,EAAW,CAAE,EAAa,EAAO,CAC1F,EAAS,EAAY,CAAE,KAAM,yBAA0B,KAAM,EAAW,CAAE,EAAa,EAAO,CAC9F,EAAS,EAAY,CAAE,KAAM,kBAAmB,KAAM,EAAW,CAAE,EAAa,EAAO,CACxF,CAAC,CAEI,EAAO,EAAQ,SAAW,YAAc,EAAsB,EAAQ,MAAM,CAAG,EAUrF,OATa,EAAQ,SAAW,YAAc,EAAyB,EAAQ,MAAM,CAAG,aAK3E,iBAIN,GADU,EAAQ,SAAW,YAAc,EAAuB,EAAQ,MAAM,CAAG,GAFjF,EA+BX,eAAsB,EAAwB,CAC5C,aACA,YACA,QACA,SACA,MAAO,EACP,UACuE,CACvE,GAAM,CAAE,IAAG,IAAG,KAAM,EAAe,EAAU,CAIvC,EAAU,MAAM,EAFV,GAAG,EAAW,WAEW,CAQnC,KAAM,KAAK,UACT,CACE,SACA,MAAO,OAAO,EAAM,CACpB,UAAW,CACT,IACA,IACA,EAAG,OAAO,GAAK,GAAG,CACnB,CACF,EACA,EAAM,IAAW,OAAO,GAAU,SAAW,OAAO,EAAM,CAAG,EAC/D,CACD,MAAO,EACP,OAAQ,OACR,SACD,CAAC,CAEI,EAAS,EAAkC,UAAU,EAAQ,CACnE,GAAI,CAAC,EAAO,QAEV,MAAM,IAAI,EACR,+DACA,IAAI,SAAS,KAAK,UAAU,EAAQ,CAAE,CAAE,OAAQ,IAAK,CAAC,CACtD,EACD,CAGH,OAAO,EAAO"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
require(`../../_virtual/_rolldown/runtime.cjs`);const e=require(`../../utils/caip.cjs`),t=require(`../service-schemas.cjs`);let n=require(`zod`);
|
|
1
|
+
require(`../../_virtual/_rolldown/runtime.cjs`);const e=require(`../../utils/caip.cjs`),t=require(`../service-schemas.cjs`);let n=require(`zod`);const r=n.z.string().refine(t=>e.isCaip2ChainId(t),{message:`Invalid CAIP-2 chain ID`}).transform(e=>e),i=n.z.object({chainId:n.z.union([n.z.number(),n.z.string()]),chainType:n.z.string(),enabled_services:n.z.array(n.z.enum([`cross-chain-swap`,`cross-chain-quote`,`quote`,`swap`,`token-list`])),logo_url:n.z.url(),name:n.z.string(),lanes:n.z.array(n.z.union([n.z.coerce.number().int().nonnegative(),r]))}),a=i.extend({chainId:n.z.coerce.number().int().nonnegative(),chainType:n.z.literal(`evm`),router:t.EvmAddressSchema.optional(),wrapped_token:t.EvmAddressSchema.optional()}),o=i.extend({chainId:n.z.string().refine(t=>e.isCaip2ChainId(t)&&t.startsWith(`solana:`),{error:`Is not a valid Solana CAIP-2 ID`}).transform(e=>e),chainType:n.z.literal(`svm`)}),s=n.z.array(n.z.union([a,o,i.refine(e=>e.chainType!==`evm`&&e.chainType!==`svm`,`Known chain types must match their expected schema`)])),c=n.z.array(n.z.object({address:t.EvmAddressSchema,decimals:n.z.number().int().nonnegative().max(18),logo_url:n.z.url(),name:n.z.string(),symbol:n.z.string()})),l=n.z.union([t.EvmAddressSchema,t.SolAddressSchema,t.HyperliquidAddressSchema]),u=n.z.union([n.z.object({aggregator:n.z.object({id:n.z.string(),logo_url:n.z.url(),name:n.z.string()}),amountIn:n.z.coerce.bigint().nonnegative(),amountOut:n.z.coerce.bigint().nonnegative(),chainId:n.z.union([n.z.number().int().nonnegative(),r]),expiredAt:n.z.number().int().nonnegative(),fees:n.z.array(n.z.object({type:n.z.enum([`protocol`,`gas`,`bridge`,`slippage`,`swap`,`other`,`relay`]),name:n.z.string(),amount:n.z.coerce.bigint().nonnegative(),token:n.z.object({chainId:n.z.union([n.z.number().int().nonnegative(),r]),address:l}),extra:n.z.boolean().optional()})).optional(),gasEstimate:n.z.coerce.bigint().nonnegative().optional(),recommendedSlippage:n.z.number().int().nonnegative(),tokenIn:l,tokenInDecimals:n.z.number().int().nonnegative().max(18),tokenOut:l,tokenOutDecimals:n.z.number().int().nonnegative().max(18),uuid:n.z.uuid()}),n.z.object({done:n.z.literal(!0)})]),d=n.z.object({data:t.HexSchema,to:t.EvmAddressSchema,value:n.z.coerce.bigint().nonnegative()}),f=n.z.object({chainType:n.z.literal(`svm`),swapTransaction:n.z.base64()}),p=n.z.object({hyperliquidChain:n.z.string().min(1),destination:t.EvmAddressSchema,sourceDex:n.z.string(),destinationDex:n.z.string(),token:n.z.string().min(1),amount:n.z.string().regex(/^\d+(\.\d+)?$/,`amount must be a positive decimal string`),fromSubAccount:n.z.string()}),m=n.z.object({signatureKind:n.z.literal(`eip712`),domain:n.z.object({name:n.z.string().min(1),version:n.z.string().min(1),chainId:n.z.number().int().nonnegative(),verifyingContract:t.EvmAddressSchema}),types:n.z.record(n.z.string(),n.z.array(n.z.object({name:n.z.string(),type:n.z.string()}))),primaryType:n.z.string().min(1),value:n.z.object({wallet:t.EvmAddressSchema,chainId:n.z.string().min(1),id:t.HashSchema,nonce:n.z.number().int().nonnegative()}).loose()}),h=n.z.object({endpoint:n.z.string().min(1),method:n.z.string().min(1),body:n.z.object({type:n.z.string().min(1),id:t.HashSchema,nonce:n.z.number().int().nonnegative(),wallet:t.EvmAddressSchema,walletChainId:n.z.number().int().nonnegative(),signatureChainId:n.z.number().int().nonnegative()}).loose()}),g=n.z.object({type:n.z.literal(`hyperliquid-withdraw`),protocolVersion:n.z.number().int().positive(),nonce:n.z.string().regex(/^\d+$/,`nonce must be a decimal string`),id:t.HashSchema,requestId:t.HashSchema,parameters:p,sign:m.optional(),authorize:h.optional()}),_=n.z.union([d,f,g]),v=n.z.unknown().refine(e=>!(typeof e==`object`&&e&&`error`in e&&typeof e.error==`string`),{message:`Markr /authorize returned an error envelope despite 2xx status.`}),y=n.z.object({fee:n.z.int().nonnegative(),name:n.z.string()}),b=/[zZ]|[+-]\d{2}:?\d{2}$/,x=n.z.iso.datetime({local:!0}).refine(e=>{let t=b.test(e)?e:`${e}Z`;return n.z.iso.datetime().safeParse(t).success},{error:`Invalid ISO-8601 datetime (must be valid with timezone, or local assumed UTC)`}).transform(e=>b.test(e)?e:`${e}Z`),S=n.z.object({messageId:n.z.string().nullable(),status:n.z.enum([`pending`,`committed`,`pending_execution`,`completed`,`failed`,`refunded`,`unknown`]),description:n.z.string(),sourceChain:n.z.object({name:n.z.string(),transactionHash:n.z.union([t.HashSchema,t.SolSignatureSchema]),timestamp:x,finalized:x.or(n.z.boolean()).nullable()}),destinationChain:n.z.object({name:n.z.string().nullable(),transactionHash:n.z.union([t.HashSchema,t.SolSignatureSchema]).nullable(),bridgeHash:n.z.union([t.HashSchema,t.SolSignatureSchema]).nullable().optional(),timestamp:x.nullable(),finalized:x.nullable()}),progress:n.z.object({committed:n.z.boolean(),commitTimestamp:x.nullable().optional(),executed:n.z.boolean()}),fees:n.z.object({token:n.z.union([t.EvmAddressSchema,t.SolAddressSchema]).nullable(),amount:n.z.coerce.bigint().nonnegative().nullable()}).nullable(),transferredTokens:n.z.array(n.z.object({token:n.z.union([t.EvmAddressSchema,t.SolAddressSchema]),amount:n.z.coerce.bigint().nonnegative()})),debug:n.z.looseObject({messageId:n.z.string(),status:n.z.string(),destinationChain:n.z.string(),destinationTxHash:n.z.string(),relayTxHash:n.z.string(),retryCount:n.z.number().int().nonnegative(),lastRetryAt:n.z.iso.datetime(),sourceChain:n.z.string(),sourceTxHash:n.z.string(),createdAt:n.z.iso.datetime()}).partial().nullable().optional()}),C=n.z.object({address:t.EvmAddressSchema});exports.CrossChainStatusResponseSchema=S,exports.MarkrAuthorizeResponseSchema=v,exports.PartnerInfoResponseSchema=y,exports.QuoteResponseSchema=u,exports.SpenderAddressResponseSchema=C,exports.SupportedChainsResponseSchema=s,exports.SwapResponseSchema=_,exports.TokenListResponseSchema=c;
|
|
2
2
|
//# sourceMappingURL=_schema.cjs.map
|