Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation and tests #1

Merged
merged 23 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ on:

env:
FOUNDRY_PROFILE: ci
ETH_RPC_URL: ${{ secrets.ETH_RPC_URL }}


jobs:
check:
Expand All @@ -23,7 +25,7 @@ jobs:
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
version: stable

- name: Show Forge version
run: |
Expand Down
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ src = "src"
out = "out"
script = 'script'
libs = ["lib"]
solc = '0.8.26'
solc = '0.8.24'
optimizer = false

fs_permissions = [
Expand Down
50 changes: 50 additions & 0 deletions script/DssBlow2Deploy.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-FileCopyrightText: © 2023 Dai Foundation <www.daifoundation.org>
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pragma solidity 0.8.24;

import {Script} from "forge-std/Script.sol";
import {stdJson} from "forge-std/StdJson.sol";
import {MCD, DssInstance} from "dss-test/MCD.sol";
import {ScriptTools} from "dss-test/ScriptTools.sol";
import {DssBlow2Deploy, DssBlow2DeployParams} from "src/deployment/DssBlow2Deploy.sol";
import {DssBlow2Instance} from "src/deployment/DssBlow2Instance.sol";

contract DssBlow2DeployScript is Script {
using stdJson for string;
using ScriptTools for string;

string constant NAME = "dss-blow-2-deploy";

address constant CHAINLOG = 0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F;
DssInstance dss = MCD.loadFromChainlog(CHAINLOG);
address usdsJoin = dss.chainlog.getAddress("USDS_JOIN");
DssBlow2Instance inst;

function run() external {
vm.startBroadcast();

inst = DssBlow2Deploy.deploy(
DssBlow2DeployParams({daiJoin: address(dss.daiJoin), usdsJoin: usdsJoin, vow: address(dss.vow)})
);

vm.stopBroadcast();

ScriptTools.exportContract(NAME, "dssBlow2Deploy", inst.blow);
ScriptTools.exportContract(NAME, "daiJoin", address(dss.daiJoin));
ScriptTools.exportContract(NAME, "usdsJoin", usdsJoin);
ScriptTools.exportContract(NAME, "vow", address(dss.vow));
}
}
1 change: 1 addition & 0 deletions script/input/1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Script inputs for Mainnet.
1 change: 1 addition & 0 deletions script/output/1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Script outputs for Mainnet.
60 changes: 60 additions & 0 deletions src/DssBlow2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-FileCopyrightText: © 2023 Dai Foundation <www.daifoundation.org>
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pragma solidity 0.8.24;

interface ERC20Like {
function balanceOf(address) external returns (uint256);
function approve(address usr, uint256 wad) external returns (bool);
}

interface JoinLike {
function dai() external view returns (address);
function usds() external view returns (address);
function join(address, uint256) external;
}

contract DssBlow2 {
address public immutable vow;
ERC20Like public immutable dai;
ERC20Like public immutable usds;
JoinLike public immutable daiJoin;
JoinLike public immutable usdsJoin;

event Blow(address indexed token, uint256 amount);

constructor(address daiJoin_, address usdsJoin_, address vow_) {
daiJoin = JoinLike(daiJoin_);
dai = ERC20Like(daiJoin.dai());
usdsJoin = JoinLike(usdsJoin_);
usds = ERC20Like(usdsJoin.usds());
vow = vow_;
dai.approve(daiJoin_, type(uint256).max);
usds.approve(usdsJoin_, type(uint256).max);
}

function blow() public {
uint256 daiBalance = dai.balanceOf(address(this));
if (daiBalance > 0) {
daiJoin.join(vow, daiBalance);
emit Blow(address(dai), daiBalance);
}
uint256 usdsBalance = usds.balanceOf(address(this));
if (usdsBalance > 0) {
usdsJoin.join(vow, usdsBalance);
emit Blow(address(usds), usdsBalance);
}
}
}
123 changes: 123 additions & 0 deletions src/DssBlow2.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// SPDX-FileCopyrightText: © 2025 Dai Foundation <www.daifoundation.org>
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pragma solidity 0.8.24;

import "dss-test/DssTest.sol";
import "./DssBlow2.sol";

contract DssBlow2Test is DssTest {
address constant CHAINLOG = 0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F;

DssInstance dss;
DssBlow2 dssBlow2;

address usds;
address usdsJoin;
address vow;

event Blow(address indexed token, uint256 amount);

function setUp() public {
vm.createSelectFork("mainnet");
// get all the relevant addresses
dss = MCD.loadFromChainlog(CHAINLOG);
usds = dss.chainlog.getAddress("USDS");
usdsJoin = dss.chainlog.getAddress("USDS_JOIN");
vow = address(dss.vow);

dssBlow2 = new DssBlow2(address(dss.daiJoin), usdsJoin, vow);

vm.label(address(dss.dai), "Dai");
vm.label(address(dss.daiJoin), "DaiJoin");
vm.label(usds, "Usds");
vm.label(usdsJoin, "UsdsJoin");
vm.label(address(dss.vow), "Vow");
}

function test_blow() public {
// send dai and usds to DssBlow2
uint256 daiAmount = 10 ether;
uint256 usdsAmount = 5 ether;
deal(address(dss.dai), address(dssBlow2), daiAmount);
deal(usds, address(dssBlow2), usdsAmount);
// store balances before blow()
uint256 vowDaiBalance = dss.vat.dai(vow);
uint256 blowDaiBalance = dss.dai.balanceOf(address(dssBlow2));
uint256 blowUsdsBalance = ERC20Like(usds).balanceOf(address(dssBlow2));
assertEq(blowDaiBalance, daiAmount);
assertEq(blowUsdsBalance, usdsAmount);
// event emission
vm.expectEmit(true, false, false, true);
emit Blow(address(dss.dai), daiAmount);
vm.expectEmit(true, false, false, true);
emit Blow(usds, usdsAmount);
// call blow()
dssBlow2.blow();
// check balances after blow()
blowDaiBalance = dss.dai.balanceOf(address(dssBlow2));
blowUsdsBalance = ERC20Like(usds).balanceOf(address(dssBlow2));
assertEq(blowDaiBalance, 0);
assertEq(blowUsdsBalance, 0);
// the vat dai balance is in rad so we multiply with ray
assertEq(dss.vat.dai(vow), vowDaiBalance + (daiAmount + usdsAmount) * RAY, "blowDaiUsds: vow balance mismatch");
}

function test_blowDai() public {
// send only dai to DssBlow2
uint256 daiAmount = 10 ether;
deal(address(dss.dai), address(dssBlow2), daiAmount);
// store balances before blow()
uint256 vowDaiBalance = dss.vat.dai(vow);
uint256 blowDaiBalance = dss.dai.balanceOf(address(dssBlow2));
uint256 blowUsdsBalance = ERC20Like(usds).balanceOf(address(dssBlow2));
assertEq(blowDaiBalance, daiAmount);
// event emission
vm.expectEmit(true, false, false, true);
emit Blow(address(dss.dai), daiAmount);
// call blow()
dssBlow2.blow();
// check balances after blow()
blowDaiBalance = dss.dai.balanceOf(vow);
blowUsdsBalance = ERC20Like(usds).balanceOf(vow);
assertEq(blowDaiBalance, 0);
assertEq(blowUsdsBalance, 0);
// the vat dai balance is in rad so we multiply with ray
assertEq(dss.vat.dai(vow), vowDaiBalance + daiAmount * RAY, "blowDai: vow balance mismatch");
}

function test_blowUsds() public {
// send only usds to DssBlow2
uint256 usdsAmount = 5 ether;
deal(usds, address(dssBlow2), usdsAmount);
// store balances before blow()
uint256 vowDaiBalance = dss.vat.dai(vow);
uint256 blowDaiBalance = dss.dai.balanceOf(address(dssBlow2));
uint256 blowUsdsBalance = ERC20Like(usds).balanceOf(address(dssBlow2));
assertEq(blowUsdsBalance, usdsAmount);
// event emission
vm.expectEmit(true, false, false, true);
emit Blow(usds, usdsAmount);
// call blow()
dssBlow2.blow();
// check balances after blow()
blowDaiBalance = dss.dai.balanceOf(address(dssBlow2));
blowUsdsBalance = ERC20Like(usds).balanceOf(address(dssBlow2));
assertEq(blowDaiBalance, 0);
assertEq(blowUsdsBalance, 0);
// the vat dai balance is in rad so we multiply with ray
assertEq(dss.vat.dai(vow), vowDaiBalance + usdsAmount * RAY, "blowUsds: vow balance mismatch");
}
}
31 changes: 31 additions & 0 deletions src/deployment/DssBlow2Deploy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: © 2023 Dai Foundation <www.daifoundation.org>
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pragma solidity 0.8.24;

import {DssBlow2} from "src/DssBlow2.sol";
import {DssBlow2Instance} from "./DssBlow2Instance.sol";

struct DssBlow2DeployParams {
address daiJoin;
address usdsJoin;
address vow;
}

library DssBlow2Deploy {
function deploy(DssBlow2DeployParams memory p) internal returns (DssBlow2Instance memory r) {
r.blow = address(new DssBlow2({daiJoin_: p.daiJoin, usdsJoin_: p.usdsJoin, vow_: p.vow}));
}
}
20 changes: 20 additions & 0 deletions src/deployment/DssBlow2Instance.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: © 2023 Dai Foundation <www.daifoundation.org>
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pragma solidity 0.8.24;

struct DssBlow2Instance {
address blow;
}