A decentralized stablecoin protocol pegged to USD, inspired by Maker-like mechanics and built with Vyper + Moccasin + Boa.
β
Overcollateralized
β
Multi-collateral (WETH + WBTC)
β
Chainlink-style price feeds
β
Liquidation mechanism with bonus
β
Unit tests + fuzz/invariant testing (80%+ coverage)
β
Deployed on zkSync Sepolia
- ERC20 compliant
- Minting controlled via minters
- Ownership controlled via
ownable - Built using Snekmate ERC20
Contract: Decentralized Stable Coin (MDSC)
Symbol: MDSC
Decimals: 18
The protocol logic lives here.
Supports:
- π¦ Deposit collateral (WETH/WBTC)
- π΅ Mint DSC against collateral
- π₯ Burn DSC to reduce debt
- π³ Redeem collateral
- 𧨠Liquidations for unhealthy positions
| Parameter | Value |
|---|---|
| Liquidation Threshold | 50% |
| Liquidation Bonus | 10% |
| Min Health Factor | 1.0 |
| Precision | 1e18 |
| Feed Precision | 1e8 |
| Additional Feed Precision | 1e10 |
β
Users must remain safely overcollateralized.
1e18, position can be liquidated.
User
β
β deposit_collateral()
βΌ
DSCEngine ββββββββββββββΊ Price Feeds (ETH/USD, BTC/USD)
β
β mint_dsc()
βΌ
DecentralizedStableCoin (MDSC)
-
ERC20 token based on Snekmate
-
Supports:
mint(address,uint256)burn_from(address,uint256)set_minter(address,bool)- ownership transfer
Core protocol engine:
-
Deposit collateral
-
Mint/burn DSC
-
Health factor checks
-
Liquidations
-
Oracle price conversions:
get_usd_value(token, amount)get_token_amount_from_usd(token, usd_amount_in_wei)
Oracle safety layer β
Prevents stale price usage:
- enforces timeout (
3 hours) - reverts when feed stale
- renders protocol unusable when oracles stale (by design)
This project includes:
- Token mint/burn logic
- Collateral deposit/redemption
- Health factor invariants
- Liquidation functionality
-
Stateful fuzzing using Hypothesis
-
Randomized sequences:
- deposits
- mint
- redeem
- price updates
-
Invariant examples:
- protocol collateral value β₯ total DSC supply
β 80%+ test coverage (unit + fuzz/invariant)
Run tests:
mox testRun tests with prints:
mox test -sRun coverage:
mox test --coverageRun only fuzz tests:
mox test -s -k fuzzCompile all contracts:
mox buildArtifacts generated in:
out/Example:
out/decentralized_stable_coin.jsonout/dsc_engine.jsonout/oracle_lib.json
Network:
- zkSync Sepolia
- Chain ID:
300 - RPC:
https://sepolia.era.zksync.dev - Explorer:
https://explorer.sepolia.era.zksync.dev
mox run script/deploy.py --network zksync-sepolia- DecentralizedStableCoin (MDSC)
0x1c17Dbb3Cd79d37fBbE0a9d2F462b09717d77D3b
- DSCEngine
0x7FaE3141341Ca5985D5412102C3C66965866BC52
-
WBTC
0xE544cAd11e108775399358Bd0790bb72c9e3AD9E -
WETH
0xdd13E55209Fd76AfE204dBda4007C227904f0a81
-
BTC/USD Price Feed
0x95Bc57e794aeb02E4a16eff406147f3ce2531F83 -
ETH/USD Price Feed
0xfEefF7c3fB57d18C5C6Cdd71e45D2D0b4F9377bF
Boa/Moccasin zkSync env currently cannot perform some contract calls due to
simulate incompatibility, so minter/ownership setup is done using cast.
cast send 0x1c17Dbb3Cd79d37fBbE0a9d2F462b09717d77D3b \
"set_minter(address,bool)" 0x7FaE3141341Ca5985D5412102C3C66965866BC52 true \
--rpc-url https://sepolia.era.zksync.dev \
--private-key $PRIVATE_KEYcast send 0x1c17Dbb3Cd79d37fBbE0a9d2F462b09717d77D3b \
"transfer_ownership(address)" 0x7FaE3141341Ca5985D5412102C3C66965866BC52 \
--rpc-url https://sepolia.era.zksync.dev \
--private-key $PRIVATE_KEY| Action | Command |
|---|---|
| Build | mox build |
| Test | mox test |
| Coverage | mox test --coverage |
| Deploy zkSync Sepolia | mox run script/deploy.py --network zksync-sepolia |
| Deploy local anvil | mox run script/deploy.py --network anvil |
Made with β€οΈ by Dhruv4ne
MIT