One execution environment, two kinds of transfer
Ethereum is best understood as a shared computer — the Ethereum Virtual Machine — whose state every node agrees on. Moving native ETH and moving an ERC-20 token are both transactions on that machine, but they are not the same operation under the hood. A native ETH transfer adjusts account balances directly. An ERC-20 transfer is a call into a token's smart contract, which keeps its own internal ledger of who holds the token; the chain itself only knows that a contract was invoked. Same network, same address format, two different on-chain mechanics.
That distinction is why the assets that settle on Ethereum split the way they do. Native ETH is the chain's own asset and its gas unit. USDT and USDC on Ethereum are ERC-20 tokens — independent contracts that happen to redeem for dollars — so a USDT or USDC payment here is a contract call, not a balance move. halfin reconciles all of them to the fiat amount you billed, but the network treats them as different transaction types, and that shows up in how fees are charged.
Because the EVM is a shared environment, an address is an address: the same Ethereum account can receive native ETH and any ERC-20 token. halfin presents the deposit address for an invoice, and whichever supported asset the customer sends lands at that address on the same network. What changes per asset is the fee the customer pays to move it, and that is governed by gas.
- Native ETH transfer — a direct balance change on the EVM, paid for in ETH gas.
- ERC-20 transfer (USDT, USDC, and other tokens) — a smart-contract call; the token's own contract tracks the balance.
- One address format and one network for all of them; the asset only changes the fee and the transaction type.
Gas: the network's unit of account for work
Every operation on the EVM costs gas — a measure of the computational work the network has to do to execute a transaction. A plain ETH transfer is cheap in gas because it does almost nothing; an ERC-20 transfer costs more because executing the token contract is more work; a complex contract interaction costs more still. Gas is denominated in ETH, which is why ETH is called the network's gas asset: no matter which token a customer is moving, the fee that pays the network is always paid in ETH.
The fee a payer actually pays is gas used multiplied by the gas price, and the gas price floats with demand for blockspace. When the network is busy, the price per unit of gas rises; when it is quiet, it falls. This is a property of the public chain and a customer-side cost — it is paid to the network by whoever sends the transaction, and it is never a halfin charge. It matters to you only as context: on a busy mainnet, the fee to move a small stablecoin payment can be a meaningful fraction of the amount, which is the whole reason lower-cost EVM L2 routes exist.
A consequence worth surfacing to customers: paying in an ERC-20 token still requires ETH for gas. A customer who holds only USDT on Ethereum and no ETH cannot move that USDT until they have a little ETH to cover the gas. Customers who pay regularly on mainnet know this; it is the kind of friction that pushes rate-shopping payers toward cheaper rails.
Finality and reorg-aware crediting
Ethereum runs on proof-of-stake, and its finality model is the part of the network that most directly shapes how halfin credits a payment. Blocks are produced on a regular cadence by validators. A freshly produced block is included but not yet final — it can still be reorganized away if the network briefly converges on a competing chain. Over the next slots the validator set justifies and then finalizes the block, at which point reverting it would require an economically irrational share of stake to be slashed. Finalized is the state you can trust; merely included is not.
That gap is exactly why a payment surface must not credit on first sight. If halfin marked an invoice paid the instant it saw a transaction in a new block, a short reorganization could later drop that transaction and leave you having released goods against a payment that no longer exists. Instead, crediting on Ethereum waits for the network's confirmation threshold — enough blocks on top that the deposit has effectively committed — before an invoice flips to paid, and the same logic governs both native ETH and ERC-20 transfers.
halfin's crediting is reorg-aware throughout. If a reorganization unwinds a transaction that was already observed, halfin reflects that rather than pretending the payment is still good — the settled amount you see is one that actually held on-chain. For your integration this makes the lifecycle trustworthy: a deposit is observed, confirmations accumulate toward the threshold, and only then does the invoice settle and a signed webhook fire. You wait for the paid event, not the first appearance of a transaction.
Ethereum mainnet and the EVM L2 routes
Ethereum mainnet is the canonical home for ETH and the ERC-20 stablecoins, and it has the deepest liquidity and tooling — but it is not always the cheapest place to move value, because mainnet gas competes with everything else happening on the chain. EVM layer-2 networks address that: Base, Arbitrum, and Polygon are separate chains that share Ethereum's account and address model and execution semantics, while charging a fraction of mainnet gas. To a payer the wallet flow feels familiar; to the network they are distinct chains with their own blocks, their own fee markets, and their own settlement.
Because they are distinct networks, halfin watches each on its own settlement gate with its own confirmation behavior. A USDC payment on Base is a different deposit on a different chain from a USDC payment on Ethereum mainnet — not a cheaper version of the same thing. That separation is deliberate and load-bearing for reconciliation: the network a payment settled on comes back to you alongside the asset, so your ledger always knows whether a given transfer arrived over mainnet or over an L2.
The table is the real supported surface for the Ethereum family — what an invoice can actually draw from, not an aspirational list. Dedicated network pages cover the L2s in their own right; from here, the practical point is that the same EVM mechanics apply across the family, and the choice between mainnet and an L2 is a fee-versus-where-the-money-already-lives decision the payer makes.
| Network | Native / gas asset | Stablecoins (gates that exist) | Why a payer picks it |
|---|---|---|---|
| Ethereum (mainnet) | ETH | USDT (ERC-20), USDC (ERC-20) | Deepest liquidity; where most ETH and ERC-20 balances already sit. |
| Base | ETH (L2 gas) | USDC | Lower-cost USDC route that settles to Ethereum. |
| Arbitrum | ETH (L2 gas) | — | Cheaper EVM execution for native-asset payers already on Arbitrum. |
| Polygon | Native chain asset | — | Low-fee EVM route for payers who already hold value on Polygon. |
How halfin accepts and pays out on Ethereum
You do not integrate the Ethereum network directly — you create an invoice and let the customer settle in whatever supported asset they hold. halfin presents the deposit address for that invoice; the customer pays native ETH or an ERC-20 token to it; halfin's Ethereum gate watches the chain, matches the deposit to the invoice, waits for the confirmation threshold, and credits your balance. The network on which the payment settled is recorded on the invoice and on every webhook event, so an Ethereum deposit is never ambiguous against, say, a Base one.
Inbound and outbound use the same network in mirror image. On the receive side, the gate ascribes a confirmed deposit to your balance. On the payout side, halfin can send native ETH or ERC-20 tokens from your balance to a destination address on Ethereum as a single or batch payout — and the payer of that transaction's gas is the platform moving the funds, not your customer. Payouts are approval-gated and idempotent: a payout batch carries an idempotency key so a retried request never double-sends, and the resolved network is part of the record.
Everything that happens on-chain is surfaced through signed webhooks — verify the HMAC signature before you act on any event, then fulfill, reconcile, or pay out. The request below commits to no network at all: you bill a fiat amount, and the customer's asset choice on checkout resolves the rail. Field schemas live in the docs rather than being guessed from this snippet.
curl -X POST https://api.thehalfin.com/api/v1/invoices \
-H "X-API-Key: $HALFIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount_fiat": "120.00",
"fiat_currency": "USD",
"deferred": true,
"description": "Annual plan",
"idempotency_key": "00000000-0000-4000-8000-000000000003"
}'
# The invoice commits to no network. If the customer settles on Ethereum,
# halfin's Ethereum gate waits for the confirmation threshold (reorg-aware)
# before crediting, and the resolved asset and network come back on the
# invoice and each signed webhook event. Field schemas: docs.thehalfin.com.