# Token Transfers

## Overview

Token transfers encompass the management and tracking of supported tokens moving between your wallet and subaccounts on the Ethereal exchange. These movements primarily fall into two categories: **deposits** and **withdrawals**.

## How Do I Deposit?

Deposits allow you to move tokens from your wallet into a specified subaccount on the Ethereal exchange. The process follows these steps:

1. **Token approval**: ERC20 token approval to allow Ethereal to transfer tokens on your behalf
2. **Transact**: Execute the deposit transaction, specifying the token, amount, and target subaccount
3. **Pending**: Once deposited, tokens enter a pending state while confirmation occurs
4. **Confirmation**: The deposit is synchronised with offchain systems
5. **Availability**: After confirmation, funds become available for trading

{% hint style="info" %}
Token deposits to a fresh subaccount will also automatically create a subaccount. See [subaccounts and signers](https://docs.ethereal.trade/trading/perpetual-futures/subaccounts) section to learn more.
{% endhint %}

{% hint style="warning" %}
Default gas estimation can occasionally be too low, where explicit setting the gas limit for the transaction will be needed. If you encounter the `DelegationFailed()` error when depositing, try adding `gas: 250_000n` to the `viem` deposit call.
{% endhint %}

Here's an example of how you could deposit using `viem` in TypeScript:

```typescript
import {
  defineChain,
  erc20Abi,
  toHex,
  parseUnits,
  createWalletClient,
  http,
  createPublicClient,
  getAddress,
} from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { exchangeContractAbi } from './sample/exchange.abi';

// @see: docs.ethereal.trade for chain details
export const ETHEREAL_TESTNET_CHAIN = defineChain({
  id: 13374202,
  name: 'Ethereal Testnet',
  nativeCurrency: {
    decimals: 18,
    name: 'USDe',
    symbol: 'USDe',
  },
  rpcUrls: {
    default: {
      http: ['https://rpc.etherealtest.net'],
      webSocket: ['wss://rpc.etherealtest.net'],
    },
  },
  testnet: true,
});

const DEFAULT_SUBACCOUNT = toHex('primary', { size: 32 });

// Retrieve the token address via `GET /v1/token`.
const USDE_ADDRESS = '0xa1623E0AA40B142Cf755938b325321fB2c61Cf05';

const MAKER_MAKER_PK = '0x...'; // Your test private key

// `verifyingContract` can be found via `HTTP GET /v1/rpc/config`
const exchangeContract = getAddress(domain.verifyingContract);

const wallet = createWalletClient({
  account: privateKeyToAccount(MAKER_MAKER_PK),
  chain: ETHEREAL_TESTNET_CHAIN,
  transport: http(),
});
const publicClient = createPublicClient({ chain: ETHEREAL_TESTNET_CHAIN, transport: http() });

const deposit = async (amount: string) => {
  const nativeAmount = parseUnits(amount, 18); // USDe has 18 deciamls.
  const approveHash = await wallet.writeContract({
    address: USDE_ADDRESS,
    abi: erc20Abi,
    functionName: 'approve',
    args: [exchangeContract, nativeAmount],
  });
  await publicClient.waitForTransactionReceipt({ hash: approveHash });
  const hash = await wallet.writeContract({
    address: exchangeContract,
    abi: exchangeContractAbi,
    functionName: 'deposit',
    args: [DEFAULT_SUBACCOUNT, USDE_ADDRESS, nativeAmount, toHex('refCode', { size: 32 })],
  });
  await publicClient.waitForTransactionReceipt({ hash });
  console.log('Deposited!');
};

deposit('1000') // Deposit 1000 USDe
```

Once your deposit is confirmed, you can view your subaccount balance by calling:

```bash
curl -X 'GET' \
  'https://api.ethereal.trade/v1/subaccount/balance?subaccountId=45783bec-4675-4116-8829-f277afe063d7' \
  -H 'accept: application/json'
```

```json
{
  "hasNext": false,
  "data": [
    {
      "subaccountId": "45783bec-4675-4116-8829-f277afe063d7",
      "tokenId": "ff761c8b-6248-4673-b63d-d1f980551959",
      "tokenAddress": "0xa1623E0AA40B142Cf755938b325321fB2c61Cf05",
      "tokenName": "USD",
      "amount": "35.644112906",
      "available": "12005.889199245",
      "totalUsed": "326.590624473",
      "updatedAt": 1743851518800
    }
  ]
}
```

Each token in your subaccount has two balance components:

* **Available (free)**: Tokens that can be used to open new positions or withdraw
* **TotalUsed (locked)**: Tokens currently allocated as margin for open positions and resting orders

{% hint style="info" %}
Visit [supported tokens](https://docs.ethereal.trade/developer-guides/trading-api/broken-reference) and [subaccounts and signers](https://docs.ethereal.trade/trading/perpetual-futures/subaccounts) to learn more.
{% endhint %}

{% hint style="warning" %}
Important to raise that the trading API does **NOT** allow users to withdraw unrealized profits. Any unrealized profits must be realized before they become available margin. Refer to [margin health](https://docs.ethereal.trade/trading/perpetual-futures/liquidations) to learn more about initial and maintenance margin.
{% endhint %}

## Depositing USDe

Ethereal is designed to support any approved ERC20 token, though USDe will be the only depositable token available at launch. Since USDe serves as the native gas token on the Ethereal appchain, it requires an additional wrapping step before deposit.

Users must first wrap their USDe to `WUSDe` using our immutable wrapping contract, which implements the same interface as `WETH` for familiar functionality. The `WUSDe` contract address can be retrieved by querying the `USD` endpoint in our API, and once wrapped, the tokens can be deposited normally into the platform.

```bash
curl -X 'GET' \
  'https://api.ethereal.trade/v1/token?depositEnabled=true&withdrawEnabled=true&orderBy=createdAt' \
  -H 'accept: application/json'
```

```json
{
  "hasNext": false,
  "data": [
    {
      "id": "049dbd1a-bc21-4219-8264-886b64c0c046",
      "address": "0x7e3203241340C579d6f5061419E9d352Eff1d9F2",
      "name": "USD",
      "erc20Name": "Wrapped USDe",
      "erc20Symbol": "WUSDe",
      "erc20Decimals": 18,
      "depositEnabled": true,
      "withdrawEnabled": true,
      "depositFee": "0",
      "withdrawFee": "1",
      "minDeposit": "10",
      "createdAt": 1750401257026
    }
  ]
}
```

However, this process can be cumbersome, so we recommend using the provided `depositUsd` convenience method instead, which handles the wrapping and deposit process automatically.

```typescript
const exchange = getContract({
  address: exchangeAddress,
  abi: exchangeGatewayAbi,
  client: { public: publicClient, wallet },
});

const refCode = toHex("refcode", { size: 32 });
const hash = await exchange.write.depositUsd([this.subaccountName, refCode], {
  value: 1e18, // 1 USDe
});
await publicClient.waitForTransactionReceipt({ hash });
```

## Initiate Token Withdrawals

Withdrawals move tokens from your subaccount back to your wallet. The process follows these steps:

1. **Initiation**: Withdrawals begin with a request through the trading API
2. **Deduction**: Once request is processed, funds are immediately deducted from the subaccount
3. **Onchain relay**: The initiate withdrawal request is relayed onchain
4. **Finalize**: Users can claim their withdrawal after the lockout period expires

{% hint style="warning" %}
When the wrapped native token (WUSDe) is withdrawn it is unwrapped back to the native USDe as part of the withdrawal process.
{% endhint %}

To initiate a token withdrawal:

```bash
curl -X 'POST' \
  'https://api.ethereal.trade/v1/token/ff761c8b-6248-4673-b63d-d1f980551959/withdraw' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "signature": "string",
  "data": {
    "account": "<address_of_account>",
    "subaccount": "<bytes32_subaccount_name>",
    "token": "<token_address_to_withdraw>",
    "amount": "<amount_to_withdrawl_decimal>",
    "nonce": "<nonce_in_nanoseconds_as_string>",
    "signedAt": <timestamp_in_seconds>
  }
}'
```

You can query any pending or past withdrawals by:

```bash
curl -X 'GET' \
  'https://api.ethereal.trade/v1/token/withdraw?subaccountId=45783bec-4675-4116-8829-f277afe063d7&orderBy=createdAt' \
  -H 'accept: application/json'
```

```json
{
  "hasNext": false,
  "data": [
    {
      "id": "34510374-0010-4630-9954-46bb196806b2",
      "initiatedBlockNumber": "11272338",
      "finalizedBlockNumber": "11272403",
      "status": "COMPLETED",
      "subaccount": "0x7072696d61727900000000000000000000000000000000000000000000000000",
      "token": "0xa1623E0AA40B142Cf755938b325321fB2c61Cf05",
      "amount": "9",
      "digest": "0x...",
      "createdAt": 1743812502148
    }
  ]
}
```

Once relayed onchain and after the lockout period has past, users can finalize their withdrawal:

```solidity
/// @notice Actions pending withdraw for a subaccount.
/// @notice WUSDe will be withdrawn as USDe regardless of whether it was originally deposited as WUSDe or USDe.
/// @param account Address of the account to perform on behalf of
/// @param withdrawDigest EIP712 digest of the withdraw data
function finalizeWithdraw(address account, bytes32 withdrawDigest) external;
```

{% hint style="success" %}
An offchain withdraw claimer automatically finalizes withdrawals on users behalf but using the digest a user is always able to `finalizeWithdraw()` without the claimer.
{% endhint %}

## Retrieve Transfer History

To view all historical transfers for a specific subaccount, query:

```bash
curl -X 'GET' \
  'https://api.ethereal.trade/v1/token/transfer?subaccountId=45783bec-4675-4116-8829-f277afe063d7&statuses=COMPLETED&orderBy=createdAt' \
  -H 'accept: application/json'
```

```json
{
  "hasNext": false,
  "data": [
    {
      "id": "34510374-0010-4630-9954-46bb196806b2",
      "initiatedBlockNumber": "11272338",
      "finalizedBlockNumber": "11272403",
      "status": "COMPLETED",
      "subaccount": "0x7072696d61727900000000000000000000000000000000000000000000000000",
      "token": "0xa1623E0AA40B142Cf755938b325321fB2c61Cf05",
      "type": "WITHDRAW",
      "amount": "9",
      "createdAt": 1743812502148,
      "initiatedTransactionHash": "0xe21b386030fcbc1ff6948aac02e9e83db150e03ef9d2383c2a579d42be055a2b",
      "finalizedTransactionHash": "0xe0b33c8e92f164b9bf1114987a7712e30bafbba23490436f4cc5e7ff1a44b9d7"
    },
    {
      "id": "8cfae486-714f-4731-85ca-1b5da32e3755",
      "initiatedBlockNumber": "11272299",
      "finalizedBlockNumber": "11272306",
      "status": "COMPLETED",
      "subaccount": "0x7072696d61727900000000000000000000000000000000000000000000000000",
      "token": "0xa1623E0AA40B142Cf755938b325321fB2c61Cf05",
      "type": "DEPOSIT",
      "amount": "10.000000001",
      "createdAt": 1743812469703,
      "initiatedTransactionHash": "0x6729fd9d7a2e154aa3704849fb720c3ec2f10f3d145adc0290b43761e25aacef",
      "finalizedTransactionHash": "0xf22706a0c292061e3e9d843acad98f33a96b8b7e70b3920da81c1d032f18295d"
    },
    {
      "id": "2eb362ff-f317-4865-9bee-032b4caf0b0d",
      "initiatedBlockNumber": "11205366",
      "finalizedBlockNumber": "11205371",
      "status": "COMPLETED",
      "subaccount": "0x7072696d61727900000000000000000000000000000000000000000000000000",
      "token": "0xa1623E0AA40B142Cf755938b325321fB2c61Cf05",
      "type": "DEPOSIT",
      "amount": "20000",
      "createdAt": 1743745535947,
      "initiatedTransactionHash": "0x4591653ab33603c934b9c3041edad1af779293f347ec71e2c89c0f8b22cfc8a5",
      "finalizedTransactionHash": "0xa11fbd46d39d8249820619a91e81962e4922546b6a13ce453b0db2b5bc9e3c5d"
    }
  ]
}
```

## Token Transfer Fees

Fees on transfers primarily serve to deter spam and minimize network congestion, rather than as a revenue source. Fee structures are configurable per token and may be adjusted over time.

Both deposit and withdrawals can be configured to charge fees (denomianted in native token units). As of writing right now, there are **zero deposit fees** however USDe is configured to charge **a nominal withdrawal fee of 2**.
