import { afterEach, beforeEach, describe, expect, it } from "bun:test" import { type Address, type Hex, erc20Abi, zeroAddress } from "viem" import { baseSepolia, sepolia } from "viem/chains" import { FLEX, MAGIC_ADDRESS_SERVER_URL, TOKEN_ADDRESSES, getWrappedNativeAddress } from "../constants" import { createCall } from "../utils/createCall" import { type CreateMagicAddressParams, createMagicAddress } from "./createMagicAddress" const owner = "0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2" let fetchMock: { // biome-ignore lint: no type check calls: any[] // biome-ignore lint: no type check mockImplementation: (input: any, init?: RequestInit) => Promise } const aavePoolAbi = [ { inputs: [ { internalType: "address", name: "asset", type: "address" }, { internalType: "uint256", name: "amount", type: "uint256" }, { internalType: "address", name: "onBehalfOf", type: "address" }, { internalType: "uint16", name: "referralCode", type: "uint16" } ], name: "deposit", outputs: [], stateMutability: "nonpayable", type: "function" } ] as const describe("createMagicAddress", () => { describe("should create magicAddress", () => { let fetch = global.fetch beforeEach(() => { fetchMock = { calls: [], mockImplementation: async (input, init) => { fetchMock.calls.push({ input, init }) const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url if (url === `${MAGIC_ADDRESS_SERVER_URL}/v1`) { return new Response( JSON.stringify({ jsonrpc: "2.0", id: 225, result: { magicAddress: "0x1234567890abcdef1234567890abcdef12345678", slippage: 100, estimatedFees: [] } }), { status: 200, headers: { "Content-Type": "application/json" } } ) } return new Response(null, { status: 500 }) } } fetch = global.fetch globalThis.fetch = fetchMock.mockImplementation }) afterEach(() => { // Restore the original fetch function global.fetch = fetch }) it("should return a magic address when ERC20 srcTokens", async () => { const target: Address = "0xfff9976782d46cc05630d1f6ebab18b2324d6b14" const dummyAavePoolAddress: Address = "0x7d2768de32b0b80b7a3454c06bdac94a69ddc7a9" const slippage = 100 const erc20Actions = [ createCall({ target: target, value: 0n, actionType: 0, abi: erc20Abi, functionName: "approve", args: [dummyAavePoolAddress, FLEX.AMOUNT] }) ] const expectedSrcTokens = [ { token: "0xffffffffffffffffffffffffffffffffffffffff", chainId: baseSepolia.id, minAmount: "0x0" } ] const expectedActions = [ { calls: [ { target, value: "0x0", callType: 0, data: "0x095ea7b30000000000000000000000007d2768de32b0b80b7a3454c06bdac94a69ddc7a90000000000000000000000000000000000000000000000000000000000000000", tokenAddressOffset: "0x0", tokenAmountOffset: "0x24", actionType: 0 } ], fallBack: [], asset: "0xffffffffffffffffffffffffffffffffffffffff", isNative: false } ] const params: CreateMagicAddressParams = { owner, destChain: sepolia, slippage: slippage, actions: { ERC20: { action: erc20Actions, fallBack: [] } }, srcTokens: [{ tokenType: "ERC20", chain: baseSepolia }] } await createMagicAddress(params) // Check the fetch call expect(fetchMock.calls.length).toBe(1) expect(fetchMock.calls[0].input).toBe( `${MAGIC_ADDRESS_SERVER_URL}/v1` ) expect(fetchMock.calls[0].init).toEqual( expect.objectContaining({ method: "POST", body: JSON.stringify({ jsonrpc: "2.0", id: 225, method: "ma_createMagicAddress", params: [ { executionChainId: sepolia.id, owner, slippage, srcTokens: expectedSrcTokens, actions: expectedActions } ] }), headers: { "Content-Type": "application/json" } }) ) }) it("should return a magic address with USDC srcTokens", async () => { const target: Address = TOKEN_ADDRESSES[baseSepolia.id].USDC ?? "0xffffffffffffffffffffffffffffffffffffffff" const dummyAavePoolAddress: Address = "0x7d2768de32b0b80b7a3454c06bdac94a69ddc7a9" const slippage = 100 const erc20Actions = [ createCall({ target: target, value: 0n, actionType: 0, abi: erc20Abi, functionName: "approve", args: [dummyAavePoolAddress, FLEX.AMOUNT] }) ] const expectedSrcTokens = [ { token: TOKEN_ADDRESSES[baseSepolia.id].USDC, chainId: baseSepolia.id, minAmount: "0x0" } ] const expectedActions = [ { calls: [ { target, value: "0x0", callType: 0, data: "0x095ea7b30000000000000000000000007d2768de32b0b80b7a3454c06bdac94a69ddc7a90000000000000000000000000000000000000000000000000000000000000000", tokenAddressOffset: "0x0", tokenAmountOffset: "0x24", actionType: 0 } ], fallBack: [], asset: "0xffffffffffffffffffffffffffffffffffffffff", isNative: false } ] const params: CreateMagicAddressParams = { owner, destChain: sepolia, slippage: slippage, actions: { ERC20: { action: erc20Actions, fallBack: [] } }, srcTokens: [{ tokenType: "USDC", chain: baseSepolia }] } await createMagicAddress(params) // Check the fetch call expect(fetchMock.calls.length).toBe(1) expect(fetchMock.calls[0].input).toBe( `${MAGIC_ADDRESS_SERVER_URL}/v1` ) expect(fetchMock.calls[0].init).toEqual( expect.objectContaining({ method: "POST", body: JSON.stringify({ jsonrpc: "2.0", id: 225, method: "ma_createMagicAddress", params: [ { executionChainId: sepolia.id, owner, slippage, srcTokens: expectedSrcTokens, actions: expectedActions } ] }), headers: { "Content-Type": "application/json" } }) ) }) it("should return a magic address when called with native actions", async () => { const target: Address = "0xfff9976782d46cc05630d1f6ebab18b2324d6b14" const dummyAavePoolAddress: Address = "0x7d2768de32b0b80b7a3454c06bdac94a69ddc7a9" const slippage = 100 const naitveActions = [ createCall({ target: target, value: 0n, actionType: 0, abi: erc20Abi, functionName: "approve", args: [dummyAavePoolAddress, FLEX.AMOUNT] }) ] const expectedSrcTokens = [ { token: zeroAddress, chainId: baseSepolia.id, minAmount: "0x0" } ] const expectedActions = [ { calls: [ { target: getWrappedNativeAddress(sepolia.id), value: "0x0", callType: 0, data: "0x2e1a7d4d0000000000000000000000000000000000000000000000000000000000000000", tokenAddressOffset: "0x0", tokenAmountOffset: "0x4", actionType: 0 }, { target, value: "0x0", callType: 0, data: "0x095ea7b30000000000000000000000007d2768de32b0b80b7a3454c06bdac94a69ddc7a90000000000000000000000000000000000000000000000000000000000000000", tokenAddressOffset: "0x0", tokenAmountOffset: "0x24", actionType: 0 } ], fallBack: [ { target: getWrappedNativeAddress(sepolia.id), value: "0x0", callType: 0, data: "0x2e1a7d4d0000000000000000000000000000000000000000000000000000000000000000", tokenAddressOffset: "0x0", tokenAmountOffset: "0x4", actionType: 0 } ], asset: getWrappedNativeAddress(sepolia.id), isNative: true } ] const params: CreateMagicAddressParams = { owner, destChain: sepolia, slippage: slippage, actions: { NATIVE: { action: naitveActions, fallBack: [] } }, srcTokens: [{ tokenType: "NATIVE", chain: baseSepolia }] } await createMagicAddress(params) // Check the fetch call expect(fetchMock.calls.length).toBe(1) expect(fetchMock.calls[0].input).toBe( `${MAGIC_ADDRESS_SERVER_URL}/v1` ) expect(fetchMock.calls[0].init).toEqual( expect.objectContaining({ method: "POST", body: JSON.stringify({ jsonrpc: "2.0", id: 225, method: "ma_createMagicAddress", params: [ { executionChainId: sepolia.id, owner, slippage, srcTokens: expectedSrcTokens, actions: expectedActions } ] }), headers: { "Content-Type": "application/json" } }) ) }) it("should handle API error response", async () => { const errorMessage = "Invalid parameters" fetchMock = { calls: [], mockImplementation: async (input, init) => { fetchMock.calls.push({ input, init }) return new Response( JSON.stringify({ jsonrpc: "2.0", id: 225, error: errorMessage }), { status: 500, headers: { "Content-Type": "application/json" } } ) } } fetch = global.fetch globalThis.fetch = fetchMock.mockImplementation expect( createMagicAddress({ destChain: sepolia, owner, actions: { NATIVE: { action: [], fallBack: [] } } }) ).rejects.toThrow(`Failed to create magic address ${errorMessage}`) }) }) describe("actual fetch test", () => { it( "should return a magic address when called with valid parameters", async () => { const target: Address = "0xfff9976782d46cc05630d1f6ebab18b2324d6b14" const dummyAavePoolAddress: Address = "0x7d2768de32b0b80b7a3454c06bdac94a69ddc7a9" const data: Hex = "0xa9059cbb0000000000000000000000009775137314fe595c943712b0b336327dfa80ae8a0000000000000000000000000000000000000000000000000000000000000000" const tokenAddressOffset = 0n const tokenAmountOffset = 36n const actions = [ { target: target, value: 0n, actionType: 0, data: data, tokenAddressOffset: tokenAddressOffset, tokenAmountOffset: tokenAmountOffset, shouldNotFail: true } ] const erc20Actions = [ createCall({ target: target, value: 0n, actionType: 0, abi: erc20Abi, functionName: "approve", args: [dummyAavePoolAddress, FLEX.AMOUNT] }), createCall({ target: target, value: 0n, actionType: 0, abi: aavePoolAbi, functionName: "deposit", args: [FLEX.TOKEN_ADDRESS, FLEX.AMOUNT, target, 10] }) ] const params: CreateMagicAddressParams = { owner: "0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2", destChain: sepolia, slippage: 100, actions: { ERC20: { action: erc20Actions, fallBack: [] } }, srcTokens: [{ tokenType: "ERC20", chain: baseSepolia }] // config: { baseUrl: MAGIC_ADDRESS_SERVER_URL } } const result = await createMagicAddress(params) console.log(result) }, { timeout: 30000 } ) }) })