> ## Documentation Index
> Fetch the complete documentation index at: https://docs.initia.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Creating Standard ERC20s via ERC20Factory

[Tutorial GitHub Repository](https://github.com/initia-labs/examples/tree/main/evm/erc20-factory)

For developers looking to create standard ERC20 tokens on EVM rollups, the
recommended approach is to use the
[ERC20Factory](/resources/developer/contract-references/evm/erc20-factory)
contract.

## Project Setup

First, create a new project directory:

```sh theme={null}
mkdir erc20-factory
cd erc20-factory
```

Initialize the project and install the required dependencies:

```sh theme={null}
npm init
npm install viem
npm install dotenv
```

Create two directories:

* `src`: For your script files.
* `abis`: For your contract
  [ABIs](https://www.alchemy.com/docs/smart-contract-abi).

```sh theme={null}
mkdir src
mkdir abis
```

Download the ERC20 and ERC20Factory ABIs into the `abis` directory:

```sh theme={null}
curl -o abis/erc20Abi.json \
  https://raw.githubusercontent.com/initia-labs/examples/main/evm/erc20-factory/abis/erc20Abi.json

curl -o abis/erc20FactoryAbi.json \
  https://raw.githubusercontent.com/initia-labs/examples/main/evm/erc20-factory/abis/erc20FactoryAbi.json
```

## Setting Up Environment Variables

Create a `.env` file in your project root:

```sh theme={null}
touch .env
```

This file will store your private environment variables, such as your wallet's
private key and the deployed ERC20Factory contract address. These values are
required for signing transactions and interacting with the MiniEVM.

The `ERC20Factory` contract is automatically deployed on all MiniEVM rollups as
part of the chain’s bootstrapping process.

To retrieve the deployed factory address, query
`{ROLLUP_REST_URL}/minievm/evm/v1/contracts/erc20_factory`.

`{ROLLUP_REST_URL}` refers to the REST endpoint of the rollup you are
interacting with.

Example:

```bash theme={null}
curl -X GET "https://rest-evm-1.anvil.asia-southeast.initia.xyz/minievm/evm/v1/contracts/erc20_factory" -H "accept: application/json"
```

Example response:

```json theme={null}
{
  "address": "0xf36924c9C2aD25d73c43F6aC59BB6D06BC944D93"
}
```

Store this address in your `.env` file as `ERC20_FACTORY_ADDRESS`.

Next, export an EVM-compatible private key from your Ethereum wallet. This
private key will be used to sign and send transactions to the MiniEVM.

Your `.env` file should look like this:

```
PRIVATE_KEY={{ETHEREUM_PRIVATE_KEY}}
ERC20_FACTORY_ADDRESS={{FACTORY_CONTRACT_ADDRESS}}
```

<Warning>
  Never share your `.env` file or private key. Anyone with this key can control
  your account. Keep it local and **add `.env` to your `.gitignore`** so it is
  never committed to Git.
</Warning>

## Development

### Creating the Chain Configuration File

To interact with the MiniEVM using Viem, you need a chain configuration file.
This file contains essential details about the chain, including the chain ID,
name, native currency, and RPC URLs.

Create a `chain.js` file in the `src` directory:

```sh theme={null}
touch src/chain.js
```

Add the following configuration:

```js theme={null}
const { defineChain } = require('viem')

const miniEVM = defineChain({
  id: 2594729740794688,
  name: 'MiniEVM',
  nativeCurrency: {
    decimals: 18,
    name: 'Gas Token',
    symbol: 'GAS',
  },
  rpcUrls: {
    default: {
      http: ['https://jsonrpc-evm-1.anvil.asia-southeast.initia.xyz'],
    },
  },
})

module.exports = miniEVM
```

### Interacting with the ERC20Factory Contract

Next, create a script to interact with the ERC20Factory contract:

```sh theme={null}
touch src/index.js
```

Start with the imports:

```js theme={null}
const {
  createPublicClient,
  createWalletClient,
  decodeEventLog,
  getContract,
  http,
} = require('viem')
const { privateKeyToAccount } = require('viem/accounts')
const erc20Abi = require('../abis/erc20Abi.json')
const erc20FactoryAbi = require('../abis/erc20FactoryAbi.json')
const miniEVM = require('./chain')
```

Load the environment variables:

<Note>
  You can also find factory addresses on the
  [Networks](/resources/developer/initia-l1) page or by calling the
  `/minievm/evm/v1/contracts/erc20_factory` endpoint on any MiniEVM rollup.
</Note>

```js theme={null}
require('dotenv').config()

const rawPrivateKey = process.env.PRIVATE_KEY
if (!rawPrivateKey) {
  throw new Error('PRIVATE_KEY environment variable is not set.')
}
const privateKey = rawPrivateKey.startsWith('0x')
  ? rawPrivateKey
  : `0x${rawPrivateKey}`

const erc20FactoryAddress = process.env.ERC20_FACTORY_ADDRESS
if (!erc20FactoryAddress) {
  throw new Error('ERC20_FACTORY_ADDRESS environment variable is not set.')
}
```

To interact with the chain and contracts, create a wallet client and a public
client:

```js theme={null}
// Create an account from the private key.
const account = privateKeyToAccount(privateKey)

// Create a wallet client.
const client = createWalletClient({
  account,
  chain: miniEVM,
  transport: http(),
})

// Create a public client.
const publicClient = createPublicClient({
  chain: miniEVM,
  transport: http(),
})
```

Now you can create a new ERC20 token using the `createERC20` method of the
ERC20Factory contract.

Implement the `createERC20` function:

```js theme={null}
// Send the transaction.
async function createERC20() {
  try {
    // Call createERC20 function on the factory contract to create a new ERC20 token.
    const hash = await client.writeContract({
      address: erc20FactoryAddress,
      abi: erc20FactoryAbi,
      functionName: 'createERC20',
      args: ['Test', 'TST', 18],
    })

    console.log('Transaction sent. Hash:', hash)

    // Wait briefly for the transaction to be processed.
    await new Promise((resolve) => setTimeout(resolve, 500))

    // Get the transaction receipt and parse the logs for the ERC20Created event.
    const receipt = await publicClient.getTransactionReceipt({ hash })

    const erc20CreatedLog = receipt.logs.find(
      // Check if the log is from the factory address.
      (log) => log.address.toLowerCase() === erc20FactoryAddress.toLowerCase(),
    )

    // Check whether the ERC20Created event exists in the logs, then decode the created ERC20 address.
    if (erc20CreatedLog) {
      const decodedLog = decodeEventLog({
        abi: erc20FactoryAbi,
        data: erc20CreatedLog.data,
        topics: erc20CreatedLog.topics,
      })

      console.log('New ERC20 address:', decodedLog.args.erc20)

      // Try reading data from the new ERC20 contract.
      const erc20 = await getContract({
        address: decodedLog.args.erc20,
        abi: erc20Abi,
        client: {
          public: publicClient,
          wallet: client,
        },
      })

      console.log('ERC20 name:', await erc20.read.name())
      console.log('ERC20 symbol:', await erc20.read.symbol())
      console.log('ERC20 decimals:', await erc20.read.decimals())
    } else {
      console.log('ERC20Created event not found in logs')
    }
  } catch (error) {
    console.error('Error sending transaction:', error)
  }
}

createERC20()
```

### Full Script Reference

Here is the entire script for reference:

```js theme={null}
const {
  createPublicClient,
  createWalletClient,
  decodeEventLog,
  getContract,
  http,
} = require('viem')
const { privateKeyToAccount } = require('viem/accounts')
const erc20Abi = require('../abis/erc20Abi.json')
const erc20FactoryAbi = require('../abis/erc20FactoryAbi.json')
const miniEVM = require('./chain')

require('dotenv').config()

const rawPrivateKey = process.env.PRIVATE_KEY
if (!rawPrivateKey) {
  throw new Error('PRIVATE_KEY environment variable is not set.')
}
const privateKey = rawPrivateKey.startsWith('0x')
  ? rawPrivateKey
  : `0x${rawPrivateKey}`

const erc20FactoryAddress = process.env.ERC20_FACTORY_ADDRESS
if (!erc20FactoryAddress) {
  throw new Error('ERC20_FACTORY_ADDRESS environment variable is not set.')
}

// Create an account from the private key.
const account = privateKeyToAccount(privateKey)

// Create a wallet client.
const client = createWalletClient({
  account,
  chain: miniEVM,
  transport: http(),
})

// Create a public client.
const publicClient = createPublicClient({
  chain: miniEVM,
  transport: http(),
})

// Send the transaction.
async function createERC20() {
  try {
    // Call createERC20 function on the factory contract to create a new ERC20 token.
    const hash = await client.writeContract({
      address: erc20FactoryAddress,
      abi: erc20FactoryAbi,
      functionName: 'createERC20',
      args: ['Test', 'TST', 18],
    })

    console.log('Transaction sent. Hash:', hash)

    // Wait briefly for the transaction to be processed.
    await new Promise((resolve) => setTimeout(resolve, 500))

    // Get the transaction receipt and parse the logs for the ERC20Created event.
    const receipt = await publicClient.getTransactionReceipt({ hash })

    const erc20CreatedLog = receipt.logs.find(
      // Check if the log is from the factory address.
      (log) => log.address.toLowerCase() === erc20FactoryAddress.toLowerCase(),
    )

    // Check whether the ERC20Created event exists in the logs, then decode the created ERC20 address.
    if (erc20CreatedLog) {
      const decodedLog = decodeEventLog({
        abi: erc20FactoryAbi,
        data: erc20CreatedLog.data,
        topics: erc20CreatedLog.topics,
      })

      console.log('New ERC20 address:', decodedLog.args.erc20)

      // Try reading data from the new ERC20 contract.
      const erc20 = await getContract({
        address: decodedLog.args.erc20,
        abi: erc20Abi,
        client: {
          public: publicClient,
          wallet: client,
        },
      })

      console.log('ERC20 name:', await erc20.read.name())
      console.log('ERC20 symbol:', await erc20.read.symbol())
      console.log('ERC20 decimals:', await erc20.read.decimals())
    } else {
      console.log('ERC20Created event not found in logs')
    }
  } catch (error) {
    console.error('Error sending transaction:', error)
  }
}

createERC20()
```

### Running the Script

Run the script to create a new ERC20 token:

```sh theme={null}
node src/index.js
```

A successful run outputs something like this:

```sh theme={null}
Transaction sent. Hash: 0x58b0b9326e9c877c0b966db112b3df8db710f986ba309345601ac222ddcb4c77
New ERC20 address: 0x9F363EB9649879b1C4993f9Ea9821d48346c3e04
ERC20 name: Test
ERC20 symbol: TST
ERC20 decimals: 18
```

You have now successfully created a new ERC20 token on the MiniEVM!
