eth_secp256k1 keys. The same account can be represented as an EVM hex address (0x...) or a Cosmos Bech32 address (c8...). Exchange and custody systems can generate deposit addresses offline using standard Ethereum key derivation.
The JavaScript and Java examples below are excerpted from compile-tested samples. See Tested Exchange Examples.
Address rules
| Item | Value |
|---|---|
| Account key type | eth_secp256k1 |
| EVM address | 20-byte Ethereum address, displayed as 0x... |
| Cosmos account address | Bech32 with c8 prefix |
| Mainnet EVM chain ID | 2184 / 0x888 |
| Testnet EVM chain ID | 2185 / 0x889 |
| Recommended wallet path | Ethereum-compatible path, usually m/44'/60'/0'/0/index |
0x... for EVM JSON-RPC, contracts, wallet prompts, and token transfers. Use c8... for Cosmos REST/LCD account queries or IBC-facing UX. Both formats point to the same 20 account bytes.
Memos and destination tags
Memo support depends on the transfer path, not the address format.| Transfer path | Transaction memo support | Integration guidance |
|---|---|---|
| EVM native CTM transfer | No standard memo or destination-tag field. Standard account-to-account transfers should use empty data. | Use unique deposit addresses and credit by transaction hash plus recipient address. |
| EVM ERC-20 transfer | No memo or destination-tag field in the standard transfer(address,uint256) call. | Use unique deposit addresses. If routing metadata is required, use an application-specific contract flow instead of a standard ERC-20 transfer. |
| Cosmos SDK transfer | Yes. Cosmos transactions can include a transaction-level memo. | Support this only for Cosmos-native send paths, and make sure the scanner reads, stores, and validates the transaction memo. |
| IBC ICS-20 transfer | Yes. ICS-20 transfers include a message-level memo parameter. | Treat the memo as transfer message data for IBC routing or application metadata, not as part of the recipient address. |
Activation behavior
There is no separate c8ntinuum-specific address activation transaction documented in this source set. A generated address is valid before it receives funds. Operationally, an address becomes visible to most account queries after it has a balance, nonce, code, or other on-chain account state. For deposit systems:- You can assign unused generated addresses before funding.
- Treat an address as activated after the first successful funding transaction or account-state query returns data.
- Validate syntax before accepting a withdrawal destination, but do not require the destination to already exist on-chain.
- Require native
CTMon sender accounts before constructing outgoing transactions because gas is paid in native CTM.
JavaScript
Install:Java
Dependencies:web3j for Ethereum keys. The tested Java sample includes a small Bech32 helper for c8... conversion.
Go
Dependencies:go-ethereum for keys and cosmos-sdk for Bech32 conversion.
Rust
Dependencies:alloy for keys and address parsing, plus bech32 for c8... conversion.
Validation checklist
- Accept
0x...addresses only when they are 20 bytes and parse as valid Ethereum addresses. - Prefer EIP-55 checksum casing for stored and displayed
0x...addresses. - Accept
c8...only when Bech32 decoding succeeds, the prefix is exactlyc8, and the decoded byte length is 20. - Store a canonical account identifier internally, such as lowercase 20-byte hex, then derive display formats as needed.
- Reject validator operator (
c8valoper...) and consensus (c8valcons...) addresses for normal user deposits unless the product explicitly supports validator operations.