You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
229 lines
8.2 KiB
229 lines
8.2 KiB
import "./messages"; |
|
|
|
|
|
|
|
struct Proposal { |
|
type: Int; // 0 - Blacklist, 1 - Full liquidation, 2 - Change percents |
|
data: Cell; // Address for blacklist, Distribution for percents |
|
} |
|
|
|
struct Vote { |
|
id: Int; |
|
votes: map[Int]Int; // -1 - no vote, 0 - abstain, 1 - yes, 2 - no |
|
vote_end: Int; |
|
proposal: Proposal; |
|
quorum_percent: Int; |
|
ended: Bool = false; |
|
result: Bool?; // None - no result, True - passed, False - failed |
|
} |
|
|
|
fun vote_res(prop: Proposal): Cell? { |
|
if (prop.type == 0) { |
|
return BlacklistWallet { |
|
wallet: prop.data.readAddress() |
|
}.asCell(); |
|
} |
|
if (prop.type == 1) { |
|
// todo |
|
} |
|
} |
|
|
|
contract Foundation { |
|
founders: AddressList; // aka superadmins |
|
admins: Distribution/* Distribution is defined in messages.tact */; |
|
tonb: Address; |
|
votes: map[Int]Vote; |
|
profits: map[Address]Int; |
|
n_votes: Int; |
|
|
|
init(admins: Distribution, founders: AddressList, tonb: Address) { |
|
self.admins = admins; |
|
self.founders = founders; |
|
self.tonb = tonb; |
|
} |
|
|
|
receive(msg: FinishVote) { |
|
require(msg.vote_id < self.n_votes, "Invalid vote id"); |
|
let vote: Vote = self.votes.get(msg.vote_id)!!; |
|
require(vote.ended == false, "Vote already ended"); |
|
require(vote.vote_end < now(), "Vote is not finished yet"); |
|
let n_yes: Int = 0; |
|
let n_no: Int = 0; |
|
let n_abstain: Int = 0; |
|
let i: Int = 0; |
|
while (i < self.admins.addresses.length) { |
|
let vote_: Int = vote.votes.get(i)!!; |
|
if (vote_i == 1) { |
|
n_yes = n_yes + 1; |
|
} else if (vote_i == 2) { |
|
n_no = n_no + 1; |
|
} else if (vote_i == 0) { |
|
n_abstain = n_abstain + 1; |
|
} |
|
i = i + 1; |
|
} |
|
let n_total: Int = n_yes + n_no + n_abstain; |
|
let present: Int = n_total * 100 / self.admins.addresses.length; |
|
if (present < vote.quorum_percent) { |
|
vote.result = false; |
|
} else if (n_yes > n_no) { |
|
vote.result = true; |
|
} else { |
|
vote.result = false; |
|
} |
|
vote.ended = true; |
|
self.votes.set(msg.vote_id, vote); |
|
|
|
if (vote.result == true) { |
|
let vres: Cell? = vote_res(vote.proposal); |
|
if (vres != null) { |
|
send(SendParameters{ |
|
to: self.tonb, |
|
value: 0, |
|
mode: SendRemainingValue, |
|
body: vres!! |
|
}); |
|
} |
|
if(vote.proposal.type == 2) { |
|
// self.admins = Distribution { |
|
// addresses: vote.proposal.data.readAddressList(), |
|
// percents: vote.proposal.data.readPercents() |
|
// }; |
|
} |
|
} |
|
|
|
} |
|
|
|
receive(msg: Vote) { |
|
require(msg.adminIndex < self.admins.addresses.length, "Invalid admin index") |
|
let ctx: Context = context(); |
|
require(ctx.sender == self.admins.addresses.addresses.get(msg.adminIndex), "Only an admin can vote"); |
|
require(msg.vote_id < self.n_votes, "Invalid vote id"); |
|
let vote: Vote = self.votes.get(msg.vote_id)!!; |
|
require(vote.ended == false, "Vote already ended"); |
|
require(vote.vote_end > now(), "Vote is finished"); |
|
require(msg.vote >= 0 && msg.vote <= 2, "Invalid vote"); |
|
vote.votes.set(msg.adminIndex, msg.vote); |
|
self.votes.set(msg.vote_id, vote); |
|
} |
|
|
|
receive(msg: InitiateBlacklistVote) { |
|
let ctx: Context = context(); |
|
require(ctx.sender == self.admins.addresses.addresses.get(msg.adminIndex), "Only an admin can initiate a vote"); |
|
require(ctx.value >= ton("1.0"), "Voting requires at least 1 ton for the fees") |
|
require(msg.quorum_percent > 20 && msg.quorum_percent <= 100, "Invalid quorum percent"); |
|
require(msg.vote_time > 0 && msg.vote_time < 3 * 24 * 3600, "Invalid vote time"); |
|
let vote = Vote { |
|
id: self.n_votes, |
|
vote_end: now() + msg.vote_time, |
|
proposal: Proposal { |
|
type: 0, |
|
data: msg.address |
|
}, |
|
quorum_percent: msg.quorum_percent |
|
}; |
|
let i: Int = 0; |
|
while (i < self.admins.addresses.length) { |
|
vote.votes.set(i, -1); |
|
i = i + 1; |
|
} |
|
self.votes.set(self.n_votes, vote); |
|
self.n_votes = self.n_votes + 1; |
|
} |
|
|
|
receive(msg: InitiateDistributionVote) { |
|
let ctx: Context = context(); |
|
require(ctx.sender == self.admins.addresses.addresses.get(msg.adminIndex), "Only an admin can initiate a vote"); |
|
require(ctx.value >= ton("1.0"), "Voting requires at least 1 ton for the fees") |
|
require(msg.quorum_percent > 20 && msg.quorum_percent <= 100, "Invalid quorum percent"); |
|
require(msg.vote_time > 0 && msg.vote_time < 3 * 24 * 3600, "Invalid vote time"); |
|
let vote = Vote { |
|
id: self.n_votes, |
|
vote_end: now() + msg.vote_time, |
|
proposal: Proposal { |
|
type: 2, |
|
data: msg.distribution |
|
}, |
|
quorum_percent: msg.quorum_percent |
|
}; |
|
let i: Int = 0; |
|
while (i < self.admins.addresses.length) { |
|
vote.votes.set(i, -1); |
|
i = i + 1; |
|
} |
|
self.votes.set(self.n_votes, vote); |
|
self.n_votes = self.n_votes + 1; |
|
} |
|
|
|
receive(msg: InitiateFullLiquidationVote) { |
|
let ctx: Context = context(); |
|
require(ctx.sender == self.admins.addresses.addresses.get(msg.adminIndex), "Only an admin can initiate a vote"); |
|
require(ctx.value >= ton("1.0"), "Voting requires at least 1 ton for the fees") |
|
require(msg.quorum_percent > 85 && msg.quorum_percent <= 100, "Invalid quorum percent"); |
|
require(msg.vote_time > 0 && msg.vote_time < 3 * 24 * 3600, "Invalid vote time"); |
|
let vote = Vote { |
|
id: self.n_votes, |
|
vote_end: now() + msg.vote_time, |
|
proposal: Proposal { |
|
type: 1, |
|
data: beginCell().endCell() |
|
}, |
|
quorum_percent: msg.quorum_percent |
|
}; |
|
let i: Int = 0; |
|
while (i < self.admins.addresses.length) { |
|
vote.votes.set(i, -1); |
|
i = i + 1; |
|
} |
|
self.votes.set(self.n_votes, vote); |
|
self.n_votes = self.n_votes + 1; |
|
} |
|
|
|
receive(msg: Unstake /* staking profits */) { |
|
// If this is sent by TONB, it means that this is receiving stake profits |
|
let ctx: Context = context(); |
|
if (ctx.sender == self.tonb) { |
|
let value: Int = ctx.value; |
|
let i: Int = 0; |
|
while (i < self.admins.addresses.length) { |
|
let addr: Address = self.admins.addresses.addresses.get(i)!!; |
|
let percent: Int = self.admins.percents.percents.get(i)!!; |
|
let amount: Int = value * percent / 100; |
|
// todo: record the profit |
|
let current_profit: Int = 0; |
|
let profit_record: Int? = self.profits.get(addr); |
|
if (profit_record != null) { |
|
current_profit = profit_record!!; |
|
} |
|
self.profits.set(addr, current_profit + amount); |
|
i = i + 1; |
|
} |
|
} |
|
} |
|
|
|
receive(msg: RequestUnstake) { |
|
// If this is sent by one of the founders, it means that the user wants to unstake |
|
let ctx: Context = context(); |
|
require(self.founders.get(msg.founderIndex) == ctx.sender, "Only a founder can request unstake"); |
|
send(SendParameters{ |
|
to: self.tonb, |
|
value: msg.amount, |
|
mode: SendRemainingValue, |
|
body: Unstake { amount: 0 }.toCell() |
|
}); |
|
} |
|
|
|
receive(msg: CollectProfit) { |
|
let ctx: Context = context(); |
|
require(self.admins.addresses.addresses.get(msg.adminIndex) == ctx.sender, "Only an admin can collect profit"); |
|
let profit: Int? = self.profits.get(ctx.sender); |
|
require(profit != null, "No profit to collect"); |
|
let amount: Int = profit!!; |
|
self.profits.set(ctx.sender, 0); |
|
send(SendParameters{ |
|
to: ctx.sender, |
|
value: amount - withdraw_gas_consumption, |
|
mode: SendRemainingValue |
|
}); |
|
} |
|
} |