import { type Address, type Chain, type Hex, zeroAddress } from "viem" import { MAGIC_ADDRESS_SERVER_URL, TOKEN_ADDRESSES, getWrappedNativeAddress } from "../constants" import type { MagicAddressErrorResponse } from "../types" import { deepHexlify } from "../utils" export type SPECIFIC_TOKENS = | "USDC" | "WRAPPED_NATIVE" | "USDT" | "DAI" | "WBTC" export type TOKEN_TYPE = "ERC20" | "NATIVE" | SPECIFIC_TOKENS export enum CallType { CALL = 0, DELEGATE_CALL = 1 } export type CALL = { target: Address value: bigint data: Hex callType: CallType tokenAddressOffset: bigint tokenAmountOffset: bigint } // export type TokenAction = { // tokenType: TOKEN_TYPE // tokenAddress?: Address // actions?: Action[] // } export type CreateMagicAddressParams = { owner: string destChain: Chain slippage?: number // in basis // srcChains?: Chain[]; // tokenActions?: TokenAction[] srcTokens?: { tokenType: TOKEN_TYPE; chain: Chain; minAmount?: bigint }[] config?: { baseUrl?: string } actions: { [key in TOKEN_TYPE]?: { action: CALL[] fallBack: CALL[] } } } export const createMagicAddress = async ({ destChain, owner, slippage, actions, srcTokens, config }: CreateMagicAddressParams) => { const requestBody = { executionChainId: destChain.id, owner, slippage, srcTokens: srcTokens?.map((token) => ({ token: token.tokenType === "NATIVE" ? zeroAddress : (TOKEN_ADDRESSES[token.chain.id][ token.tokenType as SPECIFIC_TOKENS ] ?? "0xffffffffffffffffffffffffffffffffffffffff"), chainId: token.chain.id, minAmount: token?.minAmount ?? 0n })), actions: Object.entries(actions).map(([tokenType, action]) => { const isNative = tokenType === "NATIVE" const asset = tokenType === "NATIVE" ? getWrappedNativeAddress(destChain.id) : (TOKEN_ADDRESSES[destChain.id][ tokenType as SPECIFIC_TOKENS ] ?? "0xffffffffffffffffffffffffffffffffffffffff") const withdrawWethCall = isNative ? [ { target: getWrappedNativeAddress(destChain.id), value: 0n, callType: CallType.CALL, // withdraw data: "0x2e1a7d4d0000000000000000000000000000000000000000000000000000000000000000", tokenAddressOffset: 0n, tokenAmountOffset: 4n } ] : [] const calls = withdrawWethCall.concat(action.action) const fallBack = withdrawWethCall.concat(action.fallBack) return { calls: calls.map((call) => ({ ...call, actionType: call.callType })), fallBack: fallBack.map((call) => ({ ...call, actionType: call.callType })), asset, isNative } }) } const baseUrl = config?.baseUrl || MAGIC_ADDRESS_SERVER_URL const res = await fetch(`${baseUrl}/v1`, { method: "POST", body: JSON.stringify({ jsonrpc: "2.0", id: 225, method: "ma_createMagicAddress", params: [deepHexlify(requestBody)] }), headers: { "Content-Type": "application/json" } }) if (!res.ok) { const data = (await res.json()) as MagicAddressErrorResponse const errorMessage = data?.error ?? res.statusText throw new Error(`Failed to create magic address ${errorMessage}`) } const data = await res.json() // @ts-ignore return data.result }