Prerequisites

  • Node.js and npm installed
  • An Ethereum wallet with private key
  • API key from Aarc
  • Basic understanding of TypeScript and Ethereum
  • USDC tokens on Arbitrum for deposit

Setup

1

Environment Setup

Create a .env file with your credentials:

PRIVATE_KEY=your_private_key
RPC_URL=your_arbitrum_rpc_url
API_KEY=your_aarc_api_key
2

Install Dependencies

Install the required packages:

npm install ethers axios dotenv

Implementation

1

Configure Constants

Define the necessary constants for Apex Omni and USDC:

const APEX_OMNI_ADDRESS = '0x3169844a120C0f517B4eB4A750c08d8518C8466a';
const USDC = {
    symbol: 'USDC',
    address: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
    decimals: 6,
};

const DESTINATION_TOKEN = {
    decimals: USDC.decimals,
    chainId: 42161, // Arbitrum chain ID
    address: USDC.address,
};
2

Create Deposit Call Data Generator

Implement the function to generate the deposit call data:

function generateDepositCallData(
    tokenAddress: string,
    amount: string,
    userAddress: string
): string {
    // Create the contract interface with depositERC20 function
    const apexOmniInterface = new ethers.Interface([
        "function depositERC20(address _token, uint104 _amount, bytes32 _zkLinkAddress, uint8 _subAccountId, bool _mapping) external"
    ]);

    // Format the zkLink address (pad with zeros and remove 0x prefix)
    const zkLinkAddress = `0x000000000000000000000000${userAddress.slice(2)}`;

    // Generate the contract payload for depositing into Apex Omni
    return apexOmniInterface.encodeFunctionData("depositERC20", [
        tokenAddress,
        amount,
        zkLinkAddress,
        0, // subAccountId
        false
    ]);
}
3

Implement Deposit Function

Create the main function that handles the deposit process:

async function depositIntoPerp(amount: string) {
    try {
        // Generate call data for depositing into Apex Omni
        const callData = generateDepositCallData(
            USDC.address,
            amount,
            DESTINATION_WALLET
        );

        // Get deposit address from Aarc
        const depositData = await getDepositAddress({
            destinationChainId: DESTINATION_TOKEN.chainId.toString(),
            destinationTokenAddress: DESTINATION_TOKEN.address,
            toAmount: amount,
            destinationRecipient: APEX_OMNI_ADDRESS,
            transferType: 'wallet',
            targetCalldata: callData,
        });

        // Schedule the transaction
        const scheduledTx = await scheduleTransaction({
            requestId: depositData.requestId,
            fromAddress: DESTINATION_WALLET,
            toAddress: depositData.depositAddress,
            token: DESTINATION_TOKEN.address,
            amount: amount,
        });

        // Execute the transaction
        const txHash = await executeTransaction(depositData.txData);
        return txHash;
    } catch (error) {
        console.error("Error in deposit process:", error);
        throw error;
    }
}
4

Execute the Deposit

Call the function with your desired deposit amount:

// Deposit 1 USDC (1000000 = 1 USDC with 6 decimals)
depositIntoPerp("1000000");

Understanding the Flow

  1. Generate Deposit Data: Creates the encoded function call for depositing into Apex Omni, including the zkLink address formatting.
  2. Get Deposit Address: Obtains a unique deposit address from Aarc for the transaction.
  3. Schedule Transaction: Notifies Aarc about the upcoming transaction for faster processing.
  4. Execute Transaction: Sends the transaction to the blockchain.

Complete Example

For the complete implementation, including utility functions and more detailed error handling, check out our example repository.