From f9da49fdeeb5b6148f4a8717de188b0b94a03268 Mon Sep 17 00:00:00 2001 From: sunbreak1211 Date: Tue, 27 Aug 2024 10:45:19 -0300 Subject: [PATCH 1/7] Renaming + Certora adjustments --- .github/workflows/certora.yml | 16 +- Makefile | 6 +- README.md | 20 +- certora/DaiJoinMock.sol | 6 +- certora/DaiMock.sol | 4 +- certora/DaiNst.spec | 224 ------------------ certora/{DaiNst.conf => DaiUsds.conf} | 28 +-- certora/DaiUsds.spec | 230 +++++++++++++++++++ certora/{Nst.conf => Usds.conf} | 6 +- certora/{Nst.spec => Usds.spec} | 8 +- certora/{NstJoin.conf => UsdsJoin.conf} | 14 +- certora/{NstJoin.spec => UsdsJoin.spec} | 103 +++++---- deploy/{NstDeploy.sol => UsdsDeploy.sol} | 34 +-- deploy/{NstInit.sol => UsdsInit.sol} | 38 +-- deploy/{NstInstance.sol => UsdsInstance.sol} | 10 +- src/{DaiNst.sol => DaiUsds.sol} | 50 ++-- src/{Nst.sol => Usds.sol} | 32 +-- src/{NstJoin.sol => UsdsJoin.sol} | 22 +- test/DaiNst.t.sol | 96 -------- test/DaiUsds.t.sol | 96 ++++++++ test/Deployment.t.sol | 48 ++-- test/{Nst.t.sol => Usds.t.sol} | 86 +++---- test/{NstJoin.t.sol => UsdsJoin.t.sol} | 36 +-- 23 files changed, 612 insertions(+), 601 deletions(-) delete mode 100644 certora/DaiNst.spec rename certora/{DaiNst.conf => DaiUsds.conf} (52%) create mode 100644 certora/DaiUsds.spec rename certora/{Nst.conf => Usds.conf} (81%) rename certora/{Nst.spec => Usds.spec} (99%) rename certora/{NstJoin.conf => UsdsJoin.conf} (61%) rename certora/{NstJoin.spec => UsdsJoin.spec} (66%) rename deploy/{NstDeploy.sol => UsdsDeploy.sol} (60%) rename deploy/{NstInit.sol => UsdsInit.sol} (50%) rename deploy/{NstInstance.sol => UsdsInstance.sol} (88%) rename src/{DaiNst.sol => DaiUsds.sol} (56%) rename src/{Nst.sol => Usds.sol} (88%) rename src/{NstJoin.sol => UsdsJoin.sol} (80%) delete mode 100644 test/DaiNst.t.sol create mode 100644 test/DaiUsds.t.sol rename test/{Nst.t.sol => Usds.t.sol} (57%) rename test/{NstJoin.t.sol => UsdsJoin.t.sol} (61%) diff --git a/.github/workflows/certora.yml b/.github/workflows/certora.yml index 368515c..4e94a17 100644 --- a/.github/workflows/certora.yml +++ b/.github/workflows/certora.yml @@ -9,10 +9,10 @@ jobs: strategy: fail-fast: false matrix: - nst: - - nst - - nst-join - - dai-nst + usds: + - usds + - usds-join + - dai-usds steps: - name: Checkout @@ -38,7 +38,7 @@ jobs: - name: Install Certora run: pip3 install certora-cli-beta - # - name: Verify ${{ matrix.nst }} - # run: make certora-${{ matrix.nst }} - # env: - # CERTORAKEY: ${{ secrets.CERTORAKEY }} + - name: Verify ${{ matrix.usds }} + run: make certora-${{ matrix.usds }} + env: + CERTORAKEY: ${{ secrets.CERTORAKEY }} diff --git a/Makefile b/Makefile index e4012db..e75ab72 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ PATH := ~/.solc-select/artifacts/solc-0.8.21:~/.solc-select/artifacts:$(PATH) -certora-nst :; PATH=${PATH} certoraRun certora/Nst.conf$(if $(rule), --rule $(rule),) -certora-nst-join :; PATH=${PATH} certoraRun certora/NstJoin.conf$(if $(rule), --rule $(rule),) -certora-dai-nst :; PATH=${PATH} certoraRun certora/DaiNst.conf$(if $(rule), --rule $(rule),) +certora-usds :; PATH=${PATH} certoraRun certora/Usds.conf$(if $(rule), --rule $(rule),) +certora-usds-join :; PATH=${PATH} certoraRun certora/UsdsJoin.conf$(if $(rule), --rule $(rule),) +certora-dai-usds :; PATH=${PATH} certoraRun certora/DaiUsds.conf$(if $(rule), --rule $(rule),) diff --git a/README.md b/README.md index 8b30848..dcdf035 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,30 @@ -# NST Token and contracts associated +# USDS Token and contracts associated This repository includes 3 smart contracts: -- NST token -- NstJoin -- DaiNst Converter +- USDS token +- UsdsJoin +- DaiUsds Converter -### NST token +### USDS token This is a standard erc20 implementation with regular `permit` functionality + EIP-1271 smart contract signature validation. The token uses the ERC-1822 UUPS pattern for upgradeability and the ERC-1967 proxy storage slots standard. -It is important that the `NstDeploy` library sequence be used for deploying the token. +It is important that the `UsdsDeploy` library sequence be used for deploying the token. #### OZ upgradeability validations The OZ validations can be run alongside the existing tests: `VALIDATE=true forge test --ffi --build-info --extra-output storageLayout` -### NstJoin +### UsdsJoin This is the contract in charge of `mint`ing the erc20 IOUs in exchange of native `vat.dai` balance. It also manages the reverse operation, `burn`ing tokens and releasing `vat.dai`. A noticeable code difference against `DaiJoin` is this contract doesn't have any permissions system at all. -However, in practice, `NstJoin` acts the exact same way as the production `DaiJoin` implementation. This is because there isn't any `wards(address)` set there. +However, in practice, `UsdsJoin` acts the exact same way as the production `DaiJoin` implementation. This is because there isn't any `wards(address)` set there. -### DaiNst +### DaiUsds -It is a permissionless converter between `Dai` and `Nst` (both ways). Using the `public` functions of `NstJoin` and `DaiJoin` moves from one token to the other. The exchange rate is 1:1. +It is a permissionless converter between `Dai` and `Usds` (both ways). Using the `public` functions of `UsdsJoin` and `DaiJoin` moves from one token to the other. The exchange rate is 1:1. It is just a "convenience" contract, users could still get the same outcome executing the separate methods in the `join`s or use any other converter implementation. diff --git a/certora/DaiJoinMock.sol b/certora/DaiJoinMock.sol index 5898e5c..f9414d0 100644 --- a/certora/DaiJoinMock.sol +++ b/certora/DaiJoinMock.sol @@ -1,7 +1,7 @@ pragma solidity ^0.8.21; -import "../src/NstJoin.sol"; +import "../src/UsdsJoin.sol"; -contract DaiJoinMock is NstJoin { - constructor(address vat_, address dai_) NstJoin(vat_, dai_) {} +contract DaiJoinMock is UsdsJoin { + constructor(address vat_, address dai_) UsdsJoin(vat_, dai_) {} } diff --git a/certora/DaiMock.sol b/certora/DaiMock.sol index 53d26dd..c8e8040 100644 --- a/certora/DaiMock.sol +++ b/certora/DaiMock.sol @@ -1,7 +1,7 @@ pragma solidity ^0.8.21; -import "../src/Nst.sol"; +import "../src/Usds.sol"; -contract DaiMock is Nst { +contract DaiMock is Usds { } diff --git a/certora/DaiNst.spec b/certora/DaiNst.spec deleted file mode 100644 index 01f3591..0000000 --- a/certora/DaiNst.spec +++ /dev/null @@ -1,224 +0,0 @@ -// DaiNst.spec - -using Nst as nst; -using NstJoin as nstJoin; -using DaiMock as dai; -using DaiJoinMock as daiJoin; -using VatMock as vat; - -methods { - function nst.wards(address) external returns (uint256) envfree; - function nst.totalSupply() external returns (uint256) envfree; - function nst.balanceOf(address) external returns (uint256) envfree; - function nst.allowance(address, address) external returns (uint256) envfree; - function dai.wards(address) external returns (uint256) envfree; - function dai.totalSupply() external returns (uint256) envfree; - function dai.balanceOf(address) external returns (uint256) envfree; - function dai.allowance(address, address) external returns (uint256) envfree; - function vat.dai(address) external returns (uint256) envfree; - function _.vat() external => DISPATCHER(true); - function _.dai() external => DISPATCHER(true); - function _.nst() external => DISPATCHER(true); - function _.approve(address, uint256) external => DISPATCHER(true); - function _.hope(address) external => DISPATCHER(true); -} - -definition RAY() returns uint256 = 10^27; - -ghost balanceSumNst() returns mathint { - init_state axiom balanceSumNst() == 0; -} - -hook Sstore nst.balanceOf[KEY address a] uint256 balance (uint256 old_balance) { - havoc balanceSumNst assuming balanceSumNst@new() == balanceSumNst@old() + balance - old_balance && balanceSumNst@new() >= 0; -} - -invariant balanceSumNst_equals_totalSupply() balanceSumNst() == to_mathint(nst.totalSupply()); - -ghost balanceSumDai() returns mathint { - init_state axiom balanceSumDai() == 0; -} - -hook Sstore dai.balanceOf[KEY address a] uint256 balance (uint256 old_balance) { - havoc balanceSumDai assuming balanceSumDai@new() == balanceSumDai@old() + balance - old_balance && balanceSumDai@new() >= 0; -} - -invariant balanceSumDai_equals_totalSupply() balanceSumDai() == to_mathint(dai.totalSupply()); - -// Verify correct storage changes for non reverting daiToNst -rule daiToNst(address usr, uint256 wad) { - env e; - - require e.msg.sender != currentContract; - require e.msg.sender != nstJoin; - require e.msg.sender != daiJoin; - - requireInvariant balanceSumNst_equals_totalSupply(); - requireInvariant balanceSumDai_equals_totalSupply(); - - address other; - require other != usr; - address other2; - require other2 != e.msg.sender; - address anyUsr; - address anyUsr2; address anyUsr3; - require anyUsr2 != e.msg.sender || anyUsr3 != currentContract; - - mathint nstTotalSupplyBefore = nst.totalSupply(); - mathint nstBalanceOfUsrBefore = nst.balanceOf(usr); - mathint nstBalanceOfOtherBefore = nst.balanceOf(other); - mathint daiTotalSupplyBefore = dai.totalSupply(); - mathint daiBalanceOfSenderBefore = dai.balanceOf(e.msg.sender); - mathint daiBalanceOfOtherBefore = dai.balanceOf(other2); - mathint vatDaiNstJoinBefore = vat.dai(nstJoin); - mathint vatDaiDaiJoinBefore = vat.dai(daiJoin); - - daiToNst(e, usr, wad); - - mathint nstTotalSupplyAfter = nst.totalSupply(); - mathint nstBalanceOfUsrAfter = nst.balanceOf(usr); - mathint nstBalanceOfOtherAfter = nst.balanceOf(other); - mathint daiTotalSupplyAfter = dai.totalSupply(); - mathint daiBalanceOfSenderAfter = dai.balanceOf(e.msg.sender); - mathint daiBalanceOfOtherAfter = dai.balanceOf(other2); - mathint vatDaiJoinAfter = vat.dai(currentContract); - mathint vatDaiUsrAfter = vat.dai(usr); - mathint vatDaiNstJoinAfter = vat.dai(nstJoin); - mathint vatDaiDaiJoinAfter = vat.dai(daiJoin); - - assert nstTotalSupplyAfter == nstTotalSupplyBefore + wad, "daiToNst did not increase nst.totalSupply by wad"; - assert nstBalanceOfUsrAfter == nstBalanceOfUsrBefore + wad, "daiToNst did not increase nst.balanceOf[usr] by wad"; - assert nstBalanceOfOtherAfter == nstBalanceOfOtherBefore, "daiToNst did not keep unchanged the rest of nst.balanceOf[x]"; - assert daiTotalSupplyAfter == daiTotalSupplyBefore - wad, "daiToNst did not decrease dai.totalSupply by wad"; - assert daiBalanceOfSenderAfter == daiBalanceOfSenderBefore - wad, "daiToNst did not decrease dai.balanceOf[sender] by wad"; - assert daiBalanceOfOtherAfter == daiBalanceOfOtherBefore, "daiToNst did not keep unchanged the rest of dai.balanceOf[x]"; - assert vatDaiNstJoinAfter == vatDaiNstJoinBefore + wad * RAY(), "daiToNst did not increase vat.dai(nstJoin) by wad * RAY"; - assert vatDaiDaiJoinAfter == vatDaiDaiJoinBefore - wad * RAY(), "daiToNst did not decrease vat.dai(daiJoin) by wad * RAY"; -} - -// Verify revert rules on daiToNst -rule daiToNst_revert(address usr, uint256 wad) { - env e; - - requireInvariant balanceSumNst_equals_totalSupply(); - requireInvariant balanceSumDai_equals_totalSupply(); - - require e.msg.sender != currentContract; - require to_mathint(vat.dai(nstJoin)) >= nst.totalSupply() * RAY(); // Property of the relationship nst/nstJoin (not need to prove here) - require to_mathint(vat.dai(daiJoin)) >= dai.totalSupply() * RAY(); // Property of the relationship dai/daiJoin (not need to prove here) - require nst.wards(nstJoin) == 1; // Proved in NstJoin that this is necessary - require to_mathint(dai.allowance(currentContract, daiJoin)) == max_uint256; // Set in the constructor - - mathint daiBalanceOfSender = dai.balanceOf(e.msg.sender); - mathint daiAllowanceSenderDaiNst = dai.allowance(e.msg.sender, currentContract); - mathint vatDaiDaiNst = vat.dai(currentContract); - mathint vatDaiNstJoin = vat.dai(nstJoin); - mathint nstBalanceOfUsr = nst.balanceOf(usr); - - daiToNst@withrevert(e, usr, wad); - - bool revert1 = e.msg.value > 0; - bool revert2 = daiBalanceOfSender < to_mathint(wad); - bool revert3 = daiAllowanceSenderDaiNst < to_mathint(wad); - bool revert4 = vatDaiDaiNst + wad * RAY() > max_uint256; - bool revert5 = vatDaiNstJoin + wad * RAY() > max_uint256; - bool revert6 = usr == 0 || usr == nst; - - assert revert1 => lastReverted, "revert1 failed"; - assert revert2 => lastReverted, "revert2 failed"; - assert revert3 => lastReverted, "revert3 failed"; - assert revert4 => lastReverted, "revert4 failed"; - assert revert5 => lastReverted, "revert5 failed"; - assert revert6 => lastReverted, "revert6 failed"; - assert lastReverted => revert1 || revert2 || revert3 || - revert4 || revert5 || revert6, "Revert rules are not covering all the cases"; -} - -// Verify correct storage changes for non reverting nstToDai -rule nstToDai(address usr, uint256 wad) { - env e; - - require e.msg.sender != currentContract; - require e.msg.sender != nstJoin; - require e.msg.sender != daiJoin; - - requireInvariant balanceSumNst_equals_totalSupply(); - requireInvariant balanceSumDai_equals_totalSupply(); - - address other; - require other != e.msg.sender; - address other2; - require other2 != usr; - address anyUsr; - address anyUsr2; address anyUsr3; - require anyUsr2 != e.msg.sender || anyUsr3 != currentContract; - - mathint nstTotalSupplyBefore = nst.totalSupply(); - mathint nstBalanceOfSenderBefore = nst.balanceOf(e.msg.sender); - mathint nstBalanceOfOtherBefore = nst.balanceOf(other); - mathint daiTotalSupplyBefore = dai.totalSupply(); - mathint daiBalanceOfUsrBefore = dai.balanceOf(usr); - mathint daiBalanceOfOtherBefore = dai.balanceOf(other2); - mathint vatDaiNstJoinBefore = vat.dai(nstJoin); - mathint vatDaiDaiJoinBefore = vat.dai(daiJoin); - - nstToDai(e, usr, wad); - - mathint nstTotalSupplyAfter = nst.totalSupply(); - mathint nstBalanceOfSenderAfter = nst.balanceOf(e.msg.sender); - mathint nstBalanceOfOtherAfter = nst.balanceOf(other); - mathint daiTotalSupplyAfter = dai.totalSupply(); - mathint daiBalanceOfUsrAfter = dai.balanceOf(usr); - mathint daiBalanceOfOtherAfter = dai.balanceOf(other2); - mathint vatDaiJoinAfter = vat.dai(currentContract); - mathint vatDaiUsrAfter = vat.dai(usr); - mathint vatDaiNstJoinAfter = vat.dai(nstJoin); - mathint vatDaiDaiJoinAfter = vat.dai(daiJoin); - - assert nstTotalSupplyAfter == nstTotalSupplyBefore - wad, "nstToDai did not decrease nst.totalSupply by wad"; - assert nstBalanceOfSenderAfter == nstBalanceOfSenderBefore - wad, "nstToDai did not decrease nst.balanceOf[sender] by wad"; - assert nstBalanceOfOtherAfter == nstBalanceOfOtherBefore, "nstToDai did not keep unchanged the rest of nst.balanceOf[x]"; - assert daiTotalSupplyAfter == daiTotalSupplyBefore + wad, "nstToDai did not decrease dai.totalSupply by wad"; - assert daiBalanceOfUsrAfter == daiBalanceOfUsrBefore + wad, "nstToDai did not decrease dai.balanceOf[usr] by wad"; - assert daiBalanceOfOtherAfter == daiBalanceOfOtherBefore, "nstToDai did not keep unchanged the rest of dai.balanceOf[x]"; - assert vatDaiNstJoinAfter == vatDaiNstJoinBefore - wad * RAY(), "nstToDai did not decrease vat.dai(nstJoin) by wad * RAY"; - assert vatDaiDaiJoinAfter == vatDaiDaiJoinBefore + wad * RAY(), "nstToDai did not increase vat.dai(daiJoin) by wad * RAY"; -} - -// Verify revert rules on nstToDai -rule nstToDai_revert(address usr, uint256 wad) { - env e; - - requireInvariant balanceSumNst_equals_totalSupply(); - requireInvariant balanceSumDai_equals_totalSupply(); - - require e.msg.sender != currentContract; - require to_mathint(vat.dai(nstJoin)) >= nst.totalSupply() * RAY(); // Property of the relationship nst/nstJoin (not need to prove here) - require to_mathint(vat.dai(daiJoin)) >= dai.totalSupply() * RAY(); // Property of the relationship dai/daiJoin (not need to prove here) - require dai.wards(daiJoin) == 1; // Assume proved property of daiJoin - require to_mathint(nst.allowance(currentContract, nstJoin)) == max_uint256; // Set in the constructor - - mathint nstBalanceOfSender = nst.balanceOf(e.msg.sender); - mathint nstAllowanceSenderDaiNst = nst.allowance(e.msg.sender, currentContract); - mathint vatDaiDaiNst = vat.dai(currentContract); - mathint vatDaiDaiJoin = vat.dai(daiJoin); - mathint daiBalanceOfUsr = dai.balanceOf(usr); - - nstToDai@withrevert(e, usr, wad); - - bool revert1 = e.msg.value > 0; - bool revert2 = nstBalanceOfSender < to_mathint(wad); - bool revert3 = nstAllowanceSenderDaiNst < to_mathint(wad); - bool revert4 = vatDaiDaiNst + wad * RAY() > max_uint256; - bool revert5 = vatDaiDaiJoin + wad * RAY() > max_uint256; - bool revert6 = usr == 0 || usr == dai; - - assert revert1 => lastReverted, "revert1 failed"; - assert revert2 => lastReverted, "revert2 failed"; - assert revert3 => lastReverted, "revert3 failed"; - assert revert4 => lastReverted, "revert4 failed"; - assert revert5 => lastReverted, "revert5 failed"; - assert revert6 => lastReverted, "revert6 failed"; - assert lastReverted => revert1 || revert2 || revert3 || - revert4 || revert5 || revert6, "Revert rules are not covering all the cases"; -} diff --git a/certora/DaiNst.conf b/certora/DaiUsds.conf similarity index 52% rename from certora/DaiNst.conf rename to certora/DaiUsds.conf index 51cf141..5eea443 100644 --- a/certora/DaiNst.conf +++ b/certora/DaiUsds.conf @@ -1,33 +1,33 @@ { "files": [ - "src/DaiNst.sol", - "src/NstJoin.sol", - "src/Nst.sol", + "src/DaiUsds.sol", + "src/UsdsJoin.sol", + "src/Usds.sol", "certora/DaiJoinMock.sol", "certora/DaiMock.sol", "certora/VatMock.sol", ], "link": [ - "DaiNst:nstJoin=NstJoin", - "DaiNst:nst=Nst", - "DaiNst:daiJoin=DaiJoinMock", - "DaiNst:dai=DaiMock", - "NstJoin:nst=Nst", - "NstJoin:vat=VatMock", - "DaiJoinMock:nst=DaiMock", + "DaiUsds:usdsJoin=UsdsJoin", + "DaiUsds:usds=Usds", + "DaiUsds:daiJoin=DaiJoinMock", + "DaiUsds:dai=DaiMock", + "UsdsJoin:usds=Usds", + "UsdsJoin:vat=VatMock", + "DaiJoinMock:usds=DaiMock", "DaiJoinMock:vat=VatMock" ], "rule_sanity": "basic", "solc": "solc-0.8.21", "solc_optimize_map": { - "DaiNst": "200", - "NstJoin": "200", - "Nst": "200", + "DaiUsds": "200", + "UsdsJoin": "200", + "Usds": "200", "DaiJoinMock": "0", "DaiMock": "0", "VatMock": "0" }, - "verify": "DaiNst:certora/DaiNst.spec", + "verify": "DaiUsds:certora/DaiUsds.spec", "prover_args": [ "-mediumTimeout 180" ], diff --git a/certora/DaiUsds.spec b/certora/DaiUsds.spec new file mode 100644 index 0000000..80f0a10 --- /dev/null +++ b/certora/DaiUsds.spec @@ -0,0 +1,230 @@ +// DaiUsds.spec + +using Usds as usds; +using UsdsJoin as usdsJoin; +using DaiMock as dai; +using DaiJoinMock as daiJoin; +using VatMock as vat; + +methods { + function usds.wards(address) external returns (uint256) envfree; + function usds.totalSupply() external returns (uint256) envfree; + function usds.balanceOf(address) external returns (uint256) envfree; + function usds.allowance(address, address) external returns (uint256) envfree; + function dai.wards(address) external returns (uint256) envfree; + function dai.totalSupply() external returns (uint256) envfree; + function dai.balanceOf(address) external returns (uint256) envfree; + function dai.allowance(address, address) external returns (uint256) envfree; + function vat.dai(address) external returns (uint256) envfree; + function _.vat() external => DISPATCHER(true); + function _.dai() external => DISPATCHER(true); + function _.usds() external => DISPATCHER(true); + function _.approve(address, uint256) external => DISPATCHER(true); + function _.hope(address) external => DISPATCHER(true); +} + +definition RAY() returns uint256 = 10^27; + +ghost balanceSumUsds() returns mathint { + init_state axiom balanceSumUsds() == 0; +} + +hook Sstore usds.balanceOf[KEY address a] uint256 balance (uint256 old_balance) { + havoc balanceSumUsds assuming balanceSumUsds@new() == balanceSumUsds@old() + balance - old_balance && balanceSumUsds@new() >= 0; +} + +invariant balanceSumUsds_equals_totalSupply() balanceSumUsds() == to_mathint(usds.totalSupply()) + filtered { + m -> m.selector != sig:Usds.upgradeToAndCall(address, bytes).selector + } + +ghost balanceSumDai() returns mathint { + init_state axiom balanceSumDai() == 0; +} + +hook Sstore dai.balanceOf[KEY address a] uint256 balance (uint256 old_balance) { + havoc balanceSumDai assuming balanceSumDai@new() == balanceSumDai@old() + balance - old_balance && balanceSumDai@new() >= 0; +} + +invariant balanceSumDai_equals_totalSupply() balanceSumDai() == to_mathint(dai.totalSupply()) + filtered { + m -> m.selector != sig:DaiMock.upgradeToAndCall(address, bytes).selector + } + +// Verify correct storage changes for non reverting daiToUsds +rule daiToUsds(address usr, uint256 wad) { + env e; + + require e.msg.sender != currentContract; + require e.msg.sender != usdsJoin; + require e.msg.sender != daiJoin; + + requireInvariant balanceSumUsds_equals_totalSupply(); + requireInvariant balanceSumDai_equals_totalSupply(); + + address other; + require other != usr; + address other2; + require other2 != e.msg.sender; + address anyUsr; + address anyUsr2; address anyUsr3; + require anyUsr2 != e.msg.sender || anyUsr3 != currentContract; + + mathint usdsTotalSupplyBefore = usds.totalSupply(); + mathint usdsBalanceOfUsrBefore = usds.balanceOf(usr); + mathint usdsBalanceOfOtherBefore = usds.balanceOf(other); + mathint daiTotalSupplyBefore = dai.totalSupply(); + mathint daiBalanceOfSenderBefore = dai.balanceOf(e.msg.sender); + mathint daiBalanceOfOtherBefore = dai.balanceOf(other2); + mathint vatDaiUsdsJoinBefore = vat.dai(usdsJoin); + mathint vatDaiDaiJoinBefore = vat.dai(daiJoin); + + daiToUsds(e, usr, wad); + + mathint usdsTotalSupplyAfter = usds.totalSupply(); + mathint usdsBalanceOfUsrAfter = usds.balanceOf(usr); + mathint usdsBalanceOfOtherAfter = usds.balanceOf(other); + mathint daiTotalSupplyAfter = dai.totalSupply(); + mathint daiBalanceOfSenderAfter = dai.balanceOf(e.msg.sender); + mathint daiBalanceOfOtherAfter = dai.balanceOf(other2); + mathint vatDaiJoinAfter = vat.dai(currentContract); + mathint vatDaiUsrAfter = vat.dai(usr); + mathint vatDaiUsdsJoinAfter = vat.dai(usdsJoin); + mathint vatDaiDaiJoinAfter = vat.dai(daiJoin); + + assert usdsTotalSupplyAfter == usdsTotalSupplyBefore + wad, "daiToUsds did not increase usds.totalSupply by wad"; + assert usdsBalanceOfUsrAfter == usdsBalanceOfUsrBefore + wad, "daiToUsds did not increase usds.balanceOf[usr] by wad"; + assert usdsBalanceOfOtherAfter == usdsBalanceOfOtherBefore, "daiToUsds did not keep unchanged the rest of usds.balanceOf[x]"; + assert daiTotalSupplyAfter == daiTotalSupplyBefore - wad, "daiToUsds did not decrease dai.totalSupply by wad"; + assert daiBalanceOfSenderAfter == daiBalanceOfSenderBefore - wad, "daiToUsds did not decrease dai.balanceOf[sender] by wad"; + assert daiBalanceOfOtherAfter == daiBalanceOfOtherBefore, "daiToUsds did not keep unchanged the rest of dai.balanceOf[x]"; + assert vatDaiUsdsJoinAfter == vatDaiUsdsJoinBefore + wad * RAY(), "daiToUsds did not increase vat.dai(usdsJoin) by wad * RAY"; + assert vatDaiDaiJoinAfter == vatDaiDaiJoinBefore - wad * RAY(), "daiToUsds did not decrease vat.dai(daiJoin) by wad * RAY"; +} + +// Verify revert rules on daiToUsds +rule daiToUsds_revert(address usr, uint256 wad) { + env e; + + requireInvariant balanceSumUsds_equals_totalSupply(); + requireInvariant balanceSumDai_equals_totalSupply(); + + require e.msg.sender != currentContract; + require to_mathint(vat.dai(usdsJoin)) >= usds.totalSupply() * RAY(); // Property of the relationship usds/usdsJoin (not need to prove here) + require to_mathint(vat.dai(daiJoin)) >= dai.totalSupply() * RAY(); // Property of the relationship dai/daiJoin (not need to prove here) + require usds.wards(usdsJoin) == 1; // Proved in UsdsJoin that this is necessary + require to_mathint(dai.allowance(currentContract, daiJoin)) == max_uint256; // Set in the constructor + + mathint daiBalanceOfSender = dai.balanceOf(e.msg.sender); + mathint daiAllowanceSenderDaiUsds = dai.allowance(e.msg.sender, currentContract); + mathint vatDaiDaiUsds = vat.dai(currentContract); + mathint vatDaiUsdsJoin = vat.dai(usdsJoin); + mathint usdsBalanceOfUsr = usds.balanceOf(usr); + + daiToUsds@withrevert(e, usr, wad); + + bool revert1 = e.msg.value > 0; + bool revert2 = daiBalanceOfSender < to_mathint(wad); + bool revert3 = daiAllowanceSenderDaiUsds < to_mathint(wad); + bool revert4 = vatDaiDaiUsds + wad * RAY() > max_uint256; + bool revert5 = vatDaiUsdsJoin + wad * RAY() > max_uint256; + bool revert6 = usr == 0 || usr == usds; + + assert revert1 => lastReverted, "revert1 failed"; + assert revert2 => lastReverted, "revert2 failed"; + assert revert3 => lastReverted, "revert3 failed"; + assert revert4 => lastReverted, "revert4 failed"; + assert revert5 => lastReverted, "revert5 failed"; + assert revert6 => lastReverted, "revert6 failed"; + assert lastReverted => revert1 || revert2 || revert3 || + revert4 || revert5 || revert6, "Revert rules are not covering all the cases"; +} + +// Verify correct storage changes for non reverting usdsToDai +rule usdsToDai(address usr, uint256 wad) { + env e; + + require e.msg.sender != currentContract; + require e.msg.sender != usdsJoin; + require e.msg.sender != daiJoin; + + requireInvariant balanceSumUsds_equals_totalSupply(); + requireInvariant balanceSumDai_equals_totalSupply(); + + address other; + require other != e.msg.sender; + address other2; + require other2 != usr; + address anyUsr; + address anyUsr2; address anyUsr3; + require anyUsr2 != e.msg.sender || anyUsr3 != currentContract; + + mathint usdsTotalSupplyBefore = usds.totalSupply(); + mathint usdsBalanceOfSenderBefore = usds.balanceOf(e.msg.sender); + mathint usdsBalanceOfOtherBefore = usds.balanceOf(other); + mathint daiTotalSupplyBefore = dai.totalSupply(); + mathint daiBalanceOfUsrBefore = dai.balanceOf(usr); + mathint daiBalanceOfOtherBefore = dai.balanceOf(other2); + mathint vatDaiUsdsJoinBefore = vat.dai(usdsJoin); + mathint vatDaiDaiJoinBefore = vat.dai(daiJoin); + + usdsToDai(e, usr, wad); + + mathint usdsTotalSupplyAfter = usds.totalSupply(); + mathint usdsBalanceOfSenderAfter = usds.balanceOf(e.msg.sender); + mathint usdsBalanceOfOtherAfter = usds.balanceOf(other); + mathint daiTotalSupplyAfter = dai.totalSupply(); + mathint daiBalanceOfUsrAfter = dai.balanceOf(usr); + mathint daiBalanceOfOtherAfter = dai.balanceOf(other2); + mathint vatDaiJoinAfter = vat.dai(currentContract); + mathint vatDaiUsrAfter = vat.dai(usr); + mathint vatDaiUsdsJoinAfter = vat.dai(usdsJoin); + mathint vatDaiDaiJoinAfter = vat.dai(daiJoin); + + assert usdsTotalSupplyAfter == usdsTotalSupplyBefore - wad, "usdsToDai did not decrease usds.totalSupply by wad"; + assert usdsBalanceOfSenderAfter == usdsBalanceOfSenderBefore - wad, "usdsToDai did not decrease usds.balanceOf[sender] by wad"; + assert usdsBalanceOfOtherAfter == usdsBalanceOfOtherBefore, "usdsToDai did not keep unchanged the rest of usds.balanceOf[x]"; + assert daiTotalSupplyAfter == daiTotalSupplyBefore + wad, "usdsToDai did not decrease dai.totalSupply by wad"; + assert daiBalanceOfUsrAfter == daiBalanceOfUsrBefore + wad, "usdsToDai did not decrease dai.balanceOf[usr] by wad"; + assert daiBalanceOfOtherAfter == daiBalanceOfOtherBefore, "usdsToDai did not keep unchanged the rest of dai.balanceOf[x]"; + assert vatDaiUsdsJoinAfter == vatDaiUsdsJoinBefore - wad * RAY(), "usdsToDai did not decrease vat.dai(usdsJoin) by wad * RAY"; + assert vatDaiDaiJoinAfter == vatDaiDaiJoinBefore + wad * RAY(), "usdsToDai did not increase vat.dai(daiJoin) by wad * RAY"; +} + +// Verify revert rules on usdsToDai +rule usdsToDai_revert(address usr, uint256 wad) { + env e; + + requireInvariant balanceSumUsds_equals_totalSupply(); + requireInvariant balanceSumDai_equals_totalSupply(); + + require e.msg.sender != currentContract; + require to_mathint(vat.dai(usdsJoin)) >= usds.totalSupply() * RAY(); // Property of the relationship usds/usdsJoin (not need to prove here) + require to_mathint(vat.dai(daiJoin)) >= dai.totalSupply() * RAY(); // Property of the relationship dai/daiJoin (not need to prove here) + require dai.wards(daiJoin) == 1; // Assume proved property of daiJoin + require to_mathint(usds.allowance(currentContract, usdsJoin)) == max_uint256; // Set in the constructor + + mathint usdsBalanceOfSender = usds.balanceOf(e.msg.sender); + mathint usdsAllowanceSenderDaiUsds = usds.allowance(e.msg.sender, currentContract); + mathint vatDaiDaiUsds = vat.dai(currentContract); + mathint vatDaiDaiJoin = vat.dai(daiJoin); + mathint daiBalanceOfUsr = dai.balanceOf(usr); + + usdsToDai@withrevert(e, usr, wad); + + bool revert1 = e.msg.value > 0; + bool revert2 = usdsBalanceOfSender < to_mathint(wad); + bool revert3 = usdsAllowanceSenderDaiUsds < to_mathint(wad); + bool revert4 = vatDaiDaiUsds + wad * RAY() > max_uint256; + bool revert5 = vatDaiDaiJoin + wad * RAY() > max_uint256; + bool revert6 = usr == 0 || usr == dai; + + assert revert1 => lastReverted, "revert1 failed"; + assert revert2 => lastReverted, "revert2 failed"; + assert revert3 => lastReverted, "revert3 failed"; + assert revert4 => lastReverted, "revert4 failed"; + assert revert5 => lastReverted, "revert5 failed"; + assert revert6 => lastReverted, "revert6 failed"; + assert lastReverted => revert1 || revert2 || revert3 || + revert4 || revert5 || revert6, "Revert rules are not covering all the cases"; +} diff --git a/certora/Nst.conf b/certora/Usds.conf similarity index 81% rename from certora/Nst.conf rename to certora/Usds.conf index f8641dd..ce812c0 100644 --- a/certora/Nst.conf +++ b/certora/Usds.conf @@ -1,17 +1,17 @@ { "files": [ - "src/Nst.sol", + "src/Usds.sol", "certora/Auxiliar.sol", "certora/SignerMock.sol" ], "rule_sanity": "basic", "solc": "solc-0.8.21", "solc_optimize_map": { - "Nst": "200", + "Usds": "200", "Auxiliar": "0", "SignerMock": "0" }, - "verify": "Nst:certora/Nst.spec", + "verify": "Usds:certora/Usds.spec", "prover_args": [ "-mediumTimeout 180" ], diff --git a/certora/Nst.spec b/certora/Usds.spec similarity index 99% rename from certora/Nst.spec rename to certora/Usds.spec index 1633720..f87c50a 100644 --- a/certora/Nst.spec +++ b/certora/Usds.spec @@ -1,4 +1,4 @@ -// Nst.spec +// Usds.spec using Auxiliar as aux; using SignerMock as signer; @@ -13,7 +13,6 @@ methods { function balanceOf(address) external returns (uint256) envfree; function allowance(address, address) external returns (uint256) envfree; function nonces(address) external returns (uint256) envfree; - function deploymentChainId() external returns (uint256) envfree; function DOMAIN_SEPARATOR() external returns (bytes32) envfree; function PERMIT_TYPEHASH() external returns (bytes32) envfree; function aux.call_ecrecover(bytes32, uint8, bytes32, bytes32) external returns (address) envfree; @@ -32,7 +31,10 @@ hook Sstore balanceOf[KEY address a] uint256 balance (uint256 old_balance) { havoc balanceSum assuming balanceSum@new() == balanceSum@old() + balance - old_balance && balanceSum@new() >= 0; } -invariant balanceSum_equals_totalSupply() balanceSum() == to_mathint(totalSupply()); +invariant balanceSum_equals_totalSupply() balanceSum() == to_mathint(totalSupply()) + filtered { + m -> m.selector != sig:upgradeToAndCall(address, bytes).selector + } // Verify correct storage changes for non reverting rely rule rely(address usr) { diff --git a/certora/NstJoin.conf b/certora/UsdsJoin.conf similarity index 61% rename from certora/NstJoin.conf rename to certora/UsdsJoin.conf index 21a5831..e3a6e18 100644 --- a/certora/NstJoin.conf +++ b/certora/UsdsJoin.conf @@ -1,21 +1,21 @@ { "files": [ - "src/NstJoin.sol", - "src/Nst.sol", + "src/UsdsJoin.sol", + "src/Usds.sol", "certora/VatMock.sol", ], "link": [ - "NstJoin:nst=Nst", - "NstJoin:vat=VatMock" + "UsdsJoin:usds=Usds", + "UsdsJoin:vat=VatMock" ], "rule_sanity": "basic", "solc": "solc-0.8.21", "solc_optimize_map": { - "NstJoin": "200", - "Nst": "200", + "UsdsJoin": "200", + "Usds": "200", "VatMock": "0" }, - "verify": "NstJoin:certora/NstJoin.spec", + "verify": "UsdsJoin:certora/UsdsJoin.spec", "prover_args": [ "-mediumTimeout 180" ], diff --git a/certora/NstJoin.spec b/certora/UsdsJoin.spec similarity index 66% rename from certora/NstJoin.spec rename to certora/UsdsJoin.spec index 5f700ef..8598abd 100644 --- a/certora/NstJoin.spec +++ b/certora/UsdsJoin.spec @@ -1,14 +1,14 @@ -// NstJoin.spec +// UsdsJoin.spec -using Nst as nst; +using Usds as usds; using VatMock as vat; methods { - function nst.wards(address) external returns (uint256) envfree; - function nst.totalSupply() external returns (uint256) envfree; - function nst.balanceOf(address) external returns (uint256) envfree; - function nst.allowance(address, address) external returns (uint256) envfree; - function nst.nonces(address) external returns (uint256) envfree; + function usds.wards(address) external returns (uint256) envfree; + function usds.totalSupply() external returns (uint256) envfree; + function usds.balanceOf(address) external returns (uint256) envfree; + function usds.allowance(address, address) external returns (uint256) envfree; + function usds.nonces(address) external returns (uint256) envfree; function vat.dai(address) external returns (uint256) envfree; } @@ -18,11 +18,14 @@ ghost balanceSum() returns mathint { init_state axiom balanceSum() == 0; } -hook Sstore nst.balanceOf[KEY address a] uint256 balance (uint256 old_balance) { +hook Sstore usds.balanceOf[KEY address a] uint256 balance (uint256 old_balance) { havoc balanceSum assuming balanceSum@new() == balanceSum@old() + balance - old_balance && balanceSum@new() >= 0; } -invariant balanceSum_equals_totalSupply() balanceSum() == to_mathint(nst.totalSupply()); +invariant balanceSum_equals_totalSupply() balanceSum() == to_mathint(usds.totalSupply()) + filtered { + m -> m.selector != sig:Usds.upgradeToAndCall(address, bytes).selector + } // Verify correct storage changes for non reverting join rule join(address usr, uint256 wad) { @@ -38,36 +41,36 @@ rule join(address usr, uint256 wad) { address anyUsr2; address anyUsr3; require anyUsr2 != e.msg.sender || anyUsr3 != currentContract; - mathint wardsBefore = nst.wards(anyUsr); - mathint totalSupplyBefore = nst.totalSupply(); - mathint balanceOfSenderBefore = nst.balanceOf(e.msg.sender); - mathint balanceOfOtherBefore = nst.balanceOf(other); - mathint allowanceSenderJoinBefore = nst.allowance(e.msg.sender, currentContract); - mathint allowanceOtherBefore = nst.allowance(anyUsr2, anyUsr3); - mathint noncesBefore = nst.nonces(anyUsr); + mathint wardsBefore = usds.wards(anyUsr); + mathint totalSupplyBefore = usds.totalSupply(); + mathint balanceOfSenderBefore = usds.balanceOf(e.msg.sender); + mathint balanceOfOtherBefore = usds.balanceOf(other); + mathint allowanceSenderJoinBefore = usds.allowance(e.msg.sender, currentContract); + mathint allowanceOtherBefore = usds.allowance(anyUsr2, anyUsr3); + mathint noncesBefore = usds.nonces(anyUsr); mathint vatDaiJoinBefore = vat.dai(currentContract); mathint vatDaiUsrBefore = vat.dai(usr); join(e, usr, wad); - mathint wardsAfter = nst.wards(anyUsr); - mathint totalSupplyAfter = nst.totalSupply(); - mathint balanceOfSenderAfter = nst.balanceOf(e.msg.sender); - mathint balanceOfOtherAfter = nst.balanceOf(other); - mathint allowanceSenderJoinAfter = nst.allowance(e.msg.sender, currentContract); - mathint allowanceOtherAfter = nst.allowance(anyUsr2, anyUsr3); - mathint noncesAfter = nst.nonces(anyUsr); + mathint wardsAfter = usds.wards(anyUsr); + mathint totalSupplyAfter = usds.totalSupply(); + mathint balanceOfSenderAfter = usds.balanceOf(e.msg.sender); + mathint balanceOfOtherAfter = usds.balanceOf(other); + mathint allowanceSenderJoinAfter = usds.allowance(e.msg.sender, currentContract); + mathint allowanceOtherAfter = usds.allowance(anyUsr2, anyUsr3); + mathint noncesAfter = usds.nonces(anyUsr); mathint vatDaiJoinAfter = vat.dai(currentContract); mathint vatDaiUsrAfter = vat.dai(usr); - assert wardsAfter == wardsBefore, "join did not keep unchanged nst.wards"; - assert totalSupplyAfter == totalSupplyBefore - wad, "join did not decrease nst.totalSupply by wad"; - assert balanceOfSenderAfter == balanceOfSenderBefore - wad, "join did not decrease nst.balanceOf[sender] by wad"; - assert balanceOfOtherAfter == balanceOfOtherBefore, "join did not keep unchanged the rest of nst.balanceOf[x]"; + assert wardsAfter == wardsBefore, "join did not keep unchanged usds.wards"; + assert totalSupplyAfter == totalSupplyBefore - wad, "join did not decrease usds.totalSupply by wad"; + assert balanceOfSenderAfter == balanceOfSenderBefore - wad, "join did not decrease usds.balanceOf[sender] by wad"; + assert balanceOfOtherAfter == balanceOfOtherBefore, "join did not keep unchanged the rest of usds.balanceOf[x]"; assert allowanceSenderJoinBefore != max_uint256 => allowanceSenderJoinAfter == allowanceSenderJoinBefore - wad, "join did not decrease allowance[sender][join]"; assert allowanceSenderJoinBefore == max_uint256 => allowanceSenderJoinAfter == allowanceSenderJoinBefore, "join did not keep unchaged allowance[sender][join] when is max_uint256"; assert allowanceOtherAfter == allowanceOtherBefore, "join did not keep unchanged the rest of allowance[x][y]"; - assert noncesAfter == noncesBefore, "join did not keep unchanged every nst.nonces[x]"; + assert noncesAfter == noncesBefore, "join did not keep unchanged every usds.nonces[x]"; assert usr != currentContract => vatDaiJoinAfter == vatDaiJoinBefore - wad * RAY(), "join did not decrease vat.dai(join) by wad * RAY"; assert usr != currentContract => vatDaiUsrAfter == vatDaiUsrBefore + wad * RAY(), "join did not increase vat.dai(usr) by wad * RAY"; assert usr == currentContract => vatDaiUsrAfter == vatDaiUsrBefore, "join did not keep unchanged vat.dai(usr) when usr == join"; @@ -79,8 +82,8 @@ rule join_revert(address usr, uint256 wad) { require e.msg.sender != currentContract; - mathint balanceOfSender = nst.balanceOf(e.msg.sender); - mathint allowanceSenderJoin = nst.allowance(e.msg.sender, currentContract); + mathint balanceOfSender = usds.balanceOf(e.msg.sender); + mathint allowanceSenderJoin = usds.allowance(e.msg.sender, currentContract); mathint vatDaiUsr = vat.dai(usr); mathint vatDaiJoin = vat.dai(currentContract); @@ -113,32 +116,32 @@ rule exit(address usr, uint256 wad) { require other != usr; address anyUsr; address anyUsr2; - mathint wardsBefore = nst.wards(anyUsr); - mathint totalSupplyBefore = nst.totalSupply(); - mathint balanceOfUsrBefore = nst.balanceOf(usr); - mathint balanceOfOtherBefore = nst.balanceOf(other); - mathint allowanceBefore = nst.allowance(anyUsr, anyUsr2); - mathint noncesBefore = nst.nonces(anyUsr); + mathint wardsBefore = usds.wards(anyUsr); + mathint totalSupplyBefore = usds.totalSupply(); + mathint balanceOfUsrBefore = usds.balanceOf(usr); + mathint balanceOfOtherBefore = usds.balanceOf(other); + mathint allowanceBefore = usds.allowance(anyUsr, anyUsr2); + mathint noncesBefore = usds.nonces(anyUsr); mathint vatDaiSenderBefore = vat.dai(e.msg.sender); mathint vatDaiJoinBefore = vat.dai(currentContract); exit(e, usr, wad); - mathint wardsAfter = nst.wards(anyUsr); - mathint totalSupplyAfter = nst.totalSupply(); - mathint balanceOfUsrAfter = nst.balanceOf(usr); - mathint balanceOfOtherAfter = nst.balanceOf(other); - mathint allowanceAfter = nst.allowance(anyUsr, anyUsr2); - mathint noncesAfter = nst.nonces(anyUsr); + mathint wardsAfter = usds.wards(anyUsr); + mathint totalSupplyAfter = usds.totalSupply(); + mathint balanceOfUsrAfter = usds.balanceOf(usr); + mathint balanceOfOtherAfter = usds.balanceOf(other); + mathint allowanceAfter = usds.allowance(anyUsr, anyUsr2); + mathint noncesAfter = usds.nonces(anyUsr); mathint vatDaiSenderAfter = vat.dai(e.msg.sender); mathint vatDaiJoinAfter = vat.dai(currentContract); - assert wardsAfter == wardsBefore, "exit did not keep unchanged nst.wards"; - assert totalSupplyAfter == totalSupplyBefore + wad, "exit did not increase nst.totalSupply by wad"; - assert balanceOfUsrAfter == balanceOfUsrBefore + wad, "exit did not increase nst.balanceOf[usr] by wad"; - assert balanceOfOtherAfter == balanceOfOtherBefore, "exit did not keep unchanged the rest of nst.balanceOf[x]"; + assert wardsAfter == wardsBefore, "exit did not keep unchanged usds.wards"; + assert totalSupplyAfter == totalSupplyBefore + wad, "exit did not increase usds.totalSupply by wad"; + assert balanceOfUsrAfter == balanceOfUsrBefore + wad, "exit did not increase usds.balanceOf[usr] by wad"; + assert balanceOfOtherAfter == balanceOfOtherBefore, "exit did not keep unchanged the rest of usds.balanceOf[x]"; assert allowanceAfter == allowanceBefore, "exit did not keep unchanged every allowance[x][y]"; - assert noncesAfter == noncesBefore, "exit did not keep unchanged every nst.nonces[x]"; + assert noncesAfter == noncesBefore, "exit did not keep unchanged every usds.nonces[x]"; assert vatDaiSenderAfter == vatDaiSenderBefore - wad * RAY(), "exit did not decrease vat.dai(sender) by wad * RAY"; assert vatDaiJoinAfter == vatDaiJoinBefore + wad * RAY(), "exit did not increase vat.dai(join) by wad * RAY"; } @@ -151,8 +154,8 @@ rule exit_revert(address usr, uint256 wad) { requireInvariant balanceSum_equals_totalSupply(); - mathint wardsJoin = nst.wards(currentContract); - mathint totalSupply = nst.totalSupply(); + mathint wardsJoin = usds.wards(currentContract); + mathint totalSupply = usds.totalSupply(); mathint vatDaiSender = vat.dai(e.msg.sender); mathint vatDaiJoin = vat.dai(currentContract); @@ -163,7 +166,7 @@ rule exit_revert(address usr, uint256 wad) { bool revert3 = vatDaiJoin + wad * RAY() > max_uint256; bool revert4 = wardsJoin != 1; bool revert5 = totalSupply + wad > max_uint256; - bool revert6 = usr == 0 || usr == nst; + bool revert6 = usr == 0 || usr == usds; assert revert1 => lastReverted, "revert1 failed"; assert revert2 => lastReverted, "revert2 failed"; diff --git a/deploy/NstDeploy.sol b/deploy/UsdsDeploy.sol similarity index 60% rename from deploy/NstDeploy.sol rename to deploy/UsdsDeploy.sol index 13db433..2f6c0cb 100644 --- a/deploy/NstDeploy.sol +++ b/deploy/UsdsDeploy.sol @@ -19,32 +19,32 @@ pragma solidity ^0.8.21; import { ScriptTools } from "dss-test/ScriptTools.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import { Nst } from "src/Nst.sol"; -import { NstJoin } from "src/NstJoin.sol"; -import { DaiNst } from "src/DaiNst.sol"; +import { Usds } from "src/Usds.sol"; +import { UsdsJoin } from "src/UsdsJoin.sol"; +import { DaiUsds } from "src/DaiUsds.sol"; -import { NstInstance } from "./NstInstance.sol"; +import { UsdsInstance } from "./UsdsInstance.sol"; interface DaiJoinLike { function vat() external view returns (address); } -library NstDeploy { +library UsdsDeploy { function deploy( address deployer, address owner, address daiJoin - ) internal returns (NstInstance memory instance) { - address _nstImp = address(new Nst()); - address _nst = address((new ERC1967Proxy(_nstImp, abi.encodeCall(Nst.initialize, ())))); - ScriptTools.switchOwner(_nst, deployer, owner); - - address _nstJoin = address(new NstJoin(DaiJoinLike(daiJoin).vat(), _nst)); - address _daiNst = address(new DaiNst(daiJoin, _nstJoin)); - - instance.nst = _nst; - instance.nstImp = _nstImp; - instance.nstJoin = _nstJoin; - instance.daiNst = _daiNst; + ) internal returns (UsdsInstance memory instance) { + address _usdsImp = address(new Usds()); + address _usds = address((new ERC1967Proxy(_usdsImp, abi.encodeCall(Usds.initialize, ())))); + ScriptTools.switchOwner(_usds, deployer, owner); + + address _usdsJoin = address(new UsdsJoin(DaiJoinLike(daiJoin).vat(), _usds)); + address _daiUsds = address(new DaiUsds(daiJoin, _usdsJoin)); + + instance.usds = _usds; + instance.usdsImp = _usdsImp; + instance.usdsJoin = _usdsJoin; + instance.daiUsds = _daiUsds; } } diff --git a/deploy/NstInit.sol b/deploy/UsdsInit.sol similarity index 50% rename from deploy/NstInit.sol rename to deploy/UsdsInit.sol index 0ccc200..694ca11 100644 --- a/deploy/NstInit.sol +++ b/deploy/UsdsInit.sol @@ -17,45 +17,45 @@ pragma solidity >=0.8.0; import { DssInstance } from "dss-test/MCD.sol"; -import { NstInstance } from "./NstInstance.sol"; +import { UsdsInstance } from "./UsdsInstance.sol"; -interface NstLike { +interface UsdsLike { function rely(address) external; function version() external view returns (string memory); function getImplementation() external view returns (address); } -interface NstJoinLike { - function nst() external view returns (address); +interface UsdsJoinLike { + function usds() external view returns (address); function vat() external view returns (address); } -interface DaiNstLike { +interface DaiUsdsLike { function daiJoin() external view returns (address); - function nstJoin() external view returns (address); + function usdsJoin() external view returns (address); } -library NstInit { +library UsdsInit { function init( DssInstance memory dss, - NstInstance memory instance + UsdsInstance memory instance ) internal { - require(keccak256(bytes(NstLike(instance.nst).version())) == keccak256("1"), "NstInit/version-does-not-match"); - require(NstLike(instance.nst).getImplementation() == instance.nstImp, "NstInit/imp-does-not-match"); + require(keccak256(bytes(UsdsLike(instance.usds).version())) == keccak256("1"), "UsdsInit/version-does-not-match"); + require(UsdsLike(instance.usds).getImplementation() == instance.usdsImp, "UsdsInit/imp-does-not-match"); - require(NstJoinLike(instance.nstJoin).vat() == address(dss.vat), "NstInit/vat-does-not-match"); - require(NstJoinLike(instance.nstJoin).nst() == instance.nst, "NstInit/nst-does-not-match"); + require(UsdsJoinLike(instance.usdsJoin).vat() == address(dss.vat), "UsdsInit/vat-does-not-match"); + require(UsdsJoinLike(instance.usdsJoin).usds() == instance.usds, "UsdsInit/usds-does-not-match"); address daiJoin = dss.chainlog.getAddress("MCD_JOIN_DAI"); - require(DaiNstLike(instance.daiNst).daiJoin() == daiJoin, "NstInit/daiJoin-does-not-match"); - require(DaiNstLike(instance.daiNst).nstJoin() == instance.nstJoin, "NstInit/nstJoin-does-not-match"); + require(DaiUsdsLike(instance.daiUsds).daiJoin() == daiJoin, "UsdsInit/daiJoin-does-not-match"); + require(DaiUsdsLike(instance.daiUsds).usdsJoin() == instance.usdsJoin, "UsdsInit/usdsJoin-does-not-match"); - NstLike(instance.nst).rely(instance.nstJoin); + UsdsLike(instance.usds).rely(instance.usdsJoin); - dss.chainlog.setAddress("NST", instance.nst); - dss.chainlog.setAddress("NST_IMP", instance.nstImp); - dss.chainlog.setAddress("NST_JOIN", instance.nstJoin); - dss.chainlog.setAddress("DAI_NST", instance.daiNst); + dss.chainlog.setAddress("USDS", instance.usds); + dss.chainlog.setAddress("USDS_IMP", instance.usdsImp); + dss.chainlog.setAddress("USDS_JOIN", instance.usdsJoin); + dss.chainlog.setAddress("DAI_USDS", instance.daiUsds); } } diff --git a/deploy/NstInstance.sol b/deploy/UsdsInstance.sol similarity index 88% rename from deploy/NstInstance.sol rename to deploy/UsdsInstance.sol index 5c8aea4..ddf7a1c 100644 --- a/deploy/NstInstance.sol +++ b/deploy/UsdsInstance.sol @@ -16,9 +16,9 @@ pragma solidity >=0.8.0; -struct NstInstance { - address nst; - address nstImp; - address nstJoin; - address daiNst; +struct UsdsInstance { + address usds; + address usdsImp; + address usdsJoin; + address daiUsds; } diff --git a/src/DaiNst.sol b/src/DaiUsds.sol similarity index 56% rename from src/DaiNst.sol rename to src/DaiUsds.sol index 94f2225..4fb69cf 100644 --- a/src/DaiNst.sol +++ b/src/DaiUsds.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -/// DaiNst.sol -- Dai/Nst Exchanger +/// DaiUsds.sol -- Dai/Usds Exchanger // Copyright (C) 2023 Dai Foundation // @@ -29,8 +29,8 @@ interface DaiJoinLike is JoinLike { function dai() external view returns (address); } -interface NstJoinLike is JoinLike { - function nst() external view returns (address); +interface UsdsJoinLike is JoinLike { + function usds() external view returns (address); } interface GemLike { @@ -42,43 +42,43 @@ interface VatLike { function hope(address) external; } -contract DaiNst { - DaiJoinLike public immutable daiJoin; - NstJoinLike public immutable nstJoin; - GemLike public immutable dai; - GemLike public immutable nst; +contract DaiUsds { + DaiJoinLike public immutable daiJoin; + UsdsJoinLike public immutable usdsJoin; + GemLike public immutable dai; + GemLike public immutable usds; - event DaiToNst(address indexed caller, address indexed usr, uint256 wad); - event NstToDai(address indexed caller, address indexed usr, uint256 wad); + event DaiToUsds(address indexed caller, address indexed usr, uint256 wad); + event UsdsToDai(address indexed caller, address indexed usr, uint256 wad); - constructor(address daiJoin_, address nstJoin_) { - daiJoin = DaiJoinLike(daiJoin_); - nstJoin = NstJoinLike(nstJoin_); + constructor(address daiJoin_, address usdsJoin_) { + daiJoin = DaiJoinLike(daiJoin_); + usdsJoin = UsdsJoinLike(usdsJoin_); address vat = daiJoin.vat(); - require(vat == nstJoin.vat(), "DaiNst/vat-not-same"); + require(vat == usdsJoin.vat(), "DaiUsds/vat-not-same"); - dai = GemLike(daiJoin.dai()); - nst = GemLike(nstJoin.nst()); + dai = GemLike(daiJoin.dai()); + usds = GemLike(usdsJoin.usds()); dai.approve(address(daiJoin), type(uint256).max); - nst.approve(address(nstJoin), type(uint256).max); + usds.approve(address(usdsJoin), type(uint256).max); VatLike(vat).hope(address(daiJoin)); - VatLike(vat).hope(address(nstJoin)); + VatLike(vat).hope(address(usdsJoin)); } - function daiToNst(address usr, uint256 wad) external { + function daiToUsds(address usr, uint256 wad) external { dai.transferFrom(msg.sender, address(this), wad); daiJoin.join(address(this), wad); - nstJoin.exit(usr, wad); - emit DaiToNst(msg.sender, usr, wad); + usdsJoin.exit(usr, wad); + emit DaiToUsds(msg.sender, usr, wad); } - function nstToDai(address usr, uint256 wad) external { - nst.transferFrom(msg.sender, address(this), wad); - nstJoin.join(address(this), wad); + function usdsToDai(address usr, uint256 wad) external { + usds.transferFrom(msg.sender, address(this), wad); + usdsJoin.join(address(this), wad); daiJoin.exit(usr, wad); - emit NstToDai(msg.sender, usr, wad); + emit UsdsToDai(msg.sender, usr, wad); } } diff --git a/src/Nst.sol b/src/Usds.sol similarity index 88% rename from src/Nst.sol rename to src/Usds.sol index c7a4253..a10c8da 100644 --- a/src/Nst.sol +++ b/src/Usds.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -/// Nst.sol -- Nst token +/// Usds.sol -- Usds token // Copyright (C) 2017, 2018, 2019 dbrock, rain, mrchico // Copyright (C) 2023 Dai Foundation @@ -29,12 +29,12 @@ interface IERC1271 { ) external view returns (bytes4); } -contract Nst is UUPSUpgradeable { +contract Usds is UUPSUpgradeable { mapping (address => uint256) public wards; // --- ERC20 Data --- - string public constant name = "Nst Stablecoin"; - string public constant symbol = "NST"; + string public constant name = "Sky USD"; + string public constant symbol = "USDS"; string public constant version = "1"; uint8 public constant decimals = 18; uint256 public totalSupply; @@ -53,7 +53,7 @@ contract Nst is UUPSUpgradeable { bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); modifier auth { - require(wards[msg.sender] == 1, "Nst/not-authorized"); + require(wards[msg.sender] == 1, "Usds/not-authorized"); _; } @@ -105,9 +105,9 @@ contract Nst is UUPSUpgradeable { // --- ERC20 Mutations --- function transfer(address to, uint256 value) external returns (bool) { - require(to != address(0) && to != address(this), "Nst/invalid-address"); + require(to != address(0) && to != address(this), "Usds/invalid-address"); uint256 balance = balanceOf[msg.sender]; - require(balance >= value, "Nst/insufficient-balance"); + require(balance >= value, "Usds/insufficient-balance"); unchecked { balanceOf[msg.sender] = balance - value; @@ -120,14 +120,14 @@ contract Nst is UUPSUpgradeable { } function transferFrom(address from, address to, uint256 value) external returns (bool) { - require(to != address(0) && to != address(this), "Nst/invalid-address"); + require(to != address(0) && to != address(this), "Usds/invalid-address"); uint256 balance = balanceOf[from]; - require(balance >= value, "Nst/insufficient-balance"); + require(balance >= value, "Usds/insufficient-balance"); if (from != msg.sender) { uint256 allowed = allowance[from][msg.sender]; if (allowed != type(uint256).max) { - require(allowed >= value, "Nst/insufficient-allowance"); + require(allowed >= value, "Usds/insufficient-allowance"); unchecked { allowance[from][msg.sender] = allowed - value; @@ -155,7 +155,7 @@ contract Nst is UUPSUpgradeable { // --- Mint/Burn --- function mint(address to, uint256 value) external auth { - require(to != address(0) && to != address(this), "Nst/invalid-address"); + require(to != address(0) && to != address(this), "Usds/invalid-address"); unchecked { balanceOf[to] = balanceOf[to] + value; // note: we don't need an overflow check here b/c balanceOf[to] <= totalSupply and there is an overflow check below } @@ -166,12 +166,12 @@ contract Nst is UUPSUpgradeable { function burn(address from, uint256 value) external { uint256 balance = balanceOf[from]; - require(balance >= value, "Nst/insufficient-balance"); + require(balance >= value, "Usds/insufficient-balance"); if (from != msg.sender) { uint256 allowed = allowance[from][msg.sender]; if (allowed != type(uint256).max) { - require(allowed >= value, "Nst/insufficient-allowance"); + require(allowed >= value, "Usds/insufficient-allowance"); unchecked { allowance[from][msg.sender] = allowed - value; @@ -224,8 +224,8 @@ contract Nst is UUPSUpgradeable { uint256 deadline, bytes memory signature ) public { - require(block.timestamp <= deadline, "Nst/permit-expired"); - require(owner != address(0), "Nst/invalid-owner"); + require(block.timestamp <= deadline, "Usds/permit-expired"); + require(owner != address(0), "Usds/invalid-owner"); uint256 nonce; unchecked { nonce = nonces[owner]++; } @@ -244,7 +244,7 @@ contract Nst is UUPSUpgradeable { )) )); - require(_isValidSignature(owner, digest, signature), "Nst/invalid-permit"); + require(_isValidSignature(owner, digest, signature), "Usds/invalid-permit"); allowance[owner][spender] = value; emit Approval(owner, spender, value); diff --git a/src/NstJoin.sol b/src/UsdsJoin.sol similarity index 80% rename from src/NstJoin.sol rename to src/UsdsJoin.sol index edc52f7..da1555f 100644 --- a/src/NstJoin.sol +++ b/src/UsdsJoin.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -/// NstJoin.sol -- Nst adapter +/// UsdsJoin.sol -- Usds adapter // Copyright (C) 2018 Rain // Copyright (C) 2023 Dai Foundation @@ -20,7 +20,7 @@ pragma solidity ^0.8.21; -interface NstLike { +interface UsdsLike { function burn(address,uint256) external; function mint(address,uint256) external; } @@ -29,9 +29,9 @@ interface VatLike { function move(address,address,uint256) external; } -contract NstJoin { - VatLike public immutable vat; // CDP Engine - NstLike public immutable nst; // Stablecoin Token +contract UsdsJoin { + VatLike public immutable vat; // CDP Engine + UsdsLike public immutable usds; // Stablecoin Token uint256 constant RAY = 10 ** 27; @@ -39,25 +39,25 @@ contract NstJoin { event Join(address indexed caller, address indexed usr, uint256 wad); event Exit(address indexed caller, address indexed usr, uint256 wad); - constructor(address vat_, address nst_) { - vat = VatLike(vat_); - nst = NstLike(nst_); + constructor(address vat_, address usds_) { + vat = VatLike(vat_); + usds = UsdsLike(usds_); } function join(address usr, uint256 wad) external { vat.move(address(this), usr, RAY * wad); - nst.burn(msg.sender, wad); + usds.burn(msg.sender, wad); emit Join(msg.sender, usr, wad); } function exit(address usr, uint256 wad) external { vat.move(msg.sender, address(this), RAY * wad); - nst.mint(usr, wad); + usds.mint(usr, wad); emit Exit(msg.sender, usr, wad); } // To fully cover daiJoin abi function dai() external view returns (address) { - return address(nst); + return address(usds); } } diff --git a/test/DaiNst.t.sol b/test/DaiNst.t.sol deleted file mode 100644 index a1e651d..0000000 --- a/test/DaiNst.t.sol +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later - -pragma solidity ^0.8.21; - -import "dss-test/DssTest.sol"; -import "dss-interfaces/Interfaces.sol"; -import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; - -import { Nst } from "src/Nst.sol"; -import { NstJoin } from "src/NstJoin.sol"; -import { DaiNst } from "src/DaiNst.sol"; - -contract DaiNstTest is DssTest { - ChainlogAbstract constant chainLog = ChainlogAbstract(0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F); - - VatAbstract vat; - DaiAbstract dai; - DaiJoinAbstract daiJoin; - Nst nst; - NstJoin nstJoin; - DaiNst daiNst; - - event DaiToNst(address indexed caller, address indexed usr, uint256 wad); - event NstToDai(address indexed caller, address indexed usr, uint256 wad); - - function setUp() public { - vm.createSelectFork(vm.envString("ETH_RPC_URL")); - - vat = VatAbstract(chainLog.getAddress("MCD_VAT")); - dai = DaiAbstract(chainLog.getAddress("MCD_DAI")); - daiJoin = DaiJoinAbstract(chainLog.getAddress("MCD_JOIN_DAI")); - address pauseProxy = chainLog.getAddress("MCD_PAUSE_PROXY"); - nst = Nst(address(new ERC1967Proxy(address(new Nst()), abi.encodeCall(Nst.initialize, ())))); - nstJoin = new NstJoin(address(vat), address(nst)); - nst.rely(address(nstJoin)); - nst.deny(address(this)); - - daiNst = new DaiNst(address(daiJoin), address(nstJoin)); - - vm.prank(pauseProxy); vat.suck(address(this), address(this), 10_000 * RAD); - } - - function testExchange() public { - uint256 daiSup = dai.totalSupply(); - vat.hope(address(daiJoin)); - daiJoin.exit(address(this), 10_000 * WAD); - assertEq(dai.balanceOf(address(this)), 10_000 * WAD); - assertEq(dai.totalSupply() - daiSup, 10_000 * WAD); - assertEq(nst.balanceOf(address(this)), 0); - assertEq(nst.totalSupply(), 0); - - dai.approve(address(daiNst), 4_000 * WAD); - vm.expectEmit(true, true, true, true); - emit DaiToNst(address(this), address(this), 4_000 * WAD); - daiNst.daiToNst(address(this), 4_000 * WAD); - assertEq(dai.balanceOf(address(this)), 6_000 * WAD); - assertEq(dai.totalSupply() - daiSup, 6_000 * WAD); - assertEq(nst.balanceOf(address(this)), 4_000 * WAD); - assertEq(nst.totalSupply(), 4_000 * WAD); - - nst.approve(address(daiNst), 2_000 * WAD); - vm.expectEmit(true, true, true, true); - emit NstToDai(address(this), address(this), 2_000 * WAD); - daiNst.nstToDai(address(this), 2_000 * WAD); - assertEq(dai.balanceOf(address(this)), 8_000 * WAD); - assertEq(dai.totalSupply() - daiSup, 8_000 * WAD); - assertEq(nst.balanceOf(address(this)), 2_000 * WAD); - assertEq(nst.totalSupply(), 2_000 * WAD); - - address receiver = address(123); - assertEq(dai.balanceOf(receiver), 0); - assertEq(nst.balanceOf(receiver), 0); - - dai.approve(address(daiNst), 1_500 * WAD); - vm.expectEmit(true, true, true, true); - emit DaiToNst(address(this), receiver, 1_500 * WAD); - daiNst.daiToNst(receiver, 1_500 * WAD); - assertEq(dai.balanceOf(address(this)), 6_500 * WAD); - assertEq(dai.balanceOf(receiver), 0); - assertEq(dai.totalSupply() - daiSup, 6_500 * WAD); - assertEq(nst.balanceOf(address(this)), 2_000 * WAD); - assertEq(nst.balanceOf(receiver), 1_500 * WAD); - assertEq(nst.totalSupply(), 3_500 * WAD); - - nst.approve(address(daiNst), 500 * WAD); - vm.expectEmit(true, true, true, true); - emit NstToDai(address(this), receiver, 500 * WAD); - daiNst.nstToDai(receiver, 500 * WAD); - assertEq(dai.balanceOf(address(this)), 6_500 * WAD); - assertEq(dai.balanceOf(receiver), 500 * WAD); - assertEq(dai.totalSupply() - daiSup, 7_000 * WAD); - assertEq(nst.balanceOf(address(this)), 1_500 * WAD); - assertEq(nst.balanceOf(receiver), 1_500 * WAD); - assertEq(nst.totalSupply(), 3_000 * WAD); - } -} diff --git a/test/DaiUsds.t.sol b/test/DaiUsds.t.sol new file mode 100644 index 0000000..fabd166 --- /dev/null +++ b/test/DaiUsds.t.sol @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pragma solidity ^0.8.21; + +import "dss-test/DssTest.sol"; +import "dss-interfaces/Interfaces.sol"; +import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; + +import { Usds } from "src/Usds.sol"; +import { UsdsJoin } from "src/UsdsJoin.sol"; +import { DaiUsds } from "src/DaiUsds.sol"; + +contract DaiUsdsTest is DssTest { + ChainlogAbstract constant chainLog = ChainlogAbstract(0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F); + + VatAbstract vat; + DaiAbstract dai; + DaiJoinAbstract daiJoin; + Usds usds; + UsdsJoin usdsJoin; + DaiUsds daiUsds; + + event DaiToUsds(address indexed caller, address indexed usr, uint256 wad); + event UsdsToDai(address indexed caller, address indexed usr, uint256 wad); + + function setUp() public { + vm.createSelectFork(vm.envString("ETH_RPC_URL")); + + vat = VatAbstract(chainLog.getAddress("MCD_VAT")); + dai = DaiAbstract(chainLog.getAddress("MCD_DAI")); + daiJoin = DaiJoinAbstract(chainLog.getAddress("MCD_JOIN_DAI")); + address pauseProxy = chainLog.getAddress("MCD_PAUSE_PROXY"); + usds = Usds(address(new ERC1967Proxy(address(new Usds()), abi.encodeCall(Usds.initialize, ())))); + usdsJoin = new UsdsJoin(address(vat), address(usds)); + usds.rely(address(usdsJoin)); + usds.deny(address(this)); + + daiUsds = new DaiUsds(address(daiJoin), address(usdsJoin)); + + vm.prank(pauseProxy); vat.suck(address(this), address(this), 10_000 * RAD); + } + + function testExchange() public { + uint256 daiSup = dai.totalSupply(); + vat.hope(address(daiJoin)); + daiJoin.exit(address(this), 10_000 * WAD); + assertEq(dai.balanceOf(address(this)), 10_000 * WAD); + assertEq(dai.totalSupply() - daiSup, 10_000 * WAD); + assertEq(usds.balanceOf(address(this)), 0); + assertEq(usds.totalSupply(), 0); + + dai.approve(address(daiUsds), 4_000 * WAD); + vm.expectEmit(true, true, true, true); + emit DaiToUsds(address(this), address(this), 4_000 * WAD); + daiUsds.daiToUsds(address(this), 4_000 * WAD); + assertEq(dai.balanceOf(address(this)), 6_000 * WAD); + assertEq(dai.totalSupply() - daiSup, 6_000 * WAD); + assertEq(usds.balanceOf(address(this)), 4_000 * WAD); + assertEq(usds.totalSupply(), 4_000 * WAD); + + usds.approve(address(daiUsds), 2_000 * WAD); + vm.expectEmit(true, true, true, true); + emit UsdsToDai(address(this), address(this), 2_000 * WAD); + daiUsds.usdsToDai(address(this), 2_000 * WAD); + assertEq(dai.balanceOf(address(this)), 8_000 * WAD); + assertEq(dai.totalSupply() - daiSup, 8_000 * WAD); + assertEq(usds.balanceOf(address(this)), 2_000 * WAD); + assertEq(usds.totalSupply(), 2_000 * WAD); + + address receiver = address(123); + assertEq(dai.balanceOf(receiver), 0); + assertEq(usds.balanceOf(receiver), 0); + + dai.approve(address(daiUsds), 1_500 * WAD); + vm.expectEmit(true, true, true, true); + emit DaiToUsds(address(this), receiver, 1_500 * WAD); + daiUsds.daiToUsds(receiver, 1_500 * WAD); + assertEq(dai.balanceOf(address(this)), 6_500 * WAD); + assertEq(dai.balanceOf(receiver), 0); + assertEq(dai.totalSupply() - daiSup, 6_500 * WAD); + assertEq(usds.balanceOf(address(this)), 2_000 * WAD); + assertEq(usds.balanceOf(receiver), 1_500 * WAD); + assertEq(usds.totalSupply(), 3_500 * WAD); + + usds.approve(address(daiUsds), 500 * WAD); + vm.expectEmit(true, true, true, true); + emit UsdsToDai(address(this), receiver, 500 * WAD); + daiUsds.usdsToDai(receiver, 500 * WAD); + assertEq(dai.balanceOf(address(this)), 6_500 * WAD); + assertEq(dai.balanceOf(receiver), 500 * WAD); + assertEq(dai.totalSupply() - daiSup, 7_000 * WAD); + assertEq(usds.balanceOf(address(this)), 1_500 * WAD); + assertEq(usds.balanceOf(receiver), 1_500 * WAD); + assertEq(usds.totalSupply(), 3_000 * WAD); + } +} diff --git a/test/Deployment.t.sol b/test/Deployment.t.sol index 29a89b7..641befb 100644 --- a/test/Deployment.t.sol +++ b/test/Deployment.t.sol @@ -21,12 +21,12 @@ import "dss-interfaces/Interfaces.sol"; import { Upgrades } from "openzeppelin-foundry-upgrades/Upgrades.sol"; -import { NstInstance } from "deploy/NstInstance.sol"; -import { NstDeploy } from "deploy/NstDeploy.sol"; -import { NstInit } from "deploy/NstInit.sol"; +import { UsdsInstance } from "deploy/UsdsInstance.sol"; +import { UsdsDeploy } from "deploy/UsdsDeploy.sol"; +import { UsdsInit } from "deploy/UsdsInit.sol"; -import { Nst } from "src/Nst.sol"; -import { DaiNst } from "src/DaiNst.sol"; +import { Usds } from "src/Usds.sol"; +import { DaiUsds } from "src/DaiUsds.sol"; contract DeploymentTest is DssTest { ChainlogAbstract constant chainLog = ChainlogAbstract(0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F); @@ -35,7 +35,7 @@ contract DeploymentTest is DssTest { DaiAbstract dai; address daiJoin; - NstInstance inst; + UsdsInstance inst; function setUp() public { vm.createSelectFork(vm.envString("ETH_RPC_URL")); @@ -44,43 +44,43 @@ contract DeploymentTest is DssTest { daiJoin = chainLog.getAddress("MCD_JOIN_DAI"); dai = DaiAbstract(chainLog.getAddress("MCD_DAI")); - inst = NstDeploy.deploy(address(this), pauseProxy, daiJoin); + inst = UsdsDeploy.deploy(address(this), pauseProxy, daiJoin); } function testSetUp() public { DssInstance memory dss = MCD.loadFromChainlog(address(chainLog)); - assertEq(Nst(inst.nst).wards(pauseProxy), 1); - assertEq(Nst(inst.nst).wards(inst.nstJoin), 0); - assertEq(Upgrades.getImplementationAddress(inst.nst), inst.nstImp); + assertEq(Usds(inst.usds).wards(pauseProxy), 1); + assertEq(Usds(inst.usds).wards(inst.usdsJoin), 0); + assertEq(Upgrades.getImplementationAddress(inst.usds), inst.usdsImp); vm.startPrank(pauseProxy); - NstInit.init(dss, inst); + UsdsInit.init(dss, inst); vm.stopPrank(); - assertEq(Nst(inst.nst).wards(pauseProxy), 1); - assertEq(Nst(inst.nst).wards(inst.nstJoin), 1); + assertEq(Usds(inst.usds).wards(pauseProxy), 1); + assertEq(Usds(inst.usds).wards(inst.usdsJoin), 1); deal(address(dai), address(this), 1000); assertEq(dai.balanceOf(address(this)), 1000); - assertEq(Nst(inst.nst).balanceOf(address(this)), 0); + assertEq(Usds(inst.usds).balanceOf(address(this)), 0); - dai.approve(inst.daiNst, 600); - DaiNst(inst.daiNst).daiToNst(address(this), 600); + dai.approve(inst.daiUsds, 600); + DaiUsds(inst.daiUsds).daiToUsds(address(this), 600); assertEq(dai.balanceOf(address(this)), 400); - assertEq(Nst(inst.nst).balanceOf(address(this)), 600); + assertEq(Usds(inst.usds).balanceOf(address(this)), 600); - Nst(inst.nst).approve(inst.daiNst, 400); - DaiNst(inst.daiNst).nstToDai(address(this), 400); + Usds(inst.usds).approve(inst.daiUsds, 400); + DaiUsds(inst.daiUsds).usdsToDai(address(this), 400); assertEq(dai.balanceOf(address(this)), 800); - assertEq(Nst(inst.nst).balanceOf(address(this)), 200); + assertEq(Usds(inst.usds).balanceOf(address(this)), 200); - assertEq(chainLog.getAddress("NST"), inst.nst); - assertEq(chainLog.getAddress("NST_IMP"), inst.nstImp); - assertEq(chainLog.getAddress("NST_JOIN"), inst.nstJoin); - assertEq(chainLog.getAddress("DAI_NST"), inst.daiNst); + assertEq(chainLog.getAddress("USDS"), inst.usds); + assertEq(chainLog.getAddress("USDS_IMP"), inst.usdsImp); + assertEq(chainLog.getAddress("USDS_JOIN"), inst.usdsJoin); + assertEq(chainLog.getAddress("DAI_USDS"), inst.daiUsds); } } diff --git a/test/Nst.t.sol b/test/Usds.t.sol similarity index 57% rename from test/Nst.t.sol rename to test/Usds.t.sol index db7c796..4b02360 100644 --- a/test/Nst.t.sol +++ b/test/Usds.t.sol @@ -6,9 +6,9 @@ import "token-tests/TokenFuzzTests.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import { Upgrades, Options } from "openzeppelin-foundry-upgrades/Upgrades.sol"; -import { Nst, UUPSUpgradeable, Initializable, ERC1967Utils } from "src/Nst.sol"; +import { Usds, UUPSUpgradeable, Initializable, ERC1967Utils } from "src/Usds.sol"; -contract Nst2 is UUPSUpgradeable { +contract Usds2 is UUPSUpgradeable { mapping (address => uint256) public wards; string public constant version = "2"; @@ -20,7 +20,7 @@ contract Nst2 is UUPSUpgradeable { event UpgradedTo(string version); modifier auth { - require(wards[msg.sender] == 1, "Nst/not-authorized"); + require(wards[msg.sender] == 1, "Usds/not-authorized"); _; } @@ -39,8 +39,8 @@ contract Nst2 is UUPSUpgradeable { } } -contract NstTest is TokenFuzzTests { - Nst nst; +contract UsdsTest is TokenFuzzTests { + Usds usds; bool validate; event UpgradedTo(string version); @@ -48,25 +48,25 @@ contract NstTest is TokenFuzzTests { function setUp() public { validate = vm.envOr("VALIDATE", false); - address imp = address(new Nst()); + address imp = address(new Usds()); vm.expectEmit(true, true, true, true); emit Rely(address(this)); - nst = Nst(address(new ERC1967Proxy(imp, abi.encodeCall(Nst.initialize, ())))); - assertEq(nst.version(), "1"); - assertEq(nst.wards(address(this)), 1); - assertEq(nst.getImplementation(), imp); - - _token_ = address(nst); - _contractName_ = "Nst"; - _tokenName_ ="Nst Stablecoin"; - _symbol_ = "NST"; + usds = Usds(address(new ERC1967Proxy(imp, abi.encodeCall(Usds.initialize, ())))); + assertEq(usds.version(), "1"); + assertEq(usds.wards(address(this)), 1); + assertEq(usds.getImplementation(), imp); + + _token_ = address(usds); + _contractName_ = "Usds"; + _tokenName_ ="Sky USD"; + _symbol_ = "USDS"; } function invariantMetadata() public view { - assertEq(nst.name(), "Nst Stablecoin"); - assertEq(nst.symbol(), "NST"); - assertEq(nst.version(), "1"); - assertEq(nst.decimals(), 18); + assertEq(usds.name(), "Sky USD"); + assertEq(usds.symbol(), "USDS"); + assertEq(usds.version(), "1"); + assertEq(usds.decimals(), 18); } function testDeployWithUpgradesLib() public { @@ -80,69 +80,69 @@ contract NstTest is TokenFuzzTests { vm.expectEmit(true, true, true, true); emit Rely(address(this)); address proxy = Upgrades.deployUUPSProxy( - "out/Nst.sol/Nst.json", - abi.encodeCall(Nst.initialize, ()), + "out/Usds.sol/Usds.json", + abi.encodeCall(Usds.initialize, ()), opts ); - assertEq(Nst(proxy).version(), "1"); - assertEq(Nst(proxy).wards(address(this)), 1); + assertEq(Usds(proxy).version(), "1"); + assertEq(Usds(proxy).wards(address(this)), 1); } function testUpgrade() public { - address implementation1 = nst.getImplementation(); + address implementation1 = usds.getImplementation(); - address newImpl = address(new Nst2()); + address newImpl = address(new Usds2()); vm.expectEmit(true, true, true, true); emit UpgradedTo("2"); - nst.upgradeToAndCall(newImpl, abi.encodeCall(Nst2.reinitialize, ())); + usds.upgradeToAndCall(newImpl, abi.encodeCall(Usds2.reinitialize, ())); - address implementation2 = nst.getImplementation(); + address implementation2 = usds.getImplementation(); assertEq(implementation2, newImpl); assertTrue(implementation2 != implementation1); - assertEq(nst.version(), "2"); - assertEq(nst.wards(address(this)), 1); // still a ward + assertEq(usds.version(), "2"); + assertEq(usds.wards(address(this)), 1); // still a ward } function testUpgradeWithUpgradesLib() public { - address implementation1 = nst.getImplementation(); + address implementation1 = usds.getImplementation(); Options memory opts; if (!validate) { opts.unsafeSkipAllChecks = true; } else { - opts.referenceContract = "out/Nst.sol/Nst.json"; + opts.referenceContract = "out/Usds.sol/Usds.json"; opts.unsafeAllow = 'constructor'; } vm.expectEmit(true, true, true, true); emit UpgradedTo("2"); Upgrades.upgradeProxy( - address(nst), - "out/Nst.t.sol/Nst2.json", - abi.encodeCall(Nst2.reinitialize, ()), + address(usds), + "out/Usds.t.sol/Usds2.json", + abi.encodeCall(Usds2.reinitialize, ()), opts ); - address implementation2 = nst.getImplementation(); + address implementation2 = usds.getImplementation(); assertTrue(implementation1 != implementation2); - assertEq(nst.version(), "2"); - assertEq(nst.wards(address(this)), 1); // still a ward + assertEq(usds.version(), "2"); + assertEq(usds.wards(address(this)), 1); // still a ward } function testUpgradeUnauthed() public { - address newImpl = address(new Nst2()); - vm.expectRevert("Nst/not-authorized"); - vm.prank(address(0x123)); nst.upgradeToAndCall(newImpl, abi.encodeCall(Nst2.reinitialize, ())); + address newImpl = address(new Usds2()); + vm.expectRevert("Usds/not-authorized"); + vm.prank(address(0x123)); usds.upgradeToAndCall(newImpl, abi.encodeCall(Usds2.reinitialize, ())); } function testInitializeAgain() public { vm.expectRevert(Initializable.InvalidInitialization.selector); - nst.initialize(); + usds.initialize(); } function testInitializeDirectly() public { - address implementation = nst.getImplementation(); + address implementation = usds.getImplementation(); vm.expectRevert(Initializable.InvalidInitialization.selector); - Nst(implementation).initialize(); + Usds(implementation).initialize(); } } diff --git a/test/NstJoin.t.sol b/test/UsdsJoin.t.sol similarity index 61% rename from test/NstJoin.t.sol rename to test/UsdsJoin.t.sol index f9e492d..d195872 100644 --- a/test/NstJoin.t.sol +++ b/test/UsdsJoin.t.sol @@ -6,16 +6,16 @@ import "dss-test/DssTest.sol"; import "dss-interfaces/Interfaces.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import { Nst } from "src/Nst.sol"; +import { Usds } from "src/Usds.sol"; -import { NstJoin } from "src/NstJoin.sol"; +import { UsdsJoin } from "src/UsdsJoin.sol"; -contract NstJoinTest is DssTest { +contract UsdsJoinTest is DssTest { ChainlogAbstract constant chainLog = ChainlogAbstract(0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F); VatAbstract vat; - Nst nst; - NstJoin nstJoin; + Usds usds; + UsdsJoin usdsJoin; event Join(address indexed caller, address indexed usr, uint256 wad); event Exit(address indexed caller, address indexed usr, uint256 wad); @@ -25,32 +25,32 @@ contract NstJoinTest is DssTest { vat = VatAbstract(chainLog.getAddress("MCD_VAT")); address pauseProxy = chainLog.getAddress("MCD_PAUSE_PROXY"); - nst = Nst(address(new ERC1967Proxy(address(new Nst()), abi.encodeCall(Nst.initialize, ())))); - nstJoin = new NstJoin(address(vat), address(nst)); - assertEq(nstJoin.dai(), address(nstJoin.nst())); - nst.rely(address(nstJoin)); - nst.deny(address(this)); + usds = Usds(address(new ERC1967Proxy(address(new Usds()), abi.encodeCall(Usds.initialize, ())))); + usdsJoin = new UsdsJoin(address(vat), address(usds)); + assertEq(usdsJoin.dai(), address(usdsJoin.usds())); + usds.rely(address(usdsJoin)); + usds.deny(address(this)); vm.prank(pauseProxy); vat.suck(address(this), address(this), 10_000 * RAD); } function testJoinExit() public { address receiver = address(123); - assertEq(nst.balanceOf(receiver), 0); + assertEq(usds.balanceOf(receiver), 0); assertEq(vat.dai(address(this)), 10_000 * RAD); vm.expectRevert("Vat/not-allowed"); - nstJoin.exit(receiver, 4_000 * WAD); - vat.hope(address(nstJoin)); + usdsJoin.exit(receiver, 4_000 * WAD); + vat.hope(address(usdsJoin)); vm.expectEmit(true, true, true, true); emit Exit(address(this), receiver, 4_000 * WAD); - nstJoin.exit(receiver, 4_000 * WAD); - assertEq(nst.balanceOf(receiver), 4_000 * WAD); + usdsJoin.exit(receiver, 4_000 * WAD); + assertEq(usds.balanceOf(receiver), 4_000 * WAD); assertEq(vat.dai(address(this)), 6_000 * RAD); vm.startPrank(receiver); - nst.approve(address(nstJoin), 2_000 * WAD); + usds.approve(address(usdsJoin), 2_000 * WAD); vm.expectEmit(true, true, true, true); emit Join(receiver, address(this), 2_000 * WAD); - nstJoin.join(address(this), 2_000 * WAD); - assertEq(nst.balanceOf(receiver), 2_000 * WAD); + usdsJoin.join(address(this), 2_000 * WAD); + assertEq(usds.balanceOf(receiver), 2_000 * WAD); assertEq(vat.dai(address(this)), 8_000 * RAD); } } From 7a494ab58dd01582518b9713a5c3f7620535c0c1 Mon Sep 17 00:00:00 2001 From: sunbreak1211 Date: Tue, 27 Aug 2024 11:15:52 -0300 Subject: [PATCH 2/7] Add missing recursive for Certora CI file --- .github/workflows/certora.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/certora.yml b/.github/workflows/certora.yml index 4e94a17..3338ea8 100644 --- a/.github/workflows/certora.yml +++ b/.github/workflows/certora.yml @@ -17,6 +17,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 + with: + submodules: recursive - uses: actions/setup-java@v2 with: From 52466ba72a61f1c67351b9e7f6284a8add78a8c7 Mon Sep 17 00:00:00 2001 From: sunbreak1211 <129470872+sunbreak1211@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:16:55 -0300 Subject: [PATCH 3/7] Add missing spacing Co-authored-by: telome <130504305+telome@users.noreply.github.com> --- test/DaiUsds.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/DaiUsds.t.sol b/test/DaiUsds.t.sol index fabd166..4b1c887 100644 --- a/test/DaiUsds.t.sol +++ b/test/DaiUsds.t.sol @@ -76,7 +76,7 @@ contract DaiUsdsTest is DssTest { emit DaiToUsds(address(this), receiver, 1_500 * WAD); daiUsds.daiToUsds(receiver, 1_500 * WAD); assertEq(dai.balanceOf(address(this)), 6_500 * WAD); - assertEq(dai.balanceOf(receiver), 0); + assertEq(dai.balanceOf(receiver), 0); assertEq(dai.totalSupply() - daiSup, 6_500 * WAD); assertEq(usds.balanceOf(address(this)), 2_000 * WAD); assertEq(usds.balanceOf(receiver), 1_500 * WAD); From fadac09f91799560f94d8151dfeb1a7d6fe7ac08 Mon Sep 17 00:00:00 2001 From: sunbreak1211 <129470872+sunbreak1211@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:18:06 -0300 Subject: [PATCH 4/7] Add another spacing Co-authored-by: telome <130504305+telome@users.noreply.github.com> --- test/Usds.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Usds.t.sol b/test/Usds.t.sol index 4b02360..3bc7655 100644 --- a/test/Usds.t.sol +++ b/test/Usds.t.sol @@ -58,7 +58,7 @@ contract UsdsTest is TokenFuzzTests { _token_ = address(usds); _contractName_ = "Usds"; - _tokenName_ ="Sky USD"; + _tokenName_ = "Sky USD"; _symbol_ = "USDS"; } From eb1994fb51021b2cac35120a3648137d849dde5d Mon Sep 17 00:00:00 2001 From: sunbreak1211 <129470872+sunbreak1211@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:21:13 -0300 Subject: [PATCH 5/7] More spacing fixes Co-authored-by: telome <130504305+telome@users.noreply.github.com> --- test/UsdsJoin.t.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/UsdsJoin.t.sol b/test/UsdsJoin.t.sol index d195872..026c94b 100644 --- a/test/UsdsJoin.t.sol +++ b/test/UsdsJoin.t.sol @@ -14,8 +14,8 @@ contract UsdsJoinTest is DssTest { ChainlogAbstract constant chainLog = ChainlogAbstract(0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F); VatAbstract vat; - Usds usds; - UsdsJoin usdsJoin; + Usds usds; + UsdsJoin usdsJoin; event Join(address indexed caller, address indexed usr, uint256 wad); event Exit(address indexed caller, address indexed usr, uint256 wad); From f4484a51a70e4d8fe84993ada0331d093803e6a8 Mon Sep 17 00:00:00 2001 From: sunbreak1211 Date: Tue, 27 Aug 2024 12:28:56 -0300 Subject: [PATCH 6/7] Adjust token name --- src/Usds.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Usds.sol b/src/Usds.sol index a10c8da..d3e4c01 100644 --- a/src/Usds.sol +++ b/src/Usds.sol @@ -33,7 +33,7 @@ contract Usds is UUPSUpgradeable { mapping (address => uint256) public wards; // --- ERC20 Data --- - string public constant name = "Sky USD"; + string public constant name = "USDS Stablecoin"; string public constant symbol = "USDS"; string public constant version = "1"; uint8 public constant decimals = 18; From cfd4ac83eb75a0498bf7efec1ecfd525b666c004 Mon Sep 17 00:00:00 2001 From: sunbreak1211 Date: Tue, 27 Aug 2024 12:48:07 -0300 Subject: [PATCH 7/7] Fix test --- test/Usds.t.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Usds.t.sol b/test/Usds.t.sol index 3bc7655..7dc9e44 100644 --- a/test/Usds.t.sol +++ b/test/Usds.t.sol @@ -58,12 +58,12 @@ contract UsdsTest is TokenFuzzTests { _token_ = address(usds); _contractName_ = "Usds"; - _tokenName_ = "Sky USD"; + _tokenName_ = "USDS Stablecoin"; _symbol_ = "USDS"; } function invariantMetadata() public view { - assertEq(usds.name(), "Sky USD"); + assertEq(usds.name(), "USDS Stablecoin"); assertEq(usds.symbol(), "USDS"); assertEq(usds.version(), "1"); assertEq(usds.decimals(), 18);