Skip to content

Commit

Permalink
Adding burnable token, centrally issued token, payment forwarder tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
miohtama committed May 10, 2017
1 parent d438f64 commit e827f7d
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 17 deletions.
27 changes: 27 additions & 0 deletions contracts/BurnableToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
pragma solidity ^0.4.8;

import 'zeppelin/contracts/token/StandardToken.sol';


contract BurnableToken is StandardToken {

address public constant BURN_ADDRESS = 0;

/** How many tokens we burned */
event Burned(address burner, uint burnedAmount);

/**
* Burn extra tokens from a balance.
*
*/
function burn(uint burnAmount) {
address burner = msg.sender;
balances[burner] = safeSub(balances[burner], burnAmount);
totalSupply = safeSub(totalSupply, burnAmount);
Burned(burner, burnAmount);

// Keep exchanges happy by sending the burned amount to
// "burn address"
Transfer(burner, BURN_ADDRESS, burnAmount);
}
}
32 changes: 32 additions & 0 deletions contracts/CentrallyIssuedToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
pragma solidity ^0.4.6;

import "./BurnableToken.sol";
import "./UpgradeableToken.sol";


/**
* Centrally issued Ethereum token.
*
* We mix in burnable and upgradeable traits.
*
* Token supply is created in the token contract creation and allocated to owner.
* The owner can then transfer from its supply to crowdsale participants.
* The owner, or anybody, can burn any excessive tokens they are holding.
*
*/
contract CentrallyIssuedToken is BurnableToken, UpgradeableToken {

string public name;
string public symbol;
uint public decimals;

function CentrallyIssuedToken(address _owner, string _name, string _symbol, uint _totalSupply, uint _decimals) UpgradeableToken(_owner) {
name = _name;
symbol = _symbol;
totalSupply = _totalSupply;
decimals = _decimals;

// Allocate initial balance to the owner
balances[_owner] = _totalSupply;
}
}
12 changes: 6 additions & 6 deletions contracts/CrowdsaleToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ contract CrowdsaleToken is ReleasableToken, MintableToken, UpgradeableToken {
*
* This token must be created through a team multisig wallet, so that it is owned by that wallet.
*/
function CrowdsaleToken(string _name, string _symbol, uint _initialSupply, uint _decimals) {
function CrowdsaleToken(string _name, string _symbol, uint _initialSupply, uint _decimals)
UpgradeableToken(msg.sender) {

// Create from team multisig
// Create any address, can be transferred
// to team multisig via changeOwner(),
// also remember to call setUpgradeMaster()
owner = msg.sender;

// Initially set the upgrade master same as owner
upgradeMaster = owner;

name = _name;
symbol = _symbol;

Expand All @@ -47,7 +47,7 @@ contract CrowdsaleToken is ReleasableToken, MintableToken, UpgradeableToken {
decimals = _decimals;

// Create initially all balance on the team multisig
balances[msg.sender] = totalSupply;
balances[owner] = totalSupply;
}

/**
Expand Down
10 changes: 10 additions & 0 deletions contracts/PaymentForwarder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,14 @@ contract PaymentForwarder is Haltable {
if(!teamMultisig.send(weiAmount)) throw;
}

/**
* Pay on a behalf of the sender.
*
* @param customerId Identifier in the central database, UUID v4
*
*/
function payForMyself(uint128 customerId) public payable {
pay(customerId, msg.sender);
}

}
21 changes: 12 additions & 9 deletions contracts/UpgradeableToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ pragma solidity ^0.4.8;
import "zeppelin/contracts/token/ERC20.sol";
import "zeppelin/contracts/token/StandardToken.sol";
import "./UpgradeAgent.sol";
import "./SafeMathLib.sol";

/**
* A token upgrade mechanism where users can opt-in amount of tokens to the next smart contract revision.
Expand All @@ -12,8 +11,6 @@ import "./SafeMathLib.sol";
*/
contract UpgradeableToken is StandardToken {

using SafeMathLib for uint;

/** Contract / person who can set the upgrade path. This can be the same as team multisig wallet, as what it is with its default value. */
address public upgradeMaster;

Expand All @@ -34,13 +31,20 @@ contract UpgradeableToken is StandardToken {
*/
enum UpgradeState {Unknown, NotAllowed, WaitingForAgent, ReadyToUpgrade, Upgrading}

/**
* Somebody has upgraded some of his tokens.
*/
event Upgrade(address indexed _from, address indexed _to, uint256 _value);

/**
* New upgrade agent available.
*/
event UpgradeAgentSet(address agent);

/**
* Do not allow construction without upgrade master set.
*/
function UpgradeAgentEnabledToken(address _upgradeMaster) {
function UpgradeableToken(address _upgradeMaster) {
upgradeMaster = _upgradeMaster;
}

Expand All @@ -58,11 +62,11 @@ contract UpgradeableToken is StandardToken {
// Validate input value.
if (value == 0) throw;

balances[msg.sender] = balances[msg.sender].minus(value);
balances[msg.sender] = safeSub(balances[msg.sender], value);

// Take tokens out from circulation
totalSupply = totalSupply.minus(value);
totalUpgraded = totalUpgraded.plus(value);
totalSupply = safeSub(totalSupply, value);
totalUpgraded = safeAdd(totalUpgraded, value);

// Upgrade agent reissues the tokens
upgradeAgent.upgradeFrom(msg.sender, value);
Expand All @@ -89,7 +93,6 @@ contract UpgradeableToken is StandardToken {

// Bad interface
if(!upgradeAgent.isUpgradeAgent()) throw;

// Make sure that token supplies match in source and target
if (upgradeAgent.originalSupply() != totalSupply) throw;

Expand All @@ -111,7 +114,7 @@ contract UpgradeableToken is StandardToken {
*
* This allows us to set a new owner for the upgrade mechanism.
*/
function setUpgradeMaster(address master) external {
function setUpgradeMaster(address master) public {
if (master == 0x0) throw;
if (msg.sender != upgradeMaster) throw;
upgradeMaster = master;
Expand Down
56 changes: 56 additions & 0 deletions ico/tests/contracts/test_burn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""Burn functionality."""

import pytest
from ethereum.tester import TransactionFailed
from web3.contract import Contract


@pytest.fixture
def token(chain, team_multisig):
args = [
team_multisig,
"Token",
"TKN",
1000000,
0
]
contract, hash = chain.provider.deploy_contract('CentrallyIssuedToken', deploy_args=args)
assert contract.call().balanceOf(team_multisig) == 1000000
return contract


@pytest.fixture
def token_with_customer_balance(chain, team_multisig, token, customer) -> Contract:
"""Create a Crowdsale token where transfer restrictions have been lifted."""

# Make sure customer 1 has some token balance
token.transact({"from": team_multisig}).transfer(customer, 10000)

return token


def test_burn(token_with_customer_balance: Contract, customer: str):
"""Burn tokens."""

token = token_with_customer_balance
initial_balance = token.call().balanceOf(customer)
initial_supply = token.call().totalSupply()
amount = 1000

token.transact({"from": customer}).burn(amount)

assert token.call().balanceOf(customer) == initial_balance - amount
assert token.call().totalSupply() == initial_supply - amount

events = token.pastEvents("Transfer").get()
assert len(events) == 1 + 1 # plus initial transfer
e = events[-1]
assert e["args"]["to"] == '0x0000000000000000000000000000000000000000'
assert e["args"]["from"] == customer
assert e["args"]["value"] == amount

events = token.pastEvents("Burned").get()
assert len(events) == 1
e = events[-1]
assert e["args"]["burner"] == customer
assert e["args"]["burnedAmount"] == amount
31 changes: 29 additions & 2 deletions ico/tests/contracts/test_forwarder.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def payment_forwarder(chain, team_multisig):


def test_pay_once(web3, payment_forwarder, team_multisig, customer):
"""Pay for myself."""
"""Pay for an address."""

value = to_wei("1.0", "ether")
customer_id = int(uuid.uuid4().hex, 16) # Customer ids are 128-bit UUID v4
Expand All @@ -44,7 +44,7 @@ def test_pay_once(web3, payment_forwarder, team_multisig, customer):


def test_pay_twice(web3, payment_forwarder, team_multisig, customer, customer_2):
"""Pay for myself twice."""
"""Pay for an address twice."""

value = to_wei("1.0", "ether")
customer_id = int(uuid.uuid4().hex, 16) # Customer ids are 128-bit UUID v4
Expand All @@ -71,6 +71,31 @@ def test_pay_twice(web3, payment_forwarder, team_multisig, customer, customer_2)
assert e["args"]["benefactor"] == customer


def test_pay_for_myself(web3, payment_forwarder, team_multisig, customer):
"""Pay for myself."""

value = to_wei("1.0", "ether")
customer_id = int(uuid.uuid4().hex, 16) # Customer ids are 128-bit UUID v4

team_multisig_begin = web3.eth.getBalance(team_multisig)
payment_forwarder.transact({"value": value, "from": customer}).payForMyself(customer_id)
team_multisig_end = web3.eth.getBalance(team_multisig)

assert team_multisig_end - team_multisig_begin > 0
assert payment_forwarder.call().totalTransferred() == value
assert payment_forwarder.call().paymentsByCustomer(customer_id) == value
assert payment_forwarder.call().customerCount() == 1

# Check we properly generate an event
events = payment_forwarder.pastEvents("PaymentForwarded").get()
assert len(events) == 1
e = events[-1]
assert e["args"]["source"] == customer
assert e["args"]["amount"] == value
assert e["args"]["customerId"] == customer_id
assert e["args"]["benefactor"] == customer


def test_halt(web3, payment_forwarder, team_multisig, customer):
"""We can stop crowdsale."""

Expand All @@ -95,3 +120,5 @@ def test_unhalt(web3, payment_forwarder, team_multisig, customer):
assert payment_forwarder.call().customerCount() == 0
payment_forwarder.transact({"value": value, "from": customer}).pay(customer_id, customer)
assert payment_forwarder.call().customerCount() == 1


0 comments on commit e827f7d

Please sign in to comment.