Project to explain what is, how to setup, and interact with Maker DAO VAT Smart Contract.
This document is based on DAI Glossary and Maker Protocol 101
The example projects are based on Foundry Forge Dewiz Template and Maker DAO DSS codebase.
Make sure you have node.js, shfmt and foundry installed.
# Install tools from the nodejs ecosystem: prettier, solhint, husky and lint-staged
make nodejs-deps
# Install smart contract dependencies through `foundry update`
make update
geth --datadir ~/temp/ --dev --http --http.api web3,eth,rpc,debug,net,txpool,admin --http.corsdomain "*" --http.vhosts "*" --http.addr 127.0.0.1 --ws --ws.api eth,net,debug,web3 --ws.addr 127.0.0.1 --ws.origins "*" --graphql --graphql.corsdomain "*" --graphql.vhosts "*" --vmdebug
export ETH_FROM=0x
export FOUNDRY_ETH_FROM=$ETH_FROM
export ETHERSCAN_API_KEY=""
export POLYGONSCAN_API_KEY=""
export ETH_KEYSTORE="/Users/johndoe/temp/keystore"
export FOUNDRY_ETH_KEYSTORE_DIR=$ETH_KEYSTORE
export ETH_PASSWORD="$ETH_KEYSTORE/passwd.txt"
export FOUNDRY_ETH_PASSWORD_FILE=$ETH_PASSWORD
Wrapper around forge
/cast
which figure out wallet and password automatically if you are using geth
keystore.
scripts/forge-deploy.sh
: Deploys a contract. Accepts the same options asforge create
scripts/forge-verify.sh
: Verifies a deployed contract. Accepts the same options asforge verify-contract
scripts/cast-send.sh
: Signs and publish a transaction. Accepts the same options ascast send
The image bellow show us in a big picture the VAT setup
In order to run this project to understand the Maker DAO VAT Smart Contract and its operations you need to deploy it and other Smart Contracts configure them. Below are for instructions deployment and example scripts that will help you to do it and most importantly to help you to understand why these artifacts are needed.
Due changes in Solidity arithmetics from version 0.5.x to 0.8.x the Maker DAO's DSS Smart Contracts need to be compiled using 0.6.x version to avoid overflow
errors when frob
method is called - you will see more details about it below. So, we split this example project in two. Rome DAO
simulates Maker DAO very
close to what we have in mainnet and this project simulates an user operating it using the Solidity version 0.8.x
Hence, when you see from Rome DAO
instruction below please use scripts saved in Rome DAO
project. Its URL is https://github.com/dewiz-xyz/rome-dao
Deploy an ERC-20 to simulate DAI
tokens. It will be managed by the protocol, Rome DAO, that simulates Maker DAO mechanisms.
Example:
<rome-dao-path>./scripts/forge-script.sh ./src/dai.s.sol:DaiDeploy --fork-url=$RPC_URL --broadcast -vvvv
Deploy Vat
from vat.sol
. VAT is the main compoment of Maker DAO. It manages the DAI supply versus debts tokenized in different collaterals.
Example:
<rome-dao-path>./scripts/forge-script.sh ./src/vat.s.sol:VatDeploy --fork-url=$RPC_URL --broadcast -vvvv
Deploy an ERC-20 that simulates a tokenized asset you want to be a collateral for Rome DAO's DAI
.
Example:
./scripts/forge-script.sh ./src/Denarius.s.sol:DenariusDeploy --fork-url=$RPC_URL --broadcast -vvvv
Deploy a GemJoin
contract from join.sol
and allow it to spend your collateral. GemJoin
holds your collateral and Vat
Smart Contract use it to manage
them.
GemJoin(address vat, bytes32 ilk, address gem);
denarius.approve(address(gemJoin), type(uint256).max);
Where:
vat
:<vat_addr>
ilk
:'Denarius-A'
gem
:$DENARIUS
ERC20 token address
Example:
<rome-dao-path>./scripts/forge-script.sh ./src/GemJoin.s.sol:GemJoinDeploy --fork-url=$RPC_URL --broadcast -vvvv
Deploy a DaiJoin
contract from join.sol
. DaiJoin
holds DAI
and Vat
Smart Contract use it to manage them.
DaiJoin(address vat, address dai)
Where:
vat_
:<vat_addr>
dai_
:$DAI
ERC20 token address
Example:
<rome-dao-path>./scripts/forge-script.sh ./src/DaiJoin.s.sol:DaiJoinDeploy --fork-url=$RPC_URL --broadcast -vvvv
Then, using Rome DAO
scripts for:
- Allow
DaiJoin
to mint$DAI
- Allow
DaiJoin
to burn$DAI
- Give Hope (permission) to
DaiJoin
operates moves$DAI
for you withinVat
- Make
$DAI
to rely onDaiJoin
Example:
<rome-dao-path>./scripts/forge-script.sh ./src/DaiJoin.s.sol:DaiJoinReceiveAllowance --fork-url=$RPC_URL --broadcast -vvvv
Vat
needs to rely on (authorize) GemJoin
and DaiJoin
Smart Contracts, initialize it. Also it is needed to set the global debt ceiling, set collateral debt ceiling, and set the collateral price.
See the sample code below:
vat.rely(<gem_join_addr>);
vat.rely(<dai_join_addr>);
vat.init(<bond-or-collateral-name>);
vat.file('Line', 1_000_000 * 10**45); // RAD: 45 decimals
vat.file('Denarius-A', 'line', 1_000_000 * 10**45); // RAD: 45 decimals
vat.file('Denarius-A', 'spot', 1 * 10**27) // RAY: 27 decimals
Example:
<rome-dao-path>./scripts/forge-script.sh ./src/vat.s.sol:VatInitialize --fork-url=$RPC_URL --broadcast -vvvv
vat.file('Line', 1_000_000 * 10**45); // RAD: 45 decimals
Pay attention that now you define a name for your collateral, in our case: Denarius-A
. The collateral data is stored within a struct called ilk
. There is a
mapping called ilks
that allow the Vat
supports several collaterals with different rate configurations.
vat.file('Denarius-A', 'line', 1_000_000 * 10**45); // RAD: 45 decimals
Spot defines the collateral price within the Vat
vat.file('Denarius-A', 'spot', 1 * 10**27) // RAY: 27 decimals
In the above example, it makes $DENARIUS
price equals DAI
price ( 1 to 1 ).
The image bellow describe in a spreadsheet with numbers the operation we are going to do:
This other image describe the flow performed in Solidity code to borrow DAI from the DAO:
gemJoin.join(<your_addr>, <amount>); // <amount> with 10**18 precision
This will add collateral to the system, but it will remain unemcumbered (not locked). The next step is to draw internal dai
from the Vat
using frob()
:
vat.frob(
'Denarius-A', // ilk
<your_wallet>,
<your_wallet>,
<your_wallet>, // To keep it simple, use your address for both `u`, `v` and `w`
int dink, // with 10**18 precision
int dart // with 10**18 precision
)
dink
: how much collateral to lock(+ add ) or unlock(- sub) withinVat
. It means the collateral is now encumbered (locked) into the system.dart
: how much normalized debt to add(+)/remove(-). Rememberingdebt = ilk.rate * urn.art
. To get the value fordart
, divide the desired amount byilk.rate
(this is a floating point division, which can be tricky). See the RwaUrn component to understand how it can be done- Recommendation: to leave
dink = dart*2
when callingvat.frob
for drawing to make the collateralization rate in 200%.
Now it is a great time. Calling the method below after vat.frob
you are allowed to receive $DAI
in your wallet.
daiJoin.exit(<your_wallet>, <amount>); // <amount> with 10**18 precision
Example:
./scripts/forge-script.sh ./src/Borrow.s.sol:BorrowDenariusA --fork-url=$RPC_URL --broadcast -vvvv
Status Information about your Positions in Dai
, $DENARIUS
and within the Rome DAO (simulating Maker) protocol
To know what is your actual positions in Dai
, $DENARIUS
and within the Rome DAO (simulating Maker) protocol there is a helper script that gives you
these information reading the different smart contracts of the Protocol. Just call:
./scripts/forge-script.sh ./src/Operation.s.sol:InfoBalances --fork-url=$RPC_URL --broadcast -vvvv
This image below describe the flow performed in Solidity code to payback DAI to the DAO:
daiJoin.join(<your_addr>, <amount>); // <amount> with 10**18 precision
This will burn ERC-20 $DAI
and add it to <your_addr>
internal balance on the Vat
.
vat.frob(
'Denarius-A', // ilk
<your_wallet>,
<your_wallet>,
<your_wallet>, // To keep it simple, use your address for both `u`, `v` and `w`
int dink, // with 10**18 precision
int dart // with 10**18 precision
)
dink
: how much collateral to unlock. MUST BE NEGATIVE. Collateral is now uncumbered (unlocked), but still in the system.dart
: how much normalized debt to remove. MUST BE NEGATIVE. Remember thatdebt = ilk.rate * urn.art
. To get the value fordart
, divide the desired amount byilk.rate
(this is a floating point division, which can be tricky)- See the RwaUrn component to understand how it can be done
gemJoin.exit(<your_wallet>, <amount>); // <amount> with 10**18 precision
Example:
./scripts/forge-script.sh ./src/Payback.s.sol:PayBackDenariusA --fork-url=$RPC_URL --broadcast -vvvv
All the steps above have described how Maker DAO protocol manages DAI supply and its collaterals.