본문으로 건너뛰기

Precompiled Contracts

Monolythium is built on cosmos/evm, which exposes Cosmos SDK modules as EVM precompiled contracts. This allows Solidity developers to call staking, distribution, governance, IBC, and other native chain functions directly from smart contracts and EVM tooling, without switching to Cosmos transaction formats.


Overview

Precompiled contracts are special contracts deployed at deterministic, low-numbered addresses. Unlike regular contracts, their logic is implemented in Go within the node software rather than in EVM bytecode. This means they can access Cosmos SDK modules that are otherwise unreachable from the EVM.

On Monolythium, all static precompiles are activated at genesis:

ActiveStaticPrecompiles = AvailableStaticPrecompiles

This ensures every precompile listed below is available from block 1.


Available Precompiles

PrecompileAddressModuleDescription
Staking0x0000000000000000000000000000000000000800x/stakingDelegate, undelegate, redelegate
Distribution0x0000000000000000000000000000000000000801x/distributionClaim staking rewards
ICS20 Transfer0x0000000000000000000000000000000000000802x/ibc/transferCross-chain token transfers via IBC
Vesting0x0000000000000000000000000000000000000803x/vestingVesting account management
Bank0x0000000000000000000000000000000000000804x/bankToken transfers, balance queries
Governance0x0000000000000000000000000000000000000805x/govSubmit proposals, vote
Slashing0x0000000000000000000000000000000000000806x/slashingQuery validator signing info
Evidence0x0000000000000000000000000000000000000807x/evidenceSubmit misbehavior evidence

Additionally, the P256 precompile is available at 0x0000000000000000000000000000000000000100 for elliptic curve signature verification.

정보

Addresses 0x800--0x807 are derived from the genesis app_state.evm.params.active_static_precompiles configuration.


Staking Precompile

The staking precompile at 0x800 lets Solidity contracts delegate, undelegate, and redelegate LYTH on behalf of the caller.

Interface

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IStaking {
/// @notice Delegate tokens to a validator
/// @param delegatorAddress The delegator's bech32 address
/// @param validatorAddress The validator's operator address
/// @param amount Amount in alyth
function delegate(
address delegatorAddress,
string calldata validatorAddress,
uint256 amount
) external returns (bool);

/// @notice Undelegate tokens from a validator
/// @param delegatorAddress The delegator's bech32 address
/// @param validatorAddress The validator's operator address
/// @param amount Amount in alyth
function undelegate(
address delegatorAddress,
string calldata validatorAddress,
uint256 amount
) external returns (bool);

/// @notice Redelegate tokens between validators
/// @param delegatorAddress The delegator's bech32 address
/// @param srcValidator Source validator operator address
/// @param dstValidator Destination validator operator address
/// @param amount Amount in alyth
function redelegate(
address delegatorAddress,
string calldata srcValidator,
string calldata dstValidator,
uint256 amount
) external returns (bool);
}

Example Contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "./IStaking.sol";

contract StakingHelper {
IStaking constant STAKING =
IStaking(0x0000000000000000000000000000000000000800);

/// @notice Delegate the caller's LYTH to a validator
function delegateTo(
string calldata validatorAddress,
uint256 amount
) external {
bool success = STAKING.delegate(
msg.sender,
validatorAddress,
amount
);
require(success, "Delegation failed");
}

/// @notice Move stake between validators in one call
function switchValidator(
string calldata fromValidator,
string calldata toValidator,
uint256 amount
) external {
bool success = STAKING.redelegate(
msg.sender,
fromValidator,
toValidator,
amount
);
require(success, "Redelegation failed");
}
}

Distribution Precompile

The distribution precompile at 0x801 enables claiming staking rewards from Solidity.

Interface

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IDistribution {
/// @notice Claim rewards from a specific validator
/// @param delegatorAddress The delegator's bech32 address
/// @param validatorAddress The validator's operator address
function withdrawDelegatorReward(
address delegatorAddress,
string calldata validatorAddress
) external returns (bool);
}

Example: Claim and Compound

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IDistribution {
function withdrawDelegatorReward(
address delegatorAddress,
string calldata validatorAddress
) external returns (bool);
}

interface IStaking {
function delegate(
address delegatorAddress,
string calldata validatorAddress,
uint256 amount
) external returns (bool);
}

contract AutoCompounder {
IDistribution constant DISTRIBUTION =
IDistribution(0x0000000000000000000000000000000000000801);
IStaking constant STAKING =
IStaking(0x0000000000000000000000000000000000000800);

/// @notice Claim rewards and re-stake them
function claimAndCompound(
string calldata validatorAddress,
uint256 rewardAmount
) external {
DISTRIBUTION.withdrawDelegatorReward(msg.sender, validatorAddress);
STAKING.delegate(msg.sender, validatorAddress, rewardAmount);
}
}

