Open-source project
circlefin/stablecoin-evm avatar
circlefin/stablecoin-evm

Circle's FiatToken contracts put a proxy in front of an upgradeable stablecoin

Source repository for smart contracts used by Circle's stablecoins on EVM-compatible blockchains

808 stars595 forksTypeScriptApache-2.0

At a glance

What is it?
The stablecoin-evm repository holds the Solidity contracts behind Circle's stablecoins on EVM compatible chains, built with Hardhat and Foundry, where an implementation contract holds the logic and a proxy redirects calls to it so the token can be upgraded, with pause, blacklist and mint controls held by separate addresses. It suits engineers who need to read or extend a regulated stablecoin's contract surface.
Who is it for?
Adopt this repository when you need to read, audit or extend the contract behaviour behind a Circle stablecoin on an EVM chain, since it is the upstream source of the FiatToken implementation, its proxy and its deployment scripts, with the control model documented in the same tree.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 72 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What this repository actually is

The README is explicit that this repository contains the smart contracts used by Circle's stablecoins on EVM compatible blockchains, and that all contracts are written in Solidity and managed with the Hardhat framework. That framing matters more than the usual open source SDK description, because the thing being published is the contract behaviour of a regulated issuer rather than a general purpose token template. The repository is a working tree for that code rather than a released package, with `contracts/` holding the Solidity, `test/` the test suite, `scripts/` the deployment scripts including `scripts/deploy/deploy-fiat-token.s.sol`, and `doc/` the design documents. There is also a `lib/` directory, a `foundry.toml` next to `hardhat.config.ts`, and cached and verification artifact directories, which is the layout of a project that builds and verifies real deployments rather than a library that someone installs.

An implementation contract behind a proxy

The contract design follows OpenZeppelin's Proxy Upgrade Pattern, and the README explains the two pieces. There is an implementation contract, `FiatTokenV2_2.sol` under `contracts/v2/`, which contains the main logic for the FiatToken functionalities, and a proxy contract, `FiatTokenProxy.sol` under `contracts/v1/`, which redirects function calls to the implementation contract. That arrangement is what allows the token to be upgraded, since a new implementation contract can be deployed and the proxy updated to point at it. The upgrade path is not a detail left to the reader, either. The `doc/` directory links a deployment process document, a document on preparing an upgrade, and a specific document on upgrading from v2.1 to v2.2, so the version transitions are recorded rather than implied. The token implements the ERC20 interface, which is the part most integrators will care about first.

The control addresses are the security model

The feature list reads like a compliance checklist, and the addresses that hold each power are the whole point. The contract is pausable, meaning the entire contract can be frozen if a serious bug is found or there is a serious key compromise, with no transfers possible while paused, and access to that is controlled by the `pauser` address. It is upgradable, with that access guarded by a `proxyOwner` address, and only the `proxyOwner` address can change the `proxyOwner` address. It carries a blacklist, where blacklisted addresses can neither transfer nor receive tokens, controlled by the `blacklister` address. And it is ownable, where the Owner can change the `owner`, `pauser`, `blacklister` or `masterMinter` addresses, but the owner cannot change the `proxyOwner` address. That last asymmetry is deliberate and is the kind of detail worth reading twice.

Minting runs on an allowance system

Tokens can be minted or burned on demand, and the contract supports having multiple minters at the same time. A `masterMinter` address controls the list of minters and how much each is allowed to mint, and the README compares the mint allowance to the ERC20 allowance, since as each minter mints new tokens their allowance decreases, and when it gets too low they need the allowance increased again by the `masterMinter`. The design consequence for an integrator is that issuance is a multi party process with a central allocator, which is a different model from a single issuer key. It also means the master minter is a privileged role in its own right, and the upgrade path does not change that, since a new implementation can be pointed at by the proxy owner.

Hardhat and Foundry in the same build

The build runs both toolchains, which the package scripts make clear. `compile` runs `hardhat compile`, then `forge build`, then a `flatten-generated-types.sh` script, which is how the TypeScript type definitions for the contracts are produced. The static check script chains the typechain generation, the TypeScript typecheck, the ESLint pass, the Prettier format check and the Solidity lint, and the individual commands are `typecheck`, `lint`, `lint --fix` and `solhint`. Tests run through Hardhat with a raised Node memory limit, coverage has its own script, and there is a `contract-size` script that checks the size of the contracts in the repository and ignores the tests. Formatting is Prettier across Solidity, JavaScript, TypeScript, JSON and Markdown, and the pre-commit hook runs the formatter and the static check, with Husky installed by a postinstall step that also runs `setup.sh`.

Deployment is a simulate, then a broadcast, then a verify

