# Ethereum → Goliath

Bridging from Ethereum Mainnet (`1`) to Goliath Mainnet (`327`) is a **single transaction** on Ethereum — no signed intent, no post-transfer API call. The relayer observes the `Deposit` event on Ethereum and mints the corresponding asset on Goliath automatically.

**Typical completion time: \~5 minutes. No bridge fee (Ethereum gas only).**

## Prerequisites

* A wallet connected to **Ethereum Mainnet** (Chain ID `1`).
* Enough **ETH** for gas.
* For ERC-20 deposits (USDC, XCN): enough token balance + ETH for gas + approval (see [Step 2](#step-2-approve-erc-20-allowance-usdc-and-xcn-only)).

## Supported Tokens

| Token    | Ethereum-side asset | Address on Ethereum                          |
| -------- | ------------------- | -------------------------------------------- |
| **ETH**  | Native ETH          | n/a — pass `msg.value`                       |
| **USDC** | ERC-20              | `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48` |
| **XCN**  | ERC-20              | `0xA2cd3D43c775978A96BdBf12d733D5A1ED94fb18` |

Destination (mint) addresses on Goliath are listed in [Contracts & Events](/developer-guide/bridge/contracts-and-events.md#goliath-mainnet-bridged-tokens).

## The BridgeLock Contract

Ethereum Mainnet BridgeLock: `0xa9fd64b5095d626f5a3a67e6db7fb766345f8092`

Two user-facing functions:

```solidity
// Native ETH
function depositNative(address destinationAddress)
    external payable
    returns (bytes32 depositId);

// ERC-20 tokens
function deposit(address token, uint256 amount, address destinationAddress)
    external
    returns (bytes32 depositId);
```

Both emit:

```solidity
event Deposit(
    bytes32 indexed depositId,
    address indexed token,         // address(0) for native ETH
    address indexed sender,
    address destinationAddress,
    uint256 amount,
    uint64  timestamp,
    uint64  sourceChainId
);
```

Full ABI in [Contracts & Events](/developer-guide/bridge/contracts-and-events.md#bridgelock-abi-user-functions).

## Step 1 — (Optional) Quote the transfer

Deposits have no fee, but calling [`GET /bridge/fee-quote`](/developer-guide/bridge/api-reference.md#get-bridge-fee-quote) confirms the token is supported and returns the same canonical `inputAmount` / `outputAmount` shape you will see later in status responses.

```
GET https://bridge.goliath.net/api/v1/bridge/fee-quote?token=USDC&amount=100&direction=ETHEREUM_TO_GOLIATH
```

```json
{
  "inputAmount": "100000000",
  "inputFormatted": "100.0",
  "feeAmount": "0",
  "feeFormatted": "0.0",
  "feeBps": 0,
  "outputAmount": "100000000",
  "outputFormatted": "100.0",
  "token": "USDC"
}
```

Skip this step if you already know the amount.

## Step 2 — Approve ERC-20 allowance (USDC and XCN only)

Skip for native ETH.

```ts
// viem example
import { writeContract, waitForTransactionReceipt } from "viem/actions";
import { erc20Abi, maxUint256, parseUnits } from "viem";

const BRIDGE_LOCK = "0xa9fd64b5095d626f5a3a67e6db7fb766345f8092";
const USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";

const approveTx = await writeContract(walletClient, {
  address: USDC,
  abi: erc20Abi,
  functionName: "approve",
  args: [BRIDGE_LOCK, maxUint256],   // or an exact amount
  chain: ethereumMainnet,
});

await waitForTransactionReceipt(publicClient, { hash: approveTx });
```

{% hint style="info" %}
Approving `maxUint256` is the default in the Onyx App — users pay gas for `approve` only once per token. Switch to an exact amount if your policy forbids unbounded allowances.
{% endhint %}

## Step 3 — Submit the deposit

### Native ETH

```ts
import { parseEther } from "viem";

const amount = parseEther("0.05");            // 0.05 ETH

const depositTx = await writeContract(walletClient, {
  address: BRIDGE_LOCK,
  abi: bridgeLockAbi,
  functionName: "depositNative",
  args: [recipientOnGoliath],                 // address that receives ETH on Goliath
  value: amount,
  chain: ethereumMainnet,
});
```

### USDC (6 decimals)

```ts
import { parseUnits } from "viem";

const amount = parseUnits("100", 6);          // 100 USDC

const depositTx = await writeContract(walletClient, {
  address: BRIDGE_LOCK,
  abi: bridgeLockAbi,
  functionName: "deposit",
  args: [USDC, amount, recipientOnGoliath],
  chain: ethereumMainnet,
});
```

### XCN (ERC-20 on Ethereum, 18 decimals)

```ts
const XCN = "0xA2cd3D43c775978A96BdBf12d733D5A1ED94fb18";
const amount = parseUnits("1000", 18);        // 1000 XCN

const depositTx = await writeContract(walletClient, {
  address: BRIDGE_LOCK,
  abi: bridgeLockAbi,
  functionName: "deposit",
  args: [XCN, amount, recipientOnGoliath],
  chain: ethereumMainnet,
});
```

{% hint style="warning" %}
`recipientOnGoliath` is the address that will receive the asset on Goliath. It is typically the same as the signer, but it doesn't have to be — pass the user's selected destination address.
{% endhint %}

## Step 4 — Extract the `depositId` (optional)

The `Deposit` event includes a `depositId` — a `bytes32` identifier you can use in place of the tx hash when polling status. It's emitted as an indexed topic so you can parse it directly from the receipt logs:

```ts
import { decodeEventLog } from "viem";

const receipt = await waitForTransactionReceipt(publicClient, { hash: depositTx });

const depositLog = receipt.logs.find(
  (l) => l.address.toLowerCase() === BRIDGE_LOCK.toLowerCase()
);
const { args } = decodeEventLog({
  abi: bridgeLockAbi,
  data: depositLog.data,
  topics: depositLog.topics,
});
const depositId = args.depositId;             // bytes32
```

You can also skip this step entirely and just poll by `originTxHash` — both identifiers resolve to the same operation.

## Step 5 — Poll the bridge API

Once the tx is included on Ethereum, poll [`GET /bridge/status`](/developer-guide/bridge/api-reference.md#get-bridge-status) until the operation reaches `COMPLETED`.

```ts
async function pollStatus(originTxHash: string) {
  const url =
    `https://bridge.goliath.net/api/v1/bridge/status?originTxHash=${originTxHash}`;

  while (true) {
    const res = await fetch(url);
    if (res.status === 404) {
      // Relayer hasn't picked it up yet. Wait and retry.
      await sleep(5000);
      continue;
    }

    const data = await res.json();
    if (["COMPLETED", "FAILED", "EXPIRED"].includes(data.status)) {
      return data;
    }
    await sleep(5000);
  }
}

const result = await pollStatus(depositTx);
console.log("Goliath tx:", result.destinationTxHash);
```

Response shape:

```json
{
  "operationId": "c59f9a66-...-1b43a",
  "direction": "ETHEREUM_TO_GOLIATH",
  "status": "COMPLETED",
  "token": "USDC",
  "amount": "100000000",
  "amountFormatted": "100.0",
  "fee": null,
  "outputAmount": "100000000",
  "outputAmountFormatted": "100.0",
  "sender": "0xabc...",
  "recipient": "0xabc...",
  "originChainId": 1,
  "destinationChainId": 327,
  "originTxHash": "0x...",
  "destinationTxHash": "0x...",
  "originConfirmations": 12,
  "requiredConfirmations": 12,
  "timestamps": {
    "depositedAt": "2026-04-19T10:00:00.000Z",
    "finalizedAt": "2026-04-19T10:02:24.000Z",
    "destinationSubmittedAt": "2026-04-19T10:02:28.000Z",
    "completedAt": "2026-04-19T10:02:40.000Z"
  },
  "estimatedCompletionTime": null,
  "holdUntil": null,
  "error": null,
  "settlement": {
    "chainId": 327,
    "chainName": "goliath",
    "tokenSymbol": "USDC",
    "tokenAddress": "0xC8410270bb53f6c99A2EFe6eD3686a8630Efe22B",
    "tokenIsNative": false,
    "amountAtomic": "100000000",
    "amountFormatted": "100.0",
    "txHash": "0x...",
    "completedAt": "2026-04-19T10:02:40.000Z"
  }
}
```

Poll every 5–10 seconds. See [API Reference — `/bridge/status`](/developer-guide/bridge/api-reference.md#get-bridge-status) for all fields.

{% hint style="info" %}
A **404 response is expected** for the first few seconds after you submit the tx — the relayer indexes the event asynchronously. Treat 404 as "keep polling" until your own timeout (e.g. 15 minutes) elapses.
{% endhint %}

## Step 6 — Verify on Goliath

Once `status === "COMPLETED"`:

* `destinationTxHash` is the Goliath transaction that credited the recipient.
* `settlement.tokenAddress` is the bridged ERC-20 contract on Goliath (or `null` if the asset is native XCN).
* `settlement.amountFormatted` is the delivered amount (equal to `amount` for deposits, since there's no deposit fee).

You can render the explorer link as `https://explorer.goliath.net/tx/{destinationTxHash}`.

## Putting It All Together

A minimal viem end-to-end example for USDC:

```ts
import {
  createPublicClient, createWalletClient, http,
  parseUnits, maxUint256, erc20Abi, decodeEventLog,
} from "viem";
import { mainnet } from "viem/chains";
import { bridgeLockAbi } from "./abi/bridgeLock";

const BRIDGE_LOCK = "0xa9fd64b5095d626f5a3a67e6db7fb766345f8092";
const USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";

async function bridgeUsdcToGoliath(amountHuman: string, recipient: `0x${string}`) {
  const amount = parseUnits(amountHuman, 6);

  // 1. Approve
  const approveHash = await walletClient.writeContract({
    address: USDC,
    abi: erc20Abi,
    functionName: "approve",
    args: [BRIDGE_LOCK, maxUint256],
    chain: mainnet,
  });
  await publicClient.waitForTransactionReceipt({ hash: approveHash });

  // 2. Deposit
  const depositHash = await walletClient.writeContract({
    address: BRIDGE_LOCK,
    abi: bridgeLockAbi,
    functionName: "deposit",
    args: [USDC, amount, recipient],
    chain: mainnet,
  });

  // 3. Poll bridge API
  return pollStatus(depositHash);
}
```

## Common Errors

| Symptom                                                     | Cause                                                                     | Fix                                         |
| ----------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------- |
| `ERC20: insufficient allowance`                             | Skipped Step 2.                                                           | Run `approve` first.                        |
| Tx reverts with `BridgeLock: amount must be greater than 0` | Passed `amount = 0` / `msg.value = 0`.                                    | Send a non-zero amount.                     |
| `/bridge/status` returns 404 for several minutes            | Event still being indexed.                                                | Keep polling. Usually resolves inside 30 s. |
| Deposit included but mint never lands                       | Destination address is a contract that cannot receive the bridged ERC-20. | Unblock the recipient or contact support.   |

## Next

* [Goliath → Ethereum](/developer-guide/bridge/goliath-to-ethereum.md) — the reverse direction.
* [REST API Reference](/developer-guide/bridge/api-reference.md#get-bridge-status) — full `/status` response schema.
* [Contracts & Events](/developer-guide/bridge/contracts-and-events.md#bridgelock-abi-user-functions) — the full ABI.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.goliath.net/developer-guide/bridge/ethereum-to-goliath.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
