LiquidityGaugeV1
Source Code
Source code of the LiquidityGaugeV1 can be found on Github. The following view methods and functions are using the 3pool as an example.
Deposit and Withdrawals¶
deposit
¶
LiquidityGauge.deposit(_value: uint256, addr: address = msg.sender):
Function to deposit _value
amount of LP tokens into the gauge.
Emits: Deposit
Input | Type | Description |
---|---|---|
_value | uint256 | Amount of tokens to deposit |
addr | address | Address to deposit for. Defaults to msg.sender |
Warning
Prior to depositing, ensure that the gauge has been approved to transfer amount
LP tokens on behalf of the caller (see set_approve_deposit
).
Source code
event Deposit:
provider: indexed(address)
value: uint256
# caller -> recipient -> can deposit?
approved_to_deposit: public(HashMap[address, HashMap[address, bool]])
@external
@nonreentrant('lock')
def deposit(_value: uint256, addr: address = msg.sender):
"""
@notice Deposit `_value` LP tokens
@param _value Number of tokens to deposit
@param addr Address to deposit for
"""
if addr != msg.sender:
assert self.approved_to_deposit[msg.sender][addr], "Not approved"
self._checkpoint(addr)
if _value != 0:
_balance: uint256 = self.balanceOf[addr] + _value
_supply: uint256 = self.totalSupply + _value
self.balanceOf[addr] = _balance
self.totalSupply = _supply
self._update_liquidity_limit(addr, _balance, _supply)
assert ERC20(self.lp_token).transferFrom(msg.sender, self, _value)
log Deposit(addr, _value)
withdraw
¶
LiquidityGauge.withdraw(_value: uint256):
Function to withdraw _value
LP tokens from the gauge.
Emits: Withdraw
Input | Type | Description |
---|---|---|
_value | uint256 | Amount of tokens to withdraw |
Source code
event Withdraw:
provider: indexed(address)
value: uint256
@external
@nonreentrant('lock')
def withdraw(_value: uint256):
"""
@notice Withdraw `_value` LP tokens
@param _value Number of tokens to withdraw
"""
self._checkpoint(msg.sender)
_balance: uint256 = self.balanceOf[msg.sender] - _value
_supply: uint256 = self.totalSupply - _value
self.balanceOf[msg.sender] = _balance
self.totalSupply = _supply
self._update_liquidity_limit(msg.sender, _balance, _supply)
assert ERC20(self.lp_token).transfer(msg.sender, _value)
log Withdraw(msg.sender, _value)
approved_to_deposit
¶
LiquidityGauge.approved_to_deposit(arg0: address, arg1: address) -> bool: view
Getter method to check if address arg0
is approved to deposit for address arg1
. Can be modified by calling set_approve_deposit
.
Returns: true or flase (bool
).
Input | Type | Description |
---|---|---|
arg0 | address | Caller |
arg1 | address | Recipient |
Source code
# caller -> recipient -> can deposit?
approved_to_deposit: public(HashMap[address, HashMap[address, bool]])
@external
def set_approve_deposit(addr: address, can_deposit: bool):
"""
@notice Set whether `addr` can deposit tokens for `msg.sender`
@param addr Address to set approval on
@param can_deposit bool - can this account deposit for `msg.sender`?
"""
self.approved_to_deposit[addr][msg.sender] = can_deposit
set_approve_deposit
¶
LiquidityGauge.set_approve_deposit(addr: address, can_deposit: bool):
Function to approve or revoke premission for address (arg0
) to deposit into the gauge on behalf of the caller.
Input | Type | Description |
---|---|---|
addr | address | Address to set approval for |
can_deposit | bool | true or false |
Source code
# caller -> recipient -> can deposit?
approved_to_deposit: public(HashMap[address, HashMap[address, bool]])
@external
def set_approve_deposit(addr: address, can_deposit: bool):
"""
@notice Set whether `addr` can deposit tokens for `msg.sender`
@param addr Address to set approval on
@param can_deposit bool - can this account deposit for `msg.sender`?
"""
self.approved_to_deposit[addr][msg.sender] = can_deposit
Contract Info Methods¶
minter
¶
LiquidityGauge.minter() -> address: view
Getter for the minter contract address.
Returns: minter contract (address
).
Source code
interface Minter:
def token() -> address: view
def controller() -> address: view
def minted(user: address, gauge: address) -> uint256: view
minter: public(address)
@external
def __init__(lp_addr: address, _minter: address):
"""
@notice Contract constructor
@param lp_addr Liquidity Pool contract address
@param _minter Minter contract address
"""
assert lp_addr != ZERO_ADDRESS
assert _minter != ZERO_ADDRESS
self.lp_token = lp_addr
self.minter = _minter
crv_addr: address = Minter(_minter).token()
self.crv_token = crv_addr
controller_addr: address = Minter(_minter).controller()
self.controller = controller_addr
self.voting_escrow = Controller(controller_addr).voting_escrow()
self.period_timestamp[0] = block.timestamp
self.inflation_rate = CRV20(crv_addr).rate()
self.future_epoch_time = CRV20(crv_addr).future_epoch_time_write()
crv_token
¶
LiquidityGauge.crv_token() -> address: view
Getter for the CRV token address.
Returns: CRV token (address
).
Source code
crv_token: public(address)
@external
def __init__(lp_addr: address, _minter: address):
"""
@notice Contract constructor
@param lp_addr Liquidity Pool contract address
@param _minter Minter contract address
"""
assert lp_addr != ZERO_ADDRESS
assert _minter != ZERO_ADDRESS
self.lp_token = lp_addr
self.minter = _minter
crv_addr: address = Minter(_minter).token()
self.crv_token = crv_addr
controller_addr: address = Minter(_minter).controller()
self.controller = controller_addr
self.voting_escrow = Controller(controller_addr).voting_escrow()
self.period_timestamp[0] = block.timestamp
self.inflation_rate = CRV20(crv_addr).rate()
self.future_epoch_time = CRV20(crv_addr).future_epoch_time_write()
lp_token
¶
LiquidityGauge.lp_token() -> address: view
Getter for the lp token of the liquidity pool.
Returns: lp token (address
).
Source code
lp_token: public(address)
@external
def __init__(lp_addr: address, _minter: address):
"""
@notice Contract constructor
@param lp_addr Liquidity Pool contract address
@param _minter Minter contract address
"""
assert lp_addr != ZERO_ADDRESS
assert _minter != ZERO_ADDRESS
self.lp_token = lp_addr
self.minter = _minter
crv_addr: address = Minter(_minter).token()
self.crv_token = crv_addr
controller_addr: address = Minter(_minter).controller()
self.controller = controller_addr
self.voting_escrow = Controller(controller_addr).voting_escrow()
self.period_timestamp[0] = block.timestamp
self.inflation_rate = CRV20(crv_addr).rate()
self.future_epoch_time = CRV20(crv_addr).future_epoch_time_write()
controller
¶
LiquidityGauge.controller() -> address: view
Getter for the gauge controller contract.
Returns: gauge controller contract (address
).
Source code
controller: public(address)
@external
def __init__(lp_addr: address, _minter: address):
"""
@notice Contract constructor
@param lp_addr Liquidity Pool contract address
@param _minter Minter contract address
"""
assert lp_addr != ZERO_ADDRESS
assert _minter != ZERO_ADDRESS
self.lp_token = lp_addr
self.minter = _minter
crv_addr: address = Minter(_minter).token()
self.crv_token = crv_addr
controller_addr: address = Minter(_minter).controller()
self.controller = controller_addr
self.voting_escrow = Controller(controller_addr).voting_escrow()
self.period_timestamp[0] = block.timestamp
self.inflation_rate = CRV20(crv_addr).rate()
self.future_epoch_time = CRV20(crv_addr).future_epoch_time_write()
voting_escorw
¶
LiquidityGauge.voting_escrow() -> address: view
Getter for the voting-escrow token.
Returns: voting-escorw contract (address
).
Source code
controller: public(address)
@external
def __init__(lp_addr: address, _minter: address):
"""
@notice Contract constructor
@param lp_addr Liquidity Pool contract address
@param _minter Minter contract address
"""
assert lp_addr != ZERO_ADDRESS
assert _minter != ZERO_ADDRESS
self.lp_token = lp_addr
self.minter = _minter
crv_addr: address = Minter(_minter).token()
self.crv_token = crv_addr
controller_addr: address = Minter(_minter).controller()
self.controller = controller_addr
self.voting_escrow = Controller(controller_addr).voting_escrow()
self.period_timestamp[0] = block.timestamp
self.inflation_rate = CRV20(crv_addr).rate()
self.future_epoch_time = CRV20(crv_addr).future_epoch_time_write()
totalSupply
¶
LiquidityGauge.totalSupply() -> uint256: view
Getter for the total supply of the lp token.
Returns: total lp token supply (uint256
).
future_epoch_time
¶
LiquidityGauge.future_epoch_time() -> uint256: view
Getter for the future epoch time.
Returns: future epoch timestamp (uint256
).
Source code
interface CRV20:
def future_epoch_time_write() -> uint256: nonpayable
def rate() -> uint256: view
future_epoch_time: public(uint256)
@external
def __init__(lp_addr: address, _minter: address):
"""
@notice Contract constructor
@param lp_addr Liquidity Pool contract address
@param _minter Minter contract address
"""
assert lp_addr != ZERO_ADDRESS
assert _minter != ZERO_ADDRESS
self.lp_token = lp_addr
self.minter = _minter
crv_addr: address = Minter(_minter).token()
self.crv_token = crv_addr
controller_addr: address = Minter(_minter).controller()
self.controller = controller_addr
self.voting_escrow = Controller(controller_addr).voting_escrow()
self.period_timestamp[0] = block.timestamp
self.inflation_rate = CRV20(crv_addr).rate()
self.future_epoch_time = CRV20(crv_addr).future_epoch_time_write()
@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
working_supply
¶
LiquidityGauge.working_supply() -> uint256: view
Getter for the working supply.
Returns: working supply (uint256
).
period
¶
LiquidityGauge.period() -> int128: view
Getter for the current period. This variable is updated whenever _checkpoint
function is called.
Returns: period (int128
).
Source code
interface Controller:
def period() -> int128: view
def period_write() -> int128: nonpayable
def period_timestamp(p: int128) -> uint256: view
def gauge_relative_weight(addr: address, time: uint256) -> uint256: view
def voting_escrow() -> address: view
def checkpoint(): nonpayable
def checkpoint_gauge(addr: address): nonpayable
period: public(int128)
@internal
def _checkpoint(addr: address):
"""
@notice Checkpoint for a user
@param addr User address
"""
_token: address = self.crv_token
_controller: address = self.controller
_period: int128 = self.period
_period_time: uint256 = self.period_timestamp[_period]
_integrate_inv_supply: uint256 = self.integrate_inv_supply[_period]
rate: uint256 = self.inflation_rate
new_rate: uint256 = rate
prev_future_epoch: uint256 = self.future_epoch_time
if prev_future_epoch >= _period_time:
self.future_epoch_time = CRV20(_token).future_epoch_time_write()
new_rate = CRV20(_token).rate()
self.inflation_rate = new_rate
Controller(_controller).checkpoint_gauge(self)
_working_balance: uint256 = self.working_balances[addr]
_working_supply: uint256 = self.working_supply
# Update integral of 1/supply
if block.timestamp > _period_time:
prev_week_time: uint256 = _period_time
week_time: uint256 = min((_period_time + WEEK) / WEEK * WEEK, block.timestamp)
for i in range(500):
dt: uint256 = week_time - prev_week_time
w: uint256 = Controller(_controller).gauge_relative_weight(self, prev_week_time / WEEK * WEEK)
if _working_supply > 0:
if prev_future_epoch >= prev_week_time and prev_future_epoch < week_time:
# If we went across one or multiple epochs, apply the rate
# of the first epoch until it ends, and then the rate of
# the last epoch.
# If more than one epoch is crossed - the gauge gets less,
# but that'd meen it wasn't called for more than 1 year
_integrate_inv_supply += rate * w * (prev_future_epoch - prev_week_time) / _working_supply
rate = new_rate
_integrate_inv_supply += rate * w * (week_time - prev_future_epoch) / _working_supply
else:
_integrate_inv_supply += rate * w * dt / _working_supply
# On precisions of the calculation
# rate ~= 10e18
# last_weight > 0.01 * 1e18 = 1e16 (if pool weight is 1%)
# _working_supply ~= TVL * 1e18 ~= 1e26 ($100M for example)
# The largest loss is at dt = 1
# Loss is 1e-9 - acceptable
if week_time == block.timestamp:
break
prev_week_time = week_time
week_time = min(week_time + WEEK, block.timestamp)
_period += 1
self.period = _period
self.period_timestamp[_period] = block.timestamp
self.integrate_inv_supply[_period] = _integrate_inv_supply
# Update user-specific integrals
self.integrate_fraction[addr] += _working_balance * (_integrate_inv_supply - self.integrate_inv_supply_of[addr]) / 10 ** 18
self.integrate_inv_supply_of[addr] = _integrate_inv_supply
self.integrate_checkpoint_of[addr] = block.timestamp
period_timestamp
¶
LiquidityGauge.period_timestamp(arg0: uint256) -> uint256: view
Getter for timestamp of period arg0
.
Returns: period timestamp (uint256
).
Input | Type | Description |
---|---|---|
arg0 | uint256 | Period (period ) |
Source code
period_timestamp: public(uint256[100000000000000000000000000000])
@external
def __init__(lp_addr: address, _minter: address):
"""
@notice Contract constructor
@param lp_addr Liquidity Pool contract address
@param _minter Minter contract address
"""
assert lp_addr != ZERO_ADDRESS
assert _minter != ZERO_ADDRESS
self.lp_token = lp_addr
self.minter = _minter
crv_addr: address = Minter(_minter).token()
self.crv_token = crv_addr
controller_addr: address = Minter(_minter).controller()
self.controller = controller_addr
self.voting_escrow = Controller(controller_addr).voting_escrow()
self.period_timestamp[0] = block.timestamp
self.inflation_rate = CRV20(crv_addr).rate()
self.future_epoch_time = CRV20(crv_addr).future_epoch_time_write()
inflation_rate
¶
LiquidityGauge.inflation_rate() -> uint256: view
Getter for the current inflation rate.
Returns: rate (uint256
).
Source code
interface CRV20:
def future_epoch_time_write() -> uint256: nonpayable
def rate() -> uint256: view
inflation_rate: public(uint256)
@external
def __init__(lp_addr: address, _minter: address):
"""
@notice Contract constructor
@param lp_addr Liquidity Pool contract address
@param _minter Minter contract address
"""
assert lp_addr != ZERO_ADDRESS
assert _minter != ZERO_ADDRESS
self.lp_token = lp_addr
self.minter = _minter
crv_addr: address = Minter(_minter).token()
self.crv_token = crv_addr
controller_addr: address = Minter(_minter).controller()
self.controller = controller_addr
self.voting_escrow = Controller(controller_addr).voting_escrow()
self.period_timestamp[0] = block.timestamp
self.inflation_rate = CRV20(crv_addr).rate()
self.future_epoch_time = CRV20(crv_addr).future_epoch_time_write()
User Info Methods¶
balanceOf
¶
LiquidityGauge.balaceOf(arg0: address) -> uint256: view
Getter method for the current amount of LP tokens that addr
has deposited into the gauge.
Returns: amount (uint256
).
Input | Type | Description |
---|---|---|
arg0 | address | User Address |
working_balances
(more on this)¶
LiquidityGauge.working_balances(arg0: address) -> uint256: view
Getter for the working balance of user arg0
.
Returns: working balance (uint256
).
Input | Type | Description |
---|---|---|
arg0 | address | User Address |
Tip
Working balance essentially is the effective balance after the has been applied.
Checkpoints¶
user_checkpoint
¶
LiquidityGauge.user_checkpoint(addr: address) -> bool:
Function to record a checkpoint for addr
and therefore updating their boost.
Returns: True (bool
).
Input | Type | Description |
---|---|---|
addr | address | Address |
Source code
@external
def user_checkpoint(addr: address) -> bool:
"""
@notice Record a checkpoint for `addr`
@param addr User address
@return bool success
"""
assert (msg.sender == addr) or (msg.sender == self.minter) # dev: unauthorized
self._checkpoint(addr)
self._update_liquidity_limit(addr, self.balanceOf[addr], self.totalSupply)
return True
Note
This function is only callable by addr
or Minter
. Checkpoint can not be triggered for another user.
kick
¶
LiquidityGauge.kick(addr: address):
Function to kick address addr
for abusing their boost.
Returns: True (bool
)
Input | Type | Description |
---|---|---|
addr | address | Address to kick |
Source code
@external
def kick(addr: address):
"""
@notice Kick `addr` for abusing their boost
@dev Only if either they had another voting event, or their voting escrow lock expired
@param addr Address to kick
"""
_voting_escrow: address = self.voting_escrow
t_last: uint256 = self.integrate_checkpoint_of[addr]
t_ve: uint256 = VotingEscrow(_voting_escrow).user_point_history__ts(
addr, VotingEscrow(_voting_escrow).user_point_epoch(addr)
)
_balance: uint256 = self.balanceOf[addr]
assert ERC20(self.voting_escrow).balanceOf(addr) == 0 or t_ve > t_last # dev: kick not allowed
assert self.working_balances[addr] > _balance * TOKENLESS_PRODUCTION / 100 # dev: kick not needed
self._checkpoint(addr)
self._update_liquidity_limit(addr, self.balanceOf[addr], self.totalSupply)
return True
Warning
This function is only callable when the current boost for addr
is greater than it should be, due to an expired veCRV lock.
integrate_fraction
(todo)¶
LiquidityGauge.integrate_fraction(arg0: address) -> uint256: view
Getter for the integrate fraction.
Returns: integrate fraction (uint256
).
Input | Type | Description |
---|---|---|
arg0 | address | User Address |
Source code
integrate_inv_supply
(todo)¶
LiquidityGauge.integrate_inv_supply(arg0: uint256) -> uint256: view
Getter for the integrate inverse supply at period arg0
.
Returns:
Input | Type | Description |
---|---|---|
arg0 | address | Address |
Source code
integrate_inv_supply_of
(todo)¶
LiquidityGauge.integrate_inv_supply_of(arg0: address) -> uint256: view
todo
Returns:
Input | Type | Description |
---|---|---|
arg0 | address | Address |
Source code
integrate_checkpoint_of
(todo)¶
LiquidityGauge.integrate_checkpoint_of(arg0: address) -> uint256: view
todo
Returns:
Input | Type | Description |
---|---|---|
arg0 | address | Address |