The Curve DAO has a total of three Aragon Agent ownership addresses, which are governed by two independent DAOs:
The Community DAO (or just “the DAO”) governs the day-to-day operation of the protocol.
Voting is based on a user’s holdings of “Vote-Escrowed CRV” (veCRV). veCRV is obtained by locking CRV for up to 4 years, with 1 veCRV equal to 1 CRV locked for 4 years. As the lock time decreases, an account’s veCRV balance decreases linearly as the time remaining until unlock decreases. veCRV is non-transferrable.
An account must have a minimum balance of 2500 veCRV to make a DAO vote. Each vote lasts for one week. Votes cannot be executed after the they successfully passed.
Contract Source & Deployment
CurveOwnershipAgent contract is deployed to the Ethereum mainnet at: 0x40907540d8a6C65c637785e8f8B742ae6b0b9968.
CurveParameterAgent contract is deployed to the Ethereum mainnet at: 0x4EEb3bA4f221cA16ed4A0cC7254E2E32DF948c5f.
The ownership admin controls most functionality within the protocol. Performing an action via the ownership admin requires a 30% quorum with 51% support.
The parameter admin has authority to modify parameters on pools, such as adjusting the amplification co-efficient. Performing an action via the paramater admin requries a 15% quorum with 51% support.
The EmergencyDAO has limited authority to kill non-factory pools and gauges during extraordinary circumstances.
Contract Source & Deployment
EmergencyDAO contract is deployed to the Ethereum mainnet at: 0x467947EE34aF926cF1DCac093870f613C96B1E0c.
This DAO consists of nine members, comprised of a mix of the Curve team and prominent figures within the DeFi community. Each member has one vote. Any member may propose a vote.
All members of the EmergencyDAO may propose new votes. A vote lasts for 24 hours and can be executed immediately once it receives 66% support.
|Name||Details - Telegram|
| || ||@banteg|
| || ||@calchulus|
| || ||@C2tP|
| || ||@Daryllautk|
| || |
| || ||@nagakingg|
| || |
| || ||@0xaddi|
| || |
To obtain the current members' addresses, query
getOwners within the EmergencyDAO contract.
Non-Factory liquidity pools can be killed by calling the
kill_me() function of the pool. The function can only be called within the first 2 months after deploying the pool.
This function is only callable by the
admin of the pools.
kill_me() sets the
is_killed variable of the pool to True. By doing this, the contract prevents users from performing actions such as
remove_liquidity_one_coin. Users can only remove funds by calling
In general, a pool can be "unkilled" again by calling the function
unkill_me(). This reverts the changes made when it was killed."
is_killed: bool kill_deadline: uint256 KILL_DEADLINE_DT: constant(uint256) = 2 * 30 * 86400 @external def kill_me(): assert msg.sender == self.owner # dev: only owner assert self.kill_deadline > block.timestamp # dev: deadline has passed self.is_killed = True @external def unkill_me(): assert msg.sender == self.owner # dev: only owner self.is_killed = False
Gauges can be killed by calling the
set_killed() function on the corresponding gauge, thereby setting the
is_killed variable to True.
By doing this, the rate of the gauge is set to 0, effectively stopping all the $CRV emissions.
is_killed: public(bool) @external def set_killed(_is_killed: bool): """ @notice Set the killed status for this contract @dev When killed, the gauge always yields a rate of 0 and so cannot mint CRV @param _is_killed Killed status to set """ assert msg.sender == self.admin self.is_killed = _is_killed @internal def _checkpoint(addr: address): """ @notice Checkpoint for a user @param addr User address """ _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(CRV).future_epoch_time_write() new_rate = CRV20(CRV).rate() self.inflation_rate = new_rate if self.is_killed: # Stop distributing inflation as soon as killed rate = 0 # Update integral of 1/supply if block.timestamp > _period_time: _working_supply: uint256 = self.working_supply Controller(GAUGE_CONTROLLER).checkpoint_gauge(self) 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(GAUGE_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 _working_balance: uint256 = self.working_balances[addr] 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