Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Mainnet] Dividend Token Contract #27

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
23 changes: 13 additions & 10 deletions Mainnet/DividendToken/DividendToken.Tests/DividendTokenTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Moq;
using NBitcoin;
using Stratis.SmartContracts;
using Stratis.SmartContracts.CLR;
using Xunit;
Expand All @@ -20,7 +19,8 @@ public class DividendTokenTests

private readonly string name;
private readonly string symbol;
private readonly ulong totalSupply;
private readonly UInt256 totalSupply;
private readonly uint decimals;

public DividendTokenTests()
{
Expand All @@ -37,6 +37,7 @@ public DividendTokenTests()
this.name = "Test Token";
this.symbol = "TST";
this.totalSupply = 1_000;
this.decimals = 0;
}

[Fact]
Expand All @@ -46,7 +47,7 @@ public void Deposited_Dividend_Should_Be_Distributed_Equaly()

this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.owner, dividend));

var contract = new DividendToken(this.mContractState.Object, this.totalSupply, this.name, this.symbol);
var contract = new DividendToken(this.mContractState.Object, this.totalSupply, this.name, this.symbol, this.decimals);

Assert.True(contract.TransferTo(this.tokenHolder, 100));

Expand All @@ -66,7 +67,7 @@ public void Multiple_Deposited_Dividend_Should_Be_Distributed_Equaly()

this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.owner, dividend));

var contract = new DividendToken(this.mContractState.Object, this.totalSupply, this.name, this.symbol);
var contract = new DividendToken(this.mContractState.Object, this.totalSupply, this.name, this.symbol, this.decimals);

Assert.True(contract.TransferTo(this.tokenHolder, 100));

Expand All @@ -93,7 +94,7 @@ public void Cumulative_Dividends_Should_Be_Distributed_Equaly()

this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.owner, dividend));

var contract = new DividendToken(this.mContractState.Object, this.totalSupply, this.name, this.symbol);
var contract = new DividendToken(this.mContractState.Object, this.totalSupply, this.name, this.symbol, this.decimals);

Assert.True(contract.TransferTo(this.tokenHolder, 100));

Expand All @@ -118,7 +119,7 @@ public void Cumulative_Deposits_Should_Be_Distributed_Equaly_When_Dividends_Have

this.mContractState.Setup(m => m.Message).Returns(new Message(this.contract, this.owner, dividend));

var contract = new DividendToken(this.mContractState.Object, this.totalSupply, this.name, this.symbol);
var contract = new DividendToken(this.mContractState.Object, this.totalSupply, this.name, this.symbol, this.decimals);

Assert.True(contract.TransferTo(this.tokenHolder, 1));

Expand All @@ -144,7 +145,7 @@ public void Deposited_Dividend_Should_Be_Withdrawable()
this.mContractState.Setup(m => m.GetBalance).Returns(() => dividend);
this.mTransactionExecutor.Setup(m => m.Transfer(this.mContractState.Object, this.tokenHolder, 5)).Returns(TransferResult.Transferred(true));

var contract = new DividendToken(this.mContractState.Object, this.totalSupply, this.name, this.symbol);
var contract = new DividendToken(this.mContractState.Object, this.totalSupply, this.name, this.symbol, this.decimals);

Assert.True(contract.TransferTo(this.tokenHolder, 11));

Expand All @@ -157,7 +158,9 @@ public void Deposited_Dividend_Should_Be_Withdrawable()
this.mTransactionExecutor.Verify(s => s.Transfer(this.mContractState.Object, this.tokenHolder, 5), Times.Once);
Assert.Equal(0ul, contract.GetDividends());
var account = this.persistentState.GetStruct<DividendToken.Account>($"Account:{this.tokenHolder}");
Assert.Equal(500ul, account.DividendBalance);
Assert.Equal((UInt256)500, account.DividendBalance);
Assert.Equal(5ul, account.WithdrawnDividends);
Assert.Equal(dividend, account.CreditedDividends);
}

[Fact]
Expand All @@ -169,7 +172,7 @@ public void GetDividends_Returns_Current_Sender_Dividends()
this.mContractState.Setup(m => m.GetBalance).Returns(() => dividend);
this.mTransactionExecutor.Setup(m => m.Transfer(this.mContractState.Object, this.tokenHolder, 100)).Returns(TransferResult.Transferred(true));

var contract = new DividendToken(this.mContractState.Object, this.totalSupply, this.name, this.symbol);
var contract = new DividendToken(this.mContractState.Object, this.totalSupply, this.name, this.symbol, this.decimals);

Assert.True(contract.TransferTo(this.tokenHolder, 100));

Expand All @@ -191,7 +194,7 @@ public void GetTotalDividends_Returns_Current_Sender_TotalDividends()
this.mContractState.Setup(m => m.GetBalance).Returns(() => dividend);
this.mTransactionExecutor.Setup(m => m.Transfer(this.mContractState.Object, this.tokenHolder, 100)).Returns(TransferResult.Transferred(true));

