Transaction Hash:
Block:
16769294 at Mar-06-2023 12:25:11 PM +UTC
Transaction Fee:
0.001480489212587526 ETH
$3.63
Gas Used:
62,613 Gas / 23.645077102 Gwei
Emitted Events:
160 |
Dai.Transfer( src=[Receiver] Vyper_contract, dst=[Sender] 0xa64f2228ccec96076c82abb903021c33859082f8, wad=129657534246575342465 )
|
161 |
Vyper_contract.Claim( recipient=[Sender] 0xa64f2228ccec96076c82abb903021c33859082f8, claimed=129657534246575342465 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x690B9A9E...Db4FaC990
Miner
| (builder0x69) | 2.568999616204797299 Eth | 2.569030922704797299 Eth | 0.0000313065 | |
0x6B175474...495271d0F | |||||
0x96A51692...03D8638b3 | |||||
0xA64f2228...3859082F8 |
0.084884144140445463 Eth
Nonce: 195
|
0.083403654927857937 Eth
Nonce: 196
| 0.001480489212587526 |
Execution Trace
Vyper_contract.claim( beneficiary=0xA64f2228cceC96076c82abb903021C33859082F8, amount=6363317570000000000000 )
Vyper_contract.claim( beneficiary=0xA64f2228cceC96076c82abb903021C33859082F8, amount=6363317570000000000000 )
-
Dai.transfer( dst=0xA64f2228cceC96076c82abb903021C33859082F8, wad=129657534246575342465 ) => ( True )
-
File 1 of 3: Vyper_contract
File 2 of 3: Dai
File 3 of 3: Vyper_contract
# @version 0.2.16 """ @title Simple Vesting Escrow @author Curve Finance, Yearn Finance @license MIT @notice Vests ERC20 tokens for a single address @dev Intended to be deployed many times via `VotingEscrowFactory` """ from vyper.interfaces import ERC20 event Fund: recipient: indexed(address) amount: uint256 event Claim: recipient: indexed(address) claimed: uint256 event RugPull: recipient: address rugged: uint256 event CommitOwnership: admin: address event ApplyOwnership: admin: address recipient: public(address) token: public(ERC20) start_time: public(uint256) end_time: public(uint256) cliff_length: public(uint256) total_locked: public(uint256) total_claimed: public(uint256) disabled_at: public(uint256) initialized: public(bool) admin: public(address) future_admin: public(address) @external def __init__(): # ensure that the original contract cannot be initialized self.initialized = True @external @nonreentrant('lock') def initialize( admin: address, token: address, recipient: address, amount: uint256, start_time: uint256, end_time: uint256, cliff_length: uint256, ) -> bool: """ @notice Initialize the contract. @dev This function is seperate from `__init__` because of the factory pattern used in `VestingEscrowFactory.deploy_vesting_contract`. It may be called once per deployment. @param admin Admin address @param token Address of the ERC20 token being distributed @param recipient Address to vest tokens for @param amount Amount of tokens being vested for `recipient` @param start_time Epoch time at which token distribution starts @param end_time Time until everything should be vested @param cliff_length Duration after which the first portion vests """ assert not self.initialized # dev: can only initialize once self.initialized = True self.token = ERC20(token) self.admin = admin self.start_time = start_time self.end_time = end_time self.cliff_length = cliff_length assert self.token.transferFrom(msg.sender, self, amount) # dev: could not fund escrow self.recipient = recipient self.disabled_at = end_time # Set to maximum time self.total_locked = amount log Fund(recipient, amount) return True @internal @view def _total_vested_at(time: uint256 = block.timestamp) -> uint256: start: uint256 = self.start_time end: uint256 = self.end_time locked: uint256 = self.total_locked if time < start + self.cliff_length: return 0 return min(locked * (time - start) / (end - start), locked) @internal @view def _unclaimed(time: uint256 = block.timestamp) -> uint256: return self._total_vested_at(time) - self.total_claimed @external @view def unclaimed() -> uint256: """ @notice Get the number of unclaimed, vested tokens for recipient """ # NOTE: if `rug_pull` is activated, limit by the activation timestamp return self._unclaimed(min(block.timestamp, self.disabled_at)) @internal @view def _locked(time: uint256 = block.timestamp) -> uint256: return self.total_locked - self._total_vested_at(time) @external @view def locked() -> uint256: """ @notice Get the number of locked tokens for recipient """ # NOTE: if `rug_pull` is activated, limit by the activation timestamp return self._locked(min(block.timestamp, self.disabled_at)) @external def claim(beneficiary: address = msg.sender, amount: uint256 = MAX_UINT256): """ @notice Claim tokens which have vested @param beneficiary Address to transfer claimed tokens to @param amount Amount of tokens to claim """ assert msg.sender == self.recipient # dev: not recipient claim_period_end: uint256 = min(block.timestamp, self.disabled_at) claimable: uint256 = min(self._unclaimed(claim_period_end), amount) self.total_claimed += claimable assert self.token.transfer(beneficiary, claimable) log Claim(beneficiary, claimable) @external def rug_pull(): """ @notice Disable further flow of tokens and clawback the unvested part to admin """ assert msg.sender == self.admin # dev: admin only # NOTE: Rugging more than once is futile self.disabled_at = block.timestamp ruggable: uint256 = self._locked() assert self.token.transfer(self.admin, ruggable) log RugPull(self.recipient, ruggable) @external def commit_transfer_ownership(addr: address): """ @notice Transfer ownership of the contract to `addr` @param addr Address to have ownership transferred to """ assert msg.sender == self.admin # dev: admin only self.future_admin = addr log CommitOwnership(addr) @external def apply_transfer_ownership(): """ @notice Apply pending ownership transfer """ assert msg.sender == self.future_admin # dev: future admin only self.admin = msg.sender self.future_admin = ZERO_ADDRESS log ApplyOwnership(msg.sender) @external def renounce_ownership(): """ @notice Renounce admin control of the escrow """ assert msg.sender == self.admin # dev: admin only self.future_admin = ZERO_ADDRESS self.admin = ZERO_ADDRESS log ApplyOwnership(ZERO_ADDRESS) @external def collect_dust(token: address): assert msg.sender == self.recipient # dev: recipient only assert (token != self.token.address or block.timestamp > self.disabled_at) assert ERC20(token).transfer(self.recipient, ERC20(token).balanceOf(self))
File 2 of 3: 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 3 of 3: Vyper_contract
# @version 0.2.16 """ @title Simple Vesting Escrow @author Curve Finance, Yearn Finance @license MIT @notice Vests ERC20 tokens for a single address @dev Intended to be deployed many times via `VotingEscrowFactory` """ from vyper.interfaces import ERC20 event Fund: recipient: indexed(address) amount: uint256 event Claim: recipient: indexed(address) claimed: uint256 event RugPull: recipient: address rugged: uint256 event CommitOwnership: admin: address event ApplyOwnership: admin: address recipient: public(address) token: public(ERC20) start_time: public(uint256) end_time: public(uint256) cliff_length: public(uint256) total_locked: public(uint256) total_claimed: public(uint256) disabled_at: public(uint256) initialized: public(bool) admin: public(address) future_admin: public(address) @external def __init__(): # ensure that the original contract cannot be initialized self.initialized = True @external @nonreentrant('lock') def initialize( admin: address, token: address, recipient: address, amount: uint256, start_time: uint256, end_time: uint256, cliff_length: uint256, ) -> bool: """ @notice Initialize the contract. @dev This function is seperate from `__init__` because of the factory pattern used in `VestingEscrowFactory.deploy_vesting_contract`. It may be called once per deployment. @param admin Admin address @param token Address of the ERC20 token being distributed @param recipient Address to vest tokens for @param amount Amount of tokens being vested for `recipient` @param start_time Epoch time at which token distribution starts @param end_time Time until everything should be vested @param cliff_length Duration after which the first portion vests """ assert not self.initialized # dev: can only initialize once self.initialized = True self.token = ERC20(token) self.admin = admin self.start_time = start_time self.end_time = end_time self.cliff_length = cliff_length assert self.token.transferFrom(msg.sender, self, amount) # dev: could not fund escrow self.recipient = recipient self.disabled_at = end_time # Set to maximum time self.total_locked = amount log Fund(recipient, amount) return True @internal @view def _total_vested_at(time: uint256 = block.timestamp) -> uint256: start: uint256 = self.start_time end: uint256 = self.end_time locked: uint256 = self.total_locked if time < start + self.cliff_length: return 0 return min(locked * (time - start) / (end - start), locked) @internal @view def _unclaimed(time: uint256 = block.timestamp) -> uint256: return self._total_vested_at(time) - self.total_claimed @external @view def unclaimed() -> uint256: """ @notice Get the number of unclaimed, vested tokens for recipient """ # NOTE: if `rug_pull` is activated, limit by the activation timestamp return self._unclaimed(min(block.timestamp, self.disabled_at)) @internal @view def _locked(time: uint256 = block.timestamp) -> uint256: return self.total_locked - self._total_vested_at(time) @external @view def locked() -> uint256: """ @notice Get the number of locked tokens for recipient """ # NOTE: if `rug_pull` is activated, limit by the activation timestamp return self._locked(min(block.timestamp, self.disabled_at)) @external def claim(beneficiary: address = msg.sender, amount: uint256 = MAX_UINT256): """ @notice Claim tokens which have vested @param beneficiary Address to transfer claimed tokens to @param amount Amount of tokens to claim """ assert msg.sender == self.recipient # dev: not recipient claim_period_end: uint256 = min(block.timestamp, self.disabled_at) claimable: uint256 = min(self._unclaimed(claim_period_end), amount) self.total_claimed += claimable assert self.token.transfer(beneficiary, claimable) log Claim(beneficiary, claimable) @external def rug_pull(): """ @notice Disable further flow of tokens and clawback the unvested part to admin """ assert msg.sender == self.admin # dev: admin only # NOTE: Rugging more than once is futile self.disabled_at = block.timestamp ruggable: uint256 = self._locked() assert self.token.transfer(self.admin, ruggable) log RugPull(self.recipient, ruggable) @external def commit_transfer_ownership(addr: address): """ @notice Transfer ownership of the contract to `addr` @param addr Address to have ownership transferred to """ assert msg.sender == self.admin # dev: admin only self.future_admin = addr log CommitOwnership(addr) @external def apply_transfer_ownership(): """ @notice Apply pending ownership transfer """ assert msg.sender == self.future_admin # dev: future admin only self.admin = msg.sender self.future_admin = ZERO_ADDRESS log ApplyOwnership(msg.sender) @external def renounce_ownership(): """ @notice Renounce admin control of the escrow """ assert msg.sender == self.admin # dev: admin only self.future_admin = ZERO_ADDRESS self.admin = ZERO_ADDRESS log ApplyOwnership(ZERO_ADDRESS) @external def collect_dust(token: address): assert msg.sender == self.recipient # dev: recipient only assert (token != self.token.address or block.timestamp > self.disabled_at) assert ERC20(token).transfer(self.recipient, ERC20(token).balanceOf(self))