@avalabs/fusion-sdk 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants.cjs +1 -1
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +14 -2
- package/dist/constants.d.ts +14 -2
- package/dist/constants.js +1 -1
- package/dist/constants.js.map +1 -1
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts +6 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.js +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/mod.cjs +1 -1
- package/dist/mod.d.cts +4 -4
- package/dist/mod.d.ts +4 -4
- package/dist/mod.js +1 -1
- package/dist/transfer-service/avalanche-cct/_handlers/get-minimum-transfer-amount.cjs +1 -1
- package/dist/transfer-service/avalanche-cct/_handlers/get-minimum-transfer-amount.cjs.map +1 -1
- package/dist/transfer-service/avalanche-cct/_handlers/get-minimum-transfer-amount.js +1 -1
- package/dist/transfer-service/avalanche-cct/_handlers/get-minimum-transfer-amount.js.map +1 -1
- package/dist/transfer-service/avalanche-cct/_handlers/stream-quotes.cjs +1 -1
- package/dist/transfer-service/avalanche-cct/_handlers/stream-quotes.cjs.map +1 -1
- package/dist/transfer-service/avalanche-cct/_handlers/stream-quotes.js +1 -1
- package/dist/transfer-service/avalanche-cct/_handlers/stream-quotes.js.map +1 -1
- package/dist/transfer-service/avalanche-cct/_handlers/transfer-asset.cjs +1 -1
- package/dist/transfer-service/avalanche-cct/_handlers/transfer-asset.cjs.map +1 -1
- package/dist/transfer-service/avalanche-cct/_handlers/transfer-asset.js +1 -1
- package/dist/transfer-service/avalanche-cct/_handlers/transfer-asset.js.map +1 -1
- package/dist/transfer-service/avalanche-cct/_utils.cjs +1 -1
- package/dist/transfer-service/avalanche-cct/_utils.cjs.map +1 -1
- package/dist/transfer-service/avalanche-cct/_utils.js +1 -1
- package/dist/transfer-service/avalanche-cct/_utils.js.map +1 -1
- package/dist/transfer-service/avalanche-cct/avalanche-cct-service.cjs +1 -1
- package/dist/transfer-service/avalanche-cct/avalanche-cct-service.cjs.map +1 -1
- package/dist/transfer-service/avalanche-cct/avalanche-cct-service.js +1 -1
- package/dist/transfer-service/avalanche-cct/avalanche-cct-service.js.map +1 -1
- package/dist/transfer-service/markr/_api.cjs +1 -1
- package/dist/transfer-service/markr/_api.cjs.map +1 -1
- package/dist/transfer-service/markr/_api.js +1 -1
- package/dist/transfer-service/markr/_api.js.map +1 -1
- package/dist/transfer-service/markr/_handlers/estimate-native-fee.cjs +1 -1
- package/dist/transfer-service/markr/_handlers/estimate-native-fee.cjs.map +1 -1
- package/dist/transfer-service/markr/_handlers/estimate-native-fee.js +1 -1
- package/dist/transfer-service/markr/_handlers/estimate-native-fee.js.map +1 -1
- package/dist/transfer-service/markr/_handlers/transfer-asset-hyperliquid-withdraw.cjs +2 -0
- package/dist/transfer-service/markr/_handlers/transfer-asset-hyperliquid-withdraw.cjs.map +1 -0
- package/dist/transfer-service/markr/_handlers/transfer-asset-hyperliquid-withdraw.js +2 -0
- package/dist/transfer-service/markr/_handlers/transfer-asset-hyperliquid-withdraw.js.map +1 -0
- package/dist/transfer-service/markr/_handlers/transfer-asset.cjs +1 -1
- package/dist/transfer-service/markr/_handlers/transfer-asset.cjs.map +1 -1
- package/dist/transfer-service/markr/_handlers/transfer-asset.js +1 -1
- package/dist/transfer-service/markr/_handlers/transfer-asset.js.map +1 -1
- package/dist/transfer-service/markr/_hyperliquid-eip712.cjs +2 -0
- package/dist/transfer-service/markr/_hyperliquid-eip712.cjs.map +1 -0
- package/dist/transfer-service/markr/_hyperliquid-eip712.js +2 -0
- package/dist/transfer-service/markr/_hyperliquid-eip712.js.map +1 -0
- package/dist/transfer-service/markr/_hyperliquid-schema.cjs +2 -0
- package/dist/transfer-service/markr/_hyperliquid-schema.cjs.map +1 -0
- package/dist/transfer-service/markr/_hyperliquid-schema.js +2 -0
- package/dist/transfer-service/markr/_hyperliquid-schema.js.map +1 -0
- package/dist/transfer-service/markr/_hyperliquid.cjs +2 -0
- package/dist/transfer-service/markr/_hyperliquid.cjs.map +1 -0
- package/dist/transfer-service/markr/_hyperliquid.js +2 -0
- package/dist/transfer-service/markr/_hyperliquid.js.map +1 -0
- package/dist/transfer-service/markr/_schema.cjs +1 -1
- package/dist/transfer-service/markr/_schema.cjs.map +1 -1
- package/dist/transfer-service/markr/_schema.js +1 -1
- package/dist/transfer-service/markr/_schema.js.map +1 -1
- package/dist/transfer-service/markr/_type-guards.cjs +1 -1
- package/dist/transfer-service/markr/_type-guards.cjs.map +1 -1
- package/dist/transfer-service/markr/_type-guards.js +1 -1
- package/dist/transfer-service/markr/_type-guards.js.map +1 -1
- package/dist/transfer-service/markr/markr-service.cjs.map +1 -1
- package/dist/transfer-service/markr/markr-service.js.map +1 -1
- package/dist/types/service.d.cts +13 -2
- package/dist/types/service.d.ts +13 -2
- package/dist/types/signer.d.cts +37 -1
- package/dist/types/signer.d.ts +37 -1
- package/package.json +1 -1
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
require(`../../../_virtual/_rolldown/runtime.cjs`);const e=require(`../../../constants.cjs`),t=require(`../../../errors.cjs`),n=require(`../../../utils/caip.cjs`),r=require(`../../_utils.cjs`),i=require(`../_api.cjs`),a=require(`../_hyperliquid-eip712.cjs`),o=require(`../_hyperliquid.cjs`);let s=require(`viem`);function c(e){if(!/^\d+$/.test(e))throw new t.SdkError(`Hyperliquid swap response contained a malformed nonce.`,t.ErrorCode.INVALID_PARAMS);return BigInt(e)}function l(e,n,r){if(e.value.id!==n.id)throw new t.SdkError(`Relay sign.value.id does not match swap.id.`,t.ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION,{details:`expected ${n.id}, got ${String(e.value.id)}`});if(String(e.value.nonce)!==n.nonce)throw new t.SdkError(`Relay sign.value.nonce does not match swap.nonce.`,t.ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION,{details:`expected ${n.nonce}, got ${String(e.value.nonce)}`});if(e.value.wallet.toLowerCase()!==r.toLowerCase())throw new t.SdkError(`Relay sign.value.wallet does not match the signer address.`,t.ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION,{details:`expected ${r}, got ${e.value.wallet}`})}async function u({apiOptions:u,environment:f,evmSigner:p,quote:m,swap:h,onStepChange:g}){if(!a.SUPPORTED_HL_PROTOCOL_VERSIONS.includes(h.protocolVersion))throw new t.SdkError(`Unsupported Hyperliquid withdrawal protocol version. Please update @avalabs/fusion-sdk.`,t.ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION,{details:`Markr returned protocolVersion=${h.protocolVersion}; SDK supports [${a.SUPPORTED_HL_PROTOCOL_VERSIONS.join(`, `)}]`});if(!p.signTypedData)throw new t.SdkError(`Hyperliquid withdrawal requires an EIP-712 typed-data signer.`,t.ErrorCode.SIGNING_FAILED,{details:`EvmSigner.signTypedData is undefined. Wire it via wagmi useSignTypedData.`});if(!(0,s.isAddress)(m.fromAddress))throw new t.SdkError(t.ErrorReason.INVALID_PARAMS,t.ErrorCode.INVALID_PARAMS,{details:`fromAddress is not a valid EVM address.`});let _=m.fromAddress,v=n.caip2ToEip155ChainId(m.targetChain.chainId),y=c(h.nonce),b=h.protocolVersion>=2,x={currentSignature:1,currentSignatureReason:e.TransferSignatureReason.HyperliquidAuthorize,quote:m,requiredSignatures:2};g?.(x);let S;if(b){if(!h.sign||!h.authorize)throw new t.SdkError("Hyperliquid v2 swap response is missing `sign` or `authorize` envelope.",t.ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION,{details:`Required pass-through fields are absent — the upstream may have downgraded to v1.`});l(h.sign,h,_),S={domain:h.sign.domain,types:h.sign.types,primaryType:h.sign.primaryType,message:h.sign.value}}else S=a.buildRelayNonceMappingTypedData({signatureChainId:v,wallet:_,orderId:h.id,nonce:y});let C=await p.signTypedData({typedData:S,address:_,chainId:v},x);try{b&&h.authorize?await i.markrPostAuthorize(u,{uuid:m.id,signature:C,body:h.authorize.body}):await i.markrPostAuthorize(u,{uuid:m.id,signature:C,wallet:_,walletChainId:1337,signatureChainId:v,nonce:Number(y),id:h.id})}catch(e){if(e instanceof t.HttpError)return r.makeFailedTransferFromQuote(m,{environment:f,errorCode:t.ErrorCode.HL_EXCHANGE_REJECTED,errorReason:`Markr /authorize failed (HTTP ${e.status}). Please retry.`});throw e}let w=Number(h.parameters.amount),T=await o.hyperliquidGetWithdrawable({apiBaseUrl:o.hyperliquidApiBaseUrlForEnvironment(f),user:_});if(Number.isFinite(T)&&T<w)return r.makeFailedTransferFromQuote(m,{environment:f,errorCode:t.ErrorCode.HL_WITHDRAWABLE_DROPPED,errorReason:`Withdrawable balance changed during withdrawal (requested ${w}, available ${T}). Please retry with a smaller amount.`});let E={currentSignature:2,currentSignatureReason:e.TransferSignatureReason.HyperliquidSendAsset,quote:m,requiredSignatures:2};g?.(E);let D={signatureChainId:v,hyperliquidChain:h.parameters.hyperliquidChain,destination:h.parameters.destination,token:h.parameters.token,amount:h.parameters.amount,sourceDex:h.parameters.sourceDex,destinationDex:h.parameters.destinationDex,fromSubAccount:h.parameters.fromSubAccount,nonce:y},O=a.buildHyperliquidSendAssetTypedData(D),k=await p.signTypedData({typedData:O,address:_,chainId:v},E),A=a.buildHyperliquidExchangeAction(D),j=await o.hyperliquidExchangePost({apiBaseUrl:o.hyperliquidApiBaseUrlForEnvironment(f),signature:k,nonce:y,action:A});if(j.status!==`ok`)return r.makeFailedTransferFromQuote(m,{environment:f,errorCode:t.ErrorCode.HL_EXCHANGE_REJECTED,errorReason:d(j.response)});let M=Date.now();return{amountIn:m.amountIn,amountOut:m.amountOut,environment:f,fees:m.fees,fromAddress:m.fromAddress,id:m.id,partnerFeeBps:m.partnerFeeBps,sourceAsset:m.assetIn,sourceChain:m.sourceChain,status:`source-completed`,targetAsset:m.assetOut,targetChain:m.targetChain,toAddress:m.toAddress,type:m.serviceType,source:{confirmationCount:2,requiredConfirmationCount:2,startedAtMs:M,txHash:h.requestId}}}function d(e){return typeof e==`string`?`Hyperliquid /exchange rejected the transfer: ${e}`:e&&typeof e==`object`&&`error`in e&&typeof e.error==`string`?`Hyperliquid /exchange rejected the transfer: ${e.error}`:t.ErrorReason.UNKNOWN}exports._executeHyperliquidWithdraw=u;
|
|
2
|
+
//# sourceMappingURL=transfer-asset-hyperliquid-withdraw.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transfer-asset-hyperliquid-withdraw.cjs","names":["SdkError","ErrorCode","SUPPORTED_HL_PROTOCOL_VERSIONS","ErrorReason","caip2ToEip155ChainId","TransferSignatureReason","buildRelayNonceMappingTypedData","markrPostAuthorize","HttpError","makeFailedTransferFromQuote","hyperliquidGetWithdrawable","hyperliquidApiBaseUrlForEnvironment","buildHyperliquidSendAssetTypedData","buildHyperliquidExchangeAction","hyperliquidExchangePost"],"sources":["../../../../src/transfer-service/markr/_handlers/transfer-asset-hyperliquid-withdraw.ts"],"sourcesContent":["/**\n * Hyperliquid 2-phase withdrawal handler.\n *\n * Mirrors the existing `approve + swap` shape — two sequential signatures with\n * an inter-phase gate — but with EIP-712 typed-data signatures and HTTP POSTs\n * instead of serialized EVM txs.\n *\n * 1. AUTHORIZE — sign `RelayNonceMapping`, POST to Markr `/authorize`.\n * 2. PRE-FLIGHT — re-fetch `withdrawable`, abort if it dropped.\n * 3. SEND ASSET — sign `HyperliquidTransaction:SendAsset`, POST to\n * `api.hyperliquid.xyz/exchange` directly (no Markr in this path).\n */\nimport { isAddress } from 'viem';\nimport type { Environment } from '../../../constants';\nimport { TransferSignatureReason } from '../../../constants';\nimport { ErrorCode, ErrorReason, HttpError, SdkError } from '../../../errors';\nimport type { TransferService } from '../../../types/service';\nimport type { EvmSigner, EvmTypedData } from '../../../types/signer';\nimport type { Transfer, TransferStepDetails } from '../../../types/transfer';\nimport { caip2ToEip155ChainId } from '../../../utils/caip';\nimport { makeFailedTransferFromQuote } from '../../_utils';\nimport { markrPostAuthorize, type ApiOptions } from '../_api';\nimport type { HyperliquidWithdrawSwapResponse, RelayEip712SignEnvelope } from '../_schema';\nimport {\n type BuildHyperliquidSendAssetParams,\n buildHyperliquidExchangeAction,\n buildHyperliquidSendAssetTypedData,\n buildRelayNonceMappingTypedData,\n SUPPORTED_HL_PROTOCOL_VERSIONS,\n} from '../_hyperliquid-eip712';\nimport {\n hyperliquidApiBaseUrlForEnvironment,\n hyperliquidExchangePost,\n hyperliquidGetWithdrawable,\n} from '../_hyperliquid';\n\nexport interface HyperliquidWithdrawTransferParams {\n readonly apiOptions: ApiOptions;\n readonly environment: Environment;\n readonly evmSigner: EvmSigner;\n readonly quote: Parameters<TransferService['transferAsset']>[0]['quote'];\n readonly swap: HyperliquidWithdrawSwapResponse;\n readonly onStepChange?: Parameters<TransferService['transferAsset']>[0]['onStepChange'];\n}\n\n/**\n * Convert the Markr-supplied `nonce` (decimal string) into a `bigint`.\n *\n * We don't trust the wire value blindly — if Markr ever changes the encoding\n * we want a deterministic error, not a silent bad signature.\n */\nfunction parseNonce(raw: string): bigint {\n if (!/^\\d+$/.test(raw)) {\n throw new SdkError('Hyperliquid swap response contained a malformed nonce.', ErrorCode.INVALID_PARAMS);\n }\n\n return BigInt(raw);\n}\n\n/**\n * Cross-checks before signing Relay's pass-through `sign` envelope. Guards\n * against a downstream bug surfacing as a signed message bound to a different\n * order or a different wallet.\n */\nfunction assertSignEnvelopeMatchesSwap(\n sign: RelayEip712SignEnvelope,\n swap: HyperliquidWithdrawSwapResponse,\n wallet: string,\n): void {\n if (sign.value.id !== swap.id) {\n throw new SdkError('Relay sign.value.id does not match swap.id.', ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION, {\n details: `expected ${swap.id}, got ${String(sign.value.id)}`,\n });\n }\n if (String(sign.value.nonce) !== swap.nonce) {\n throw new SdkError('Relay sign.value.nonce does not match swap.nonce.', ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION, {\n details: `expected ${swap.nonce}, got ${String(sign.value.nonce)}`,\n });\n }\n if (sign.value.wallet.toLowerCase() !== wallet.toLowerCase()) {\n throw new SdkError(\n 'Relay sign.value.wallet does not match the signer address.',\n ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION,\n { details: `expected ${wallet}, got ${sign.value.wallet}` },\n );\n }\n}\n\nexport async function _executeHyperliquidWithdraw({\n apiOptions,\n environment,\n evmSigner,\n quote,\n swap,\n onStepChange,\n}: HyperliquidWithdrawTransferParams): Promise<Transfer> {\n // 0. Version guard — fail loud if Markr/Relay/HL bump the EIP-712 schemas.\n if (!SUPPORTED_HL_PROTOCOL_VERSIONS.includes(swap.protocolVersion as 1 | 2)) {\n throw new SdkError(\n 'Unsupported Hyperliquid withdrawal protocol version. Please update @avalabs/fusion-sdk.',\n ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION,\n {\n details: `Markr returned protocolVersion=${\n swap.protocolVersion\n }; SDK supports [${SUPPORTED_HL_PROTOCOL_VERSIONS.join(', ')}]`,\n },\n );\n }\n\n if (!evmSigner.signTypedData) {\n throw new SdkError('Hyperliquid withdrawal requires an EIP-712 typed-data signer.', ErrorCode.SIGNING_FAILED, {\n details: 'EvmSigner.signTypedData is undefined. Wire it via wagmi useSignTypedData.',\n });\n }\n\n if (!isAddress(quote.fromAddress)) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: 'fromAddress is not a valid EVM address.',\n });\n }\n\n const wallet = quote.fromAddress;\n const targetChainId = caip2ToEip155ChainId(quote.targetChain.chainId);\n const nonce = parseNonce(swap.nonce);\n const isV2 = swap.protocolVersion >= 2;\n\n // 1. AUTHORIZE — sign nonce-mapping locally, POST to Markr.\n const authorizeStep: TransferStepDetails = {\n currentSignature: 1,\n currentSignatureReason: TransferSignatureReason.HyperliquidAuthorize,\n quote,\n requiredSignatures: 2,\n };\n onStepChange?.(authorizeStep);\n\n // v2: sign Relay's `sign` envelope verbatim — no schema knowledge required.\n // v1 (legacy): rebuild the `RelayNonceMapping` typed data from pinned types.\n // Either path produces the same authorize signature for matching orders;\n // the v2 path additionally tolerates Relay-side schema additions (e.g.\n // `depositor`) without an SDK release.\n let authorizeTypedData: EvmTypedData;\n if (isV2) {\n if (!swap.sign || !swap.authorize) {\n throw new SdkError(\n 'Hyperliquid v2 swap response is missing `sign` or `authorize` envelope.',\n ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION,\n { details: 'Required pass-through fields are absent — the upstream may have downgraded to v1.' },\n );\n }\n assertSignEnvelopeMatchesSwap(swap.sign, swap, wallet);\n authorizeTypedData = {\n domain: swap.sign.domain,\n types: swap.sign.types,\n primaryType: swap.sign.primaryType,\n message: swap.sign.value,\n };\n } else {\n // `swap.id` (the order id) is what Relay binds the nonce-mapping to —\n // distinct from `swap.requestId` (the cross-chain tracking id). Mixing them\n // up produces a \"valid-looking\" signature for an order Relay doesn't know\n // about, so the on-chain sendAsset gets orphaned.\n authorizeTypedData = buildRelayNonceMappingTypedData({\n signatureChainId: targetChainId,\n wallet,\n orderId: swap.id,\n nonce,\n });\n }\n\n const authorizeSignature = await evmSigner.signTypedData(\n { typedData: authorizeTypedData, address: wallet, chainId: targetChainId },\n authorizeStep,\n );\n\n try {\n if (isV2 && swap.authorize) {\n await markrPostAuthorize(apiOptions, {\n uuid: quote.id,\n signature: authorizeSignature,\n // Forward Relay's `/authorize` body verbatim — Markr only normalizes\n // `nonce` to a JSON number and defaults `referrer`. Additive Relay\n // fields (e.g. `depositor`) flow through automatically.\n body: swap.authorize.body,\n });\n } else {\n await markrPostAuthorize(apiOptions, {\n uuid: quote.id,\n signature: authorizeSignature,\n wallet,\n walletChainId: 1337,\n signatureChainId: targetChainId,\n /**\n * Relay's upstream `/authorize` validates `nonce` strictly as a JSON\n * number. Sending it as a string makes the request fail upstream. HL\n * nonces are millisecond timestamps, comfortably under\n * `Number.MAX_SAFE_INTEGER`, so the Number coercion is lossless.\n */\n nonce: Number(nonce),\n // Order id (not the tracking requestId).\n id: swap.id,\n });\n }\n } catch (error) {\n // Surface as a failed transfer so the UI can prompt a retry rather than\n // crashing the panel. The user paid no on-chain cost yet.\n if (error instanceof HttpError) {\n return makeFailedTransferFromQuote(quote, {\n environment,\n errorCode: ErrorCode.HL_EXCHANGE_REJECTED,\n errorReason: `Markr /authorize failed (HTTP ${error.status}). Please retry.`,\n });\n }\n throw error;\n }\n\n // 2. PRE-FLIGHT — re-check withdrawable. Guards the second wallet prompt\n // from being asked for a balance the user no longer has (e.g. they opened\n // a position between the two signatures on another device). Relay's\n // pre-formatted `amount` is at the spot token's display precision, so it\n // parses cleanly via `Number(...)` — no need to re-format from the bigint.\n const requestedAmount = Number(swap.parameters.amount);\n const liveWithdrawable = await hyperliquidGetWithdrawable({\n apiBaseUrl: hyperliquidApiBaseUrlForEnvironment(environment),\n user: wallet,\n });\n\n if (Number.isFinite(liveWithdrawable) && liveWithdrawable < requestedAmount) {\n return makeFailedTransferFromQuote(quote, {\n environment,\n errorCode: ErrorCode.HL_WITHDRAWABLE_DROPPED,\n errorReason: `Withdrawable balance changed during withdrawal (requested ${requestedAmount}, available ${liveWithdrawable}). Please retry with a smaller amount.`,\n });\n }\n\n // 3. SEND ASSET — sign HL transaction locally, POST to api.hyperliquid.xyz/exchange.\n // We sign and submit Relay's `parameters` *verbatim* — Relay's order matcher\n // recovers the on-chain `sendAsset` by hashing these exact strings, so any\n // local re-formatting (especially of `amount`) would orphan the transfer.\n const sendAssetStep: TransferStepDetails = {\n currentSignature: 2,\n currentSignatureReason: TransferSignatureReason.HyperliquidSendAsset,\n quote,\n requiredSignatures: 2,\n };\n onStepChange?.(sendAssetStep);\n\n const sendAssetParams = {\n signatureChainId: targetChainId,\n hyperliquidChain: swap.parameters.hyperliquidChain,\n destination: swap.parameters.destination,\n token: swap.parameters.token,\n amount: swap.parameters.amount,\n sourceDex: swap.parameters.sourceDex,\n destinationDex: swap.parameters.destinationDex,\n fromSubAccount: swap.parameters.fromSubAccount,\n nonce,\n } satisfies BuildHyperliquidSendAssetParams;\n\n const sendAssetTypedData = buildHyperliquidSendAssetTypedData(sendAssetParams);\n const sendAssetSignature = await evmSigner.signTypedData(\n { typedData: sendAssetTypedData, address: wallet, chainId: targetChainId },\n sendAssetStep,\n );\n\n const exchangeAction = buildHyperliquidExchangeAction(sendAssetParams);\n\n const exchangeResponse = await hyperliquidExchangePost({\n apiBaseUrl: hyperliquidApiBaseUrlForEnvironment(environment),\n signature: sendAssetSignature,\n nonce,\n action: exchangeAction,\n });\n\n if (exchangeResponse.status !== 'ok') {\n return makeFailedTransferFromQuote(quote, {\n environment,\n errorCode: ErrorCode.HL_EXCHANGE_REJECTED,\n errorReason: extractHlExchangeError(exchangeResponse.response),\n });\n }\n\n const startedAtMs = Date.now();\n\n // HL `/exchange` answering `status: ok` is our source-side finality signal —\n // HL's matcher debits the user atomically with that ack (no separate on-chain\n // tx hash to wait on; HL doesn't expose one for `sendAsset`). Hand the tracker\n // a `source-completed` transfer keyed on Relay's `requestId` so it skips the\n // EVM finality gate and goes straight to Markr `/cross-chain/status` polling.\n return {\n amountIn: quote.amountIn,\n amountOut: quote.amountOut,\n environment,\n fees: quote.fees,\n fromAddress: quote.fromAddress,\n id: quote.id,\n partnerFeeBps: quote.partnerFeeBps,\n sourceAsset: quote.assetIn,\n sourceChain: quote.sourceChain,\n status: 'source-completed',\n targetAsset: quote.assetOut,\n targetChain: quote.targetChain,\n toAddress: quote.toAddress,\n type: quote.serviceType,\n source: {\n confirmationCount: 2,\n requiredConfirmationCount: 2,\n startedAtMs,\n // `requestId` doubles as the SDK's sourceTxHash for status polling.\n txHash: swap.requestId,\n },\n };\n}\n\nfunction extractHlExchangeError(response: unknown): string {\n if (typeof response === 'string') {\n return `Hyperliquid /exchange rejected the transfer: ${response}`;\n }\n\n if (response && typeof response === 'object' && 'error' in response && typeof response.error === 'string') {\n return `Hyperliquid /exchange rejected the transfer: ${response.error}`;\n }\n\n return ErrorReason.UNKNOWN;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{TransferSignatureReason as e}from"../../../constants.js";import{ErrorCode as t,ErrorReason as n,HttpError as r,SdkError as i}from"../../../errors.js";import{caip2ToEip155ChainId as a}from"../../../utils/caip.js";import{makeFailedTransferFromQuote as o}from"../../_utils.js";import{markrPostAuthorize as s}from"../_api.js";import{SUPPORTED_HL_PROTOCOL_VERSIONS as c,buildHyperliquidExchangeAction as l,buildHyperliquidSendAssetTypedData as u,buildRelayNonceMappingTypedData as d}from"../_hyperliquid-eip712.js";import{hyperliquidApiBaseUrlForEnvironment as f,hyperliquidExchangePost as p,hyperliquidGetWithdrawable as m}from"../_hyperliquid.js";import{isAddress as h}from"viem";function g(e){if(!/^\d+$/.test(e))throw new i(`Hyperliquid swap response contained a malformed nonce.`,t.INVALID_PARAMS);return BigInt(e)}function _(e,n,r){if(e.value.id!==n.id)throw new i(`Relay sign.value.id does not match swap.id.`,t.HL_UNSUPPORTED_PROTOCOL_VERSION,{details:`expected ${n.id}, got ${String(e.value.id)}`});if(String(e.value.nonce)!==n.nonce)throw new i(`Relay sign.value.nonce does not match swap.nonce.`,t.HL_UNSUPPORTED_PROTOCOL_VERSION,{details:`expected ${n.nonce}, got ${String(e.value.nonce)}`});if(e.value.wallet.toLowerCase()!==r.toLowerCase())throw new i(`Relay sign.value.wallet does not match the signer address.`,t.HL_UNSUPPORTED_PROTOCOL_VERSION,{details:`expected ${r}, got ${e.value.wallet}`})}async function v({apiOptions:v,environment:b,evmSigner:x,quote:S,swap:C,onStepChange:w}){if(!c.includes(C.protocolVersion))throw new i(`Unsupported Hyperliquid withdrawal protocol version. Please update @avalabs/fusion-sdk.`,t.HL_UNSUPPORTED_PROTOCOL_VERSION,{details:`Markr returned protocolVersion=${C.protocolVersion}; SDK supports [${c.join(`, `)}]`});if(!x.signTypedData)throw new i(`Hyperliquid withdrawal requires an EIP-712 typed-data signer.`,t.SIGNING_FAILED,{details:`EvmSigner.signTypedData is undefined. Wire it via wagmi useSignTypedData.`});if(!h(S.fromAddress))throw new i(n.INVALID_PARAMS,t.INVALID_PARAMS,{details:`fromAddress is not a valid EVM address.`});let T=S.fromAddress,E=a(S.targetChain.chainId),D=g(C.nonce),O=C.protocolVersion>=2,k={currentSignature:1,currentSignatureReason:e.HyperliquidAuthorize,quote:S,requiredSignatures:2};w?.(k);let A;if(O){if(!C.sign||!C.authorize)throw new i("Hyperliquid v2 swap response is missing `sign` or `authorize` envelope.",t.HL_UNSUPPORTED_PROTOCOL_VERSION,{details:`Required pass-through fields are absent — the upstream may have downgraded to v1.`});_(C.sign,C,T),A={domain:C.sign.domain,types:C.sign.types,primaryType:C.sign.primaryType,message:C.sign.value}}else A=d({signatureChainId:E,wallet:T,orderId:C.id,nonce:D});let j=await x.signTypedData({typedData:A,address:T,chainId:E},k);try{O&&C.authorize?await s(v,{uuid:S.id,signature:j,body:C.authorize.body}):await s(v,{uuid:S.id,signature:j,wallet:T,walletChainId:1337,signatureChainId:E,nonce:Number(D),id:C.id})}catch(e){if(e instanceof r)return o(S,{environment:b,errorCode:t.HL_EXCHANGE_REJECTED,errorReason:`Markr /authorize failed (HTTP ${e.status}). Please retry.`});throw e}let M=Number(C.parameters.amount),N=await m({apiBaseUrl:f(b),user:T});if(Number.isFinite(N)&&N<M)return o(S,{environment:b,errorCode:t.HL_WITHDRAWABLE_DROPPED,errorReason:`Withdrawable balance changed during withdrawal (requested ${M}, available ${N}). Please retry with a smaller amount.`});let P={currentSignature:2,currentSignatureReason:e.HyperliquidSendAsset,quote:S,requiredSignatures:2};w?.(P);let F={signatureChainId:E,hyperliquidChain:C.parameters.hyperliquidChain,destination:C.parameters.destination,token:C.parameters.token,amount:C.parameters.amount,sourceDex:C.parameters.sourceDex,destinationDex:C.parameters.destinationDex,fromSubAccount:C.parameters.fromSubAccount,nonce:D},I=u(F),L=await x.signTypedData({typedData:I,address:T,chainId:E},P),R=l(F),z=await p({apiBaseUrl:f(b),signature:L,nonce:D,action:R});if(z.status!==`ok`)return o(S,{environment:b,errorCode:t.HL_EXCHANGE_REJECTED,errorReason:y(z.response)});let B=Date.now();return{amountIn:S.amountIn,amountOut:S.amountOut,environment:b,fees:S.fees,fromAddress:S.fromAddress,id:S.id,partnerFeeBps:S.partnerFeeBps,sourceAsset:S.assetIn,sourceChain:S.sourceChain,status:`source-completed`,targetAsset:S.assetOut,targetChain:S.targetChain,toAddress:S.toAddress,type:S.serviceType,source:{confirmationCount:2,requiredConfirmationCount:2,startedAtMs:B,txHash:C.requestId}}}function y(e){return typeof e==`string`?`Hyperliquid /exchange rejected the transfer: ${e}`:e&&typeof e==`object`&&`error`in e&&typeof e.error==`string`?`Hyperliquid /exchange rejected the transfer: ${e.error}`:n.UNKNOWN}export{v as _executeHyperliquidWithdraw};
|
|
2
|
+
//# sourceMappingURL=transfer-asset-hyperliquid-withdraw.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transfer-asset-hyperliquid-withdraw.js","names":[],"sources":["../../../../src/transfer-service/markr/_handlers/transfer-asset-hyperliquid-withdraw.ts"],"sourcesContent":["/**\n * Hyperliquid 2-phase withdrawal handler.\n *\n * Mirrors the existing `approve + swap` shape — two sequential signatures with\n * an inter-phase gate — but with EIP-712 typed-data signatures and HTTP POSTs\n * instead of serialized EVM txs.\n *\n * 1. AUTHORIZE — sign `RelayNonceMapping`, POST to Markr `/authorize`.\n * 2. PRE-FLIGHT — re-fetch `withdrawable`, abort if it dropped.\n * 3. SEND ASSET — sign `HyperliquidTransaction:SendAsset`, POST to\n * `api.hyperliquid.xyz/exchange` directly (no Markr in this path).\n */\nimport { isAddress } from 'viem';\nimport type { Environment } from '../../../constants';\nimport { TransferSignatureReason } from '../../../constants';\nimport { ErrorCode, ErrorReason, HttpError, SdkError } from '../../../errors';\nimport type { TransferService } from '../../../types/service';\nimport type { EvmSigner, EvmTypedData } from '../../../types/signer';\nimport type { Transfer, TransferStepDetails } from '../../../types/transfer';\nimport { caip2ToEip155ChainId } from '../../../utils/caip';\nimport { makeFailedTransferFromQuote } from '../../_utils';\nimport { markrPostAuthorize, type ApiOptions } from '../_api';\nimport type { HyperliquidWithdrawSwapResponse, RelayEip712SignEnvelope } from '../_schema';\nimport {\n type BuildHyperliquidSendAssetParams,\n buildHyperliquidExchangeAction,\n buildHyperliquidSendAssetTypedData,\n buildRelayNonceMappingTypedData,\n SUPPORTED_HL_PROTOCOL_VERSIONS,\n} from '../_hyperliquid-eip712';\nimport {\n hyperliquidApiBaseUrlForEnvironment,\n hyperliquidExchangePost,\n hyperliquidGetWithdrawable,\n} from '../_hyperliquid';\n\nexport interface HyperliquidWithdrawTransferParams {\n readonly apiOptions: ApiOptions;\n readonly environment: Environment;\n readonly evmSigner: EvmSigner;\n readonly quote: Parameters<TransferService['transferAsset']>[0]['quote'];\n readonly swap: HyperliquidWithdrawSwapResponse;\n readonly onStepChange?: Parameters<TransferService['transferAsset']>[0]['onStepChange'];\n}\n\n/**\n * Convert the Markr-supplied `nonce` (decimal string) into a `bigint`.\n *\n * We don't trust the wire value blindly — if Markr ever changes the encoding\n * we want a deterministic error, not a silent bad signature.\n */\nfunction parseNonce(raw: string): bigint {\n if (!/^\\d+$/.test(raw)) {\n throw new SdkError('Hyperliquid swap response contained a malformed nonce.', ErrorCode.INVALID_PARAMS);\n }\n\n return BigInt(raw);\n}\n\n/**\n * Cross-checks before signing Relay's pass-through `sign` envelope. Guards\n * against a downstream bug surfacing as a signed message bound to a different\n * order or a different wallet.\n */\nfunction assertSignEnvelopeMatchesSwap(\n sign: RelayEip712SignEnvelope,\n swap: HyperliquidWithdrawSwapResponse,\n wallet: string,\n): void {\n if (sign.value.id !== swap.id) {\n throw new SdkError('Relay sign.value.id does not match swap.id.', ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION, {\n details: `expected ${swap.id}, got ${String(sign.value.id)}`,\n });\n }\n if (String(sign.value.nonce) !== swap.nonce) {\n throw new SdkError('Relay sign.value.nonce does not match swap.nonce.', ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION, {\n details: `expected ${swap.nonce}, got ${String(sign.value.nonce)}`,\n });\n }\n if (sign.value.wallet.toLowerCase() !== wallet.toLowerCase()) {\n throw new SdkError(\n 'Relay sign.value.wallet does not match the signer address.',\n ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION,\n { details: `expected ${wallet}, got ${sign.value.wallet}` },\n );\n }\n}\n\nexport async function _executeHyperliquidWithdraw({\n apiOptions,\n environment,\n evmSigner,\n quote,\n swap,\n onStepChange,\n}: HyperliquidWithdrawTransferParams): Promise<Transfer> {\n // 0. Version guard — fail loud if Markr/Relay/HL bump the EIP-712 schemas.\n if (!SUPPORTED_HL_PROTOCOL_VERSIONS.includes(swap.protocolVersion as 1 | 2)) {\n throw new SdkError(\n 'Unsupported Hyperliquid withdrawal protocol version. Please update @avalabs/fusion-sdk.',\n ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION,\n {\n details: `Markr returned protocolVersion=${\n swap.protocolVersion\n }; SDK supports [${SUPPORTED_HL_PROTOCOL_VERSIONS.join(', ')}]`,\n },\n );\n }\n\n if (!evmSigner.signTypedData) {\n throw new SdkError('Hyperliquid withdrawal requires an EIP-712 typed-data signer.', ErrorCode.SIGNING_FAILED, {\n details: 'EvmSigner.signTypedData is undefined. Wire it via wagmi useSignTypedData.',\n });\n }\n\n if (!isAddress(quote.fromAddress)) {\n throw new SdkError(ErrorReason.INVALID_PARAMS, ErrorCode.INVALID_PARAMS, {\n details: 'fromAddress is not a valid EVM address.',\n });\n }\n\n const wallet = quote.fromAddress;\n const targetChainId = caip2ToEip155ChainId(quote.targetChain.chainId);\n const nonce = parseNonce(swap.nonce);\n const isV2 = swap.protocolVersion >= 2;\n\n // 1. AUTHORIZE — sign nonce-mapping locally, POST to Markr.\n const authorizeStep: TransferStepDetails = {\n currentSignature: 1,\n currentSignatureReason: TransferSignatureReason.HyperliquidAuthorize,\n quote,\n requiredSignatures: 2,\n };\n onStepChange?.(authorizeStep);\n\n // v2: sign Relay's `sign` envelope verbatim — no schema knowledge required.\n // v1 (legacy): rebuild the `RelayNonceMapping` typed data from pinned types.\n // Either path produces the same authorize signature for matching orders;\n // the v2 path additionally tolerates Relay-side schema additions (e.g.\n // `depositor`) without an SDK release.\n let authorizeTypedData: EvmTypedData;\n if (isV2) {\n if (!swap.sign || !swap.authorize) {\n throw new SdkError(\n 'Hyperliquid v2 swap response is missing `sign` or `authorize` envelope.',\n ErrorCode.HL_UNSUPPORTED_PROTOCOL_VERSION,\n { details: 'Required pass-through fields are absent — the upstream may have downgraded to v1.' },\n );\n }\n assertSignEnvelopeMatchesSwap(swap.sign, swap, wallet);\n authorizeTypedData = {\n domain: swap.sign.domain,\n types: swap.sign.types,\n primaryType: swap.sign.primaryType,\n message: swap.sign.value,\n };\n } else {\n // `swap.id` (the order id) is what Relay binds the nonce-mapping to —\n // distinct from `swap.requestId` (the cross-chain tracking id). Mixing them\n // up produces a \"valid-looking\" signature for an order Relay doesn't know\n // about, so the on-chain sendAsset gets orphaned.\n authorizeTypedData = buildRelayNonceMappingTypedData({\n signatureChainId: targetChainId,\n wallet,\n orderId: swap.id,\n nonce,\n });\n }\n\n const authorizeSignature = await evmSigner.signTypedData(\n { typedData: authorizeTypedData, address: wallet, chainId: targetChainId },\n authorizeStep,\n );\n\n try {\n if (isV2 && swap.authorize) {\n await markrPostAuthorize(apiOptions, {\n uuid: quote.id,\n signature: authorizeSignature,\n // Forward Relay's `/authorize` body verbatim — Markr only normalizes\n // `nonce` to a JSON number and defaults `referrer`. Additive Relay\n // fields (e.g. `depositor`) flow through automatically.\n body: swap.authorize.body,\n });\n } else {\n await markrPostAuthorize(apiOptions, {\n uuid: quote.id,\n signature: authorizeSignature,\n wallet,\n walletChainId: 1337,\n signatureChainId: targetChainId,\n /**\n * Relay's upstream `/authorize` validates `nonce` strictly as a JSON\n * number. Sending it as a string makes the request fail upstream. HL\n * nonces are millisecond timestamps, comfortably under\n * `Number.MAX_SAFE_INTEGER`, so the Number coercion is lossless.\n */\n nonce: Number(nonce),\n // Order id (not the tracking requestId).\n id: swap.id,\n });\n }\n } catch (error) {\n // Surface as a failed transfer so the UI can prompt a retry rather than\n // crashing the panel. The user paid no on-chain cost yet.\n if (error instanceof HttpError) {\n return makeFailedTransferFromQuote(quote, {\n environment,\n errorCode: ErrorCode.HL_EXCHANGE_REJECTED,\n errorReason: `Markr /authorize failed (HTTP ${error.status}). Please retry.`,\n });\n }\n throw error;\n }\n\n // 2. PRE-FLIGHT — re-check withdrawable. Guards the second wallet prompt\n // from being asked for a balance the user no longer has (e.g. they opened\n // a position between the two signatures on another device). Relay's\n // pre-formatted `amount` is at the spot token's display precision, so it\n // parses cleanly via `Number(...)` — no need to re-format from the bigint.\n const requestedAmount = Number(swap.parameters.amount);\n const liveWithdrawable = await hyperliquidGetWithdrawable({\n apiBaseUrl: hyperliquidApiBaseUrlForEnvironment(environment),\n user: wallet,\n });\n\n if (Number.isFinite(liveWithdrawable) && liveWithdrawable < requestedAmount) {\n return makeFailedTransferFromQuote(quote, {\n environment,\n errorCode: ErrorCode.HL_WITHDRAWABLE_DROPPED,\n errorReason: `Withdrawable balance changed during withdrawal (requested ${requestedAmount}, available ${liveWithdrawable}). Please retry with a smaller amount.`,\n });\n }\n\n // 3. SEND ASSET — sign HL transaction locally, POST to api.hyperliquid.xyz/exchange.\n // We sign and submit Relay's `parameters` *verbatim* — Relay's order matcher\n // recovers the on-chain `sendAsset` by hashing these exact strings, so any\n // local re-formatting (especially of `amount`) would orphan the transfer.\n const sendAssetStep: TransferStepDetails = {\n currentSignature: 2,\n currentSignatureReason: TransferSignatureReason.HyperliquidSendAsset,\n quote,\n requiredSignatures: 2,\n };\n onStepChange?.(sendAssetStep);\n\n const sendAssetParams = {\n signatureChainId: targetChainId,\n hyperliquidChain: swap.parameters.hyperliquidChain,\n destination: swap.parameters.destination,\n token: swap.parameters.token,\n amount: swap.parameters.amount,\n sourceDex: swap.parameters.sourceDex,\n destinationDex: swap.parameters.destinationDex,\n fromSubAccount: swap.parameters.fromSubAccount,\n nonce,\n } satisfies BuildHyperliquidSendAssetParams;\n\n const sendAssetTypedData = buildHyperliquidSendAssetTypedData(sendAssetParams);\n const sendAssetSignature = await evmSigner.signTypedData(\n { typedData: sendAssetTypedData, address: wallet, chainId: targetChainId },\n sendAssetStep,\n );\n\n const exchangeAction = buildHyperliquidExchangeAction(sendAssetParams);\n\n const exchangeResponse = await hyperliquidExchangePost({\n apiBaseUrl: hyperliquidApiBaseUrlForEnvironment(environment),\n signature: sendAssetSignature,\n nonce,\n action: exchangeAction,\n });\n\n if (exchangeResponse.status !== 'ok') {\n return makeFailedTransferFromQuote(quote, {\n environment,\n errorCode: ErrorCode.HL_EXCHANGE_REJECTED,\n errorReason: extractHlExchangeError(exchangeResponse.response),\n });\n }\n\n const startedAtMs = Date.now();\n\n // HL `/exchange` answering `status: ok` is our source-side finality signal —\n // HL's matcher debits the user atomically with that ack (no separate on-chain\n // tx hash to wait on; HL doesn't expose one for `sendAsset`). Hand the tracker\n // a `source-completed` transfer keyed on Relay's `requestId` so it skips the\n // EVM finality gate and goes straight to Markr `/cross-chain/status` polling.\n return {\n amountIn: quote.amountIn,\n amountOut: quote.amountOut,\n environment,\n fees: quote.fees,\n fromAddress: quote.fromAddress,\n id: quote.id,\n partnerFeeBps: quote.partnerFeeBps,\n sourceAsset: quote.assetIn,\n sourceChain: quote.sourceChain,\n status: 'source-completed',\n targetAsset: quote.assetOut,\n targetChain: quote.targetChain,\n toAddress: quote.toAddress,\n type: quote.serviceType,\n source: {\n confirmationCount: 2,\n requiredConfirmationCount: 2,\n startedAtMs,\n // `requestId` doubles as the SDK's sourceTxHash for status polling.\n txHash: swap.requestId,\n },\n };\n}\n\nfunction extractHlExchangeError(response: unknown): string {\n if (typeof response === 'string') {\n return `Hyperliquid /exchange rejected the transfer: ${response}`;\n }\n\n if (response && typeof response === 'object' && 'error' in response && typeof response.error === 'string') {\n return `Hyperliquid /exchange rejected the transfer: ${response.error}`;\n }\n\n return ErrorReason.UNKNOWN;\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
require(`../../../_virtual/_rolldown/runtime.cjs`);const e=require(`../../../constants.cjs`),t=require(`../../../errors.cjs`),n=require(`../../../utils/caip.cjs`),r=require(`../../_utils.cjs`),i=require(`../../../_utils/chain.cjs`),a=require(`../../_evm-errors.cjs`),o=require(`../_api.cjs`),s=require(`../../../utils/sol-address.cjs`),c=require(`../_utils.cjs`),l=require(`../_type-guards.cjs`),u=require(`../../../utils/solana.cjs`),d=require(`./estimate-native-fee.cjs`);let
|
|
1
|
+
require(`../../../_virtual/_rolldown/runtime.cjs`);const e=require(`../../../constants.cjs`),t=require(`../../../errors.cjs`),n=require(`../../../utils/caip.cjs`),r=require(`../../_utils.cjs`),i=require(`../../../_utils/chain.cjs`),a=require(`../../_evm-errors.cjs`),o=require(`../_api.cjs`),s=require(`../../../utils/sol-address.cjs`),c=require(`../_utils.cjs`),l=require(`../_type-guards.cjs`),u=require(`../../../utils/solana.cjs`),d=require(`./estimate-native-fee.cjs`),f=require(`./transfer-asset-hyperliquid-withdraw.cjs`);let p=require(`viem`);function m({apiOptions:n,appId:r,environment:a,evmSigner:o,solanaSigner:s}){return async({quote:c,gasSettings:l,fallbackToDefaultOnBatchFailure:u,onStepChange:d})=>{if(c.serviceType!==e.ServiceType.MARKR)throw new t.SdkError(t.ErrorReason.INCORRECT_PROVIDER_PROVIDED,t.ErrorCode.INVALID_PARAMS);let f=Math.floor(Date.now()/1e3);if(c.expiresAt<=f)throw new t.SdkError(t.ErrorReason.QUOTE_EXPIRED,t.ErrorCode.INVALID_PARAMS);let p=c.sourceChain.chainId;if(p===e.HyperliquidChainIds.MAINNET)return h({apiOptions:n,appId:r,environment:a,evmSigner:o,quote:c,onStepChange:d});if(i.isSolanaNamespace(p))return _({apiOptions:n,appId:r,environment:a,solanaSigner:s,quote:c,onStepChange:d});if(i.isEvmNamespace(p))return g({apiOptions:n,appId:r,environment:a,evmSigner:o,quote:c,gasSettings:l,fallbackToDefaultOnBatchFailure:u,onStepChange:d});throw new t.SdkError(t.ErrorReason.CHAIN_NOT_SUPPORTED,t.ErrorCode.INVALID_PARAMS,{details:`Unsupported source chain namespace: ${p}`})}}async function h({apiOptions:e,appId:n,environment:r,evmSigner:i,quote:a,onStepChange:s}){let u=c.calculateMarkrMinimumAmountOut({amountOut:a.amountOut,assetOut:a.assetOut,slippageBps:a.slippageBps}),d=c.assetToAddressString(a.assetIn,a.sourceChain.chainId),p=c.assetToAddressString(a.assetOut,a.targetChain.chainId),m=await o.markrSwap(e,{amountIn:a.amountIn.toString(),appId:n,minAmountOut:u.toString(),tokenIn:d,tokenOut:p,uuid:a.id});if(!l.isHyperliquidWithdrawSwapResponse(m))throw new t.SdkError(t.ErrorReason.CHAIN_NOT_SUPPORTED,t.ErrorCode.INVALID_PARAMS,{details:"Expected `hyperliquid-withdraw` swap response from Markr for Hyperliquid source chain, but received a different variant."});return f._executeHyperliquidWithdraw({apiOptions:e,environment:r,evmSigner:i,quote:a,swap:m,onStepChange:s})}async function g({apiOptions:i,appId:s,environment:u,evmSigner:f,quote:m,gasSettings:h,fallbackToDefaultOnBatchFailure:g,onStepChange:_}){let y=m.fromAddress;if(!(0,p.isAddress)(y))throw new t.SdkError(t.ErrorReason.INVALID_PARAMS,t.ErrorCode.INVALID_PARAMS,{details:`fromAddress is not a valid EVM address.`});let b=c.calculateMarkrMinimumAmountOut({amountOut:m.amountOut,assetOut:m.assetOut,slippageBps:m.slippageBps}),x=r.getEvmClientForChain({chain:m.sourceChain}),S=c.assetToAddressString(m.assetIn,m.sourceChain.chainId),C=c.assetToAddressString(m.assetOut,m.targetChain.chainId);if(!(0,p.isAddress)(S))throw new t.SdkError(t.ErrorReason.INVALID_PARAMS,t.ErrorCode.INVALID_PARAMS,{details:`assetIn address is not a valid EVM address.`});let w=S,T=c.isTokenAddressNative(w),E=m.sourceChain.chainId.toLowerCase()!==m.targetChain.chainId.toLowerCase(),D=m.amountIn+c.getAdditiveSourceAssetFeeAmount(m),{address:O}=await o.markrGetSpenderAddress(i,{chainId:n.caip2ToEip155ChainId(m.sourceChain.chainId),crossChainSwap:E,quoteId:m.id});if(!T&&!O)throw new t.SdkError(t.ErrorReason.CHAIN_NOT_SUPPORTED,t.ErrorCode.INVALID_PARAMS,{details:`Missing Markr spender address for source chain ${m.sourceChain.chainId}.`});let k=!1,A;if(!T&&O&&await x.readContract({address:w,abi:p.erc20Abi,functionName:`allowance`,args:[y,O]})<D){k=!0;let e=(0,p.encodeFunctionData)({abi:p.erc20Abi,functionName:`approve`,args:[O,D]}),t=r.applyFeeUnitsBpsMargin(await a.estimateGasWithRevert(x,{account:y,to:w,data:e},p.erc20Abi,`Failed to estimate gas for ERC20 approval transaction.`),h?.estimateGasMarginBps);A={chainId:n.caip2ToEip155HexChainId(m.sourceChain.chainId),data:e,from:y,gas:t,to:w,value:0n,...h?.maxFeePerGas===void 0?null:{maxFeePerGas:h.maxFeePerGas,maxPriorityFeePerGas:h.maxPriorityFeePerGas}}}let j=v({approvalRequest:A,requiresApprovalSignature:k,signBatch:f.signBatch,sourceChainId:m.sourceChain.chainId,targetChainId:m.targetChain.chainId}),M=await o.markrSwap(i,{amountIn:m.amountIn.toString(),appId:s,minAmountOut:b.toString(),tokenIn:S,tokenOut:C,uuid:m.id});if(!l.isEvmSwapResponse(M))throw new t.SdkError(t.ErrorReason.CHAIN_NOT_SUPPORTED,t.ErrorCode.INVALID_PARAMS,{details:`Received non-EVM swap response from Markr, but only EVM swaps are supported for this source chain.`});let N=(t,n=Date.now())=>{let r=m.sourceChain.chainId===m.targetChain.chainId;return{amountIn:m.amountIn,amountOut:m.amountOut,environment:u,fees:m.fees,fromAddress:m.fromAddress,id:m.id,partnerFeeBps:m.partnerFeeBps,sourceAsset:m.assetIn,sourceChain:m.sourceChain,status:`source-pending`,targetAsset:m.assetOut,targetChain:m.targetChain,toAddress:m.toAddress,type:e.ServiceType.MARKR,source:{confirmationCount:0,requiredConfirmationCount:r?1:2,startedAtMs:n,txHash:t}}};if(j&&A){let r={currentSignature:1,currentSignatureReason:e.TransferSignatureReason.TokensTransfer,quote:m,requiredSignatures:1},i=f.signBatch;if(!i)throw new t.SdkError(`One-click batch signer is not available.`,t.ErrorCode.SIGNING_FAILED);let a={chainId:n.caip2ToEip155HexChainId(m.sourceChain.chainId),data:M.data,from:y,gas:void 0,to:M.to,value:M.value,...h?.maxFeePerGas===void 0?null:{maxFeePerGas:h.maxFeePerGas,maxPriorityFeePerGas:h.maxPriorityFeePerGas}};_?.(r);try{let e=(await i([A,a],async e=>x.sendRawTransaction({serializedTransaction:e}),r)).at(-1);if(!e)throw new t.SdkError(`One-click batch signing returned no transaction hashes.`,t.ErrorCode.SIGNING_FAILED);return N(e)}catch(e){if(!g)throw e}}if(k&&A){let n={currentSignature:1,currentSignatureReason:e.TransferSignatureReason.AllowanceApproval,quote:m,requiredSignatures:2};_?.(n);let i=await f.sign(A,async e=>x.sendRawTransaction({serializedTransaction:e}),n);if((await x.waitForTransactionReceipt({hash:i})).status===`reverted`)return r.makeFailedTransferFromQuote(m,{environment:u,errorCode:t.ErrorCode.TRANSACTION_REVERTED,errorReason:`ERC20 approval transaction was reverted`})}let P=await d._estimateGasFromSwapResponse({crossChain:E,fromAddress:y,feeUnitsMarginBps:h?.estimateGasMarginBps,sourceClient:x,swap:M}),F={chainId:n.caip2ToEip155HexChainId(m.sourceChain.chainId),data:M.data,from:y,gas:P,to:M.to,value:M.value,...h?.maxFeePerGas===void 0?null:{maxFeePerGas:h.maxFeePerGas,maxPriorityFeePerGas:h.maxPriorityFeePerGas}},I={currentSignature:k?2:1,currentSignatureReason:e.TransferSignatureReason.TokensTransfer,quote:m,requiredSignatures:k?2:1};_?.(I);let L=Date.now();return N(await f.sign(F,async e=>x.sendRawTransaction({serializedTransaction:e}),I),L)}async function _({apiOptions:n,appId:i,environment:a,solanaSigner:d,quote:f,onStepChange:p}){if(!d)throw new t.SdkError(t.ErrorReason.INVALID_PARAMS,t.ErrorCode.INVALID_PARAMS,{details:`solanaSigner is required for Solana transfers but was not provided.`});if(!s.isSolAddress(f.fromAddress))throw new t.SdkError(t.ErrorReason.INVALID_PARAMS,t.ErrorCode.INVALID_PARAMS,{details:`fromAddress is not a valid Solana address.`});let m=c.calculateMarkrMinimumAmountOut({amountOut:f.amountOut,assetOut:f.assetOut,slippageBps:f.slippageBps}),h=c.assetToAddressString(f.assetIn,f.sourceChain.chainId),g=c.assetToAddressString(f.assetOut,f.targetChain.chainId),_=await o.markrSwap(n,{amountIn:f.amountIn.toString(),appId:i,minAmountOut:m.toString(),tokenIn:h,tokenOut:g,userPublicKey:f.fromAddress,uuid:f.id});if(!l.isSolanaSwapResponse(_))throw new t.SdkError(t.ErrorReason.CHAIN_NOT_SUPPORTED,t.ErrorCode.INVALID_PARAMS,{details:`Received non-SVM swap response from Markr, but only SVM swaps are supported for this source chain.`});console.debug(`[Fusion SDK] Original swap transaction (base64):`,_.swapTransaction);let v=await u.refreshSolanaSwapTransactionBlockhash(_.swapTransaction,r.getSolanaRpcForChain({chain:f.sourceChain}));console.debug(`[Fusion SDK] Refreshed swap transaction with new blockhash (base64):`,v);let y={currentSignature:1,currentSignatureReason:e.TransferSignatureReason.TokensTransfer,quote:f,requiredSignatures:1};p?.(y);let b=Date.now(),x=await d.signAndSend({account:f.fromAddress,serializedTx:v},y),S=f.sourceChain.chainId.toLowerCase()!==f.targetChain.chainId.toLowerCase();return{amountIn:f.amountIn,amountOut:f.amountOut,environment:a,fees:f.fees,fromAddress:f.fromAddress,id:f.id,partnerFeeBps:f.partnerFeeBps,sourceAsset:f.assetIn,sourceChain:f.sourceChain,status:`source-pending`,targetAsset:f.assetOut,targetChain:f.targetChain,toAddress:f.toAddress,type:e.ServiceType.MARKR,source:{confirmationCount:0,requiredConfirmationCount:S?2:32,startedAtMs:b,txHash:x}}}function v({approvalRequest:e,requiresApprovalSignature:t,signBatch:n,sourceChainId:r,targetChainId:a}){return i.isEvmNamespace(r)&&i.isEvmNamespace(a)&&r===a&&t&&e!==void 0&&typeof n==`function`}exports.transferAssetFactory=m;
|
|
2
2
|
//# sourceMappingURL=transfer-asset.cjs.map
|
|
@@ -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
|