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

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
});
}
}