The deployment section in the README is a numbered sequence, and the order is the safety mechanism. You copy `.env.example` to `.env` and fill in values, and the README says this file must not be checked into the repository. You create a `blacklist.remote.json` file with the list of addresses to blacklist, also not to be checked in. Then you simulate the deployment:

sh
yarn forge:simulate scripts/deploy/deploy-fiat-token.s.sol --rpc-url <testnet OR mainnet>

The next step is to validate that all transactions to be broadcast are filled in with the correct values, and only then to deploy:

sh
yarn forge:broadcast scripts/deploy/deploy-fiat-token.s.sol --rpc-url <testnet OR mainnet>

Finally you verify the contracts on an Etherscan flavoured block explorer, with `ETHERSCAN_KEY` set in the `.env` file, using the `forge:verify` script. The scripts underneath read their values from the environment with dotenv, including the gas estimate multiplier, the deployer private key and the Etherscan API key, and there is a `forge:resume` target for resuming a broadcast that was interrupted.

The environment file is the configuration surface

What you deploy is mostly decided by the environment file, and the `.env.example` shows the shape of it. There is a private key configuration section with `DEPLOYER_PRIVATE_KEY`, described as the key to a pre-funded deployer address. Then the FiatToken configuration, with `TOKEN_NAME`, `TOKEN_SYMBOL`, `TOKEN_CURRENCY` and `TOKEN_DECIMALS`, which the comments describe as the ERC20 name, symbol, the fiat currency the token represents and the number of decimals, with the example values naming USDC, USD and six decimals. Then the addresses, with `PROXY_ADMIN_ADDRESS`, `OWNER_ADDRESS` and `MASTER_MINTER_OWNER_ADDRESS` as required, and the optional `PAUSER_ADDRESS`, `BLACKLISTER_ADDRESS`, and the optional addresses of an already deployed FiatTokenProxy and implementation contract. The comments state that the pauser and blacklister default to the owner address, so the defaults are a single point of control unless you set them separately.

Licence, documentation set and release naming

The licence is Apache-2.0, and the environment file carries the standard Apache 2.0 header with an SPDX identifier, so the terms for reuse and modification are the usual permissive ones, with the patent grant and the notice requirement that come with them. The documentation set is unusually complete for a contract repository, and the README links it as additional documentation: a FiatToken design document, a MasterMinter design document, the deployment process, preparing an upgrade, a specific guide to upgrading from v2.1 to v2.2, a Celo FiatToken extension document, and a bytecode verification document. That last one is the item a reviewer should read first, because it is about proving that the deployed bytecode matches the source in this repository. The releases are named by timestamp rather than by version, with tags such as `release-2026-08-12T202509` on 2026-08-12 and earlier ones in June 2026, and the last push to the repository was on 2026-08-12, so the repository is the record of what was deployed and when.

Editorial conclusion

Adopt this repository when you need to read, audit or extend the contract behaviour behind a Circle stablecoin on an EVM chain, since it is the upstream source of the FiatToken implementation, its proxy and its deployment scripts, with the control model documented in the same tree. Do not treat it as a template for issuing your own token, because the roles here, the pauser, the blacklister and the master minter, are the regulatory controls of a specific issuer, and copying them does not give you the rights or the backing those roles assume. Verify first which version you are looking at, since the implementation lives in a versioned file and the upgrade path is documented separately, and read the bytecode verification document before trusting a deployment.

Frequently asked questions

What is the FiatToken proxy pattern in Circle's stablecoin contracts?

There are two main contracts, an implementation contract that holds the main logic for the FiatToken functionalities and a proxy contract that redirects function calls to it. That lets a new implementation be deployed and the proxy updated to point at it, so the token's functionality can be upgraded.

Which roles can pause or blacklist holders of the token?

The `pauser` address controls the pause, which freezes the whole contract and stops all transfers, and the `blacklister` address controls blacklisting, which prevents an address from transferring or receiving tokens. The owner can change both addresses, and the owner can also change the `masterMinter` and the `proxyOwner`.

How do I deploy the contracts?

Copy `.env.example` to `.env` and fill in the values, create a `blacklist.remote.json` file, then run `yarn forge:simulate` against the target RPC URL, check that the transactions to be broadcast contain the right values, run `yarn forge:broadcast` and finally verify with `forge:verify` using the Etherscan API key from the environment.

How do multiple minters work in the FiatToken contract?

The contract supports multiple minters at once, and a `masterMinter` address controls the list of minters and how much each may mint. The mint allowance works like the ERC20 allowance, decreasing as a minter mints, and it has to be raised again by the master minter when it runs low.

Official sources

  1. circlefin/stablecoin-evm on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/circlefin-stablecoin-evm.svg)](https://hysenlabs.com/projects/circlefin-stablecoin-evm)

Was this useful?