ETH Price: $2,412.35 (+7.35%)

Transaction Decoder

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 Code
0x197E90f9...88E5D7cf7
(Sky: MCD Pot)
0x35D1b3F3...259A0492B
(Sky: MCD Vat)
(Titan Builder)
7.452051343382539331 Eth7.452068874182539331 Eth0.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 )
      • Pot.exit( wad=217448960680902775100 )
        • Vat.move( src=0x197E90f9FAD81970bA7976f33CbD77088E5D7cf7, dst=0x2129F8a9b6C3092a600Da82Ce859B7A9a69983E4, rad=240000000000000000001066842940223559749898658200 )
        • Vat.can( 0x2129F8a9b6C3092a600Da82Ce859B7A9a69983E4, 0x9759A6Ac90977b93B58547b4A71c78317f391A28 ) => ( 1 )
        • DaiJoin.exit( usr=0x5D4b2A02c59197eB2cAe95A6Df9fE27af60459d4, wad=240000000000000000000 )
          • Vat.move( src=0x2129F8a9b6C3092a600Da82Ce859B7A9a69983E4, dst=0x9759A6Ac90977b93B58547b4A71c78317f391A28, rad=240000000000000000000000000000000000000000000000 )
          • Dai.mint( usr=0x5D4b2A02c59197eB2cAe95A6Df9fE27af60459d4, wad=240000000000000000000 )
          • DefisaverLogger.logActionDirectEvent( _logName=McdDsrWithdraw, _data=0x00000000000000000000000000000000000000000000000D02AB486CEDC000000000000000000000000000005D4B2A02C59197EB2CAE95A6DF9FE27AF60459D4 )
            File 1 of 7: DSProxy
            // 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));
                }
            }