Deployed Contracts (Testnet)
Canonical contract addresses on Maroo Testnet — utilities, AA, EAS, ERC-8004, inherited EVM precompiles, and Maroo precompiles.
Stable addresses for the contracts and precompiles available on Maroo Testnet. The Maroo precompile interfaces and ABIs ship in the npm package
@maroo-chain/contracts; the ERC-8004 IdentityRegistry and ReputationRegistry ABIs ship as identityRegistryAbi and reputationRegistryAbi in @maroo-chain/viem. Mainnet addresses for Maroo precompiles match these (the Maroo precompiles use 0x10.. prefixes and stay constant across networks). In the contract tables, the Explorer-verified column shows whether the testnet explorer (https://explorer-testnet.maroo.io) has verified source code for that address: ✓ if it does, — if it does not.OKRW — the native coin
On Maroo Testnet, OKRW is the chain's native coin and gas token. Move it the way you move ETH — as a transaction's
The one OKRW contract surface is the IOkrw precompile:
value / msg.value — and read balances with eth_getBalance / address.balance. It has no ERC20 representation on the testnet: x/erc20 registers no token pair, and 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE — the native-ERC20 address in maroo's source-default genesis — has no code there. An ERC20 call to that address returns empty data, and a transaction to it succeeds without moving anything, so a DEX router, vault or wallet flow built on approve / transferFrom needs a native (payable) path instead.The one OKRW contract surface is the IOkrw precompile:
| Surface | Address | Use for |
|---|---|---|
| IOkrw precompile | 0x1000000000000000000000000000000000000001 | getParams; mint (authorized minter only — a valid call from any other address reverts with UnauthorizedMinter) |
import {IOkrw, OkrwParams} from "@maroo-chain/contracts/precompiles/okrw/IOkrw.sol";
// OKRW moves as the native coin — no token contract is involved.
function pay(address payable to) external payable {
(bool ok, ) = to.call{value: msg.value}("");
require(ok, "OKRW transfer failed");
}
// The precompile's read surface: who may mint, and the native denom string.
function okrwParams() external view returns (address minter, string memory mintDenom) {
OkrwParams memory p = IOkrw(0x1000000000000000000000000000000000000001).getParams();
return (p.minter, p.mintDenom);
} Note: Need the native denom string — for a Cosmos coin, a denom-keyed PCL policy, or a privacy amount? Read it at runtime:
getParams().mintDenom from the precompile above, or evm_denom from https://api-testnet.maroo.io/cosmos/evm/vm/v1/params. On the testnet it is atokrw (display tokrw); maroo's source default aokrw is not in use there. Maroo precompiles
These precompile addresses are stable across testnet and mainnet. Use the interfaces from
Each precompile's interface and call examples are on its overview page:
@maroo-chain/contracts/precompiles/... to call them. A precompile has no EVM bytecode (eth_getCode returns 0x), so from Solidity call a function that returns nothing with a low-level call — see maroo-architecture.| Precompile | Address | Purpose |
|---|---|---|
IOkrw | 0x1000000000000000000000000000000000000001 | OKRW mint / params |
IPcl | 0x1000000000000000000000000000000000000005 | Programmable Compliance Layer |
IEas | 0x1000000000000000000000000000000000000009 | EAS discovery (getParams) |
IAgent | 0x100000000000000000000000000000000000000A | Agent discovery + reverse lookup |
IPrivacy | 0x100000000000000000000000000000000000000b | Shielded OKRW (deposit / transfer / withdraw) |
Each precompile's interface and call examples are on its overview page:
okrw-precompile-overview, pcl-precompile-overview, eas-precompile-overview, agent-precompile-overview and privacy-precompile-overview.export const ADDR = {
OKRW_PRECOMPILE: "0x1000000000000000000000000000000000000001",
PCL: "0x1000000000000000000000000000000000000005",
EAS_PRECOMPILE: "0x1000000000000000000000000000000000000009",
AGENT: "0x100000000000000000000000000000000000000A",
PRIVACY: "0x100000000000000000000000000000000000000b",
// ERC-8004 IdentityRegistry preinstall
ERC8004_IDENTITY:"0x8004000000000000000000000000000000000001",
} as const; EAS and ERC-8004 preinstalls
The actual EAS attestation contract, SchemaRegistry, and Indexer are deployed as preinstalls — resolve their canonical addresses via
KYC / KYB attestations are issued and stored on the EAS contract (the standard EAS interface), and the Indexer keeps per-recipient and per-schema lookups. PCL's attestation check,
Both registries are preinstalls at fixed addresses, not per-dApp deployments; on Maroo Testnet they arrived with the v0.4.0 upgrade (block 6,372,905), and
IEas.getParams() rather than hard-coding.| Contract | Address | Explorer-verified |
|---|---|---|
| SchemaRegistry | 0x1000000000000000000000000000000000000006 | ✓ |
| EAS | 0x1000000000000000000000000000000000000007 | ✓ |
| Indexer | 0x1000000000000000000000000000000000000008 | ✓ |
KYC / KYB attestations are issued and stored on the EAS contract (the standard EAS interface), and the Indexer keeps per-recipient and per-schema lookups. PCL's attestation check,
EAS_POLICY, reads these two through the addresses carried in the policy's own parameters (easContract, indexContract), so set them to the eas and indexer addresses that IEas.getParams() returns. The EAS precompile at 0x…09 only returns these three addresses.| Contract | Address | Explorer-verified |
|---|---|---|
| ERC-8004 IdentityRegistry | 0x8004000000000000000000000000000000000001 | ✓ |
| ERC-8004 ReputationRegistry | 0x8004000000000000000000000000000000000002 | ✓ |
Both registries are preinstalls at fixed addresses, not per-dApp deployments; on Maroo Testnet they arrived with the v0.4.0 upgrade (block 6,372,905), and
IAgent.getParams() returns both addresses. There is no Validation registry.import { createPublicClient, http } from "viem";
const EAS_PRECOMPILE = "0x1000000000000000000000000000000000000009";
const easAbi = [{
name: "getParams", type: "function", stateMutability: "view",
inputs: [],
outputs: [{ type: "tuple", components: [
{ name: "schemaRegistry", type: "address" },
{ name: "eas", type: "address" },
{ name: "indexer", type: "address" },
]}],
}] as const;
const client = createPublicClient({ transport: http("https://rpc-testnet.maroo.io") });
const { eas, indexer, schemaRegistry } = await client.readContract({
address: EAS_PRECOMPILE,
abi: easAbi,
functionName: "getParams",
}); Importing the ABIs
@maroo-chain/contracts ships every Maroo precompile interface — IOkrw, IPcl, IEas, IAgent, IPrivacy — as Solidity sources and typed ABIs (version 0.0.9 at the time of writing). There is no root import; use the full paths:- Solidity:
@maroo-chain/contracts/precompiles/<name>/I<Name>.sol - TypeScript:
@maroo-chain/contracts/abi/precompiles/<name>/I<Name>(needsmoduleResolutionnode16,nodenextorbundler)
iAgentAbi lists the 33 errors every precompile shares before its two functions, getParams and getAgentIds — the functions are there, at the end. The ERC-8004 registry ABIs are not in this package: use identityRegistryAbi and reputationRegistryAbi from @maroo-chain/viem.// npm install @maroo-chain/contracts viem
import { createPublicClient, getContract, http } from "viem";
import { iPclAbi } from "@maroo-chain/contracts/abi/precompiles/pcl/IPcl";
const client = createPublicClient({ transport: http("https://rpc-testnet.maroo.io") });
const pcl = getContract({ address: "0x1000000000000000000000000000000000000005", abi: iPclAbi, client });
const admin = await pcl.read.policyAdmin(); Foundry remappings
The Solidity sources import
cosmos-evm-contracts, which the package does not declare as a dependency. Install it too (npm install @maroo-chain/contracts cosmos-evm-contracts) and add both remappings to remappings.txt:@maroo-chain/contracts/=node_modules/@maroo-chain/contracts/
cosmos-evm-contracts/=node_modules/cosmos-evm-contracts/ General-purpose utilities
| Contract | Address | Explorer-verified |
|---|---|---|
| Multicall3 | 0xcA11bde05977b3631167028862bE2a173976CA11 | ✓ |
| Create2 deployer | 0x4e59b44847b379578588920ca78fbf26c0b4956c | — |
| Safe singleton factory | 0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7 | — |
| EIP-2935 history | 0x0000F90827F1C53a10cb7A02335B175320002935 | — |
Multicall3, the Create2 deployer and the Safe singleton factory are the standard deployments at their canonical Ethereum addresses, so cross-chain tooling finds them without configuration. The EIP-2935 history contract sits at its canonical address too, but on Maroo Testnet its
get(blockNumber) does not return that block's hash — read block hashes with eth_getBlockByNumber, or blockhash() for the last 256 blocks.Account abstraction (ERC-4337, EIP-7702)
| Contract | Address | Explorer-verified |
|---|---|---|
| EntryPoint v0.9 | 0x433709009B8330FDa32311DF1C2AFA402eD8D009 | — |
| EntryPoint v0.8 | 0x4337084d9e255ff0702461cf8895ce9e3b5ff108 | ✓ |
| SimpleAccountFactory | 0x13E9ed32155810FDbd067D4522C492D6f68E5944 | ✓ |
| Simple7702Account | 0x4Cd241E8d1510e30b2076397afc7508Ae59C66c9 | ✓ |
To create new smart accounts, use EntryPoint v0.9, deployed at the canonical v0.9 address. Its SenderCreator (
0x0A630a99Df908A81115A3022927Be82f9299987e) has code, and accounts have been deployed through it on Maroo Testnet from a factory named in the UserOperation's initCode. Use a factory built for v0.9; the SimpleAccountFactory listed here cannot create accounts.The v0.8 set does not create accounts on Maroo Testnet. EntryPoint v0.8 has code, but the SenderCreator it points to (
0x449ed7c3e6fee6a97311d4b55475df59c44add33) has none, so a UserOperation whose initCode names a factory reverts in EntryPoint v0.8 without a reason. SimpleAccountFactory's accountImplementation() and senderCreator() both return the zero address, so its createAccount reverts with only callable from SenderCreator for every caller and no account can be created through this factory. EntryPoint v0.8 still executes UserOperations that carry no initCode, for example from an EIP-7702-delegated EOA. Simple7702Account has code (an EIP-7702 delegate account implementation).PCL lists EntryPoint v0.8 and v0.9 as trusted (
IPcl.getParams().entrypoints; the list also holds the EntryPoint v0.6 address, 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789, which has no code on Maroo Testnet), so it evaluates each UserOperation against its sender, the smart account, rather than the bundler that submits the batch. The bundler is still checked as the sender of the outer batch transaction.Inherited EVM precompiles
| Precompile | Address |
|---|---|
| P256 (signature verification) | 0x0000000000000000000000000000000000000100 |
| Bech32 (bech32 ↔ hex) | 0x0000000000000000000000000000000000000400 |
| Staking | 0x0000000000000000000000000000000000000800 |
| Distribution | 0x0000000000000000000000000000000000000801 |
| Bank | 0x0000000000000000000000000000000000000804 |
| Gov | 0x0000000000000000000000000000000000000805 |
| Slashing | 0x0000000000000000000000000000000000000806 |
These come from the chain's EVM runtime unchanged and are active on Maroo Testnet. Like the Maroo precompiles, they have no EVM bytecode. The Gov precompile is the entry point for taking part in governance from Solidity, for example voting on a proposal with
vote.Conclusion
Pin these addresses in a single module of your codebase (the snippet above is a good template). The Maroo precompile addresses are stable across networks; resolve the EAS preinstall addresses at runtime with
IEas.getParams(), and the ERC-8004 registry addresses with IAgent.getParams().