var contract = new DividendToken(this.mContractState.Object, this.totalSupply, this.name, this.symbol);
var contract = new DividendToken(this.mContractState.Object, this.totalSupply, this.name, this.symbol, this.decimals);

Assert.True(contract.TransferTo(this.tokenHolder, 100));

Expand Down
8 changes: 8 additions & 0 deletions Mainnet/DividendToken/DividendToken.Tests/InMemoryState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ public T GetStruct<T>(string key)

public ulong GetUInt64(string key) => this.GetValue<ulong>(key);

public UInt128 GetUInt128(string key) => this.GetValue<UInt128>(key);

public UInt256 GetUInt256(string key) => this.GetValue<UInt256>(key);

public bool IsContract(Address address)
{
throw new NotImplementedException();
Expand Down Expand Up @@ -70,5 +74,9 @@ public void SetStruct<T>(string key, T value)
public void SetUInt32(string key, uint value) => this.storage.AddOrReplace(key, value);

public void SetUInt64(string key, ulong value) => this.storage.AddOrReplace(key, value);

public void SetUInt128(string key, UInt128 value) => this.storage.AddOrReplace(key, value);

public void SetUInt256(string key, UInt256 value) => this.storage.AddOrReplace(key, value);
}
}
87 changes: 46 additions & 41 deletions Mainnet/DividendToken/DividendToken/DividendToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ public ulong Dividends
private void SetAccount(Address address, Account account) => PersistentState.SetStruct($"Account:{address}", account);


public DividendToken(ISmartContractState state, ulong totalSupply, string name, string symbol)
public DividendToken(ISmartContractState state, UInt256 totalSupply, string name, string symbol,uint decimals)
: base(state)
{
this.TotalSupply = totalSupply;
this.Name = name;
this.Symbol = symbol;
this.Decimals = decimals;
this.SetBalance(Message.Sender, totalSupply);
}

Expand All @@ -39,15 +40,16 @@ public void DistributeDividends()
{
Dividends += Message.Value;
}
public bool TransferTo(Address to, ulong amount)

public bool TransferTo(Address to, UInt256 amount)
{
UpdateAccount(Message.Sender);
UpdateAccount(to);

return TransferTokensTo(to, amount);
}

public bool TransferFrom(Address from, Address to, ulong amount)
public bool TransferFrom(Address from, Address to, UInt256 amount)
{
UpdateAccount(from);
UpdateAccount(to);
Expand All @@ -62,26 +64,23 @@ private Account UpdateAccount(Address address)

if (newDividends > 0)
{
account.DividendBalance = checked(account.DividendBalance + newDividends);
account.DividendBalance += newDividends;
account.CreditedDividends = Dividends;
SetAccount(address, account);
}

return account;
}

private ulong GetWithdrawableDividends(Address address, Account account)
private UInt256 GetWithdrawableDividends(Address address, Account account)
{
return checked(account.DividendBalance + GetNewDividends(address, account)); //Delay divide by TotalSupply to final stage for avoid decimal value loss.
return account.DividendBalance + GetNewDividends(address, account); //Delay divide by TotalSupply to final stage for avoid decimal value loss.
}

private ulong GetNewDividends(Address address, Account account)
private UInt256 GetNewDividends(Address address, Account account)
{
checked
{
var notCreditedDividends = Dividends - account.CreditedDividends;
return GetBalance(address) * notCreditedDividends;
}
var notCreditedDividends = checked(Dividends - account.CreditedDividends);
return GetBalance(address) * notCreditedDividends;
}

/// <summary>
Expand Down Expand Up @@ -116,7 +115,8 @@ public ulong GetDividends(Address address)
public ulong GetTotalDividends(Address address)
{
var account = GetAccount(address);
return checked(GetWithdrawableDividends(address, account) + account.WithdrawnDividends) / TotalSupply;
var withdrawable = GetWithdrawableDividends(address, account) / TotalSupply;
return withdrawable + account.WithdrawnDividends;
}

