Write a Smart Contract

From the moment you decide to create a dApp on Chiliz Chain, you need to take the specifics of our blockchain into account.

While Chiliz Chain is EVM-compatible, treating it exactly like Ethereum can lead to usability issues, specifically regarding token decimals and gas mechanics.

Let's explore!

Prerequisites

Environment

Before you start writing your contract, make sure that your environment is ready.

First, you must have a wallet configured for Spicy Testnet (for development) or Chiliz Chain Mainnet (for production).

Connect to Chiliz Chain

Second, you will need need $CHZ to pay for gas deployment on both Testnet and Mainnet. While you can buy Mainnet $CHZ on any crypto exchange, you can rely on faucets for Testnet tokens.

Testnet Faucets

In terms of tooling, you can rely on Hardhat or Remix. See for instance:

Deploy with Remix

Finally, Chiliz Chain contracts are written in the Solidity language:

Fan Tokens / CAP-20

The most specific aspect of Chiliz Chain is the CAP-20 standard, used for Fan Tokens.

While technically identical to the ERC-20 standard code-wise, CAP-20 tokens have a specific configuration for decimals. In short: It uses 0 decimals, while regular ERC-20 token use 18 decimals.

About Fan Tokens

Therefore, if you deploy a Fan Token with 18 decimals, it may not display correctly in ecosystem wallets or be compatible with future Socios.com integrations.

Best practices in writing a smart contract

Use Battle-Tested Libraries (OpenZeppelin)

Do not start from creating your contract from scratch. The single most effective security practice is to base your code on community-audited standards, to reduce the risk of vulnerabilities

For Chiliz Chain development, we strongly recommend using OpenZeppelin Contracts. They provide secure and community-vetted implementations for tokens contracts.

By using standard contracts, your ensure that your tokens are compatible with known wallets (like MetaMask) and the Chiliz ecosystem (Socios.com).

General EVM Best Practices

Regardless of the chain, these three patterns are non-negotiable for secure Solidity development.

The "Checks-Effects-Interactions" Pattern

This is your primary defense against Reentrancy Attacks. Always structure your functions in this exact order:

  1. Checks: Validate inputs and conditions (e.g., require statements).

  2. Effects: Update the contract state (e.g., reduce balances).

  3. Interactions: Interact with other contracts or send funds (e.g., transfer).

Robust Access Control

Never leave sensitive functions unprotected. If a function mints tokens, changes fees, or upgrades logic, it must be restricted.

  • Simple: Use Ownable for single-admin contracts.

  • Complex: Use AccessControl for contracts requiring multiple roles (e.g., MINTER_ROLE, ADMIN_ROLE).

Input Validation

Assume all input is malicious. Use require() statements at the very beginning of your functions to validate parameters.

For instance:

  • Check for zero addresses (address(0)).

  • Check for zero amounts when transferring.

  • Verify array lengths match if passing multiple arrays.

  • etc.

Chiliz-Specific Implementation Details

While Chiliz Chain is EVM-compatible, certain "local rules" apply, specifically regarding token decimals and EVM versions.

CAP-20 Compliance (Fan Tokens)

If you are writing a contract that interacts with Fan Tokens (e.g., a Staking Pool for $PSG or $BAR), you must handle 0 decimals.

This means:

  • Do not assume 1 Token = 10^18 units. For Fan Tokens, 1 Token = 1 unit.

  • Avoid hardcoding 1e18 in your math if your contract is meant to be generic. Use the token.decimals() function dynamically.

EVM Version & Compiler

Chiliz Chain is compatible with the "Shanghai" EVM version. The recommended Solidity Version is 0.8.24.

Gas Optimization on Chiliz

Transactions on Chiliz Chain are significantly cheaper than Ethereum, but unoptimized code can still lead to congestion or failed transactions during high-traffic events (e.g., during a live match).

Here are three ways to save on gas:

  • Use Custom Errors: Instead of long string messages in require, use error definitions to save gas.

  • Order your state variables to fit into 32-byte slots. Put uint128, address, and bool next to each other where possible.

  • Prefer external for functions that are never called internally by the contract itself.

Last updated

Was this helpful?