IBC Transfer Precompile

The ICS-20 precompile at 0x802 enables cross-chain token transfers directly from Solidity. This is the same precompile described in the IBC documentation.

Interface

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IICS20Transfer {
function transfer(
string calldata sourcePort,
string calldata sourceChannel,
string calldata denom,
uint256 amount,
string calldata receiver,
uint64 revisionNumber,
uint64 revisionHeight,
uint64 timeoutTimestamp,
string calldata memo
) external returns (bool);
}

Example: IBC Send

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract IBCSender {
IICS20Transfer constant IBC_TRANSFER =
IICS20Transfer(0x0000000000000000000000000000000000000802);

function sendViaIBC(
string calldata channel,
string calldata denom,
uint256 amount,
string calldata receiver
) external {
IBC_TRANSFER.transfer(
"transfer",
channel,
denom,
amount,
receiver,
0, // revision number
0, // revision height
uint64(block.timestamp + 600) * 1_000_000_000, // 10-minute timeout
"" // memo
);
}
}
경고

The IBC precompile is only available after the IBC activation block (500,000 on mainnet/testnet). Calls before activation will revert.


Governance Precompile

The governance precompile at 0x805 allows contracts to submit proposals and cast votes.

Interface

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IGov {
/// @notice Vote on a governance proposal
/// @param voter The voter's address
/// @param proposalId The proposal ID
/// @param option Vote option (1=Yes, 2=Abstain, 3=No, 4=NoWithVeto)
function vote(
address voter,
uint64 proposalId,
uint8 option
) external returns (bool);
}

Example: DAO Voting Proxy

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract VotingProxy {
IGov constant GOV =
IGov(0x0000000000000000000000000000000000000805);

/// @notice Cast a Yes vote on a proposal
function voteYes(uint64 proposalId) external {
bool success = GOV.vote(msg.sender, proposalId, 1);
require(success, "Vote failed");
}

/// @notice Cast a No vote on a proposal
function voteNo(uint64 proposalId) external {
bool success = GOV.vote(msg.sender, proposalId, 3);
require(success, "Vote failed");
}
}

Gas Costs

Precompile calls have different gas characteristics from regular EVM contract calls. Because precompiles execute native Go code rather than EVM opcodes, their gas consumption does not follow standard opcode pricing.

ConsiderationDetail
Gas estimationAlways use eth_estimateGas before submitting a transaction
Static costsEach precompile has a base gas cost set in the node software
Variable costsSome operations (e.g., staking with many delegations) scale with input size
OverheadPrecompile calls include a fixed overhead for Cosmos SDK message routing
// Estimate gas for a precompile call
const gasEstimate = await provider.estimateGas({
to: "0x0000000000000000000000000000000000000800",
data: stakingCalldata,
from: signer.address
});
console.log("Estimated gas:", gasEstimate.toString());
Gas Tip

Never hardcode gas limits for precompile calls. Chain upgrades may change the underlying gas costs. Always estimate dynamically.


Important Notes

Caller Context

Precompiles operate on the caller's address (msg.sender). When a user calls a precompile through your contract, the precompile sees your contract's address as the sender, not the original user.

If the precompile requires the delegator/voter to be the transaction sender, the user must call the precompile directly or through a contract that correctly forwards context.

Delegatecall Behavior

Using delegatecall to invoke a precompile may produce unexpected results. Precompiles are stateless from an EVM perspective, and their internal logic depends on the msg.sender value. Avoid delegatecall unless you have verified the behavior on testnet.

Testnet First

Always test precompile interactions on the Monolythium testnet (mono_6940-1, chain ID 6940) before deploying to mainnet. Precompile interfaces and gas costs may differ between network upgrades.

# Testnet RPC
https://evm.testnet.mononodes.xyz

# Verify on testnet explorer
https://testnet.monoscan.xyz

FAQ

Can I call precompiles from any Solidity contract?

Yes. Precompiles are callable at their fixed addresses from any contract or EOA transaction, just like any other contract call.

Do precompiles support view / staticcall?

Some precompile functions support read-only queries via staticcall. Functions that modify state (delegate, vote, transfer) require a full transaction.

What happens if I call a precompile that does not exist?

Calling an address with no precompile and no deployed contract returns empty data. The call will not revert, but no operation is performed. Always verify you are calling the correct address.

Are precompile addresses the same on testnet and mainnet?

Yes. All precompile addresses are identical across Monolythium networks.

Can I interact with precompiles using Hardhat or Foundry?

Yes. Use the precompile address as the to field and ABI-encode the function call, or import the interface and call it directly in Solidity.