Skip to main content

Ethereum

Slugeth
Address formatEIP-55 checksummed hex, 0x…
Account pathm/44'/60'/0'
Cloud storesxpub
DerivationM/0/{i} — public, no Edge required
Confirmations12
TokensERC-20
Explorerhttps://etherscan.io/tx/{txid} · https://etherscan.io/address/{address}

One address across the EVM chains

Ethereum, BSC, Polygon, Avalanche and Base all use coin type 60', so one extended public key serves all five and one address receives on all five.

Issue it once per customer:

curl -X POST $EDGE/addresses \
-H "X-API-Key: $CHAINOS_EDGE_KEY" \
-H "Idempotency-Key: addr-cust_88213-evm" \
-d '{ "chain": "eth", "userRef": "cust_88213" }'

That address receives ETH and ERC-20 on Ethereum, BNB and BEP-20 on BSC, POL and ERC-20 on Polygon, and AVAX and ERC-20 on Avalanche. A deposit arriving on BSC to an address you created with "chain": "eth" is still that customer's deposit; the webhook's chain field tells you which network it landed on.

Do not issue an address per EVM chain

It works, in the sense that all five are valid — but they are five distinct addresses at five distinct indices, they count four times against your active-address quota, and your customer now has four "Ethereum-family" addresses to be confused by.

Issue for eth and use it on all five. Tell your customer which networks it accepts.

TRON and XRP do not share this path. See HD derivation.

Avalanche shares the address and nothing else. It is a separate L1 rather than an Ethereum network: its gas is paid in AVAX, and one confirmation there is final rather than probable. See Avalanche.

Base shares the address and the asset — its native token is ETH too — and is still a different chain with a different balance. It is an OP-Stack rollup, so its confirmation waits for Ethereum to finalize the batch (roughly half an hour) and its fee carries a second, L1 component. It is the easiest of the four to mistake for Ethereum itself. See Base.

Confirmations

Twelve. This is the standard margin for post-Merge PoS: a reorg deeper than a couple of blocks requires a consensus failure rather than ordinary luck, and twelve blocks is roughly two and a half minutes.

deposit_detected fires at 1, deposit_confirmed at 12. Credit on the second.

Fees — EIP-1559

fee = gasUnits × (baseFee × 2 + priorityFee)
Field
baseFeeGweiThe current block's base fee, burned
priorityFeeGweiThe tip to the proposer
maxFeeGweiWhat the build sets as the ceiling
gasUnits21,000 for a plain transfer; roughly 45,000–65,000 for an ERC-20

The baseFee × 2 headroom is deliberate: the base fee can rise up to 12.5% per block, so a transaction priced at exactly the current base fee will not be included if the next block is fuller. Doubling gives about six blocks of margin. You are only charged the actual base fee at inclusion time — the ceiling is a ceiling, not a price.

INSUFFICIENT_GAS is the error you will see most

The fee must be paid in ETH, by the sending address. An ERC-20 deposit address holds tokens and no ETH — because your customer sent you USDT and nobody sent you ETH. That is the default state of every ERC-20 deposit address, not an unusual one.

A decision: sending a token needs the token balance to cover the amount AND the ETH balance to cover the fee; sending the chain's own coin needs one balance to cover amount plus fee.

Plan for it in your design rather than treating it as an error path. A token sweep is a two-pass job: collect the addresses that need gas, fund them, sweep on the next pass. See Sweeps.

ERC-20 tokens

Monitored contracts on mainnet:

TokenContractDecimals
USDT0xdAC17F958D2ee523a2206206994597C13D831ec76
USDC0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB486
RLUSD0x8292Bb45bf1Ee4d140127049757C2E0fF06317eD18
PYUSD0x6c3ea9036406852006290770BEdFcAbA0e23A0e86
EURC0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c6
cNGN0x17CDB2a01e7a34CbB3DD4b83260B05d0274C8dab6
DAI0x6B175474E89094C44Da98b954EedeAC495271d0F18
FDUSD0xc5f0f7b66764F6ec8C8Dff7BA683102295E1640918
USDD0x4f8e5DE400DE08B164E7421B3EE387f461beCD1A18

Branch on tokenContract, never on tokenSymbol. Anyone can deploy a contract that calls itself USDT, and several have. The contract address is the identity; the symbol is a label the contract chose for itself.

{ "amount": "1000000", "amountFormatted": "1.00", "decimals": 6,
"tokenSymbol": "USDT",
"tokenContract": "0xdAC17F958D2ee523a2206206994597C13D831ec7" }

Note the decimals: USDT and USDC on Ethereum are 6, not 18. "1000000" is one dollar. The same tokens on BSC are 18 — see BSC. Read decimals from the payload rather than assuming per token, and per chain rather than per symbol: the contract addresses differ on every one of the four networks this address receives on.

A deposit of a token ChainOS does not monitor produces no webhook. The funds are at your address and recoverable from your mnemonic; they are simply not tracked. To add a contract, ask — the adapter takes a list per logical symbol.

Balances

{ "confirmed": "1000000000000000000", "available": "1000000000000000000",
"reserved": "0", "decimals": 18 }

available == confirmed on Ethereum. Nothing is immobilised — that is an XRP, Solana and Stellar concern.

"1000000000000000000" is 1 ETH, as a string, because 10^18 does not survive a JavaScript Number. See API conventions.

Operational notes

  • Reverted transactions still cost the fee. An EVM transaction can be mined and revert; withdrawal_failed fires and the gas is spent. Do not treat a failure as a free retry.
  • Nonce management is Cloud's. Nonces are assigned at build time. If two withdrawals from one address are built concurrently, the second waits — the API serialises per address rather than issuing two transactions with the same nonce and letting one silently replace the other.
  • Contract destinations are not special-cased. A withdrawal to a contract address succeeds if the contract accepts it. ChainOS does not simulate the call, so a destination whose receive hook reverts produces withdrawal_failed after the fee is spent. Validate destinations with POST /v1/addresses/validate, which checks the format — it cannot check the behaviour.
  • Gas spikes are visible as maxFeeGwei climbing in a fee estimate. There is nothing to fix, but a withdrawal queue that ignores the estimate will price transactions your customers did not agree to.