diff --git a/.changeset/five-sloths-wave.md b/.changeset/five-sloths-wave.md new file mode 100644 index 00000000..95c1664e --- /dev/null +++ b/.changeset/five-sloths-wave.md @@ -0,0 +1,6 @@ +--- +'@relayprotocol/relay-sdk': minor +'@relayprotocol/relay-kit-ui': minor +--- + +Integrate hyperliquid direct deposits diff --git a/demo/pages/ui/swap.tsx b/demo/pages/ui/swap.tsx index 012fae77..cb9296a6 100644 --- a/demo/pages/ui/swap.tsx +++ b/demo/pages/ui/swap.tsx @@ -36,7 +36,7 @@ import { adaptTronWallet } from '@relayprotocol/relay-tron-wallet-adapter' import Head from 'next/head' import { isTronWallet, TronWallet } from '@dynamic-labs/tron' -const WALLET_VM_TYPES = ['evm', 'bvm', 'svm', 'suivm', 'tvm'] as const +const WALLET_VM_TYPES = ['evm', 'bvm', 'svm', 'suivm', 'tvm', 'hypevm'] as const const SwapWidgetPage: NextPage = () => { useDynamicEvents('walletAdded', (newWallet) => { diff --git a/packages/sdk/src/utils/executeSteps/index.ts b/packages/sdk/src/utils/executeSteps/index.ts index e9e4370a..25317de7 100644 --- a/packages/sdk/src/utils/executeSteps/index.ts +++ b/packages/sdk/src/utils/executeSteps/index.ts @@ -6,7 +6,7 @@ import type { import type { AxiosRequestConfig } from 'axios' import { getClient, RelayClient } from '../../client.js' import { LogLevel } from '../logger.js' -import { prepareHyperliquidSignatureStep } from '../../utils/index.js' +import { prepareHyperliquidSteps } from '../../utils/index.js' import { canBatchTransactions, prepareBatchTransaction @@ -124,18 +124,10 @@ export async function executeSteps( } } - // Check if Hyperliquid and if so, rewrite steps to become a signature step - if ( - chainId === 1337 && - json.steps[0] && - (json.steps[0].id as any) !== 'sign' - ) { + // Check if Hyperliquid and if so, prepare the steps for Hyperliquid signing + if (chainId === 1337) { const activeWalletChainId = await wallet?.getChainId() - const signatureStep = prepareHyperliquidSignatureStep( - json.steps, - activeWalletChainId - ) - json.steps = [signatureStep] + json.steps = prepareHyperliquidSteps(json.steps, activeWalletChainId) } // Update state on first call or recursion diff --git a/packages/sdk/src/utils/executeSteps/signatureStep.ts b/packages/sdk/src/utils/executeSteps/signatureStep.ts index 9e8e83de..6c45f74e 100644 --- a/packages/sdk/src/utils/executeSteps/signatureStep.ts +++ b/packages/sdk/src/utils/executeSteps/signatureStep.ts @@ -9,7 +9,7 @@ import type { AxiosRequestConfig } from 'axios' import { LogLevel } from '../logger.js' import type { RelayClient } from '../../client.js' import type { SetStateData } from './index.js' -import { sendUsd } from '../hyperliquid.js' +import { postHyperliquidSignature } from '../hyperliquid.js' /** * Handles the execution of a signature step item, including signing, posting, and validation. @@ -57,6 +57,7 @@ export async function handleSignatureStepItem({ breakdown: json?.breakdown, details: json?.details }) + signature = await wallet.handleSignMessageStep(stepItem, step) if (signature) { @@ -67,8 +68,12 @@ export async function handleSignatureStepItem({ } } - if (chain.id === 1337 && signature) { - await sendUsd(client, signature, stepItem) + if ( + chain.id === 1337 && + signature && + step?.id === ('hyperliquid-signature' as any) + ) { + await postHyperliquidSignature(client, signature, stepItem) } if (postData) { diff --git a/packages/sdk/src/utils/hyperliquid.ts b/packages/sdk/src/utils/hyperliquid.ts index 97bc0a36..8c88cd10 100644 --- a/packages/sdk/src/utils/hyperliquid.ts +++ b/packages/sdk/src/utils/hyperliquid.ts @@ -4,15 +4,17 @@ import axios from 'axios' import type { RelayClient } from '../client.js' import { LogLevel } from './logger.js' -export function prepareHyperliquidSignatureStep( - steps: Execute['steps'], +function prepareHyperliquidSignatureStep( + step: Execute['steps'][0], chainId: number ) { - const items = steps[0]?.items - const amount = items[0]?.data?.action?.parameters?.amount - const destination = items[0]?.data?.action?.parameters?.destination + const stepItem = step?.items?.[0] + const action = stepItem?.data?.action + const eip712Types = stepItem?.data?.eip712Types + const eip712PrimaryType = stepItem?.data?.eip712PrimaryType + const signatureStep = { - id: 'sign' as any, + id: 'hyperliquid-signature' as any, action: 'Confirm transaction in your wallet', description: `Sign a message to confirm the transaction`, kind: 'signature' as const, @@ -29,12 +31,7 @@ export function prepareHyperliquidSignatureStep( verifyingContract: '0x0000000000000000000000000000000000000000' }, types: { - 'HyperliquidTransaction:UsdSend': [ - { name: 'hyperliquidChain', type: 'string' }, - { name: 'destination', type: 'string' }, - { name: 'amount', type: 'string' }, - { name: 'time', type: 'uint64' } - ], + ...eip712Types, EIP712Domain: [ { name: 'name', type: 'string' }, { name: 'version', type: 'string' }, @@ -42,31 +39,28 @@ export function prepareHyperliquidSignatureStep( { name: 'verifyingContract', type: 'address' } ] }, - primaryType: 'HyperliquidTransaction:UsdSend', + primaryType: eip712PrimaryType, value: { - type: 'usdSend', - signatureChainId: `0x${chainId.toString(16)}`, - hyperliquidChain: 'Mainnet', - destination: destination?.toLowerCase(), - amount, - time: new Date().getTime() + ...action.parameters, + type: action.type, + signatureChainId: `0x${chainId.toString(16)}` } } }, check: { - endpoint: `/intents/status?requestId=${steps[0]?.requestId}`, + endpoint: `/intents/status?requestId=${step?.requestId}`, method: 'GET' } } ], - requestId: steps[0]?.requestId, - depositAddress: steps[0]?.depositAddress + requestId: step?.requestId, + depositAddress: step?.depositAddress } return signatureStep } -export async function sendUsd( +export async function postHyperliquidSignature( client: RelayClient, signature: string, stepItem: Execute['steps'][0]['items'][0] @@ -76,22 +70,18 @@ export async function sendUsd( LogLevel.Verbose ) const { r, s, v } = parseSignature(signature as `0x${string}`) - const currentTime = stepItem?.data?.sign?.value?.time ?? new Date().getTime() + + const action = stepItem?.data?.sign?.value + const nonce = action?.nonce ?? action?.time + const res = await axios.post('https://api.hyperliquid.xyz/exchange', { signature: { r, s, v: Number(v ?? 0n) }, - nonce: currentTime, - action: { - type: stepItem?.data?.sign?.value?.type, - signatureChainId: `0x${stepItem?.data?.sign?.domain?.chainId?.toString(16)}`, - hyperliquidChain: 'Mainnet', - destination: stepItem?.data?.sign?.value?.destination?.toLowerCase(), - amount: stepItem?.data?.sign?.value?.amount, - time: currentTime - } + nonce, + action }) if ( !res || @@ -107,3 +97,55 @@ export async function sendUsd( ) return res.data } + +function updateHyperliquidSignatureChainId( + step: Execute['steps'][0], + activeWalletChainId: number +): Execute['steps'][0] { + return { + ...step, + items: step.items?.map((item) => ({ + ...item, + data: { + ...item.data, + sign: { + ...item.data.sign, + domain: { + ...item.data.sign.domain, + chainId: activeWalletChainId + } + }, + ...(item.data.post && { + post: { + ...item.data.post, + body: { + ...item.data.post.body, + signatureChainId: activeWalletChainId + } + } + }) + } + })) + } +} + +export function prepareHyperliquidSteps( + steps: Execute['steps'], + activeWalletChainId: number +): Execute['steps'] { + return steps.map((step) => { + // Skip steps that have already been converted (id is set to 'sign' by prepareHyperliquidSignatureStep) + if ((step.id as string) === 'hyperliquid-signature') { + return step + } + // Update signature steps to use the active wallet chain ID + if (step.kind === 'signature') { + return updateHyperliquidSignatureChainId(step, activeWalletChainId) + } + // Convert transaction steps to Hyperliquid signature steps + if (step.kind === 'transaction') { + return prepareHyperliquidSignatureStep(step, activeWalletChainId) + } + return step + }) +} diff --git a/packages/sdk/src/utils/index.ts b/packages/sdk/src/utils/index.ts index c8871413..e2fc192f 100644 --- a/packages/sdk/src/utils/index.ts +++ b/packages/sdk/src/utils/index.ts @@ -15,5 +15,5 @@ export { } from './simulateContract.js' export { safeStructuredClone } from './structuredClone.js' export { repeatUntilOk } from './repeatUntilOk.js' -export { prepareHyperliquidSignatureStep } from './hyperliquid.js' +export { prepareHyperliquidSteps } from './hyperliquid.js' export { isRelayApiUrl, getApiKeyHeader } from './apiKey.js' diff --git a/packages/ui/src/components/widgets/SwapWidgetRenderer.tsx b/packages/ui/src/components/widgets/SwapWidgetRenderer.tsx index 9313e85b..2f896458 100644 --- a/packages/ui/src/components/widgets/SwapWidgetRenderer.tsx +++ b/packages/ui/src/components/widgets/SwapWidgetRenderer.tsx @@ -523,8 +523,7 @@ const SwapWidgetRenderer: FC = ({ toToken.decimals ).toString(), referrer: relayClient?.source ?? undefined, - useDepositAddress: - !fromChainWalletVMSupported || fromToken?.chainId === 1337, + useDepositAddress: !fromChainWalletVMSupported, refundTo: fromToken?.chainId === 1337 ? address : undefined, slippageTolerance: slippageTolerance, topupGas: gasTopUpEnabled && gasTopUpRequired,