From f332903d8a774d880c2173cc31acdc4c2f402fba Mon Sep 17 00:00:00 2001 From: Bill Gleim Date: Sat, 18 Feb 2017 13:40:42 -0800 Subject: [PATCH 1/7] Add MeshStandardToken --- contracts/MeshStandardToken.sol | 61 +++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 contracts/MeshStandardToken.sol diff --git a/contracts/MeshStandardToken.sol b/contracts/MeshStandardToken.sol new file mode 100644 index 0000000..f182a9f --- /dev/null +++ b/contracts/MeshStandardToken.sol @@ -0,0 +1,61 @@ +/* +This Token Contract implements the standard token functionality (https://github.com/ethereum/EIPs/issues/20) as well as the following OPTIONAL extras intended for use by mesh organizations. + +In other words. This is intended for deployment in something like a Token Factory or Mist wallet, and then used by humans performing within mesh organizations. +Imagine coins, currencies, shares, voting weight, etc. +Machine-based, rapid creation of many tokens would not necessarily need these extra features or will be minted in other manners. + +1) Initial Finite Supply (upon creation one specifies how much is minted). +2) In the absence of a token registry: Optional Decimal, Symbol & Name. +3) Optional approveAndCall() functionality to notify a contract if an approval() has occurred. + +.*/ +pragma solidity ^0.4.4; + +import "./StandardToken.sol"; + +contract MeshStandardToken is StandardToken { + + function () { + //if ether is sent to this address, send it back. + throw; + } + + /* Public variables of the token */ + + /* + NOTE: + The following variables are OPTIONAL vanities. One does not have to include them. + They allow one to customise the token contract & in no way influences the core functionality. + Some wallets/interfaces might not even bother to look at this information. + */ + string public name; //fancy name: eg Simon Bucks + uint8 public decimals; //How many decimals to show. ie. There could 1000 base units with 3 decimals. Meaning 0.980 SBX = 980 base units. It's like comparing 1 wei to 1 ether. + string public symbol; //An identifier: eg SBX + string public version = 'M0.1'; //mesh 0.1 standard. Just an arbitrary versioning scheme. + + function HumanStandardToken( + uint256 _initialAmount, + string _tokenName, + uint8 _decimalUnits, + string _tokenSymbol + ) { + balances[msg.sender] = _initialAmount; // Give the creator all initial tokens + totalSupply = _initialAmount; // Update total supply + name = _tokenName; // Set the name for display purposes + decimals = _decimalUnits; // Amount of decimals for display purposes + symbol = _tokenSymbol; // Set the symbol for display purposes + } + + /* Approves and then calls the receiving contract */ + function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) { + allowed[msg.sender][_spender] = _value; + Approval(msg.sender, _spender, _value); + + //call the receiveApproval function on the contract you want to be notified. This crafts the function signature manually so one doesn't have to include a contract in here just for this. + //receiveApproval(address _from, uint256 _value, address _tokenContract, bytes _extraData) + //it is assumed that when does this that the call *should* succeed, otherwise one would use vanilla approve instead. + if(!_spender.call(bytes4(bytes32(sha3("receiveApproval(address,uint256,address,bytes)"))), msg.sender, _value, this, _extraData)) { throw; } + return true; + } +} From c25a42b931cc1d01f5f4490a10176e6620701cf9 Mon Sep 17 00:00:00 2001 From: Bill Gleim Date: Sat, 18 Feb 2017 13:42:30 -0800 Subject: [PATCH 2/7] Add MeshStandardToken constructor --- contracts/MeshStandardToken.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/MeshStandardToken.sol b/contracts/MeshStandardToken.sol index f182a9f..caeb40c 100644 --- a/contracts/MeshStandardToken.sol +++ b/contracts/MeshStandardToken.sol @@ -34,7 +34,7 @@ contract MeshStandardToken is StandardToken { string public symbol; //An identifier: eg SBX string public version = 'M0.1'; //mesh 0.1 standard. Just an arbitrary versioning scheme. - function HumanStandardToken( + function MeshStandardToken( uint256 _initialAmount, string _tokenName, uint8 _decimalUnits, From dad090c7defa97d079bdce33a084073655c21fe1 Mon Sep 17 00:00:00 2001 From: Bill Gleim Date: Tue, 21 Feb 2017 17:06:41 -0800 Subject: [PATCH 3/7] Deploy Mesh Token --- truffle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/truffle.js b/truffle.js index 7273191..e813433 100644 --- a/truffle.js +++ b/truffle.js @@ -1,7 +1,7 @@ module.exports = { "build": "webpack", "deploy": [ - "HumanStandardToken" + "MeshStandardToken" ], "rpc": { // Default RPC configuration. From 7665c4407e79d5fc4b9d65a73623f5a0bb1b94d0 Mon Sep 17 00:00:00 2001 From: Bill Gleim Date: Thu, 23 Feb 2017 15:01:51 -0800 Subject: [PATCH 4/7] Upgrade to truffle 3.x --- truffle.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/truffle.js b/truffle.js index e813433..2491c7c 100644 --- a/truffle.js +++ b/truffle.js @@ -1,11 +1,9 @@ module.exports = { - "build": "webpack", - "deploy": [ - "MeshStandardToken" - ], - "rpc": { - // Default RPC configuration. - "host": "localhost", - "port": 8545 + networks: { + development: { + host: "localhost", + port: 8545, + network_id: "*" // Match any network id + } } -} +}; From 0f502ae66e53c376d843dad174fda076581b7cf7 Mon Sep 17 00:00:00 2001 From: Bill Gleim Date: Thu, 23 Feb 2017 15:02:32 -0800 Subject: [PATCH 5/7] add test drivers for mesh+ --- test/humanStandardToken.js | 294 +++++++++++++++++++++++++++++++++++++ test/meshStandardToken.js | 294 +++++++++++++++++++++++++++++++++++++ test/standardToken.js | 1 + test/tokenTester.js | 18 +++ 4 files changed, 607 insertions(+) create mode 100644 test/humanStandardToken.js create mode 100644 test/meshStandardToken.js create mode 100644 test/standardToken.js create mode 100644 test/tokenTester.js diff --git a/test/humanStandardToken.js b/test/humanStandardToken.js new file mode 100644 index 0000000..5f7e029 --- /dev/null +++ b/test/humanStandardToken.js @@ -0,0 +1,294 @@ +var HumanStandardToken = artifacts.require("./HumanStandardToken.sol"); +var SampleRecipientSuccess = artifacts.require("./test/SampleRecipientSuccess.sol"); + +contract("HumanStandardToken", function(accounts) { + +//CREATION + + it("creation: should create an initial balance of 10000 for the creator", function(done) { + HumanStandardToken.new(10000, 'Simon Bucks', 1, 'SBX', {from: accounts[0]}).then(function(ctr) { + return ctr.balanceOf.call(accounts[0]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 10000); + done(); + }).catch(done); + }); + + it("creation: test correct setting of vanity information", function(done) { + var ctr; + HumanStandardToken.new(10000, 'Simon Bucks', 1, 'SBX', {from: accounts[0]}).then(function(result) { + ctr = result; + return ctr.name.call(); + }).then(function (result) { + assert.strictEqual(result, 'Simon Bucks'); + return ctr.decimals.call(); + }).then(function(result) { + assert.strictEqual(result.toNumber(), 1); + return ctr.symbol.call(); + }).then(function(result) { + assert.strictEqual(result, 'SBX'); + done(); + }).catch(done); + }); + + it("creation: should succeed in creating over 2^256 - 1 (max) tokens", function(done) { + //2^256 - 1 + HumanStandardToken.new('115792089237316195423570985008687907853269984665640564039457584007913129639935', 'Simon Bucks', 1, 'SBX', {from: accounts[0]}).then(function(ctr) { + return ctr.totalSupply(); + }).then(function (result) { + var match = result.equals('1.15792089237316195423570985008687907853269984665640564039457584007913129639935e+77'); + assert.isTrue(match); + done(); + }).catch(done); + }); + +//TRANSERS +//normal transfers without approvals. + + //this is not *good* enough as the contract could still throw an error otherwise. + //ideally one should check balances before and after, but estimateGas currently always throws an error. + //it's not giving estimate on gas used in the event of an error. + it("transfers: ether transfer should be reversed.", function(done) { + var ctr; + HumanStandardToken.new(10000, 'Simon Bucks', 1, 'SBX', {from: accounts[0]}).then(function(result) { + ctr = result; + return web3.eth.sendTransaction({from: accounts[0], to: ctr.address, value: web3.toWei("10", "Ether")}); + }).catch(function(result) { + done(); + }).catch(done); + }); + + + it("transfers: should transfer 10000 to accounts[1] with accounts[0] having 10000", function(done) { + var ctr; + HumanStandardToken.new(10000, 'Simon Bucks', 1, 'SBX', {from: accounts[0]}).then(function(result) { + ctr = result; + return ctr.transfer(accounts[1], 10000, {from: accounts[0]}); + }).then(function (result) { + return ctr.balanceOf.call(accounts[1]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 10000); + done(); + }).catch(done); + }); + + it("transfers: should fail when trying to transfer 10001 to accounts[1] with accounts[0] having 10000", function(done) { + var ctr; + HumanStandardToken.new(10000, 'Simon Bucks', 1, 'SBX', {from: accounts[0]}).then(function(result) { + ctr = result; + return ctr.transfer.call(accounts[1], 10001, {from: accounts[0]}); + }).then(function (result) { + assert.isFalse(result); + done(); + }).catch(done); + }); + + it("transfers: should fail when trying to transfer zero.", function(done) { + var ctr; + HumanStandardToken.new(10000, 'Simon Bucks', 1, 'SBX', {from: accounts[0]}).then(function(result) { + ctr = result; + return ctr.transfer.call(accounts[1], 0, {from: accounts[0]}); + }).then(function (result) { + assert.isFalse(result); + done(); + }).catch(done); + }); + + //NOTE: testing uint256 wrapping is impossible in this standard token since you can't supply > 2^256 -1. + + //todo: transfer max amounts. + +//APPROVALS + + it("approvals: msg.sender should approve 100 to accounts[1]", function(done) { + var ctr = null; + HumanStandardToken.new(10000, 'Simon Bucks', 1, 'SBX', {from: accounts[0]}).then(function(result) { + ctr = result; + return ctr.approve(accounts[1], 100, {from: accounts[0]}); + }).then(function (result) { + return ctr.allowance.call(accounts[0], accounts[1]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 100); + done(); + }).catch(done); + }); + + it("approvals: msg.sender should approve 100 to SampleRecipient and then NOTIFY SampleRecipient. It should succeed.", function(done) { + var ctr = null; + var sampleCtr = null + HumanStandardToken.new(10000, 'Simon Bucks', 1, 'SBX', {from: accounts[0]}).then(function(result) { + ctr = result; + return SampleRecipientSuccess.new({from: accounts[0]}); + }).then(function(result) { + sampleCtr = result; + return ctr.approveAndCall(sampleCtr.address, 100, '0x42', {from: accounts[0]}); + }).then(function (result) { + return ctr.allowance.call(accounts[0], sampleCtr.address); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 100); + return sampleCtr.value.call(); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 100); + done(); + }).catch(done); + }); + + it("approvals: msg.sender should approve 100 to SampleRecipient and then NOTIFY SampleRecipient and throw.", function(done) { + var ctr = null; + var sampleCtr = null + HumanStandardToken.new(10000, 'Simon Bucks', 1, 'SBX', {from: accounts[0]}).then(function(result) { + ctr = result; + return SampleRecipientThrow.new({from: accounts[0]}); + }).then(function(result) { + sampleCtr = result; + return ctr.approveAndCall.call(sampleCtr.address, 100, '0x42', {from: accounts[0]}); + }).catch(function (result) { + //It will catch OOG. + done(); + }).catch(done) + }); + + //bit overkill. But is for testing a bug + it("approvals: msg.sender approves accounts[1] of 100 & withdraws 20 once.", function(done) { + var ctr = null; + HumanStandardToken.new(10000, 'Simon Bucks', 1, 'SBX', {from: accounts[0]}).then(function(result) { + ctr = result; + return ctr.balanceOf.call(accounts[0]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 10000); + return ctr.approve(accounts[1], 100, {from: accounts[0]}); + }).then(function (result) { + return ctr.balanceOf.call(accounts[2]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 0); + return ctr.allowance.call(accounts[0], accounts[1]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 100); + return ctr.transferFrom.call(accounts[0], accounts[2], 20, {from: accounts[1]}); + }).then(function (result) { + return ctr.transferFrom(accounts[0], accounts[2], 20, {from: accounts[1]}); + }).then(function (result) { + return ctr.allowance.call(accounts[0], accounts[1]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 80); + return ctr.balanceOf.call(accounts[2]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 20); + return ctr.balanceOf.call(accounts[0]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 9980); + done(); + }).catch(done); + }); + + //should approve 100 of msg.sender & withdraw 50, twice. (should succeed) + it("approvals: msg.sender approves accounts[1] of 100 & withdraws 20 twice.", function(done) { + var ctr = null; + HumanStandardToken.new(10000, 'Simon Bucks', 1, 'SBX', {from: accounts[0]}).then(function(result) { + ctr = result; + return ctr.approve(accounts[1], 100, {from: accounts[0]}); + }).then(function (result) { + return ctr.allowance.call(accounts[0], accounts[1]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 100); + return ctr.transferFrom(accounts[0], accounts[2], 20, {from: accounts[1]}); + }).then(function (result) { + return ctr.allowance.call(accounts[0], accounts[1]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 80); + return ctr.balanceOf.call(accounts[2]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 20); + return ctr.balanceOf.call(accounts[0]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 9980); + //FIRST tx done. + //onto next. + return ctr.transferFrom(accounts[0], accounts[2], 20, {from: accounts[1]}); + }).then(function (result) { + return ctr.allowance.call(accounts[0], accounts[1]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 60); + return ctr.balanceOf.call(accounts[2]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 40); + return ctr.balanceOf.call(accounts[0]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 9960); + done(); + }).catch(done); + }); + + //should approve 100 of msg.sender & withdraw 50 & 60 (should fail). + it("approvals: msg.sender approves accounts[1] of 100 & withdraws 50 & 60 (2nd tx should fail)", function(done) { + var ctr = null; + HumanStandardToken.new(10000, 'Simon Bucks', 1, 'SBX', {from: accounts[0]}).then(function(result) { + ctr = result; + return ctr.approve(accounts[1], 100, {from: accounts[0]}); + }).then(function (result) { + return ctr.allowance.call(accounts[0], accounts[1]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 100); + return ctr.transferFrom(accounts[0], accounts[2], 50, {from: accounts[1]}); + }).then(function (result) { + return ctr.allowance.call(accounts[0], accounts[1]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 50); + return ctr.balanceOf.call(accounts[2]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 50); + return ctr.balanceOf.call(accounts[0]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 9950); + //FIRST tx done. + //onto next. + return ctr.transferFrom.call(accounts[0], accounts[2], 60, {from: accounts[1]}); + }).then(function (result) { + assert.isFalse(result); + done(); + }).catch(done); + }); + + it("approvals: attempt withdrawal from acconut with no allowance (should fail)", function(done) { + var ctr = null; + HumanStandardToken.new(10000, 'Simon Bucks', 1, 'SBX', {from: accounts[0]}).then(function(result) { + ctr = result; + return ctr.transferFrom.call(accounts[0], accounts[2], 60, {from: accounts[1]}); + }).then(function (result) { + assert.isFalse(result); + done(); + }).catch(done); + }); + + it("approvals: allow accounts[1] 100 to withdraw from accounts[0]. Withdraw 60 and then approve 0 & attempt transfer.", function(done) { + var ctr = null; + HumanStandardToken.new(10000, 'Simon Bucks', 1, 'SBX', {from: accounts[0]}).then(function(result) { + ctr = result; + return ctr.approve(accounts[1], 100, {from: accounts[0]}); + }).then(function (result) { + return ctr.transferFrom(accounts[0], accounts[2], 60, {from: accounts[1]}); + }).then(function (result) { + return ctr.approve(accounts[1], 0, {from: accounts[0]}); + }).then(function (result) { + return ctr.transferFrom.call(accounts[0], accounts[2], 10, {from: accounts[1]}); + }).then(function (result) { + assert.isFalse(result); + done(); + }).catch(done); + }); + + it("approvals: approve max (2^256 - 1)", function(done) { + var ctr = null; + HumanStandardToken.new(10000, 'Simon Bucks', 1, 'SBX', {from: accounts[0]}).then(function(result) { + ctr = result; + return ctr.approve(accounts[1],'115792089237316195423570985008687907853269984665640564039457584007913129639935' , {from: accounts[0]}); + }).then(function (result) { + return ctr.allowance(accounts[0], accounts[1]); + }).then(function (result) { + var match = result.equals('1.15792089237316195423570985008687907853269984665640564039457584007913129639935e+77'); + assert.isTrue(match); + done(); + }).catch(done); + }); + +}); diff --git a/test/meshStandardToken.js b/test/meshStandardToken.js new file mode 100644 index 0000000..11c451d --- /dev/null +++ b/test/meshStandardToken.js @@ -0,0 +1,294 @@ +var MeshStandardToken = artifacts.require("./MeshStandardToken.sol"); +var SampleRecipientSuccess = artifacts.require("./test/SampleRecipientSuccess.sol"); + +contract("MeshStandardToken", function(accounts) { + +//CREATION + + it("creation: should create an initial balance of 10000 for the creator", function(done) { + MeshStandardToken.new(10000, 'Mesh Bucks', 1, 'MBX', {from: accounts[3]}).then(function(ctr) { + return ctr.balanceOf.call(accounts[3]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 10000); + done(); + }).catch(done); + }); + + it("creation: test correct setting of vanity information", function(done) { + var ctr; + MeshStandardToken.new(10000, 'Mesh Bucks', 1, 'MBX', {from: accounts[3]}).then(function(result) { + ctr = result; + return ctr.name.call(); + }).then(function (result) { + assert.strictEqual(result, 'Mesh Bucks'); + return ctr.decimals.call(); + }).then(function(result) { + assert.strictEqual(result.toNumber(), 1); + return ctr.symbol.call(); + }).then(function(result) { + assert.strictEqual(result, 'MBX'); + done(); + }).catch(done); + }); + + it("creation: should succeed in creating over 2^256 - 1 (max) tokens", function(done) { + //2^256 - 1 + MeshStandardToken.new('115792089237316195423570985008687907853269984665640564039457584007913129639935', 'Mesh Bucks', 1, 'MBX', {from: accounts[3]}).then(function(ctr) { + return ctr.totalSupply(); + }).then(function (result) { + var match = result.equals('1.15792089237316195423570985008687907853269984665640564039457584007913129639935e+77'); + assert.isTrue(match); + done(); + }).catch(done); + }); + +//TRANSERS +//normal transfers without approvals. + + //this is not *good* enough as the contract could still throw an error otherwise. + //ideally one should check balances before and after, but estimateGas currently always throws an error. + //it's not giving estimate on gas used in the event of an error. + it("transfers: ether transfer should be reversed.", function(done) { + var ctr; + MeshStandardToken.new(10000, 'Mesh Bucks', 1, 'MBX', {from: accounts[3]}).then(function(result) { + ctr = result; + return web3.eth.sendTransaction({from: accounts[3], to: ctr.address, value: web3.toWei("10", "Ether")}); + }).catch(function(result) { + done(); + }).catch(done); + }); + + + it("transfers: should transfer 10000 to accounts[4] with accounts[3] having 10000", function(done) { + var ctr; + MeshStandardToken.new(10000, 'Mesh Bucks', 1, 'MBX', {from: accounts[3]}).then(function(result) { + ctr = result; + return ctr.transfer(accounts[4], 10000, {from: accounts[3]}); + }).then(function (result) { + return ctr.balanceOf.call(accounts[4]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 10000); + done(); + }).catch(done); + }); + + it("transfers: should fail when trying to transfer 10001 to accounts[4] with accounts[3] having 10000", function(done) { + var ctr; + MeshStandardToken.new(10000, 'Mesh Bucks', 1, 'MBX', {from: accounts[3]}).then(function(result) { + ctr = result; + return ctr.transfer.call(accounts[4], 10001, {from: accounts[3]}); + }).then(function (result) { + assert.isFalse(result); + done(); + }).catch(done); + }); + + it("transfers: should fail when trying to transfer zero.", function(done) { + var ctr; + MeshStandardToken.new(10000, 'Mesh Bucks', 1, 'MBX', {from: accounts[3]}).then(function(result) { + ctr = result; + return ctr.transfer.call(accounts[4], 0, {from: accounts[3]}); + }).then(function (result) { + assert.isFalse(result); + done(); + }).catch(done); + }); + + //NOTE: testing uint256 wrapping is impossible in this standard token since you can't supply > 2^256 -1. + + //todo: transfer max amounts. + +//APPROVALS + + it("approvals: msg.sender should approve 100 to accounts[4]", function(done) { + var ctr = null; + MeshStandardToken.new(10000, 'Mesh Bucks', 1, 'MBX', {from: accounts[3]}).then(function(result) { + ctr = result; + return ctr.approve(accounts[4], 100, {from: accounts[3]}); + }).then(function (result) { + return ctr.allowance.call(accounts[3], accounts[4]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 100); + done(); + }).catch(done); + }); + + it("approvals: msg.sender should approve 100 to SampleRecipient and then NOTIFY SampleRecipient. It should succeed.", function(done) { + var ctr = null; + var sampleCtr = null + MeshStandardToken.new(10000, 'Mesh Bucks', 1, 'MBX', {from: accounts[3]}).then(function(result) { + ctr = result; + return SampleRecipientSuccess.new({from: accounts[3]}); + }).then(function(result) { + sampleCtr = result; + return ctr.approveAndCall(sampleCtr.address, 100, '0x42', {from: accounts[3]}); + }).then(function (result) { + return ctr.allowance.call(accounts[3], sampleCtr.address); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 100); + return sampleCtr.value.call(); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 100); + done(); + }).catch(done); + }); + + it("approvals: msg.sender should approve 100 to SampleRecipient and then NOTIFY SampleRecipient and throw.", function(done) { + var ctr = null; + var sampleCtr = null + MeshStandardToken.new(10000, 'Mesh Bucks', 1, 'MBX', {from: accounts[3]}).then(function(result) { + ctr = result; + return SampleRecipientThrow.new({from: accounts[3]}); + }).then(function(result) { + sampleCtr = result; + return ctr.approveAndCall.call(sampleCtr.address, 100, '0x42', {from: accounts[3]}); + }).catch(function (result) { + //It will catch OOG. + done(); + }).catch(done) + }); + + //bit overkill. But is for testing a bug + it("approvals: msg.sender approves accounts[4] of 100 & withdraws 20 once.", function(done) { + var ctr = null; + MeshStandardToken.new(10000, 'Mesh Bucks', 1, 'MBX', {from: accounts[3]}).then(function(result) { + ctr = result; + return ctr.balanceOf.call(accounts[3]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 10000); + return ctr.approve(accounts[4], 100, {from: accounts[3]}); + }).then(function (result) { + return ctr.balanceOf.call(accounts[5]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 0); + return ctr.allowance.call(accounts[3], accounts[4]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 100); + return ctr.transferFrom.call(accounts[3], accounts[5], 20, {from: accounts[4]}); + }).then(function (result) { + return ctr.transferFrom(accounts[3], accounts[5], 20, {from: accounts[4]}); + }).then(function (result) { + return ctr.allowance.call(accounts[3], accounts[4]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 80); + return ctr.balanceOf.call(accounts[5]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 20); + return ctr.balanceOf.call(accounts[3]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 9980); + done(); + }).catch(done); + }); + + //should approve 100 of msg.sender & withdraw 50, twice. (should succeed) + it("approvals: msg.sender approves accounts[4] of 100 & withdraws 20 twice.", function(done) { + var ctr = null; + MeshStandardToken.new(10000, 'Mesh Bucks', 1, 'MBX', {from: accounts[3]}).then(function(result) { + ctr = result; + return ctr.approve(accounts[4], 100, {from: accounts[3]}); + }).then(function (result) { + return ctr.allowance.call(accounts[3], accounts[4]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 100); + return ctr.transferFrom(accounts[3], accounts[5], 20, {from: accounts[4]}); + }).then(function (result) { + return ctr.allowance.call(accounts[3], accounts[4]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 80); + return ctr.balanceOf.call(accounts[5]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 20); + return ctr.balanceOf.call(accounts[3]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 9980); + //FIRST tx done. + //onto next. + return ctr.transferFrom(accounts[3], accounts[5], 20, {from: accounts[4]}); + }).then(function (result) { + return ctr.allowance.call(accounts[3], accounts[4]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 60); + return ctr.balanceOf.call(accounts[5]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 40); + return ctr.balanceOf.call(accounts[3]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 9960); + done(); + }).catch(done); + }); + + //should approve 100 of msg.sender & withdraw 50 & 60 (should fail). + it("approvals: msg.sender approves accounts[4] of 100 & withdraws 50 & 60 (2nd tx should fail)", function(done) { + var ctr = null; + MeshStandardToken.new(10000, 'Mesh Bucks', 1, 'MBX', {from: accounts[3]}).then(function(result) { + ctr = result; + return ctr.approve(accounts[4], 100, {from: accounts[3]}); + }).then(function (result) { + return ctr.allowance.call(accounts[3], accounts[4]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 100); + return ctr.transferFrom(accounts[3], accounts[5], 50, {from: accounts[4]}); + }).then(function (result) { + return ctr.allowance.call(accounts[3], accounts[4]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 50); + return ctr.balanceOf.call(accounts[5]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 50); + return ctr.balanceOf.call(accounts[3]); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 9950); + //FIRST tx done. + //onto next. + return ctr.transferFrom.call(accounts[3], accounts[5], 60, {from: accounts[4]}); + }).then(function (result) { + assert.isFalse(result); + done(); + }).catch(done); + }); + + it("approvals: attempt withdrawal from acconut with no allowance (should fail)", function(done) { + var ctr = null; + MeshStandardToken.new(10000, 'Mesh Bucks', 1, 'MBX', {from: accounts[3]}).then(function(result) { + ctr = result; + return ctr.transferFrom.call(accounts[3], accounts[5], 60, {from: accounts[4]}); + }).then(function (result) { + assert.isFalse(result); + done(); + }).catch(done); + }); + + it("approvals: allow accounts[4] 100 to withdraw from accounts[3]. Withdraw 60 and then approve 0 & attempt transfer.", function(done) { + var ctr = null; + MeshStandardToken.new(10000, 'Mesh Bucks', 1, 'MBX', {from: accounts[3]}).then(function(result) { + ctr = result; + return ctr.approve(accounts[4], 100, {from: accounts[3]}); + }).then(function (result) { + return ctr.transferFrom(accounts[3], accounts[5], 60, {from: accounts[4]}); + }).then(function (result) { + return ctr.approve(accounts[4], 0, {from: accounts[3]}); + }).then(function (result) { + return ctr.transferFrom.call(accounts[3], accounts[5], 10, {from: accounts[4]}); + }).then(function (result) { + assert.isFalse(result); + done(); + }).catch(done); + }); + + it("approvals: approve max (2^256 - 1)", function(done) { + var ctr = null; + MeshStandardToken.new(10000, 'Mesh Bucks', 1, 'MBX', {from: accounts[3]}).then(function(result) { + ctr = result; + return ctr.approve(accounts[4],'115792089237316195423570985008687907853269984665640564039457584007913129639935' , {from: accounts[3]}); + }).then(function (result) { + return ctr.allowance(accounts[3], accounts[4]); + }).then(function (result) { + var match = result.equals('1.15792089237316195423570985008687907853269984665640564039457584007913129639935e+77'); + assert.isTrue(match); + done(); + }).catch(done); + }); + +}); diff --git a/test/standardToken.js b/test/standardToken.js new file mode 100644 index 0000000..977dde7 --- /dev/null +++ b/test/standardToken.js @@ -0,0 +1 @@ +/* This has moved to humandStandardToken.js */ diff --git a/test/tokenTester.js b/test/tokenTester.js new file mode 100644 index 0000000..7591070 --- /dev/null +++ b/test/tokenTester.js @@ -0,0 +1,18 @@ +//currently commented out as TokenTester is causing a OOG error due to the Factory being too big +//Not fully needed as factory & separate tests cover token creation. + +/*contract("TokenTester", function(accounts) { + it("creates 10000 initial tokens", function(done) { + var tester = TokenTester.at(TokenTester.deployed_address); + tester.tokenContractAddress.call() + .then(function(tokenContractAddr) { + var tokenContract = HumanStandardToken.at(tokenContractAddr); + return tokenContract.balanceOf.call(TokenTester.deployed_address); + }).then(function (result) { + assert.strictEqual(result.toNumber(), 10000); // 10000 as specified in TokenTester.sol + done(); + }).catch(done); + }); + + //todo:add test on retrieving addresses +});*/ From ac7c63318e815d6f915f6ab59eb2a73507ae5600 Mon Sep 17 00:00:00 2001 From: Bill Gleim Date: Thu, 23 Feb 2017 15:02:59 -0800 Subject: [PATCH 6/7] add test driver support --- contracts/test/SampleRecipientSuccess.sol | 23 +++++++++++++++++++++++ contracts/test/SampleRecipientThrow.sol | 11 +++++++++++ 2 files changed, 34 insertions(+) create mode 100644 contracts/test/SampleRecipientSuccess.sol create mode 100644 contracts/test/SampleRecipientThrow.sol diff --git a/contracts/test/SampleRecipientSuccess.sol b/contracts/test/SampleRecipientSuccess.sol new file mode 100644 index 0000000..829338b --- /dev/null +++ b/contracts/test/SampleRecipientSuccess.sol @@ -0,0 +1,23 @@ +/* +This is an example contract that helps test the functionality of the approveAndCall() functionality of HumanStandardToken.sol. +This one assumes successful receival of approval. +*/ +pragma solidity ^0.4.8; + +contract SampleRecipientSuccess { + /* A Generic receiving function for contracts that accept tokens */ + address public from; + uint256 public value; + address public tokenContract; + bytes public extraData; + + event ReceivedApproval(uint256 _value); + + function receiveApproval(address _from, uint256 _value, address _tokenContract, bytes _extraData) { + from = _from; + value = _value; + tokenContract = _tokenContract; + extraData = _extraData; + ReceivedApproval(_value); + } +} diff --git a/contracts/test/SampleRecipientThrow.sol b/contracts/test/SampleRecipientThrow.sol new file mode 100644 index 0000000..66240ad --- /dev/null +++ b/contracts/test/SampleRecipientThrow.sol @@ -0,0 +1,11 @@ +/* +This is an example contract that helps test the functionality of the approveAndCall() functionality of HumanStandardToken.sol. +This one will throw and thus needs to propagate the error up. +*/ +pragma solidity ^0.4.8; + +contract SampleRecipientThrow { + function () { + throw; + } +} From a4bd5827438de16cf8bf3c4378691a60c2d47255 Mon Sep 17 00:00:00 2001 From: Bill Gleim Date: Thu, 23 Feb 2017 15:03:54 -0800 Subject: [PATCH 7/7] Upgrade to truffle 3.x deployment --- contracts/Migrations.sol | 23 +++++++++++++++++++++++ migrations/1_initial_migration.js | 5 +++++ migrations/2_deploy_contracts.js | 7 +++++++ 3 files changed, 35 insertions(+) create mode 100644 contracts/Migrations.sol create mode 100644 migrations/1_initial_migration.js create mode 100644 migrations/2_deploy_contracts.js diff --git a/contracts/Migrations.sol b/contracts/Migrations.sol new file mode 100644 index 0000000..7e7fe8d --- /dev/null +++ b/contracts/Migrations.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.4.4; + +contract Migrations { + address public owner; + uint public last_completed_migration; + + modifier restricted() { + if (msg.sender == owner) _; + } + + function Migrations() { + owner = msg.sender; + } + + function setCompleted(uint completed) restricted { + last_completed_migration = completed; + } + + function upgrade(address new_address) restricted { + Migrations upgraded = Migrations(new_address); + upgraded.setCompleted(last_completed_migration); + } +} diff --git a/migrations/1_initial_migration.js b/migrations/1_initial_migration.js new file mode 100644 index 0000000..4d5f3f9 --- /dev/null +++ b/migrations/1_initial_migration.js @@ -0,0 +1,5 @@ +var Migrations = artifacts.require("./Migrations.sol"); + +module.exports = function(deployer) { + deployer.deploy(Migrations); +}; diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js new file mode 100644 index 0000000..257bec0 --- /dev/null +++ b/migrations/2_deploy_contracts.js @@ -0,0 +1,7 @@ +var MeshStandardToken = artifacts.require("./MeshStandardToken.sol"); +var HumanStandardToken = artifacts.require("./HumanStandardToken.sol"); + +module.exports = function(deployer) { + deployer.deploy(MeshStandardToken); + deployer.deploy(HumanStandardToken); +};