/// <summary>
Expand All @@ -126,12 +126,12 @@ public void Withdraw()
{
var account = UpdateAccount(Message.Sender);
var balance = account.DividendBalance / TotalSupply;
var remainder = account.DividendBalance % TotalSupply;

Assert(balance > 0, "The account has no dividends.");

account.WithdrawnDividends = checked(account.WithdrawnDividends + account.DividendBalance - remainder);
account.DividendBalance = remainder;
account.WithdrawnDividends += balance;

account.DividendBalance %= TotalSupply;

SetAccount(Message.Sender, account);

Expand All @@ -145,11 +145,7 @@ public struct Account
/// <summary>
/// Withdrawable Dividend Balance. Exact value should to divided by <see cref="TotalSupply"/>
/// </summary>
public ulong DividendBalance;

/// <summary>
///
/// </summary>
public UInt256 DividendBalance;

public ulong WithdrawnDividends;

Expand All @@ -162,6 +158,7 @@ public struct Account

#region StandardToken code is inlined


public string Symbol
{
get => PersistentState.GetString(nameof(this.Symbol));
Expand All @@ -175,24 +172,32 @@ public string Name
}

/// <inheritdoc />
public ulong TotalSupply
public UInt256 TotalSupply
{
get => PersistentState.GetUInt256(nameof(this.TotalSupply));
private set => PersistentState.SetUInt256(nameof(this.TotalSupply), value);
}

public uint Decimals
{
get => PersistentState.GetUInt64(nameof(this.TotalSupply));
private set => PersistentState.SetUInt64(nameof(this.TotalSupply), value);
get => PersistentState.GetUInt32(nameof(Decimals));
private set => PersistentState.SetUInt32(nameof(Decimals), value);
}


/// <inheritdoc />
public ulong GetBalance(Address address)
public UInt256 GetBalance(Address address)
{
return PersistentState.GetUInt64($"Balance:{address}");
return PersistentState.GetUInt256($"Balance:{address}");
}

private void SetBalance(Address address, ulong value)
private void SetBalance(Address address, UInt256 value)
{
PersistentState.SetUInt64($"Balance:{address}", value);
PersistentState.SetUInt256($"Balance:{address}", value);
}

/// <inheritdoc />
private bool TransferTokensTo(Address to, ulong amount)
private bool TransferTokensTo(Address to, UInt256 amount)
{
if (amount == 0)
{
Expand All @@ -201,7 +206,7 @@ private bool TransferTokensTo(Address to, ulong amount)
return true;
}

ulong senderBalance = GetBalance(Message.Sender);
UInt256 senderBalance = GetBalance(Message.Sender);

if (senderBalance < amount)
{
Expand All @@ -218,7 +223,7 @@ private bool TransferTokensTo(Address to, ulong amount)
}

/// <inheritdoc />
private bool TransferTokensFrom(Address from, Address to, ulong amount)
private bool TransferTokensFrom(Address from, Address to, UInt256 amount)
{
if (amount == 0)
{
Expand All @@ -227,8 +232,8 @@ private bool TransferTokensFrom(Address from, Address to, ulong amount)
return true;
}

ulong senderAllowance = Allowance(from, Message.Sender);
ulong fromBalance = GetBalance(from);
UInt256 senderAllowance = Allowance(from, Message.Sender);
UInt256 fromBalance = GetBalance(from);

if (senderAllowance < amount || fromBalance < amount)
{
Expand All @@ -247,7 +252,7 @@ private bool TransferTokensFrom(Address from, Address to, ulong amount)
}

/// <inheritdoc />
public bool Approve(Address spender, ulong currentAmount, ulong amount)
public bool Approve(Address spender, UInt256 currentAmount, UInt256 amount)
{
if (Allowance(Message.Sender, spender) != currentAmount)
{
Expand All @@ -261,15 +266,15 @@ public bool Approve(Address spender, ulong currentAmount, ulong amount)
return true;
}

private void SetApproval(Address owner, Address spender, ulong value)
private void SetApproval(Address owner, Address spender, UInt256 value)
{
PersistentState.SetUInt64($"Allowance:{owner}:{spender}", value);
PersistentState.SetUInt256($"Allowance:{owner}:{spender}", value);
}

/// <inheritdoc />
public ulong Allowance(Address owner, Address spender)
public UInt256 Allowance(Address owner, Address spender)
{
return PersistentState.GetUInt64($"Allowance:{owner}:{spender}");
return PersistentState.GetUInt256($"Allowance:{owner}:{spender}");
}

public struct TransferLog
Expand All @@ -280,7 +285,7 @@ public struct TransferLog
[Index]
public Address To;

public ulong Amount;
public UInt256 Amount;
}

public struct ApprovalLog
Expand All @@ -291,9 +296,9 @@ public struct ApprovalLog
[Index]
public Address Spender;

public ulong OldAmount;
public UInt256 OldAmount;

public ulong Amount;
public UInt256 Amount;
}
#endregion
}
6 changes: 3 additions & 3 deletions Mainnet/DividendToken/DividendToken/DividendToken.csproj
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>

<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Stratis.SmartContracts" Version="1.2.1" />
<PackageReference Include="Stratis.SmartContracts.Standards" Version="1.0.0" />
<PackageReference Include="Stratis.SmartContracts" Version="1.4.1-alpha" />
<PackageReference Include="Stratis.SmartContracts.Standards" Version="1.4.1-alpha" />
</ItemGroup>
</Project>
Loading