ETH Price: $2,450.43 (-0.08%)
Gas: 0.6 Gwei

Transaction Decoder

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 Code
(builder0x69)
2.568999616204797299 Eth2.569030922704797299 Eth0.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
      # @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))