0x0::governance
Governance module handles the governance of the Pool
that it’s attached to.
Users with non zero stake can create proposals and vote on them. Winning
proposals are used to set the trade parameters for the next epoch.
Proposal
Governance
TradeParamsUpdateEvent
empty
set_whitelist
whitelisted
update
add_proposal
adjust_vote
adjust_voting_power
trade_params
stake_to_voting_power
new_proposal
remove_lowest_proposal
reset_trade_params
to_trade_params
use 0x0::constants;
use 0x0::math;
use 0x0::trade_params;
use 0x1::option;
use 0x1::u64;
use 0x2::event;
use 0x2::object;
use 0x2::tx_context;
use 0x2::vec_map;
Proposal
Proposal
struct that holds the parameters of a proposal and its current total votes.
struct Proposal has copy, drop, store
Governance
Details of a pool. This is refreshed every epoch by the first
State
action against this pool.
struct Governance has store
epoch: u64
whitelisted: bool
stable: bool
proposals: vec_map::VecMap<object::ID, governance::Proposal>
trade_params: trade_params::TradeParams
next_trade_params: trade_params::TradeParams
voting_power: u64
quorum: u64
TradeParamsUpdateEvent
Event emitted when trade parameters are updated.
struct TradeParamsUpdateEvent has copy, drop
const EInvalidMakerFee: u64 = 1;
const EInvalidTakerFee: u64 = 2;
const EMaxProposalsReachedNotEnoughVotes: u64 = 4;
const EProposalDoesNotExist: u64 = 3;
const EWhitelistedPoolCannotChange: u64 = 5;
const FEE_MULTIPLE: u64 = 1000;
const MAX_MAKER_STABLE: u64 = 50000;
const MAX_MAKER_VOLATILE: u64 = 500000;
const MAX_PROPOSALS: u64 = 100;
const MAX_TAKER_STABLE: u64 = 100000;
const MAX_TAKER_VOLATILE: u64 = 1000000;
const MIN_MAKER_STABLE: u64 = 20000;
const MIN_MAKER_VOLATILE: u64 = 200000;
const MIN_TAKER_STABLE: u64 = 50000;
const MIN_TAKER_VOLATILE: u64 = 500000;
const VOTING_POWER_THRESHOLD: u64 = 100000000000;
empty
public(friend) fun empty(stable_pool: bool, ctx: &tx_context::TxContext): governance::Governance
public(package) fun empty(stable_pool: bool, ctx: &TxContext): Governance {
let default_taker = if (stable_pool) {
MAX_TAKER_STABLE
} else {
MAX_TAKER_VOLATILE
};
let default_maker = if (stable_pool) {
MAX_MAKER_STABLE
} else {
MAX_MAKER_VOLATILE
};
Governance {
epoch: ctx.epoch(),
whitelisted: false,
stable: stable_pool,
proposals: vec_map::empty(),
trade_params: trade_params::new(
default_taker,
default_maker,
constants::default_stake_required(),
),
next_trade_params: trade_params::new(
default_taker,
default_maker,
constants::default_stake_required(),
),
voting_power: 0,
quorum: 0,
}
}
set_whitelist
Whitelist a pool. This pool can be used as a DEEP reference price for other pools. This pool will have zero fees.
public(friend) fun set_whitelist(self: &mut governance::Governance, whitelisted: bool)
public(package) fun set_whitelist(self: &mut Governance, whitelisted: bool) {
self.whitelisted = whitelisted;
self.reset_trade_params();
}
whitelisted
public(friend) fun whitelisted(self: &governance::Governance): bool
public(package) fun whitelisted(self: &Governance): bool {
self.whitelisted
}
update
Update the governance state. This is called at the start of every epoch.
public(friend) fun update(self: &mut governance::Governance, ctx: &tx_context::TxContext)
public(package) fun update(self: &mut Governance, ctx: &TxContext) {
let epoch = ctx.epoch();
if (self.epoch == epoch) return;
self.epoch = epoch;
self.quorum = math::mul(self.voting_power, constants::half());
self.proposals = vec_map::empty();
self.trade_params = self.next_trade_params;
event::emit(TradeParamsUpdateEvent {
taker_fee: self.trade_params.taker_fee(),
maker_fee: self.trade_params.maker_fee(),
stake_required: self.trade_params.stake_required(),
});
}
add_proposal
Add a new proposal to governance.
Check if proposer already voted, if so will give error.
If proposer has not voted, and there are already MAX_PROPOSALS proposals,
remove the proposal with the lowest votes if it has less votes than the voting power.
Validation of the account adding is done in State
.
public(friend) fun add_proposal(self: &mut governance::Governance, taker_fee: u64, maker_fee: u64, stake_required: u64, stake_amount: u64, balance_manager_id: object::ID)
public(package) fun add_proposal(
self: &mut Governance,
taker_fee: u64,
maker_fee: u64,
stake_required: u64,
stake_amount: u64,
balance_manager_id: ID,
) {
assert!(!self.whitelisted, EWhitelistedPoolCannotChange);
assert!(taker_fee % FEE_MULTIPLE == 0, EInvalidTakerFee);
assert!(maker_fee % FEE_MULTIPLE == 0, EInvalidMakerFee);
if (self.stable) {
assert!(
taker_fee >= MIN_TAKER_STABLE && taker_fee <= MAX_TAKER_STABLE,
EInvalidTakerFee,
);
assert!(
maker_fee >= MIN_MAKER_STABLE && maker_fee <= MAX_MAKER_STABLE,
EInvalidMakerFee,
);
} else {
assert!(
taker_fee >= MIN_TAKER_VOLATILE && taker_fee <= MAX_TAKER_VOLATILE,
EInvalidTakerFee,
);
assert!(
maker_fee >= MIN_MAKER_VOLATILE && maker_fee <= MAX_MAKER_VOLATILE,
EInvalidMakerFee,
);
};
let voting_power = stake_to_voting_power(stake_amount);
if (self.proposals.size() == MAX_PROPOSALS) {
self.remove_lowest_proposal(voting_power);
};
let new_proposal = new_proposal(taker_fee, maker_fee, stake_required);
self.proposals.insert(balance_manager_id, new_proposal);
}
adjust_vote
Vote on a proposal. Validation of the account and stake is done in State
.
If from_proposal_id
is some, the account is removing their vote from that proposal.
If to_proposal_id
is some, the account is voting for that proposal.
public(friend) fun adjust_vote(self: &mut governance::Governance, from_proposal_id: option::Option<object::ID>, to_proposal_id: option::Option<object::ID>, stake_amount: u64)
public(package) fun adjust_vote(
self: &mut Governance,
from_proposal_id: Option<ID>,
to_proposal_id: Option<ID>,
stake_amount: u64,
) {
let votes = stake_to_voting_power(stake_amount);
if (
from_proposal_id.is_some() && self
.proposals
.contains(from_proposal_id.borrow())
) {
let proposal = &mut self.proposals[from_proposal_id.borrow()];
proposal.votes = proposal.votes - votes;
if (
proposal.votes + votes > self.quorum && proposal.votes < self.quorum
) {
self.next_trade_params = self.trade_params;
};
};
to_proposal_id.do_ref!(|proposal_id| {
assert!(self.proposals.contains(proposal_id), EProposalDoesNotExist);
let proposal = &mut self.proposals[proposal_id];
proposal.votes = proposal.votes + votes;
if (proposal.votes > self.quorum) {
self.next_trade_params = proposal.to_trade_params();
};
});
}
adjust_voting_power
Adjust the total voting power by adding and removing stake. For example, if an account’s
stake goes from 2000 to 3000, then stake_before
is 2000 and stake_after
is 3000.
Validation of inputs done in State
.
public(friend) fun adjust_voting_power(self: &mut governance::Governance, stake_before: u64, stake_after: u64)
public(package) fun adjust_voting_power(
self: &mut Governance,
stake_before: u64,
stake_after: u64,
) {
self.voting_power =
self.voting_power +
stake_to_voting_power(stake_after) -
stake_to_voting_power(stake_before);
}
trade_params
public(friend) fun trade_params(self: &governance::Governance): trade_params::TradeParams
public(package) fun trade_params(self: &Governance): TradeParams {
self.trade_params
}
stake_to_voting_power
Convert stake to voting power.
fun stake_to_voting_power(stake: u64): u64
fun stake_to_voting_power(stake: u64): u64 {
let mut voting_power = stake.min(VOTING_POWER_THRESHOLD);
if (stake > VOTING_POWER_THRESHOLD) {
voting_power =
voting_power + math::sqrt(stake, constants::deep_unit()) -
math::sqrt(VOTING_POWER_THRESHOLD, constants::deep_unit());
};
voting_power
}
new_proposal
fun new_proposal(taker_fee: u64, maker_fee: u64, stake_required: u64): governance::Proposal
fun new_proposal(
taker_fee: u64,
maker_fee: u64,
stake_required: u64,
): Proposal {
Proposal { taker_fee, maker_fee, stake_required, votes: 0 }
}
remove_lowest_proposal
Remove the proposal with the lowest votes if it has less votes than the voting power. If there are multiple proposals with the same lowest votes, the latest one is removed.
fun remove_lowest_proposal(self: &mut governance::Governance, voting_power: u64)
fun remove_lowest_proposal(self: &mut Governance, voting_power: u64) {
let mut removal_id = option::none();
let mut cur_lowest_votes = constants::max_u64();
let (keys, values) = self.proposals.into_keys_values();
self.proposals.size().do!(|i| {
let proposal_votes = values[i].votes;
if (
proposal_votes < voting_power && proposal_votes <= cur_lowest_votes
) {
removal_id = option::some(keys[i]);
cur_lowest_votes = proposal_votes;
};
});
assert!(removal_id.is_some(), EMaxProposalsReachedNotEnoughVotes);
self.proposals.remove(removal_id.borrow());
}
reset_trade_params
fun reset_trade_params(self: &mut governance::Governance)
fun reset_trade_params(self: &mut Governance) {
self.proposals = vec_map::empty();
let stake = self.trade_params.stake_required();
if (self.whitelisted) {
self.trade_params = trade_params::new(0, 0, 0);
} else if (self.stable) {
self.trade_params =
trade_params::new(MAX_TAKER_STABLE, MAX_MAKER_STABLE, stake);
} else {
self.trade_params =
trade_params::new(MAX_TAKER_VOLATILE, MAX_MAKER_VOLATILE, stake);
};
self.next_trade_params = self.trade_params;
}
to_trade_params
fun to_trade_params(proposal: &governance::Proposal): trade_params::TradeParams
fun to_trade_params(proposal: &Proposal): TradeParams {
trade_params::new(
proposal.taker_fee,
proposal.maker_fee,
proposal.stake_required,
)
}