Transaction Hash:
Block:
20573155 at Aug-20-2024 11:36:47 PM +UTC
Transaction Fee:
0.000169827081105612 ETH
$0.41
Gas Used:
175,308 Gas / 0.968735489 Gwei
Emitted Events:
180 |
DSProxy.0x1cff79cd00000000000000000000000000000000000000000000000000000000( 0x1cff79cd00000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000005d4b2a02c59197eb2cae95a6df9fe27af60459d4, 0x00000000000000000000000096190d1f1c040fd8449250b90778aa360f2e0ec1, 0x0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000004000000000, 000000000000000000000000000000000000000000000000000001041cff79cd, 00000000000000000000000096190d1f1c040fd8449250b90778aa360f2e0ec1, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000084, 389f87ff00000000000000000000000000000000000000000000000000000000, 0000002000000000000000000000000000000000000000000000000000000000, 0000004000000000000000000000000000000000000000000000000d02ab486c, edc000000000000000000000000000005d4b2a02c59197eb2cae95a6df9fe27a, f60459d400000000000000000000000000000000000000000000000000000000 )
|
181 |
Vat.0xf24e23eb00000000000000000000000000000000000000000000000000000000( 0xf24e23eb00000000000000000000000000000000000000000000000000000000, 0x000000000000000000000000a950524441892a31ebddf91d3ceefa04bf454466, 0x000000000000000000000000197e90f9fad81970ba7976f33cbd77088e5d7cf7, 0x0000000000000000000000007d42289346952efba0336b1464ce8441ea8c04b0, 0000000000000000000000000000000000000000000000000000000000000020, 00000000000000000000000000000000000000000000000000000000000000e0, f24e23eb000000000000000000000000a950524441892a31ebddf91d3ceefa04, bf454466000000000000000000000000197e90f9fad81970ba7976f33cbd7708, 8e5d7cf70000000000000000000000007d42289346952efba0336b1464ce8441, ea8c04b000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
|
182 |
Pot.0x9f678cca00000000000000000000000000000000000000000000000000000000( 0x9f678cca00000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000002129f8a9b6c3092a600da82ce859b7a9a69983e4, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000020, 00000000000000000000000000000000000000000000000000000000000000e0, 9f678cca00000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
|
183 |
Vat.0xbb35783b00000000000000000000000000000000000000000000000000000000( 0xbb35783b00000000000000000000000000000000000000000000000000000000, 0x000000000000000000000000197e90f9fad81970ba7976f33cbd77088e5d7cf7, 0x0000000000000000000000002129f8a9b6c3092a600da82ce859b7a9a69983e4, 0x0000000000000000000000002a09f8e27e4da914cb648ed21d25fc61be38e198, 0000000000000000000000000000000000000000000000000000000000000020, 00000000000000000000000000000000000000000000000000000000000000e0, bb35783b000000000000000000000000197e90f9fad81970ba7976f33cbd7708, 8e5d7cf70000000000000000000000002129f8a9b6c3092a600da82ce859b7a9, a69983e40000000000000000000000002a09f8e27e4da914cb648ed21d25fc61, be38e19800000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
|
184 |
Pot.0x7f8661a100000000000000000000000000000000000000000000000000000000( 0x7f8661a100000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000002129f8a9b6c3092a600da82ce859b7a9a69983e4, 0x00000000000000000000000000000000000000000000000bc9b5e5a53317d13c, 0x0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000020, 00000000000000000000000000000000000000000000000000000000000000e0, 7f8661a100000000000000000000000000000000000000000000000bc9b5e5a5, 3317d13c00000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
|
185 |
Vat.0xbb35783b00000000000000000000000000000000000000000000000000000000( 0xbb35783b00000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000002129f8a9b6c3092a600da82ce859b7a9a69983e4, 0x0000000000000000000000009759a6ac90977b93b58547b4a71c78317f391a28, 0x0000000000000000000000002a09f8e27e4da914c7f2160a7076000000000000, 0000000000000000000000000000000000000000000000000000000000000020, 00000000000000000000000000000000000000000000000000000000000000e0, bb35783b0000000000000000000000002129f8a9b6c3092a600da82ce859b7a9, a69983e40000000000000000000000009759a6ac90977b93b58547b4a71c7831, 7f391a280000000000000000000000002a09f8e27e4da914c7f2160a70760000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
|
186 |
Dai.Transfer( src=0x0000000000000000000000000000000000000000, dst=[Sender] 0x5d4b2a02c59197eb2cae95a6df9fe27af60459d4, wad=240000000000000000000 )
|
187 |
DaiJoin.0xef693bed00000000000000000000000000000000000000000000000000000000( 0xef693bed00000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000002129f8a9b6c3092a600da82ce859b7a9a69983e4, 0x0000000000000000000000005d4b2a02c59197eb2cae95a6df9fe27af60459d4, 0x00000000000000000000000000000000000000000000000d02ab486cedc00000, 0000000000000000000000000000000000000000000000000000000000000020, 00000000000000000000000000000000000000000000000000000000000000e0, ef693bed0000000000000000000000005d4b2a02c59197eb2cae95a6df9fe27a, f60459d400000000000000000000000000000000000000000000000d02ab486c, edc0000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
|
188 |
DefisaverLogger.ActionDirectEvent( 0xf28c1e8e1a8c97027796e625e1ed041028c9642e14da6e7ad2c18838a59a2d8c, 0x0000000000000000000000002129f8a9b6c3092a600da82ce859b7a9a69983e4, 0x546cac0cb2a321009ed6c5c61f5b3897545f79ad44b29e31f2f309d76a6aa471, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000040, 00000000000000000000000000000000000000000000000d02ab486cedc00000, 0000000000000000000000005d4b2a02c59197eb2cae95a6df9fe27af60459d4 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x197E90f9...88E5D7cf7 | (Sky: MCD Pot) | ||||
0x35D1b3F3...259A0492B | (Sky: MCD Vat) | ||||
0x4838B106...B0BAD5f97
Miner
| (Titan Builder) | 7.452051343382539331 Eth | 7.452068874182539331 Eth | 0.0000175308 | |
0x5D4b2A02...af60459d4 | (Fake_Phishing440132) |
0.099807154 Eth
Nonce: 1
|
0.099637326918894388 Eth
Nonce: 2
| 0.000169827081105612 | |
0x6B175474...495271d0F |
Execution Trace
DSProxy.execute( _target=0x96190d1f1c040fd8449250b90778Aa360f2e0Ec1, _data=0x389F87FF0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000D02AB486CEDC000000000000000000000000000005D4B2A02C59197EB2CAE95A6DF9FE27AF60459D4 ) => ( response=0000000000000000000000000000000000000000000000000000000000000000 )
McdDsrWithdraw.executeActionDirect( _callData=0x00000000000000000000000000000000000000000000000D02AB486CEDC000000000000000000000000000005D4B2A02C59197EB2CAE95A6DF9FE27AF60459D4 )
-
Pot.STATICCALL( )
Pot.CALL( )
-
Vat.suck( u=0xA950524441892A31ebddF91d3cEEFa04Bf454466, v=0x197E90f9FAD81970bA7976f33CbD77088E5D7cf7, rad=715099230142848734307860458137523523830690874544 )
-
-
Vat.can( 0x2129F8a9b6C3092a600Da82Ce859B7A9a69983E4, 0x9759A6Ac90977b93B58547b4A71c78317f391A28 ) => ( 1 )
DaiJoin.exit( usr=0x5D4b2A02c59197eB2cAe95A6Df9fE27af60459d4, wad=240000000000000000000 )
-
DefisaverLogger.logActionDirectEvent( _logName=McdDsrWithdraw, _data=0x00000000000000000000000000000000000000000000000D02AB486CEDC000000000000000000000000000005D4B2A02C59197EB2CAE95A6DF9FE27AF60459D4 )
-
execute[DSProxy (ln:115)]
read[DSProxy (ln:120)]
write[DSProxy (ln:123)]
execute[DSProxy (ln:126)]
read[DSProxy (ln:120)]
write[DSProxy (ln:123)]
execute[DSProxy (ln:126)]
File 1 of 7: DSProxy
File 2 of 7: Vat
File 3 of 7: Pot
File 4 of 7: Dai
File 5 of 7: DaiJoin
File 6 of 7: DefisaverLogger
File 7 of 7: McdDsrWithdraw
// proxy.sol - execute actions atomically through the proxy's identity // Copyright (C) 2017 DappHub, LLC // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU 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 General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity ^0.4.23; contract DSAuthority { function canCall( address src, address dst, bytes4 sig ) public view returns (bool); } contract DSAuthEvents { event LogSetAuthority (address indexed authority); event LogSetOwner (address indexed owner); } contract DSAuth is DSAuthEvents { DSAuthority public authority; address public owner; constructor() public { owner = msg.sender; emit LogSetOwner(msg.sender); } function setOwner(address owner_) public auth { owner = owner_; emit LogSetOwner(owner); } function setAuthority(DSAuthority authority_) public auth { authority = authority_; emit LogSetAuthority(authority); } modifier auth { require(isAuthorized(msg.sender, msg.sig)); _; } function isAuthorized(address src, bytes4 sig) internal view returns (bool) { if (src == address(this)) { return true; } else if (src == owner) { return true; } else if (authority == DSAuthority(0)) { return false; } else { return authority.canCall(src, this, sig); } } } contract DSNote { event LogNote( bytes4 indexed sig, address indexed guy, bytes32 indexed foo, bytes32 indexed bar, uint wad, bytes fax ) anonymous; modifier note { bytes32 foo; bytes32 bar; assembly { foo := calldataload(4) bar := calldataload(36) } emit LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data); _; } } // DSProxy // Allows code execution using a persistant identity This can be very // useful to execute a sequence of atomic actions. Since the owner of // the proxy can be changed, this allows for dynamic ownership models // i.e. a multisig contract DSProxy is DSAuth, DSNote { DSProxyCache public cache; // global cache for contracts constructor(address _cacheAddr) public { require(setCache(_cacheAddr)); } function() public payable { } // use the proxy to execute calldata _data on contract _code function execute(bytes _code, bytes _data) public payable returns (address target, bytes32 response) { target = cache.read(_code); if (target == 0x0) { // deploy contract & store its address in cache target = cache.write(_code); } response = execute(target, _data); } function execute(address _target, bytes _data) public auth note payable returns (bytes32 response) { require(_target != 0x0); // call contract in current context assembly { let succeeded := delegatecall(sub(gas, 5000), _target, add(_data, 0x20), mload(_data), 0, 32) response := mload(0) // load delegatecall output switch iszero(succeeded) case 1 { // throw if delegatecall failed revert(0, 0) } } } //set new cache function setCache(address _cacheAddr) public auth note returns (bool) { require(_cacheAddr != 0x0); // invalid cache address cache = DSProxyCache(_cacheAddr); // overwrite cache return true; } } // DSProxyFactory // This factory deploys new proxy instances through build() // Deployed proxy addresses are logged contract DSProxyFactory { event Created(address indexed sender, address indexed owner, address proxy, address cache); mapping(address=>bool) public isProxy; DSProxyCache public cache = new DSProxyCache(); // deploys a new proxy instance // sets owner of proxy to caller function build() public returns (DSProxy proxy) { proxy = build(msg.sender); } // deploys a new proxy instance // sets custom owner of proxy function build(address owner) public returns (DSProxy proxy) { proxy = new DSProxy(cache); emit Created(msg.sender, owner, address(proxy), address(cache)); proxy.setOwner(owner); isProxy[proxy] = true; } } // DSProxyCache // This global cache stores addresses of contracts previously deployed // by a proxy. This saves gas from repeat deployment of the same // contracts and eliminates blockchain bloat. // By default, all proxies deployed from the same factory store // contracts in the same cache. The cache a proxy instance uses can be // changed. The cache uses the sha3 hash of a contract's bytecode to // lookup the address contract DSProxyCache { mapping(bytes32 => address) cache; function read(bytes _code) public view returns (address) { bytes32 hash = keccak256(_code); return cache[hash]; } function write(bytes _code) public returns (address target) { assembly { target := create(0, add(_code, 0x20), mload(_code)) switch iszero(extcodesize(target)) case 1 { // throw if contract failed to deploy revert(0, 0) } } bytes32 hash = keccak256(_code); cache[hash] = target; } }
File 2 of 7: Vat
// hevm: flattened sources of /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/vat.sol pragma solidity =0.5.12; ////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/vat.sol /// vat.sol -- Dai CDP database // Copyright (C) 2018 Rain <[email protected]> // // 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.5.12; */ contract Vat { // --- Auth --- mapping (address => uint) public wards; function rely(address usr) external note auth { require(live == 1, "Vat/not-live"); wards[usr] = 1; } function deny(address usr) external note auth { require(live == 1, "Vat/not-live"); wards[usr] = 0; } modifier auth { require(wards[msg.sender] == 1, "Vat/not-authorized"); _; } mapping(address => mapping (address => uint)) public can; function hope(address usr) external note { can[msg.sender][usr] = 1; } function nope(address usr) external note { can[msg.sender][usr] = 0; } function wish(address bit, address usr) internal view returns (bool) { return either(bit == usr, can[bit][usr] == 1); } // --- Data --- struct Ilk { uint256 Art; // Total Normalised Debt [wad] uint256 rate; // Accumulated Rates [ray] uint256 spot; // Price with Safety Margin [ray] uint256 line; // Debt Ceiling [rad] uint256 dust; // Urn Debt Floor [rad] } struct Urn { uint256 ink; // Locked Collateral [wad] uint256 art; // Normalised Debt [wad] } mapping (bytes32 => Ilk) public ilks; mapping (bytes32 => mapping (address => Urn )) public urns; mapping (bytes32 => mapping (address => uint)) public gem; // [wad] mapping (address => uint256) public dai; // [rad] mapping (address => uint256) public sin; // [rad] uint256 public debt; // Total Dai Issued [rad] uint256 public vice; // Total Unbacked Dai [rad] uint256 public Line; // Total Debt Ceiling [rad] uint256 public live; // Access Flag // --- Logs --- event LogNote( bytes4 indexed sig, bytes32 indexed arg1, bytes32 indexed arg2, bytes32 indexed arg3, bytes data ) anonymous; modifier note { _; assembly { // log an 'anonymous' event with a constant 6 words of calldata // and four indexed topics: the selector and the first three args let mark := msize // end of memory ensures zero mstore(0x40, add(mark, 288)) // update free memory pointer mstore(mark, 0x20) // bytes type data offset mstore(add(mark, 0x20), 224) // bytes size (padded) calldatacopy(add(mark, 0x40), 0, 224) // bytes payload log4(mark, 288, // calldata shl(224, shr(224, calldataload(0))), // msg.sig calldataload(4), // arg1 calldataload(36), // arg2 calldataload(68) // arg3 ) } } // --- Init --- constructor() public { wards[msg.sender] = 1; live = 1; } // --- Math --- function add(uint x, int y) internal pure returns (uint z) { z = x + uint(y); require(y >= 0 || z <= x); require(y <= 0 || z >= x); } function sub(uint x, int y) internal pure returns (uint z) { z = x - uint(y); require(y <= 0 || z <= x); require(y >= 0 || z >= x); } function mul(uint x, int y) internal pure returns (int z) { z = int(x) * y; require(int(x) >= 0); require(y == 0 || z / y == int(x)); } function add(uint x, uint y) internal pure returns (uint z) { require((z = x + y) >= x); } function sub(uint x, uint y) internal pure returns (uint z) { require((z = x - y) <= x); } function mul(uint x, uint y) internal pure returns (uint z) { require(y == 0 || (z = x * y) / y == x); } // --- Administration --- function init(bytes32 ilk) external note auth { require(ilks[ilk].rate == 0, "Vat/ilk-already-init"); ilks[ilk].rate = 10 ** 27; } function file(bytes32 what, uint data) external note auth { require(live == 1, "Vat/not-live"); if (what == "Line") Line = data; else revert("Vat/file-unrecognized-param"); } function file(bytes32 ilk, bytes32 what, uint data) external note auth { require(live == 1, "Vat/not-live"); if (what == "spot") ilks[ilk].spot = data; else if (what == "line") ilks[ilk].line = data; else if (what == "dust") ilks[ilk].dust = data; else revert("Vat/file-unrecognized-param"); } function cage() external note auth { live = 0; } // --- Fungibility --- function slip(bytes32 ilk, address usr, int256 wad) external note auth { gem[ilk][usr] = add(gem[ilk][usr], wad); } function flux(bytes32 ilk, address src, address dst, uint256 wad) external note { require(wish(src, msg.sender), "Vat/not-allowed"); gem[ilk][src] = sub(gem[ilk][src], wad); gem[ilk][dst] = add(gem[ilk][dst], wad); } function move(address src, address dst, uint256 rad) external note { require(wish(src, msg.sender), "Vat/not-allowed"); dai[src] = sub(dai[src], rad); dai[dst] = add(dai[dst], rad); } function either(bool x, bool y) internal pure returns (bool z) { assembly{ z := or(x, y)} } function both(bool x, bool y) internal pure returns (bool z) { assembly{ z := and(x, y)} } // --- CDP Manipulation --- function frob(bytes32 i, address u, address v, address w, int dink, int dart) external note { // system is live require(live == 1, "Vat/not-live"); Urn memory urn = urns[i][u]; Ilk memory ilk = ilks[i]; // ilk has been initialised require(ilk.rate != 0, "Vat/ilk-not-init"); urn.ink = add(urn.ink, dink); urn.art = add(urn.art, dart); ilk.Art = add(ilk.Art, dart); int dtab = mul(ilk.rate, dart); uint tab = mul(ilk.rate, urn.art); debt = add(debt, dtab); // either debt has decreased, or debt ceilings are not exceeded require(either(dart <= 0, both(mul(ilk.Art, ilk.rate) <= ilk.line, debt <= Line)), "Vat/ceiling-exceeded"); // urn is either less risky than before, or it is safe require(either(both(dart <= 0, dink >= 0), tab <= mul(urn.ink, ilk.spot)), "Vat/not-safe"); // urn is either more safe, or the owner consents require(either(both(dart <= 0, dink >= 0), wish(u, msg.sender)), "Vat/not-allowed-u"); // collateral src consents require(either(dink <= 0, wish(v, msg.sender)), "Vat/not-allowed-v"); // debt dst consents require(either(dart >= 0, wish(w, msg.sender)), "Vat/not-allowed-w"); // urn has no debt, or a non-dusty amount require(either(urn.art == 0, tab >= ilk.dust), "Vat/dust"); gem[i][v] = sub(gem[i][v], dink); dai[w] = add(dai[w], dtab); urns[i][u] = urn; ilks[i] = ilk; } // --- CDP Fungibility --- function fork(bytes32 ilk, address src, address dst, int dink, int dart) external note { Urn storage u = urns[ilk][src]; Urn storage v = urns[ilk][dst]; Ilk storage i = ilks[ilk]; u.ink = sub(u.ink, dink); u.art = sub(u.art, dart); v.ink = add(v.ink, dink); v.art = add(v.art, dart); uint utab = mul(u.art, i.rate); uint vtab = mul(v.art, i.rate); // both sides consent require(both(wish(src, msg.sender), wish(dst, msg.sender)), "Vat/not-allowed"); // both sides safe require(utab <= mul(u.ink, i.spot), "Vat/not-safe-src"); require(vtab <= mul(v.ink, i.spot), "Vat/not-safe-dst"); // both sides non-dusty require(either(utab >= i.dust, u.art == 0), "Vat/dust-src"); require(either(vtab >= i.dust, v.art == 0), "Vat/dust-dst"); } // --- CDP Confiscation --- function grab(bytes32 i, address u, address v, address w, int dink, int dart) external note auth { Urn storage urn = urns[i][u]; Ilk storage ilk = ilks[i]; urn.ink = add(urn.ink, dink); urn.art = add(urn.art, dart); ilk.Art = add(ilk.Art, dart); int dtab = mul(ilk.rate, dart); gem[i][v] = sub(gem[i][v], dink); sin[w] = sub(sin[w], dtab); vice = sub(vice, dtab); } // --- Settlement --- function heal(uint rad) external note { address u = msg.sender; sin[u] = sub(sin[u], rad); dai[u] = sub(dai[u], rad); vice = sub(vice, rad); debt = sub(debt, rad); } function suck(address u, address v, uint rad) external note auth { sin[u] = add(sin[u], rad); dai[v] = add(dai[v], rad); vice = add(vice, rad); debt = add(debt, rad); } // --- Rates --- function fold(bytes32 i, address u, int rate) external note auth { require(live == 1, "Vat/not-live"); Ilk storage ilk = ilks[i]; ilk.rate = add(ilk.rate, rate); int rad = mul(ilk.Art, rate); dai[u] = add(dai[u], rad); debt = add(debt, rad); } }
File 3 of 7: Pot
// hevm: flattened sources of /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/pot.sol pragma solidity =0.5.12; ////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/lib.sol // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU 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 General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. /* pragma solidity 0.5.12; */ contract LibNote { event LogNote( bytes4 indexed sig, address indexed usr, bytes32 indexed arg1, bytes32 indexed arg2, bytes data ) anonymous; modifier note { _; assembly { // log an 'anonymous' event with a constant 6 words of calldata // and four indexed topics: selector, caller, arg1 and arg2 let mark := msize // end of memory ensures zero mstore(0x40, add(mark, 288)) // update free memory pointer mstore(mark, 0x20) // bytes type data offset mstore(add(mark, 0x20), 224) // bytes size (padded) calldatacopy(add(mark, 0x40), 0, 224) // bytes payload log4(mark, 288, // calldata shl(224, shr(224, calldataload(0))), // msg.sig caller, // msg.sender calldataload(4), // arg1 calldataload(36) // arg2 ) } } } ////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/pot.sol /// pot.sol -- Dai Savings Rate // Copyright (C) 2018 Rain <[email protected]> // // 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.5.12; */ /* import "./lib.sol"; */ /* "Savings Dai" is obtained when Dai is deposited into this contract. Each "Savings Dai" accrues Dai interest at the "Dai Savings Rate". This contract does not implement a user tradeable token and is intended to be used with adapters. --- `save` your `dai` in the `pot` --- - `dsr`: the Dai Savings Rate - `pie`: user balance of Savings Dai - `join`: start saving some dai - `exit`: remove some dai - `drip`: perform rate collection */ contract VatLike { function move(address,address,uint256) external; function suck(address,address,uint256) external; } contract Pot is LibNote { // --- Auth --- mapping (address => uint) public wards; function rely(address guy) external note auth { wards[guy] = 1; } function deny(address guy) external note auth { wards[guy] = 0; } modifier auth { require(wards[msg.sender] == 1, "Pot/not-authorized"); _; } // --- Data --- mapping (address => uint256) public pie; // user Savings Dai uint256 public Pie; // total Savings Dai uint256 public dsr; // the Dai Savings Rate uint256 public chi; // the Rate Accumulator VatLike public vat; // CDP engine address public vow; // debt engine uint256 public rho; // time of last drip uint256 public live; // Access Flag // --- Init --- constructor(address vat_) public { wards[msg.sender] = 1; vat = VatLike(vat_); dsr = ONE; chi = ONE; rho = now; live = 1; } // --- Math --- uint256 constant ONE = 10 ** 27; function rpow(uint x, uint n, uint base) internal pure returns (uint z) { assembly { switch x case 0 {switch n case 0 {z := base} default {z := 0}} default { switch mod(n, 2) case 0 { z := base } default { z := x } let half := div(base, 2) // for rounding. for { n := div(n, 2) } n { n := div(n,2) } { let xx := mul(x, x) if iszero(eq(div(xx, x), x)) { revert(0,0) } let xxRound := add(xx, half) if lt(xxRound, xx) { revert(0,0) } x := div(xxRound, base) if mod(n,2) { let zx := mul(z, x) if and(iszero(iszero(x)), iszero(eq(div(zx, x), z))) { revert(0,0) } let zxRound := add(zx, half) if lt(zxRound, zx) { revert(0,0) } z := div(zxRound, base) } } } } } function rmul(uint x, uint y) internal pure returns (uint z) { z = mul(x, y) / ONE; } function add(uint x, uint y) internal pure returns (uint z) { require((z = x + y) >= x); } function sub(uint x, uint y) internal pure returns (uint z) { require((z = x - y) <= x); } function mul(uint x, uint y) internal pure returns (uint z) { require(y == 0 || (z = x * y) / y == x); } // --- Administration --- function file(bytes32 what, uint256 data) external note auth { require(live == 1, "Pot/not-live"); require(now == rho, "Pot/rho-not-updated"); if (what == "dsr") dsr = data; else revert("Pot/file-unrecognized-param"); } function file(bytes32 what, address addr) external note auth { if (what == "vow") vow = addr; else revert("Pot/file-unrecognized-param"); } function cage() external note auth { live = 0; dsr = ONE; } // --- Savings Rate Accumulation --- function drip() external note returns (uint tmp) { require(now >= rho, "Pot/invalid-now"); tmp = rmul(rpow(dsr, now - rho, ONE), chi); uint chi_ = sub(tmp, chi); chi = tmp; rho = now; vat.suck(address(vow), address(this), mul(Pie, chi_)); } // --- Savings Dai Management --- function join(uint wad) external note { require(now == rho, "Pot/rho-not-updated"); pie[msg.sender] = add(pie[msg.sender], wad); Pie = add(Pie, wad); vat.move(msg.sender, address(this), mul(chi, wad)); } function exit(uint wad) external note { pie[msg.sender] = sub(pie[msg.sender], wad); Pie = sub(Pie, wad); vat.move(address(this), msg.sender, mul(chi, wad)); } }
File 4 of 7: Dai
// hevm: flattened sources of /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/dai.sol pragma solidity =0.5.12; ////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/lib.sol // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU 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 General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. /* pragma solidity 0.5.12; */ contract LibNote { event LogNote( bytes4 indexed sig, address indexed usr, bytes32 indexed arg1, bytes32 indexed arg2, bytes data ) anonymous; modifier note { _; assembly { // log an 'anonymous' event with a constant 6 words of calldata // and four indexed topics: selector, caller, arg1 and arg2 let mark := msize // end of memory ensures zero mstore(0x40, add(mark, 288)) // update free memory pointer mstore(mark, 0x20) // bytes type data offset mstore(add(mark, 0x20), 224) // bytes size (padded) calldatacopy(add(mark, 0x40), 0, 224) // bytes payload log4(mark, 288, // calldata shl(224, shr(224, calldataload(0))), // msg.sig caller, // msg.sender calldataload(4), // arg1 calldataload(36) // arg2 ) } } } ////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/dai.sol // Copyright (C) 2017, 2018, 2019 dbrock, rain, mrchico // 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.5.12; */ /* import "./lib.sol"; */ contract Dai is LibNote { // --- Auth --- mapping (address => uint) public wards; function rely(address guy) external note auth { wards[guy] = 1; } function deny(address guy) external note auth { wards[guy] = 0; } modifier auth { require(wards[msg.sender] == 1, "Dai/not-authorized"); _; } // --- ERC20 Data --- string public constant name = "Dai Stablecoin"; string public constant symbol = "DAI"; string public constant version = "1"; uint8 public constant decimals = 18; uint256 public totalSupply; mapping (address => uint) public balanceOf; mapping (address => mapping (address => uint)) public allowance; mapping (address => uint) public nonces; event Approval(address indexed src, address indexed guy, uint wad); event Transfer(address indexed src, address indexed dst, uint wad); // --- Math --- function add(uint x, uint y) internal pure returns (uint z) { require((z = x + y) >= x); } function sub(uint x, uint y) internal pure returns (uint z) { require((z = x - y) <= x); } // --- EIP712 niceties --- bytes32 public DOMAIN_SEPARATOR; // bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)"); bytes32 public constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb; constructor(uint256 chainId_) public { wards[msg.sender] = 1; DOMAIN_SEPARATOR = keccak256(abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name)), keccak256(bytes(version)), chainId_, address(this) )); } // --- Token --- function transfer(address dst, uint wad) external returns (bool) { return transferFrom(msg.sender, dst, wad); } function transferFrom(address src, address dst, uint wad) public returns (bool) { require(balanceOf[src] >= wad, "Dai/insufficient-balance"); if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) { require(allowance[src][msg.sender] >= wad, "Dai/insufficient-allowance"); allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad); } balanceOf[src] = sub(balanceOf[src], wad); balanceOf[dst] = add(balanceOf[dst], wad); emit Transfer(src, dst, wad); return true; } function mint(address usr, uint wad) external auth { balanceOf[usr] = add(balanceOf[usr], wad); totalSupply = add(totalSupply, wad); emit Transfer(address(0), usr, wad); } function burn(address usr, uint wad) external { require(balanceOf[usr] >= wad, "Dai/insufficient-balance"); if (usr != msg.sender && allowance[usr][msg.sender] != uint(-1)) { require(allowance[usr][msg.sender] >= wad, "Dai/insufficient-allowance"); allowance[usr][msg.sender] = sub(allowance[usr][msg.sender], wad); } balanceOf[usr] = sub(balanceOf[usr], wad); totalSupply = sub(totalSupply, wad); emit Transfer(usr, address(0), wad); } function approve(address usr, uint wad) external returns (bool) { allowance[msg.sender][usr] = wad; emit Approval(msg.sender, usr, wad); return true; } // --- Alias --- function push(address usr, uint wad) external { transferFrom(msg.sender, usr, wad); } function pull(address usr, uint wad) external { transferFrom(usr, msg.sender, wad); } function move(address src, address dst, uint wad) external { transferFrom(src, dst, wad); } // --- Approve by signature --- function permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s) external { bytes32 digest = keccak256(abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR, keccak256(abi.encode(PERMIT_TYPEHASH, holder, spender, nonce, expiry, allowed)) )); require(holder != address(0), "Dai/invalid-address-0"); require(holder == ecrecover(digest, v, r, s), "Dai/invalid-permit"); require(expiry == 0 || now <= expiry, "Dai/permit-expired"); require(nonce == nonces[holder]++, "Dai/invalid-nonce"); uint wad = allowed ? uint(-1) : 0; allowance[holder][spender] = wad; emit Approval(holder, spender, wad); } }
File 5 of 7: DaiJoin
// hevm: flattened sources of /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/join.sol pragma solidity =0.5.12; ////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/lib.sol // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU 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 General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. /* pragma solidity 0.5.12; */ contract LibNote { event LogNote( bytes4 indexed sig, address indexed usr, bytes32 indexed arg1, bytes32 indexed arg2, bytes data ) anonymous; modifier note { _; assembly { // log an 'anonymous' event with a constant 6 words of calldata // and four indexed topics: selector, caller, arg1 and arg2 let mark := msize // end of memory ensures zero mstore(0x40, add(mark, 288)) // update free memory pointer mstore(mark, 0x20) // bytes type data offset mstore(add(mark, 0x20), 224) // bytes size (padded) calldatacopy(add(mark, 0x40), 0, 224) // bytes payload log4(mark, 288, // calldata shl(224, shr(224, calldataload(0))), // msg.sig caller, // msg.sender calldataload(4), // arg1 calldataload(36) // arg2 ) } } } ////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/join.sol /// join.sol -- Basic token adapters // Copyright (C) 2018 Rain <[email protected]> // // 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.5.12; */ /* import "./lib.sol"; */ contract GemLike { function decimals() public view returns (uint); function transfer(address,uint) external returns (bool); function transferFrom(address,address,uint) external returns (bool); } contract DSTokenLike { function mint(address,uint) external; function burn(address,uint) external; } contract VatLike { function slip(bytes32,address,int) external; function move(address,address,uint) external; } /* Here we provide *adapters* to connect the Vat to arbitrary external token implementations, creating a bounded context for the Vat. The adapters here are provided as working examples: - `GemJoin`: For well behaved ERC20 tokens, with simple transfer semantics. - `ETHJoin`: For native Ether. - `DaiJoin`: For connecting internal Dai balances to an external `DSToken` implementation. In practice, adapter implementations will be varied and specific to individual collateral types, accounting for different transfer semantics and token standards. Adapters need to implement two basic methods: - `join`: enter collateral into the system - `exit`: remove collateral from the system */ contract GemJoin is LibNote { // --- Auth --- mapping (address => uint) public wards; function rely(address usr) external note auth { wards[usr] = 1; } function deny(address usr) external note auth { wards[usr] = 0; } modifier auth { require(wards[msg.sender] == 1, "GemJoin/not-authorized"); _; } VatLike public vat; bytes32 public ilk; GemLike public gem; uint public dec; uint public live; // Access Flag constructor(address vat_, bytes32 ilk_, address gem_) public { wards[msg.sender] = 1; live = 1; vat = VatLike(vat_); ilk = ilk_; gem = GemLike(gem_); dec = gem.decimals(); } function cage() external note auth { live = 0; } function join(address usr, uint wad) external note { require(live == 1, "GemJoin/not-live"); require(int(wad) >= 0, "GemJoin/overflow"); vat.slip(ilk, usr, int(wad)); require(gem.transferFrom(msg.sender, address(this), wad), "GemJoin/failed-transfer"); } function exit(address usr, uint wad) external note { require(wad <= 2 ** 255, "GemJoin/overflow"); vat.slip(ilk, msg.sender, -int(wad)); require(gem.transfer(usr, wad), "GemJoin/failed-transfer"); } } contract ETHJoin is LibNote { // --- Auth --- mapping (address => uint) public wards; function rely(address usr) external note auth { wards[usr] = 1; } function deny(address usr) external note auth { wards[usr] = 0; } modifier auth { require(wards[msg.sender] == 1, "ETHJoin/not-authorized"); _; } VatLike public vat; bytes32 public ilk; uint public live; // Access Flag constructor(address vat_, bytes32 ilk_) public { wards[msg.sender] = 1; live = 1; vat = VatLike(vat_); ilk = ilk_; } function cage() external note auth { live = 0; } function join(address usr) external payable note { require(live == 1, "ETHJoin/not-live"); require(int(msg.value) >= 0, "ETHJoin/overflow"); vat.slip(ilk, usr, int(msg.value)); } function exit(address payable usr, uint wad) external note { require(int(wad) >= 0, "ETHJoin/overflow"); vat.slip(ilk, msg.sender, -int(wad)); usr.transfer(wad); } } contract DaiJoin is LibNote { // --- Auth --- mapping (address => uint) public wards; function rely(address usr) external note auth { wards[usr] = 1; } function deny(address usr) external note auth { wards[usr] = 0; } modifier auth { require(wards[msg.sender] == 1, "DaiJoin/not-authorized"); _; } VatLike public vat; DSTokenLike public dai; uint public live; // Access Flag constructor(address vat_, address dai_) public { wards[msg.sender] = 1; live = 1; vat = VatLike(vat_); dai = DSTokenLike(dai_); } function cage() external note auth { live = 0; } uint constant ONE = 10 ** 27; function mul(uint x, uint y) internal pure returns (uint z) { require(y == 0 || (z = x * y) / y == x); } function join(address usr, uint wad) external note { vat.move(address(this), usr, mul(ONE, wad)); dai.burn(msg.sender, wad); } function exit(address usr, uint wad) external note { require(live == 1, "DaiJoin/not-live"); vat.move(msg.sender, address(this), mul(ONE, wad)); dai.mint(usr, wad); } }
File 6 of 7: DefisaverLogger
// SPDX-License-Identifier: MIT pragma solidity =0.8.10; contract DefisaverLogger { event RecipeEvent( address indexed caller, string indexed logName ); event ActionDirectEvent( address indexed caller, string indexed logName, bytes data ); function logRecipeEvent( string memory _logName ) public { emit RecipeEvent(msg.sender, _logName); } function logActionDirectEvent( string memory _logName, bytes memory _data ) public { emit ActionDirectEvent(msg.sender, _logName, _data); } }
File 7 of 7: McdDsrWithdraw
// SPDX-License-Identifier: MIT pragma solidity =0.8.10; contract DSNote { event LogNote( bytes4 indexed sig, address indexed guy, bytes32 indexed foo, bytes32 indexed bar, uint256 wad, bytes fax ) anonymous; modifier note { bytes32 foo; bytes32 bar; assembly { foo := calldataload(4) bar := calldataload(36) } emit LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data); _; } } abstract contract DSAuthority { function canCall( address src, address dst, bytes4 sig ) public view virtual returns (bool); } contract DSAuthEvents { event LogSetAuthority(address indexed authority); event LogSetOwner(address indexed owner); } contract DSAuth is DSAuthEvents { DSAuthority public authority; address public owner; constructor() { owner = msg.sender; emit LogSetOwner(msg.sender); } function setOwner(address owner_) public auth { owner = owner_; emit LogSetOwner(owner); } function setAuthority(DSAuthority authority_) public auth { authority = authority_; emit LogSetAuthority(address(authority)); } modifier auth { require(isAuthorized(msg.sender, msg.sig), "Not authorized"); _; } function isAuthorized(address src, bytes4 sig) internal view returns (bool) { if (src == address(this)) { return true; } else if (src == owner) { return true; } else if (authority == DSAuthority(address(0))) { return false; } else { return authority.canCall(src, address(this), sig); } } } abstract contract DSProxy is DSAuth, DSNote { DSProxyCache public cache; // global cache for contracts constructor(address _cacheAddr) { if (!(setCache(_cacheAddr))){ require(isAuthorized(msg.sender, msg.sig), "Not authorized"); } } // solhint-disable-next-line no-empty-blocks receive() external payable {} // use the proxy to execute calldata _data on contract _code function execute(bytes memory _code, bytes memory _data) public payable virtual returns (address target, bytes32 response); function execute(address _target, bytes memory _data) public payable virtual returns (bytes32 response); //set new cache function setCache(address _cacheAddr) public payable virtual returns (bool); } contract DSProxyCache { mapping(bytes32 => address) cache; function read(bytes memory _code) public view returns (address) { bytes32 hash = keccak256(_code); return cache[hash]; } function write(bytes memory _code) public returns (address target) { assembly { target := create(0, add(_code, 0x20), mload(_code)) switch iszero(extcodesize(target)) case 1 { // throw if contract failed to deploy revert(0, 0) } } bytes32 hash = keccak256(_code); cache[hash] = target; } } contract DefisaverLogger { event RecipeEvent( address indexed caller, string indexed logName ); event ActionDirectEvent( address indexed caller, string indexed logName, bytes data ); function logRecipeEvent( string memory _logName ) public { emit RecipeEvent(msg.sender, _logName); } function logActionDirectEvent( string memory _logName, bytes memory _data ) public { emit ActionDirectEvent(msg.sender, _logName, _data); } } contract MainnetAuthAddresses { address internal constant ADMIN_VAULT_ADDR = 0xCCf3d848e08b94478Ed8f46fFead3008faF581fD; address internal constant FACTORY_ADDRESS = 0x5a15566417e6C1c9546523066500bDDBc53F88C7; address internal constant ADMIN_ADDR = 0x25eFA336886C74eA8E282ac466BdCd0199f85BB9; // USED IN ADMIN VAULT CONSTRUCTOR } contract AuthHelper is MainnetAuthAddresses { } contract AdminVault is AuthHelper { address public owner; address public admin; error SenderNotAdmin(); constructor() { owner = msg.sender; admin = ADMIN_ADDR; } /// @notice Admin is able to change owner /// @param _owner Address of new owner function changeOwner(address _owner) public { if (admin != msg.sender){ revert SenderNotAdmin(); } owner = _owner; } /// @notice Admin is able to set new admin /// @param _admin Address of multisig that becomes new admin function changeAdmin(address _admin) public { if (admin != msg.sender){ revert SenderNotAdmin(); } admin = _admin; } } interface IERC20 { function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint256 digits); function totalSupply() external view returns (uint256 supply); function balanceOf(address _owner) external view returns (uint256 balance); function transfer(address _to, uint256 _value) external returns (bool success); function transferFrom( address _from, address _to, uint256 _value ) external returns (bool success); function approve(address _spender, uint256 _value) external returns (bool success); function allowance(address _owner, address _spender) external view returns (uint256 remaining); event Approval(address indexed _owner, address indexed _spender, uint256 _value); } library Address { //insufficient balance error InsufficientBalance(uint256 available, uint256 required); //unable to send value, recipient may have reverted error SendingValueFail(); //insufficient balance for call error InsufficientBalanceForCall(uint256 available, uint256 required); //call to non-contract error NonContractCall(); function isContract(address account) internal view returns (bool) { // According to EIP-1052, 0x0 is the value returned for not-yet created accounts // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned // for accounts without code, i.e. `keccak256('')` bytes32 codehash; bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; // solhint-disable-next-line no-inline-assembly assembly { codehash := extcodehash(account) } return (codehash != accountHash && codehash != 0x0); } function sendValue(address payable recipient, uint256 amount) internal { uint256 balance = address(this).balance; if (balance < amount){ revert InsufficientBalance(balance, amount); } // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{value: amount}(""); if (!(success)){ revert SendingValueFail(); } } function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return _functionCallWithValue(target, data, 0, errorMessage); } function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { uint256 balance = address(this).balance; if (balance < value){ revert InsufficientBalanceForCall(balance, value); } return _functionCallWithValue(target, data, value, errorMessage); } function _functionCallWithValue( address target, bytes memory data, uint256 weiValue, string memory errorMessage ) private returns (bytes memory) { if (!(isContract(target))){ revert NonContractCall(); } // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{value: weiValue}(data); if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } } library SafeMath { function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn( token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value) ); } /// @dev Edited so it always first approves 0 and then the value, because of non standard tokens function safeApprove( IERC20 token, address spender, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0)); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); _callOptionalReturn( token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance) ); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender).sub( value, "SafeERC20: decreased allowance below zero" ); _callOptionalReturn( token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance) ); } function _callOptionalReturn(IERC20 token, bytes memory data) private { bytes memory returndata = address(token).functionCall( data, "SafeERC20: low-level call failed" ); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } abstract contract IDFSRegistry { function getAddr(bytes4 _id) public view virtual returns (address); function addNewContract( bytes32 _id, address _contractAddr, uint256 _waitPeriod ) public virtual; function startContractChange(bytes32 _id, address _newContractAddr) public virtual; function approveContractChange(bytes32 _id) public virtual; function cancelContractChange(bytes32 _id) public virtual; function changeWaitPeriod(bytes32 _id, uint256 _newWaitPeriod) public virtual; } contract AdminAuth is AuthHelper { using SafeERC20 for IERC20; AdminVault public constant adminVault = AdminVault(ADMIN_VAULT_ADDR); error SenderNotOwner(); error SenderNotAdmin(); modifier onlyOwner() { if (adminVault.owner() != msg.sender){ revert SenderNotOwner(); } _; } modifier onlyAdmin() { if (adminVault.admin() != msg.sender){ revert SenderNotAdmin(); } _; } /// @notice withdraw stuck funds function withdrawStuckFunds(address _token, address _receiver, uint256 _amount) public onlyOwner { if (_token == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) { payable(_receiver).transfer(_amount); } else { IERC20(_token).safeTransfer(_receiver, _amount); } } /// @notice Destroy the contract function kill() public onlyAdmin { selfdestruct(payable(msg.sender)); } } contract DFSRegistry is AdminAuth { error EntryAlreadyExistsError(bytes4); error EntryNonExistentError(bytes4); error EntryNotInChangeError(bytes4); error ChangeNotReadyError(uint256,uint256); error EmptyPrevAddrError(bytes4); error AlreadyInContractChangeError(bytes4); error AlreadyInWaitPeriodChangeError(bytes4); event AddNewContract(address,bytes4,address,uint256); event RevertToPreviousAddress(address,bytes4,address,address); event StartContractChange(address,bytes4,address,address); event ApproveContractChange(address,bytes4,address,address); event CancelContractChange(address,bytes4,address,address); event StartWaitPeriodChange(address,bytes4,uint256); event ApproveWaitPeriodChange(address,bytes4,uint256,uint256); event CancelWaitPeriodChange(address,bytes4,uint256,uint256); struct Entry { address contractAddr; uint256 waitPeriod; uint256 changeStartTime; bool inContractChange; bool inWaitPeriodChange; bool exists; } mapping(bytes4 => Entry) public entries; mapping(bytes4 => address) public previousAddresses; mapping(bytes4 => address) public pendingAddresses; mapping(bytes4 => uint256) public pendingWaitTimes; /// @notice Given an contract id returns the registered address /// @dev Id is keccak256 of the contract name /// @param _id Id of contract function getAddr(bytes4 _id) public view returns (address) { return entries[_id].contractAddr; } /// @notice Helper function to easily query if id is registered /// @param _id Id of contract function isRegistered(bytes4 _id) public view returns (bool) { return entries[_id].exists; } /////////////////////////// OWNER ONLY FUNCTIONS /////////////////////////// /// @notice Adds a new contract to the registry /// @param _id Id of contract /// @param _contractAddr Address of the contract /// @param _waitPeriod Amount of time to wait before a contract address can be changed function addNewContract( bytes4 _id, address _contractAddr, uint256 _waitPeriod ) public onlyOwner { if (entries[_id].exists){ revert EntryAlreadyExistsError(_id); } entries[_id] = Entry({ contractAddr: _contractAddr, waitPeriod: _waitPeriod, changeStartTime: 0, inContractChange: false, inWaitPeriodChange: false, exists: true }); emit AddNewContract(msg.sender, _id, _contractAddr, _waitPeriod); } /// @notice Reverts to the previous address immediately /// @dev In case the new version has a fault, a quick way to fallback to the old contract /// @param _id Id of contract function revertToPreviousAddress(bytes4 _id) public onlyOwner { if (!(entries[_id].exists)){ revert EntryNonExistentError(_id); } if (previousAddresses[_id] == address(0)){ revert EmptyPrevAddrError(_id); } address currentAddr = entries[_id].contractAddr; entries[_id].contractAddr = previousAddresses[_id]; emit RevertToPreviousAddress(msg.sender, _id, currentAddr, previousAddresses[_id]); } /// @notice Starts an address change for an existing entry /// @dev Can override a change that is currently in progress /// @param _id Id of contract /// @param _newContractAddr Address of the new contract function startContractChange(bytes4 _id, address _newContractAddr) public onlyOwner { if (!entries[_id].exists){ revert EntryNonExistentError(_id); } if (entries[_id].inWaitPeriodChange){ revert AlreadyInWaitPeriodChangeError(_id); } entries[_id].changeStartTime = block.timestamp; // solhint-disable-line entries[_id].inContractChange = true; pendingAddresses[_id] = _newContractAddr; emit StartContractChange(msg.sender, _id, entries[_id].contractAddr, _newContractAddr); } /// @notice Changes new contract address, correct time must have passed /// @param _id Id of contract function approveContractChange(bytes4 _id) public onlyOwner { if (!entries[_id].exists){ revert EntryNonExistentError(_id); } if (!entries[_id].inContractChange){ revert EntryNotInChangeError(_id); } if (block.timestamp < (entries[_id].changeStartTime + entries[_id].waitPeriod)){// solhint-disable-line revert ChangeNotReadyError(block.timestamp, (entries[_id].changeStartTime + entries[_id].waitPeriod)); } address oldContractAddr = entries[_id].contractAddr; entries[_id].contractAddr = pendingAddresses[_id]; entries[_id].inContractChange = false; entries[_id].changeStartTime = 0; pendingAddresses[_id] = address(0); previousAddresses[_id] = oldContractAddr; emit ApproveContractChange(msg.sender, _id, oldContractAddr, entries[_id].contractAddr); } /// @notice Cancel pending change /// @param _id Id of contract function cancelContractChange(bytes4 _id) public onlyOwner { if (!entries[_id].exists){ revert EntryNonExistentError(_id); } if (!entries[_id].inContractChange){ revert EntryNotInChangeError(_id); } address oldContractAddr = pendingAddresses[_id]; pendingAddresses[_id] = address(0); entries[_id].inContractChange = false; entries[_id].changeStartTime = 0; emit CancelContractChange(msg.sender, _id, oldContractAddr, entries[_id].contractAddr); } /// @notice Starts the change for waitPeriod /// @param _id Id of contract /// @param _newWaitPeriod New wait time function startWaitPeriodChange(bytes4 _id, uint256 _newWaitPeriod) public onlyOwner { if (!entries[_id].exists){ revert EntryNonExistentError(_id); } if (entries[_id].inContractChange){ revert AlreadyInContractChangeError(_id); } pendingWaitTimes[_id] = _newWaitPeriod; entries[_id].changeStartTime = block.timestamp; // solhint-disable-line entries[_id].inWaitPeriodChange = true; emit StartWaitPeriodChange(msg.sender, _id, _newWaitPeriod); } /// @notice Changes new wait period, correct time must have passed /// @param _id Id of contract function approveWaitPeriodChange(bytes4 _id) public onlyOwner { if (!entries[_id].exists){ revert EntryNonExistentError(_id); } if (!entries[_id].inWaitPeriodChange){ revert EntryNotInChangeError(_id); } if (block.timestamp < (entries[_id].changeStartTime + entries[_id].waitPeriod)){ // solhint-disable-line revert ChangeNotReadyError(block.timestamp, (entries[_id].changeStartTime + entries[_id].waitPeriod)); } uint256 oldWaitTime = entries[_id].waitPeriod; entries[_id].waitPeriod = pendingWaitTimes[_id]; entries[_id].inWaitPeriodChange = false; entries[_id].changeStartTime = 0; pendingWaitTimes[_id] = 0; emit ApproveWaitPeriodChange(msg.sender, _id, oldWaitTime, entries[_id].waitPeriod); } /// @notice Cancel wait period change /// @param _id Id of contract function cancelWaitPeriodChange(bytes4 _id) public onlyOwner { if (!entries[_id].exists){ revert EntryNonExistentError(_id); } if (!entries[_id].inWaitPeriodChange){ revert EntryNotInChangeError(_id); } uint256 oldWaitPeriod = pendingWaitTimes[_id]; pendingWaitTimes[_id] = 0; entries[_id].inWaitPeriodChange = false; entries[_id].changeStartTime = 0; emit CancelWaitPeriodChange(msg.sender, _id, oldWaitPeriod, entries[_id].waitPeriod); } } contract MainnetActionsUtilAddresses { address internal constant DFS_REG_CONTROLLER_ADDR = 0xF8f8B3C98Cf2E63Df3041b73f80F362a4cf3A576; address internal constant REGISTRY_ADDR = 0x287778F121F134C66212FB16c9b53eC991D32f5b; address internal constant DFS_LOGGER_ADDR = 0xcE7a977Cac4a481bc84AC06b2Da0df614e621cf3; address internal constant SUB_STORAGE_ADDR = 0x1612fc28Ee0AB882eC99842Cde0Fc77ff0691e90; address internal constant PROXY_AUTH_ADDR = 0x149667b6FAe2c63D1B4317C716b0D0e4d3E2bD70; } contract ActionsUtilHelper is MainnetActionsUtilAddresses { } abstract contract ActionBase is AdminAuth, ActionsUtilHelper { event ActionEvent( string indexed logName, bytes data ); DFSRegistry public constant registry = DFSRegistry(REGISTRY_ADDR); DefisaverLogger public constant logger = DefisaverLogger( DFS_LOGGER_ADDR ); //Wrong sub index value error SubIndexValueError(); //Wrong return index value error ReturnIndexValueError(); /// @dev Subscription params index range [128, 255] uint8 public constant SUB_MIN_INDEX_VALUE = 128; uint8 public constant SUB_MAX_INDEX_VALUE = 255; /// @dev Return params index range [1, 127] uint8 public constant RETURN_MIN_INDEX_VALUE = 1; uint8 public constant RETURN_MAX_INDEX_VALUE = 127; /// @dev If the input value should not be replaced uint8 public constant NO_PARAM_MAPPING = 0; /// @dev We need to parse Flash loan actions in a different way enum ActionType { FL_ACTION, STANDARD_ACTION, FEE_ACTION, CHECK_ACTION, CUSTOM_ACTION } /// @notice Parses inputs and runs the implemented action through a proxy /// @dev Is called by the RecipeExecutor chaining actions together /// @param _callData Array of input values each value encoded as bytes /// @param _subData Array of subscribed vales, replaces input values if specified /// @param _paramMapping Array that specifies how return and subscribed values are mapped in input /// @param _returnValues Returns values from actions before, which can be injected in inputs /// @return Returns a bytes32 value through DSProxy, each actions implements what that value is function executeAction( bytes memory _callData, bytes32[] memory _subData, uint8[] memory _paramMapping, bytes32[] memory _returnValues ) public payable virtual returns (bytes32); /// @notice Parses inputs and runs the single implemented action through a proxy /// @dev Used to save gas when executing a single action directly function executeActionDirect(bytes memory _callData) public virtual payable; /// @notice Returns the type of action we are implementing function actionType() public pure virtual returns (uint8); //////////////////////////// HELPER METHODS //////////////////////////// /// @notice Given an uint256 input, injects return/sub values if specified /// @param _param The original input value /// @param _mapType Indicated the type of the input in paramMapping /// @param _subData Array of subscription data we can replace the input value with /// @param _returnValues Array of subscription data we can replace the input value with function _parseParamUint( uint _param, uint8 _mapType, bytes32[] memory _subData, bytes32[] memory _returnValues ) internal pure returns (uint) { if (isReplaceable(_mapType)) { if (isReturnInjection(_mapType)) { _param = uint(_returnValues[getReturnIndex(_mapType)]); } else { _param = uint256(_subData[getSubIndex(_mapType)]); } } return _param; } /// @notice Given an addr input, injects return/sub values if specified /// @param _param The original input value /// @param _mapType Indicated the type of the input in paramMapping /// @param _subData Array of subscription data we can replace the input value with /// @param _returnValues Array of subscription data we can replace the input value with function _parseParamAddr( address _param, uint8 _mapType, bytes32[] memory _subData, bytes32[] memory _returnValues ) internal view returns (address) { if (isReplaceable(_mapType)) { if (isReturnInjection(_mapType)) { _param = address(bytes20((_returnValues[getReturnIndex(_mapType)]))); } else { /// @dev The last two values are specially reserved for proxy addr and owner addr if (_mapType == 254) return address(this); //DSProxy address if (_mapType == 255) return DSProxy(payable(address(this))).owner(); // owner of DSProxy _param = address(uint160(uint256(_subData[getSubIndex(_mapType)]))); } } return _param; } /// @notice Given an bytes32 input, injects return/sub values if specified /// @param _param The original input value /// @param _mapType Indicated the type of the input in paramMapping /// @param _subData Array of subscription data we can replace the input value with /// @param _returnValues Array of subscription data we can replace the input value with function _parseParamABytes32( bytes32 _param, uint8 _mapType, bytes32[] memory _subData, bytes32[] memory _returnValues ) internal pure returns (bytes32) { if (isReplaceable(_mapType)) { if (isReturnInjection(_mapType)) { _param = (_returnValues[getReturnIndex(_mapType)]); } else { _param = _subData[getSubIndex(_mapType)]; } } return _param; } /// @notice Checks if the paramMapping value indicated that we need to inject values /// @param _type Indicated the type of the input function isReplaceable(uint8 _type) internal pure returns (bool) { return _type != NO_PARAM_MAPPING; } /// @notice Checks if the paramMapping value is in the return value range /// @param _type Indicated the type of the input function isReturnInjection(uint8 _type) internal pure returns (bool) { return (_type >= RETURN_MIN_INDEX_VALUE) && (_type <= RETURN_MAX_INDEX_VALUE); } /// @notice Transforms the paramMapping value to the index in return array value /// @param _type Indicated the type of the input function getReturnIndex(uint8 _type) internal pure returns (uint8) { if (!(isReturnInjection(_type))){ revert SubIndexValueError(); } return (_type - RETURN_MIN_INDEX_VALUE); } /// @notice Transforms the paramMapping value to the index in sub array value /// @param _type Indicated the type of the input function getSubIndex(uint8 _type) internal pure returns (uint8) { if (_type < SUB_MIN_INDEX_VALUE){ revert ReturnIndexValueError(); } return (_type - SUB_MIN_INDEX_VALUE); } } abstract contract IVat { struct Urn { uint256 ink; // Locked Collateral [wad] uint256 art; // Normalised Debt [wad] } struct Ilk { uint256 Art; // Total Normalised Debt [wad] uint256 rate; // Accumulated Rates [ray] uint256 spot; // Price with Safety Margin [ray] uint256 line; // Debt Ceiling [rad] uint256 dust; // Urn Debt Floor [rad] } mapping (bytes32 => mapping (address => Urn )) public urns; mapping (bytes32 => Ilk) public ilks; mapping (bytes32 => mapping (address => uint)) public gem; // [wad] function can(address, address) virtual public view returns (uint); function dai(address) virtual public view returns (uint); function frob(bytes32, address, address, address, int, int) virtual public; function hope(address) virtual public; function nope(address) virtual public; function move(address, address, uint) virtual public; function fork(bytes32, address, address, int, int) virtual public; } abstract contract IGem { function dec() virtual public returns (uint); function gem() virtual public returns (IGem); function join(address, uint) virtual public payable; function exit(address, uint) virtual public; function approve(address, uint) virtual public; function transfer(address, uint) virtual public returns (bool); function transferFrom(address, address, uint) virtual public returns (bool); function deposit() virtual public payable; function withdraw(uint) virtual public; function allowance(address, address) virtual public returns (uint); } abstract contract IDaiJoin { function vat() public virtual returns (IVat); function dai() public virtual returns (IGem); function join(address, uint) public virtual payable; function exit(address, uint) public virtual; } interface IPot { function pie(address) external view returns (uint256); function vat() external view returns (address); function chi() external view returns (uint256); function rho() external view returns (uint256); function drip() external returns (uint256); function join(uint256) external; function exit(uint256) external; } contract DSMath { function add(uint256 x, uint256 y) internal pure returns (uint256 z) { z = x + y; } function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { z = x - y; } function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { z = x * y; } function div(uint256 x, uint256 y) internal pure returns (uint256 z) { return x / y; } function min(uint256 x, uint256 y) internal pure returns (uint256 z) { return x <= y ? x : y; } function max(uint256 x, uint256 y) internal pure returns (uint256 z) { return x >= y ? x : y; } function imin(int256 x, int256 y) internal pure returns (int256 z) { return x <= y ? x : y; } function imax(int256 x, int256 y) internal pure returns (int256 z) { return x >= y ? x : y; } uint256 constant WAD = 10**18; uint256 constant RAY = 10**27; function wmul(uint256 x, uint256 y) internal pure returns (uint256 z) { z = add(mul(x, y), WAD / 2) / WAD; } function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) { z = add(mul(x, y), RAY / 2) / RAY; } function wdiv(uint256 x, uint256 y) internal pure returns (uint256 z) { z = add(mul(x, WAD), y / 2) / y; } function rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) { z = add(mul(x, RAY), y / 2) / y; } // This famous algorithm is called "exponentiation by squaring" // and calculates x^n with x as fixed-point and n as regular unsigned. // // It's O(log n), instead of O(n) for naive repeated multiplication. // // These facts are why it works: // // If n is even, then x^n = (x^2)^(n/2). // If n is odd, then x^n = x * x^(n-1), // and applying the equation for even x gives // x^n = x * (x^2)^((n-1) / 2). // // Also, EVM division is flooring and // floor[(n-1) / 2] = floor[n / 2]. // function rpow(uint256 x, uint256 n) internal pure returns (uint256 z) { z = n % 2 != 0 ? x : RAY; for (n /= 2; n != 0; n /= 2) { x = rmul(x, x); if (n % 2 != 0) { z = rmul(z, x); } } } } abstract contract IJoin { bytes32 public ilk; function dec() virtual public view returns (uint); function gem() virtual public view returns (IGem); function join(address, uint) virtual public payable; function exit(address, uint) virtual public; } interface ICropper { function proxy(address) view external returns (address); function getOrCreateProxy(address) external returns (address); function join(address, address, uint256) external; function exit(address, address, uint256) external; function flee(address, address, uint256) external; function frob(bytes32, address, address, address, int256, int256) external; function quit(bytes32, address, address) external; } abstract contract IWETH { function allowance(address, address) public virtual view returns (uint256); function balanceOf(address) public virtual view returns (uint256); function approve(address, uint256) public virtual; function transfer(address, uint256) public virtual returns (bool); function transferFrom( address, address, uint256 ) public virtual returns (bool); function deposit() public payable virtual; function withdraw(uint256) public virtual; } library TokenUtils { using SafeERC20 for IERC20; address public constant WSTETH_ADDR = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0; address public constant STETH_ADDR = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84; address public constant WETH_ADDR = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; address public constant ETH_ADDR = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; function approveToken( address _tokenAddr, address _to, uint256 _amount ) internal { if (_tokenAddr == ETH_ADDR) return; if (IERC20(_tokenAddr).allowance(address(this), _to) < _amount) { IERC20(_tokenAddr).safeApprove(_to, _amount); } } function pullTokensIfNeeded( address _token, address _from, uint256 _amount ) internal returns (uint256) { // handle max uint amount if (_amount == type(uint256).max) { _amount = getBalance(_token, _from); } if (_from != address(0) && _from != address(this) && _token != ETH_ADDR && _amount != 0) { IERC20(_token).safeTransferFrom(_from, address(this), _amount); } return _amount; } function withdrawTokens( address _token, address _to, uint256 _amount ) internal returns (uint256) { if (_amount == type(uint256).max) { _amount = getBalance(_token, address(this)); } if (_to != address(0) && _to != address(this) && _amount != 0) { if (_token != ETH_ADDR) { IERC20(_token).safeTransfer(_to, _amount); } else { (bool success, ) = _to.call{value: _amount}(""); require(success, "Eth send fail"); } } return _amount; } function depositWeth(uint256 _amount) internal { IWETH(WETH_ADDR).deposit{value: _amount}(); } function withdrawWeth(uint256 _amount) internal { IWETH(WETH_ADDR).withdraw(_amount); } function getBalance(address _tokenAddr, address _acc) internal view returns (uint256) { if (_tokenAddr == ETH_ADDR) { return _acc.balance; } else { return IERC20(_tokenAddr).balanceOf(_acc); } } function getTokenDecimals(address _token) internal view returns (uint256) { if (_token == ETH_ADDR) return 18; return IERC20(_token).decimals(); } } abstract contract IManager { function last(address) virtual public returns (uint); function cdpCan(address, uint, address) virtual public view returns (uint); function ilks(uint) virtual public view returns (bytes32); function owns(uint) virtual public view returns (address); function urns(uint) virtual public view returns (address); function vat() virtual public view returns (address); function open(bytes32, address) virtual public returns (uint); function give(uint, address) virtual public; function cdpAllow(uint, address, uint) virtual public; function urnAllow(address, uint) virtual public; function frob(uint, int, int) virtual public; function flux(uint, address, uint) virtual public; function move(uint, address, uint) virtual public; function exit(address, uint, address, uint) virtual public; function quit(uint, address) virtual public; function enter(address, uint) virtual public; function shift(uint, uint) virtual public; } abstract contract ICdpRegistry { function open( bytes32 ilk, address usr ) public virtual returns (uint256); function cdps(bytes32, address) virtual public view returns (uint256); function owns(uint) virtual public view returns (address); function ilks(uint) virtual public view returns (bytes32); } contract MainnetMcdAddresses { address internal constant POT_ADDR = 0x197E90f9FAD81970bA7976f33CbD77088E5D7cf7; address internal constant VAT_ADDR = 0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B; address internal constant DAI_JOIN_ADDR = 0x9759A6Ac90977b93B58547b4A71c78317f391A28; address internal constant JUG_ADDRESS = 0x19c0976f590D67707E62397C87829d896Dc0f1F1; address internal constant SPOTTER_ADDRESS = 0x65C79fcB50Ca1594B025960e539eD7A9a6D434A3; address internal constant PROXY_REGISTRY_ADDR = 0x4678f0a6958e4D2Bc4F1BAF7Bc52E8F3564f3fE4; address internal constant CDP_REGISTRY = 0xBe0274664Ca7A68d6b5dF826FB3CcB7c620bADF3; address internal constant CROPPER = 0x8377CD01a5834a6EaD3b7efb482f678f2092b77e; address internal constant MCD_MANAGER_ADDR = 0x5ef30b9986345249bc32d8928B7ee64DE9435E39; address internal constant DAI_ADDRESS = 0x6B175474E89094C44Da98b954EedeAC495271d0F; } contract McdHelper is DSMath, MainnetMcdAddresses { IVat public constant vat = IVat(VAT_ADDR); error IntOverflow(); /// @notice Returns a normalized debt _amount based on the current rate /// @param _amount Amount of dai to be normalized /// @param _rate Current rate of the stability fee /// @param _daiVatBalance Balance od Dai in the Vat for that CDP function normalizeDrawAmount(uint _amount, uint _rate, uint _daiVatBalance) internal pure returns (int dart) { if (_daiVatBalance < _amount * RAY) { dart = toPositiveInt((_amount * RAY - _daiVatBalance) / _rate); dart = uint(dart) * _rate < _amount * RAY ? dart + 1 : dart; } } /// @notice Converts a number to Rad precision /// @param _wad The input number in wad precision function toRad(uint _wad) internal pure returns (uint) { return _wad * (10 ** 27); } /// @notice Converts a number to 18 decimal precision /// @dev If token decimal is bigger than 18, function reverts /// @param _joinAddr Join address of the collateral /// @param _amount Number to be converted function convertTo18(address _joinAddr, uint256 _amount) internal view returns (uint256) { return _amount * (10 ** (18 - IJoin(_joinAddr).dec())); } /// @notice Converts a uint to int and checks if positive /// @param _x Number to be converted function toPositiveInt(uint _x) internal pure returns (int y) { y = int(_x); if (y < 0){ revert IntOverflow(); } } /// @notice Gets Dai amount in Vat which can be added to Cdp /// @param _vat Address of Vat contract /// @param _daiBalance Amount of dai in vat contract for that urn /// @param _urn Urn of the Cdp /// @param _ilk Ilk of the Cdp function normalizePaybackAmount(address _vat, uint256 _daiBalance, address _urn, bytes32 _ilk) internal view returns (int amount) { (, uint rate,,,) = IVat(_vat).ilks(_ilk); (, uint art) = IVat(_vat).urns(_ilk, _urn); amount = toPositiveInt(_daiBalance / rate); amount = uint(amount) <= art ? - amount : - toPositiveInt(art); } /// @notice Gets the whole debt of the CDP /// @param _vat Address of Vat contract /// @param _usr Address of the Dai holder /// @param _urn Urn of the Cdp /// @param _ilk Ilk of the Cdp function getAllDebt(address _vat, address _usr, address _urn, bytes32 _ilk) internal view returns (uint daiAmount) { (, uint rate,,,) = IVat(_vat).ilks(_ilk); (, uint art) = IVat(_vat).urns(_ilk, _urn); uint dai = IVat(_vat).dai(_usr); uint rad = art * rate - dai; daiAmount = rad / RAY; // handles precision error (off by 1 wei) daiAmount = daiAmount * RAY < rad ? daiAmount + 1 : daiAmount; } /// @notice Checks if the join address is one of the Ether coll. types /// @param _joinAddr Join address to check function isEthJoinAddr(address _joinAddr) internal view returns (bool) { // if it's dai_join_addr don't check gem() it will fail if (_joinAddr == DAI_JOIN_ADDR) return false; // if coll is weth it's and eth type coll if (address(IJoin(_joinAddr).gem()) == TokenUtils.WETH_ADDR) { return true; } return false; } /// @notice Returns the underlying token address from the joinAddr /// @dev For eth based collateral returns 0xEee... not weth addr /// @param _joinAddr Join address to check function getTokenFromJoin(address _joinAddr) internal view returns (address) { // if it's dai_join_addr don't check gem() it will fail, return dai addr if (_joinAddr == DAI_JOIN_ADDR) { return DAI_ADDRESS; } return address(IJoin(_joinAddr).gem()); } function getUrnAndIlk(address _mcdManager, uint256 _vaultId) public view returns (address urn, bytes32 ilk) { if (_mcdManager == CROPPER) { address owner = ICdpRegistry(CDP_REGISTRY).owns(_vaultId); urn = ICropper(CROPPER).proxy(owner); ilk = ICdpRegistry(CDP_REGISTRY).ilks(_vaultId); } else { urn = IManager(_mcdManager).urns(_vaultId); ilk = IManager(_mcdManager).ilks(_vaultId); } } /// @notice Gets CDP info (collateral, debt) /// @param _manager Manager contract /// @param _cdpId Id of the CDP /// @param _ilk Ilk of the CDP function getCdpInfo(IManager _manager, uint _cdpId, bytes32 _ilk) public view returns (uint, uint) { address urn; if (address(_manager) == CROPPER) { address owner = ICdpRegistry(CDP_REGISTRY).owns(_cdpId); urn = ICropper(CROPPER).proxy(owner); } else { urn = _manager.urns(_cdpId); } (uint collateral, uint debt) = vat.urns(_ilk, urn); (,uint rate,,,) = vat.ilks(_ilk); return (collateral, rmul(debt, rate)); } /// @notice Address that owns the DSProxy that owns the CDP /// @param _manager Manager contract /// @param _cdpId Id of the CDP function getOwner(IManager _manager, uint _cdpId) public view returns (address) { address owner; if (address(_manager) == CROPPER) { owner = ICdpRegistry(CDP_REGISTRY).owns(_cdpId); } else { owner = _manager.owns(_cdpId); } DSProxy proxy = DSProxy(payable(address(uint160(owner)))); return proxy.owner(); } /// @notice Returns all the collateral of the vault, formatted in the correct decimal /// @dev Will fail if token is over 18 decimals function getAllColl(IManager _mcdManager, address _joinAddr, uint _vaultId) internal view returns (uint amount) { bytes32 ilk; if (address(_mcdManager) == CROPPER) { ilk = ICdpRegistry(CDP_REGISTRY).ilks(_vaultId); } else { ilk = _mcdManager.ilks(_vaultId); } (amount, ) = getCdpInfo( _mcdManager, _vaultId, ilk ); if (IJoin(_joinAddr).dec() != 18) { return div(amount, 10 ** sub(18, IJoin(_joinAddr).dec())); } } } contract McdDsrWithdraw is McdHelper, ActionBase { using TokenUtils for address; struct Params { uint256 amount; // amount of DAI to withdraw from DSR address to; // address that will receive withdrawn DAI } /// @inheritdoc ActionBase function executeAction( bytes memory _callData, bytes32[] memory _subData, uint8[] memory _paramMapping, bytes32[] memory _returnValues ) public payable virtual override returns (bytes32) { Params memory params = parseInputs(_callData); params.amount = _parseParamUint(params.amount, _paramMapping[0], _subData, _returnValues); params.to = _parseParamAddr(params.to, _paramMapping[1], _subData, _returnValues); (uint256 withdrawn, bytes memory logData) = _withdraw(params); emit ActionEvent("McdDsrWithdraw", logData); return bytes32(withdrawn); } /// @inheritdoc ActionBase function executeActionDirect(bytes memory _callData) public payable virtual override { Params memory params = parseInputs(_callData); (, bytes memory logData) = _withdraw(params); logger.logActionDirectEvent("McdDsrWithdraw", logData); } /// @inheritdoc ActionBase function actionType() public pure virtual override returns (uint8) { return uint8(ActionType.STANDARD_ACTION); } function rdivup(uint256 x, uint256 y) internal pure returns (uint256) { return (x * RAY + y - 1) / y; } /// @notice Withdraws DAI from Maker DSR function _withdraw(Params memory _params) internal returns (uint256 withdrawn, bytes memory logData) { IPot pot = IPot(POT_ADDR); uint256 chi = (block.timestamp > pot.rho()) ? pot.drip() : pot.chi(); uint256 pie; if (_params.amount == type(uint256).max) { pie = pot.pie(address(this)); _params.amount = pie * chi / RAY; } else { pie = rdivup(_params.amount, chi); } pot.exit(pie); if (vat.can(address(this), DAI_JOIN_ADDR) == 0) { vat.hope(DAI_JOIN_ADDR); } IDaiJoin(DAI_JOIN_ADDR).exit(_params.to, _params.amount); logData = abi.encode(_params); withdrawn = _params.amount; } function parseInputs(bytes memory _callData) internal pure returns (Params memory inputData) { inputData = abi.decode(_callData, (Params)); } }