Skip to content
Open
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b9a89fa
Preliminary BurgerGov for governance token
Hecate2 Nov 4, 2021
a1a155c
throw "The proposal does not exist" when voting_deadline == 0
Hecate2 Nov 4, 2021
138228a
Update BurgerGov.cs
vang1ong7ang Nov 4, 2021
de82d6a
Change filename, class name and token symbol; remove owner;
Hecate2 Nov 4, 2021
023ca02
Merge branch 'BurgerGov' of github.com:Hecate2/code into BurgerGov
Hecate2 Nov 4, 2021
5102dd8
remove comments
Hecate2 Nov 4, 2021
f9e385b
NOT PRODUCTION READY. Examine votes on chain with delegation and defa…
Hecate2 Nov 4, 2021
56e58a4
mint tokens to a given address when deployed
Hecate2 Nov 4, 2021
79dfa84
BecomeDefaultDelegate manually
Hecate2 Nov 4, 2021
55bc1bc
GetDefaultDelegateBalance
Hecate2 Nov 4, 2021
b8680cf
vote[proposal_id][from_account] == True/False;
Hecate2 Nov 4, 2021
86b79a6
Preliminarily tested convenient method `CountVote`; robust method `Is…
Hecate2 Nov 5, 2021
139527d
Let `ExecuteProposal` reuse `CountVote` if no voter list provided
Hecate2 Nov 5, 2021
8fd4515
More robust and efficient `CountVote` and `ExecuteProposal`
Hecate2 Nov 5, 2021
8bd71d4
Attempt to regularize keys to raw bytes
Hecate2 Nov 5, 2021
db4d4b5
vote couting logic: I voted || I have valid delegate and delegate vot…
Hecate2 Nov 5, 2021
c47cb40
optimized `GetVotersOfProposal` and `CountVote` using FindOptions
Hecate2 Nov 5, 2021
5929488
arg count starts from 0; optimzed GAS with args[(uint)j]
Hecate2 Nov 8, 2021
6abfa3c
clearer exceptions
Hecate2 Nov 8, 2021
807cb15
Update NeoBurgerGovernanceToken.cs
vang1ong7ang Nov 8, 2021
f17807c
proposal attributes stored as structs
Hecate2 Nov 8, 2021
ada94f2
Merge branch 'neoburger:main' into BurgerGov
Hecate2 Nov 8, 2021
85c96c2
Merge branch 'main' of github.com:Hecate2/code into BurgerGov
Hecate2 Nov 8, 2021
5be7f74
Merge branch 'BurgerGov' of github.com:Hecate2/code into BurgerGov
Hecate2 Nov 8, 2021
a9d0ec5
check voting deadline in refactored storage
Hecate2 Nov 8, 2021
0b662d0
method GetNextProposalID
Hecate2 Nov 8, 2021
623ed81
proposal id given by user
Hecate2 Nov 8, 2021
0aae44f
robust check for `NewProposal`
Hecate2 Nov 8, 2021
95db3f7
fix method `Vote`
Hecate2 Nov 8, 2021
525c015
Update NeoBurgerGovernanceToken.cs
vang1ong7ang Nov 8, 2021
149ddee
leave VOTING_PERIOD a readonly const
Hecate2 Nov 9, 2021
2ed8b81
initially set readonly byte[] for prefixes
Hecate2 Nov 9, 2021
95c7b87
remove useless `if (voting_deadline == 0)`
Hecate2 Nov 9, 2021
bed3983
use const instead of bytearray
Hecate2 Nov 9, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
217 changes: 217 additions & 0 deletions NeoBurgerGovernanceToken.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
using System;
using System.Numerics;
using Neo;
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Native;
using Neo.SmartContract.Framework.Services;
using Neo.SmartContract.Framework.Attributes;
using Neo.SmartContract;

namespace NeoBurger
{
[ManifestExtra("Author", "NEOBURGER")]
[ManifestExtra("Email", "developer@neo.org")]
[ManifestExtra("Description", "NeoBurger Governance Token")]
[SupportedStandards("NEP-17")]
[ContractPermission("*", "*")]
public class NeoBurgerGovernanceToken : Nep17Token
{
[InitialValue("[TODO]: ARGS", ContractParameterType.Hash160)]
private const UInt160 INITIAL_HOLDER = default;
private const ulong VOTING_PERIOD = 86400000 * 7;
private const byte PREFIX_PROPOSAL = 0x03;
private const byte PREFIX_PROPOSAL_EXECUTED_TIME = 0x05;
private const byte PREFIX_DELEGATE = 0x81;
private const byte PREFIX_DEFAULT_DELEGATE = 0x82;
private const byte PREFIX_DELEGATE_THRESHOLD = 0x83;
private const byte PREFIX_VOTE = 0xc1;

public override byte Decimals() => 8;
public override string Symbol() => "NOBUG";
public static BigInteger GetVotingPeriod() => VOTING_PERIOD;

public static object[] ProposalAttributes(BigInteger id)
{
StorageMap proposal_id_map = new(Storage.CurrentContext, new byte[] { PREFIX_PROPOSAL });
ProposalAttributesStruct proposal_attributes = (ProposalAttributesStruct)proposal_id_map.GetObject((ByteString)id);
byte[] proposal_executed_time_bytearray = new byte[] { PREFIX_PROPOSAL_EXECUTED_TIME };
StorageMap proposal_executed_time_map = new(Storage.CurrentContext, proposal_executed_time_bytearray);
BigInteger executed_time = (BigInteger)proposal_executed_time_map.Get((ByteString)id);
return new object[] { proposal_attributes.scripthash, proposal_attributes.method, proposal_attributes.args, proposal_attributes.voting_deadline, executed_time };
}

public static UInt160 GetDelegate(UInt160 from) => (UInt160)new StorageMap(Storage.CurrentContext, PREFIX_DELEGATE).Get(from);
public static UInt160 GetDefaultDelegate() => (UInt160)Storage.Get(Storage.CurrentContext, new byte[] { PREFIX_DEFAULT_DELEGATE });
public static BigInteger GetDefaultDelegateBalance() => BalanceOf(GetDefaultDelegate());
public static BigInteger GetDelegateThreshold() => (BigInteger)Storage.Get(Storage.CurrentContext, new byte[] { PREFIX_DELEGATE_THRESHOLD });
public static bool IsValidDelegate(UInt160 account) => account is not null && account.IsValid && (BalanceOf(account) > GetDelegateThreshold());
public static BigInteger GetVote(UInt160 from, BigInteger proposal_index) => (BigInteger)new StorageMap(Storage.CurrentContext, (ByteString)new byte[] { PREFIX_VOTE } + (ByteString)proposal_index).Get(from);
public static Iterator GetVotersOfProposal(BigInteger proposal_id) => new StorageMap(Storage.CurrentContext, (ByteString)new byte[] { PREFIX_VOTE } + (ByteString)proposal_id).Find((FindOptions)((byte)FindOptions.KeysOnly + (byte)FindOptions.RemovePrefix));

struct ProposalAttributesStruct
{
public BigInteger id;
public UInt160 scripthash;
public string method;
public ByteString[] args;
public BigInteger voting_deadline;
}

public static void _deploy(object data, bool update)
{
StorageMap proposal_id_map = new(Storage.CurrentContext, new byte[] { PREFIX_PROPOSAL });
ProposalAttributesStruct proposal_attributes = new();
proposal_attributes.id = 0;
proposal_id_map.PutObject((ByteString)(BigInteger)0, proposal_attributes);
Mint(INITIAL_HOLDER, 10_000_000_000_000_000);
Storage.Put(Storage.CurrentContext, new byte[] { PREFIX_DEFAULT_DELEGATE }, INITIAL_HOLDER);
Storage.Put(Storage.CurrentContext, new byte[] { PREFIX_DELEGATE_THRESHOLD }, 100_000_000_000_000);
}
public static void BecomeDefaultDelegate(UInt160 account)
{
ExecutionEngine.Assert(Runtime.CheckWitness(account));
byte[] default_delegate_bytearray = new byte[] { PREFIX_DEFAULT_DELEGATE };
UInt160 default_delegate = (UInt160)Storage.Get(Storage.CurrentContext, default_delegate_bytearray);
BigInteger to_account_balance = BalanceOf(account);
BigInteger default_delegate_balance = BalanceOf(default_delegate);
if (to_account_balance > default_delegate_balance && to_account_balance > GetDelegateThreshold())
Storage.Put(Storage.CurrentContext, default_delegate_bytearray, account);
else
throw new Exception("No enough tokens. You need "+(ByteString)default_delegate_balance+" NOBUGs to be the default delegate");
}

public static BigInteger NewProposal(BigInteger proposal_id, UInt160 scripthash, string method, ByteString[] args)
{
StorageMap proposal_id_map = new(Storage.CurrentContext, new byte[] { PREFIX_PROPOSAL });
if ((BigInteger)proposal_id_map.Get((ByteString)proposal_id) != 0 || ((ProposalAttributesStruct)proposal_id_map.GetObject((ByteString)(proposal_id - 1))).id != proposal_id - 1)
throw new Exception("Invalid proposal id");
ProposalAttributesStruct proposal_attributes = new();
proposal_attributes.id = proposal_id;
proposal_attributes.scripthash = scripthash;
proposal_attributes.method = method;
proposal_attributes.args = args;
proposal_attributes.voting_deadline = Runtime.Time + VOTING_PERIOD;
proposal_id_map.PutObject((ByteString)proposal_id, proposal_attributes);
return proposal_id;
}

public static void Delegate(UInt160 from, UInt160 to)
{
ExecutionEngine.Assert(Runtime.CheckWitness(from));
StorageMap delegate_map = new(Storage.CurrentContext, PREFIX_DELEGATE);
if (to == UInt160.Zero || to == from)
delegate_map.Delete(from);
else
delegate_map.Put(from, to);
}

public static void Vote(UInt160 from, BigInteger proposal_index, bool for_or_against)
{
ExecutionEngine.Assert(Runtime.CheckWitness(from));
StorageMap proposal_id_map = new(Storage.CurrentContext, new byte[] { PREFIX_PROPOSAL });
ProposalAttributesStruct proposal_attributes = (ProposalAttributesStruct)proposal_id_map.GetObject((ByteString)proposal_index);
BigInteger voting_deadline = proposal_attributes.voting_deadline;
if(Runtime.Time > voting_deadline)
throw new Exception("Cannot vote after the deadline");
StorageMap vote_map = new(Storage.CurrentContext, (ByteString)new byte[] { PREFIX_VOTE } + (ByteString)proposal_index);
if (for_or_against)
vote_map.Put(from, 1);
else
vote_map.Delete(from);
}

public static BigInteger CountVote(BigInteger proposal_id)
{
BigInteger sum_votes = 0;
Iterator voters = new StorageMap(Storage.CurrentContext, (ByteString)new byte[] { PREFIX_VOTE } + (ByteString)proposal_id)
.Find((FindOptions)((byte)FindOptions.KeysOnly + (byte)FindOptions.RemovePrefix));
bool default_delegate_voted = GetVote(GetDefaultDelegate(), proposal_id) > 0;
while (voters.Next())
{
UInt160 current_voter = (UInt160)(byte[])voters.Value;
if (GetVote(current_voter, proposal_id) > 0)
sum_votes += BalanceOf(current_voter);
else {
UInt160 current_delegate = GetDelegate(current_voter);
if (IsValidDelegate(current_delegate))
{
if (GetVote(current_delegate, proposal_id) > 0)
sum_votes += BalanceOf(current_voter);
}
else
{
if (default_delegate_voted)
sum_votes += BalanceOf(current_voter);
}
}
}
return sum_votes;
}

public static object ExecuteProposal(BigInteger proposal_id, UInt160[] voters)
{
object[] attributes = ProposalAttributes(proposal_id);
UInt160 scripthash = (UInt160)attributes[0];
string method = (string)attributes[1];
ByteString[] args = (ByteString[])attributes[2];
BigInteger voting_deadline = (BigInteger)attributes[3];
BigInteger proposal_executed = (BigInteger)attributes[4];
if (Runtime.Time > voting_deadline)
throw new Exception("Cannot execute proposal after the deadline");
if (proposal_executed > 0)
throw new Exception("Proposal already executed");

BigInteger voter_count = voters.Length;
BigInteger sum_votes = 0;
if (voter_count == 0)
sum_votes = CountVote(proposal_id);
else
{
bool default_delegate_voted = GetVote(GetDefaultDelegate(), proposal_id) > 0;
for (BigInteger i = 0; i < voter_count; i++)
{
UInt160 current_voter = voters[(uint)i];
//if (current_voter is null || !current_voter.IsValid)
// throw new Exception(current_voter);
if (GetVote(current_voter, proposal_id) > 0)
sum_votes += BalanceOf(current_voter);
else
{
UInt160 current_delegate = GetDelegate(current_voter);
if (IsValidDelegate(current_delegate))
{
if (GetVote(current_delegate, proposal_id) > 0)
sum_votes += BalanceOf(current_voter);
}
else
{
if (default_delegate_voted)
sum_votes += BalanceOf(current_voter);
}
}
}
}
if (sum_votes > TotalSupply() / 2)
{
new StorageMap(Storage.CurrentContext, (ByteString)new byte[] { PREFIX_PROPOSAL } + (ByteString)proposal_id)
.Put(new byte[] { PREFIX_PROPOSAL_EXECUTED_TIME }, Runtime.Time);
return Contract.Call(scripthash, method, CallFlags.All, args);
}
else
if(sum_votes != 0)
throw new Exception("Not enough votes. Got "+(ByteString)sum_votes+ " votes from given array `UInt160[] voters`");
else
throw new Exception("No vote counted from given array `UInt160[] voters`");
}

public static void OnNEP17Payment(UInt160 from, BigInteger amount, object data)
{
}

public static void Update(ByteString nefFile, string manifest)
{
ExecutionEngine.Assert(Runtime.CheckWitness(Runtime.ExecutingScriptHash));
ContractManagement.Update(nefFile, manifest, null);
}
}
}