ETH Price: $2,162.80 (+1.27%)

Transaction Decoder

Block:
23496060 at Oct-03-2025 08:28:11 AM +UTC
Transaction Fee:
0.000024590566042572 ETH $0.05
Gas Used:
112,314 Gas / 0.218944798 Gwei

Emitted Events:

811 Vyper_contract.Transfer( _from=[Receiver] 0xe1f9c8ebbc80a013caf0940fdd1a8554d763b9cf, _to=[Sender] 0x9b44473e223f8a3c047ad86f387b80402536b029, _value=1383227060747964811173 )
812 0xe1f9c8ebbc80a013caf0940fdd1a8554d763b9cf.0xc1405953cccdad6b442e266c84d66ad671e2534c6584f8e6ef92802f7ad294d5( 0xc1405953cccdad6b442e266c84d66ad671e2534c6584f8e6ef92802f7ad294d5, 0x0000000000000000000000009b44473e223f8a3c047ad86f387b80402536b029, 0x0000000000000000000000009b44473e223f8a3c047ad86f387b80402536b029, 0x000000000000000000000000d533a949740bb3306d119cc777fa900ba034cd52, 00000000000000000000000000000000000000000000004afc21b319661c87a5 )

Account State Difference:

  Address   Before After State Difference Code
(quasarbuilder)
8.39005214518988248 Eth8.390052146717128252 Eth0.000000001527245772
0x9B44473E...02536B029
0.003042480886426931 Eth
Nonce: 1260
0.003017890320384359 Eth
Nonce: 1261
0.000024590566042572
0xD533a949...bA034cd52
0xe1F9C8eb...4d763b9cf

Execution Trace

0xe1f9c8ebbc80a013caf0940fdd1a8554d763b9cf.5e1c75e1( )
  • Vyper_contract.transfer( _to=0x9B44473E223f8a3c047AD86f387B80402536B029, _value=1383227060747964811173 ) => ( True )
    # @version 0.2.4
    """
    @title Curve DAO Token
    @author Curve Finance
    @license MIT
    @notice ERC20 with piecewise-linear mining supply.
    @dev Based on the ERC-20 token standard as defined at
         https://eips.ethereum.org/EIPS/eip-20
    """
    
    from vyper.interfaces import ERC20
    
    implements: ERC20
    
    
    event Transfer:
        _from: indexed(address)
        _to: indexed(address)
        _value: uint256
    
    event Approval:
        _owner: indexed(address)
        _spender: indexed(address)
        _value: uint256
    
    event UpdateMiningParameters:
        time: uint256
        rate: uint256
        supply: uint256
    
    event SetMinter:
        minter: address
    
    event SetAdmin:
        admin: address
    
    
    name: public(String[64])
    symbol: public(String[32])
    decimals: public(uint256)
    
    balanceOf: public(HashMap[address, uint256])
    allowances: HashMap[address, HashMap[address, uint256]]
    total_supply: uint256
    
    minter: public(address)
    admin: public(address)
    
    # General constants
    YEAR: constant(uint256) = 86400 * 365
    
    # Allocation:
    # =========
    # * shareholders - 30%
    # * emplyees - 3%
    # * DAO-controlled reserve - 5%
    # * Early users - 5%
    # == 43% ==
    # left for inflation: 57%
    
    # Supply parameters
    INITIAL_SUPPLY: constant(uint256) = 1_303_030_303
    INITIAL_RATE: constant(uint256) = 274_815_283 * 10 ** 18 / YEAR  # leading to 43% premine
    RATE_REDUCTION_TIME: constant(uint256) = YEAR
    RATE_REDUCTION_COEFFICIENT: constant(uint256) = 1189207115002721024  # 2 ** (1/4) * 1e18
    RATE_DENOMINATOR: constant(uint256) = 10 ** 18
    INFLATION_DELAY: constant(uint256) = 86400
    
    # Supply variables
    mining_epoch: public(int128)
    start_epoch_time: public(uint256)
    rate: public(uint256)
    
    start_epoch_supply: uint256
    
    
    @external
    def __init__(_name: String[64], _symbol: String[32], _decimals: uint256):
        """
        @notice Contract constructor
        @param _name Token full name
        @param _symbol Token symbol
        @param _decimals Number of decimals for token
        """
        init_supply: uint256 = INITIAL_SUPPLY * 10 ** _decimals
        self.name = _name
        self.symbol = _symbol
        self.decimals = _decimals
        self.balanceOf[msg.sender] = init_supply
        self.total_supply = init_supply
        self.admin = msg.sender
        log Transfer(ZERO_ADDRESS, msg.sender, init_supply)
    
        self.start_epoch_time = block.timestamp + INFLATION_DELAY - RATE_REDUCTION_TIME
        self.mining_epoch = -1
        self.rate = 0
        self.start_epoch_supply = init_supply
    
    
    @internal
    def _update_mining_parameters():
        """
        @dev Update mining rate and supply at the start of the epoch
             Any modifying mining call must also call this
        """
        _rate: uint256 = self.rate
        _start_epoch_supply: uint256 = self.start_epoch_supply
    
        self.start_epoch_time += RATE_REDUCTION_TIME
        self.mining_epoch += 1
    
        if _rate == 0:
            _rate = INITIAL_RATE
        else:
            _start_epoch_supply += _rate * RATE_REDUCTION_TIME
            self.start_epoch_supply = _start_epoch_supply
            _rate = _rate * RATE_DENOMINATOR / RATE_REDUCTION_COEFFICIENT
    
        self.rate = _rate
    
        log UpdateMiningParameters(block.timestamp, _rate, _start_epoch_supply)
    
    
    @external
    def update_mining_parameters():
        """
        @notice Update mining rate and supply at the start of the epoch
        @dev Callable by any address, but only once per epoch
             Total supply becomes slightly larger if this function is called late
        """
        assert block.timestamp >= self.start_epoch_time + RATE_REDUCTION_TIME  # dev: too soon!
        self._update_mining_parameters()
    
    
    @external
    def start_epoch_time_write() -> uint256:
        """
        @notice Get timestamp of the current mining epoch start
                while simultaneously updating mining parameters
        @return Timestamp of the epoch
        """
        _start_epoch_time: uint256 = self.start_epoch_time
        if block.timestamp >= _start_epoch_time + RATE_REDUCTION_TIME:
            self._update_mining_parameters()
            return self.start_epoch_time
        else:
            return _start_epoch_time
    
    
    @external
    def future_epoch_time_write() -> uint256:
        """
        @notice Get timestamp of the next mining epoch start
                while simultaneously updating mining parameters
        @return Timestamp of the next epoch
        """
        _start_epoch_time: uint256 = self.start_epoch_time
        if block.timestamp >= _start_epoch_time + RATE_REDUCTION_TIME:
            self._update_mining_parameters()
            return self.start_epoch_time + RATE_REDUCTION_TIME
        else:
            return _start_epoch_time + RATE_REDUCTION_TIME
    
    
    @internal
    @view
    def _available_supply() -> uint256:
        return self.start_epoch_supply + (block.timestamp - self.start_epoch_time) * self.rate
    
    
    @external
    @view
    def available_supply() -> uint256:
        """
        @notice Current number of tokens in existence (claimed or unclaimed)
        """
        return self._available_supply()
    
    
    @external
    @view
    def mintable_in_timeframe(start: uint256, end: uint256) -> uint256:
        """
        @notice How much supply is mintable from start timestamp till end timestamp
        @param start Start of the time interval (timestamp)
        @param end End of the time interval (timestamp)
        @return Tokens mintable from `start` till `end`
        """
        assert start <= end  # dev: start > end
        to_mint: uint256 = 0
        current_epoch_time: uint256 = self.start_epoch_time
        current_rate: uint256 = self.rate
    
        # Special case if end is in future (not yet minted) epoch
        if end > current_epoch_time + RATE_REDUCTION_TIME:
            current_epoch_time += RATE_REDUCTION_TIME
            current_rate = current_rate * RATE_DENOMINATOR / RATE_REDUCTION_COEFFICIENT
    
        assert end <= current_epoch_time + RATE_REDUCTION_TIME  # dev: too far in future
    
        for i in range(999):  # Curve will not work in 1000 years. Darn!
            if end >= current_epoch_time:
                current_end: uint256 = end
                if current_end > current_epoch_time + RATE_REDUCTION_TIME:
                    current_end = current_epoch_time + RATE_REDUCTION_TIME
    
                current_start: uint256 = start
                if current_start >= current_epoch_time + RATE_REDUCTION_TIME:
                    break  # We should never get here but what if...
                elif current_start < current_epoch_time:
                    current_start = current_epoch_time
    
                to_mint += current_rate * (current_end - current_start)
    
                if start >= current_epoch_time:
                    break
    
            current_epoch_time -= RATE_REDUCTION_TIME
            current_rate = current_rate * RATE_REDUCTION_COEFFICIENT / RATE_DENOMINATOR  # double-division with rounding made rate a bit less => good
            assert current_rate <= INITIAL_RATE  # This should never happen
    
        return to_mint
    
    
    @external
    def set_minter(_minter: address):
        """
        @notice Set the minter address
        @dev Only callable once, when minter has not yet been set
        @param _minter Address of the minter
        """
        assert msg.sender == self.admin  # dev: admin only
        assert self.minter == ZERO_ADDRESS  # dev: can set the minter only once, at creation
        self.minter = _minter
        log SetMinter(_minter)
    
    
    @external
    def set_admin(_admin: address):
        """
        @notice Set the new admin.
        @dev After all is set up, admin only can change the token name
        @param _admin New admin address
        """
        assert msg.sender == self.admin  # dev: admin only
        self.admin = _admin
        log SetAdmin(_admin)
    
    
    @external
    @view
    def totalSupply() -> uint256:
        """
        @notice Total number of tokens in existence.
        """
        return self.total_supply
    
    
    @external
    @view
    def allowance(_owner : address, _spender : address) -> uint256:
        """
        @notice Check the amount of tokens that an owner allowed to a spender
        @param _owner The address which owns the funds
        @param _spender The address which will spend the funds
        @return uint256 specifying the amount of tokens still available for the spender
        """
        return self.allowances[_owner][_spender]
    
    
    @external
    def transfer(_to : address, _value : uint256) -> bool:
        """
        @notice Transfer `_value` tokens from `msg.sender` to `_to`
        @dev Vyper does not allow underflows, so the subtraction in
             this function will revert on an insufficient balance
        @param _to The address to transfer to
        @param _value The amount to be transferred
        @return bool success
        """
        assert _to != ZERO_ADDRESS  # dev: transfers to 0x0 are not allowed
        self.balanceOf[msg.sender] -= _value
        self.balanceOf[_to] += _value
        log Transfer(msg.sender, _to, _value)
        return True
    
    
    @external
    def transferFrom(_from : address, _to : address, _value : uint256) -> bool:
        """
         @notice Transfer `_value` tokens from `_from` to `_to`
         @param _from address The address which you want to send tokens from
         @param _to address The address which you want to transfer to
         @param _value uint256 the amount of tokens to be transferred
         @return bool success
        """
        assert _to != ZERO_ADDRESS  # dev: transfers to 0x0 are not allowed
        # NOTE: vyper does not allow underflows
        #       so the following subtraction would revert on insufficient balance
        self.balanceOf[_from] -= _value
        self.balanceOf[_to] += _value
        self.allowances[_from][msg.sender] -= _value
        log Transfer(_from, _to, _value)
        return True
    
    
    @external
    def approve(_spender : address, _value : uint256) -> bool:
        """
        @notice Approve `_spender` to transfer `_value` tokens on behalf of `msg.sender`
        @dev Approval may only be from zero -> nonzero or from nonzero -> zero in order
            to mitigate the potential race condition described here:
            https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
        @param _spender The address which will spend the funds
        @param _value The amount of tokens to be spent
        @return bool success
        """
        assert _value == 0 or self.allowances[msg.sender][_spender] == 0
        self.allowances[msg.sender][_spender] = _value
        log Approval(msg.sender, _spender, _value)
        return True
    
    
    @external
    def mint(_to: address, _value: uint256) -> bool:
        """
        @notice Mint `_value` tokens and assign them to `_to`
        @dev Emits a Transfer event originating from 0x00
        @param _to The account that will receive the created tokens
        @param _value The amount that will be created
        @return bool success
        """
        assert msg.sender == self.minter  # dev: minter only
        assert _to != ZERO_ADDRESS  # dev: zero address
    
        if block.timestamp >= self.start_epoch_time + RATE_REDUCTION_TIME:
            self._update_mining_parameters()
    
        _total_supply: uint256 = self.total_supply + _value
        assert _total_supply <= self._available_supply()  # dev: exceeds allowable mint amount
        self.total_supply = _total_supply
    
        self.balanceOf[_to] += _value
        log Transfer(ZERO_ADDRESS, _to, _value)
    
        return True
    
    
    @external
    def burn(_value: uint256) -> bool:
        """
        @notice Burn `_value` tokens belonging to `msg.sender`
        @dev Emits a Transfer event with a destination of 0x00
        @param _value The amount that will be burned
        @return bool success
        """
        self.balanceOf[msg.sender] -= _value
        self.total_supply -= _value
    
        log Transfer(msg.sender, ZERO_ADDRESS, _value)
        return True
    
    
    @external
    def set_name(_name: String[64], _symbol: String[32]):
        """
        @notice Change the token name and symbol to `_name` and `_symbol`
        @dev Only callable by the admin account
        @param _name New token name
        @param _symbol New token symbol
        """
        assert msg.sender == self.admin, "Only admin is allowed to change name"
        self.name = _name
        self.symbol = _symbol