diff --git a/.gitignore b/.gitignore index 7746d6d..98d9cee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .idea +build_webpack build/contracts/* node_modules package-lock.json diff --git a/contracts/IRegistry.sol b/contracts/IRegistry.sol new file mode 100644 index 0000000..d516acc --- /dev/null +++ b/contracts/IRegistry.sol @@ -0,0 +1,6 @@ +pragma solidity ^0.4.22; + +interface IRegistry { + function put(bytes32 _key, address _contractAddress) external; + function get(bytes32 _key) view external returns(address); +} diff --git a/contracts/Susu.sol b/contracts/Susu.sol index 772a666..9e89795 100644 --- a/contracts/Susu.sol +++ b/contracts/Susu.sol @@ -2,125 +2,119 @@ pragma solidity ^0.4.22; import { Ownable } from "zeppelin-solidity/contracts/ownership/Ownable.sol"; import { SafeMath } from "zeppelin-solidity/contracts/math/SafeMath.sol"; +import "./SusuDataStore.sol"; -// API Design here: https://docs.google.com/presentation/d/12qfLPD88TTfvQxWLRTu5boEav8-JdRf3IjpS5WNDTvs/edit#slide=id.g3cb5a7885c_0_11 -// Design Requirements here: https://docs.google.com/document/d/1myfOSSwCPx16uUMSLbtf5RvfLFN97vmkKxjFWIvAaEM/edit contract Susu is Ownable { using SafeMath for uint; - string public groupName; - uint256 public contribAmtWei; - uint8 public groupSize; - address[] public members; - uint public memberIdxToPayNext; - uint8 public maxMembers; - mapping(address => uint) private currentContributions; - - constructor(uint8 _groupSize, string _groupName, uint256 _contribAmtWei) public { - // Max number of members is 100. Arbitrary but should be smaller than max of uint8 - maxMembers = 100; - require(_groupSize < maxMembers); - groupName = _groupName; - contribAmtWei = _contribAmtWei; - groupSize = _groupSize; - members.push(owner); - memberIdxToPayNext = 0; - } - - function payOut() public payable onlyOwner { - if(everyonePaid()) { - resetBalances(); - paySusu(); - iterateMemberToPayNext(); + SusuDataStore public susuDataStore; + uint8 constant public MAX_MEMBERS = 5; + string constant public version = '0.0.32'; + + constructor(address _susuDataStoreAddress, address _newOwner) public { + susuDataStore = SusuDataStore(_susuDataStoreAddress); + require(susuDataStore.groupSize() <= MAX_MEMBERS); + if(susuDataStore.getManyMembers()==0) { + susuDataStore.addMember(_newOwner); } + transferOwnership(_newOwner); + } + + function groupName() external view returns(string) { + return susuDataStore.groupName(); + } + + function contribAmtWei() public view returns(uint256) { + return susuDataStore.contribAmtWei(); + } + + function memberIdxToPayNext() public view returns(uint) { + return susuDataStore.memberIdxToPayNext(); + } + + function groupSize() public view returns(uint8) { + return susuDataStore.groupSize(); } function pullPayOut() public payable { - require(msg.sender == members[memberIdxToPayNext]); + // TODO: require group is full + // TODO: require everyone has paid + require(msg.sender == getMemberAtIndex(memberIdxToPayNext())); resetBalances(); iterateMemberToPayNext(); - msg.sender.transfer(members.length * contribAmtWei); + msg.sender.transfer(getManyMembers() * contribAmtWei()); } - function everyonePaid() private view returns (bool) { - for (uint i = 0; i < members.length ; i++) - { - if(currentContributions[members[i]] != contribAmtWei) - return false; - } - return true; - } +// function everyonePaid() private view returns (bool) { +// for (uint i = 0; i < members.length ; i++) +// { +// if(currentContributions[members[i]] != contribAmtWei) +// return false; +// } +// return true; +// } function resetBalances() private { - for (uint i = 0; i < members.length ; i++) + for (uint i = 0; i < susuDataStore.getManyMembers(); i++) { - currentContributions[members[i]] = 0; + address member = susuDataStore.getMemberAtIndex(i); + susuDataStore.setContributionForMember(member, 0); } } function iterateMemberToPayNext() private { - for (uint i = 0; i < members.length ; i++) - { - if(members[i] == members[memberIdxToPayNext]) { - if(i < members.length - 1) { - memberIdxToPayNext = i.add(1); - return; - } - - memberIdxToPayNext = 0; - return; - } + uint _memberIdxToPayNext = susuDataStore.memberIdxToPayNext(); + _memberIdxToPayNext++; + uint manyMembers = susuDataStore.getManyMembers(); + if(_memberIdxToPayNext == manyMembers) { + _memberIdxToPayNext = 0; } + susuDataStore.setMemberIdxToPayNext(_memberIdxToPayNext); + // TODO: Why was there a for-loop here? Check SusuOrig. Maybe it is needed? } - function paySusu() private { - members[memberIdxToPayNext].transfer(members.length * contribAmtWei); - } - - function getMemberAtIndex(uint8 index) public view returns(address) { - return members[index]; + function getMemberAtIndex(uint _index) public view returns(address) { + return susuDataStore.getMemberAtIndex(_index); } - function getContributionForMember(address _member) public view returns(uint256) { - return currentContributions[_member]; - } - - function amIOwner() public view returns(bool) { + function amIOwner() external view returns(bool) { return (msg.sender == owner); } function getManyMembers() public view returns(uint) { - return members.length; - } - - function joinGroup() public { - require(!isRecipient(msg.sender)); - members.push(msg.sender); - } - - function contribute() public payable { - require(msg.value == contribAmtWei); - require(isRecipient(msg.sender)); - - TrackPayment(); + return susuDataStore.getManyMembers(); } - function TrackPayment() private { - require(currentContributions[msg.sender] == 0); - currentContributions[msg.sender] = msg.value; + function joinGroup() external { + require(!isRecipient(msg.sender)); + susuDataStore.addMember(msg.sender); } function isRecipient(address addr) private view returns (bool) { - for (uint i = 0; i < members.length ; i++) + uint manyMembers = getManyMembers(); + for (uint8 i = 0; i < manyMembers ; i++) { - if(members[i] == addr) + if(getMemberAtIndex(i) == addr) return true; } return false; } + function getContributionForMember(address _member) external view returns(uint256) { + return susuDataStore.getContributionForMember(_member); + } + function () external payable { - contribute(); +// require(msg.value == susuDataStore.contribAmtWei()); + require(isRecipient(msg.sender)); + require(susuDataStore.getContributionForMember(msg.sender) == 0); + susuDataStore.setContributionForMember(msg.sender, msg.value); } -} \ No newline at end of file + + // onlyOwner? + function kill(address _newSusu) public payable { + selfdestruct(_newSusu); + } + +} diff --git a/contracts/SusuDataStore.sol b/contracts/SusuDataStore.sol new file mode 100644 index 0000000..6155365 --- /dev/null +++ b/contracts/SusuDataStore.sol @@ -0,0 +1,41 @@ +pragma solidity ^0.4.22; + +contract SusuDataStore { + string public groupName; + uint256 public contribAmtWei; + uint8 public groupSize; + address[] public members; + uint public memberIdxToPayNext = 0; + mapping(address => uint) public currentContributions; + + constructor(uint8 _groupSize, string _groupName, uint256 _contribAmtWei) public { + groupName = _groupName; + contribAmtWei = _contribAmtWei; + groupSize = _groupSize; + } + + function addMember(address _member) external { + members.push(_member); + } + + function getManyMembers() external view returns(uint) { + return members.length; + } + + function getMemberAtIndex(uint _index) external view returns(address) { + return members[_index]; + } + + function getContributionForMember(address _member) external view returns(uint256) { + return currentContributions[_member]; + } + + function setContributionForMember(address _member, uint256 _contribAmtWei) external { + currentContributions[_member] = _contribAmtWei; + } + + function setMemberIdxToPayNext(uint _memberIdxToPayNext) external { + memberIdxToPayNext = _memberIdxToPayNext; + } + +} diff --git a/contracts/SusuOrig.sol b/contracts/SusuOrig.sol new file mode 100644 index 0000000..9818a72 --- /dev/null +++ b/contracts/SusuOrig.sol @@ -0,0 +1,126 @@ +pragma solidity ^0.4.22; + +import { Ownable } from "zeppelin-solidity/contracts/ownership/Ownable.sol"; +import { SafeMath } from "zeppelin-solidity/contracts/math/SafeMath.sol"; + +// API Design here: https://docs.google.com/presentation/d/12qfLPD88TTfvQxWLRTu5boEav8-JdRf3IjpS5WNDTvs/edit#slide=id.g3cb5a7885c_0_11 +// Design Requirements here: https://docs.google.com/document/d/1myfOSSwCPx16uUMSLbtf5RvfLFN97vmkKxjFWIvAaEM/edit +contract SusuOrig is Ownable { + + using SafeMath for uint; + + string public groupName; + uint256 public contribAmtWei; + uint8 public groupSize; + address[] public members; + uint public memberIdxToPayNext; + uint8 public maxMembers; + mapping(address => uint) private currentContributions; + + constructor(uint8 _groupSize, string _groupName, uint256 _contribAmtWei) public { + // Max number of members is 100. Arbitrary but should be smaller than max of uint8 + maxMembers = 100; + require(_groupSize < maxMembers); + groupName = _groupName; + contribAmtWei = _contribAmtWei; + groupSize = _groupSize; + members.push(owner); + memberIdxToPayNext = 0; + } + + function payOut() public payable onlyOwner { + if(everyonePaid()) { + resetBalances(); + paySusu(); + iterateMemberToPayNext(); + } + } + + function pullPayOut() public payable { + require(msg.sender == members[memberIdxToPayNext]); + resetBalances(); + iterateMemberToPayNext(); + msg.sender.transfer(members.length * contribAmtWei); + } + + function everyonePaid() private view returns (bool) { + for (uint i = 0; i < members.length ; i++) + { + if(currentContributions[members[i]] != contribAmtWei) + return false; + } + return true; + } + + function resetBalances() private { + for (uint i = 0; i < members.length ; i++) + { + currentContributions[members[i]] = 0; + } + } + + function iterateMemberToPayNext() private { + for (uint i = 0; i < members.length ; i++) + { + if(members[i] == members[memberIdxToPayNext]) { + if(i < members.length - 1) { + memberIdxToPayNext = i.add(1); + return; + } + + memberIdxToPayNext = 0; + return; + } + } + } + + function paySusu() private { + members[memberIdxToPayNext].transfer(members.length * contribAmtWei); + } + + function getMemberAtIndex(uint8 index) public view returns(address) { + return members[index]; + } + + function getContributionForMember(address _member) public view returns(uint256) { + return currentContributions[_member]; + } + + function amIOwner() public view returns(bool) { + return (msg.sender == owner); + } + + function getManyMembers() public view returns(uint) { + return members.length; + } + + function joinGroup() public { + require(!isRecipient(msg.sender)); + members.push(msg.sender); + } + + function contribute() public payable { + require(msg.value == contribAmtWei); + require(isRecipient(msg.sender)); + + TrackPayment(); + } + + function TrackPayment() private { + require(currentContributions[msg.sender] == 0); + currentContributions[msg.sender] = msg.value; + } + + function isRecipient(address addr) private view returns (bool) { + for (uint i = 0; i < members.length ; i++) + { + if(members[i] == addr) + return true; + } + return false; + } + + function () external payable { + contribute(); + } +} \ No newline at end of file diff --git a/contracts/SusuParent.sol b/contracts/SusuParent.sol new file mode 100644 index 0000000..93c861c --- /dev/null +++ b/contracts/SusuParent.sol @@ -0,0 +1,41 @@ +pragma solidity ^0.4.22; + +//import { Ownable } from "zeppelin-solidity/contracts/ownership/Ownable.sol"; +import "./Susu.sol"; +import "./SusuDataStore.sol"; +import "./IRegistry.sol"; + +// is Ownable ? +contract SusuParent { + + IRegistry public registry; + string constant public version = '0.0.2'; + + constructor(address _registry) public { + registry = IRegistry(_registry); + } + + // onlyOwner? + function createSusu(bytes32 _key, uint8 _groupSize, string _groupName, uint256 _contribAmtWei) external { + address susuOrigAddress = registry.get(_key); + if(susuOrigAddress == 0x0){ + SusuDataStore susuDataStore = new SusuDataStore(_groupSize, _groupName, _contribAmtWei); + Susu susu = new Susu(susuDataStore, msg.sender); + registry.put(_key, susu); + } + } + + function getSusu(bytes32 _key) view external returns(address) { + return registry.get(_key); + } + + // onlyOwner? + function upgradeSusu(bytes32 _key) external { + address susuAddressOrig = registry.get(_key); + Susu susu = Susu(susuAddressOrig); + Susu susuNew = new Susu(susu.susuDataStore(), susu.owner()); + susu.kill(susuNew); + registry.put(_key, susuNew); + } + +} diff --git a/contracts/SusuRegistry.sol b/contracts/SusuRegistry.sol new file mode 100644 index 0000000..f6262af --- /dev/null +++ b/contracts/SusuRegistry.sol @@ -0,0 +1,18 @@ +pragma solidity ^0.4.22; + +//import { Ownable } from "zeppelin-solidity/contracts/ownership/Ownable.sol"; +import "./IRegistry.sol"; + +// add Ownable +contract SusuRegistry is IRegistry { + mapping(bytes32 => address) public registry; + + // add onlyOwner + function put(bytes32 _key, address _contractAddress) external { + registry[_key] = _contractAddress; + } + + function get(bytes32 _key) view external returns(address){ + return registry[_key]; + } +} \ No newline at end of file diff --git a/contracts/openZepplin/Ownable.sol b/contracts/openZepplin/Ownable.sol deleted file mode 100644 index 1fbf571..0000000 --- a/contracts/openZepplin/Ownable.sol +++ /dev/null @@ -1,64 +0,0 @@ -pragma solidity ^0.4.24; - - -/** - * @title Ownable - * @dev The Ownable contract has an owner address, and provides basic authorization control - * functions, this simplifies the implementation of "user permissions". - */ -contract Ownable { - address public owner; - - - event OwnershipRenounced(address indexed previousOwner); - event OwnershipTransferred( - address indexed previousOwner, - address indexed newOwner - ); - - - /** - * @dev The Ownable constructor sets the original `owner` of the contract to the sender - * account. - */ - constructor() public { - owner = msg.sender; - } - - /** - * @dev Throws if called by any account other than the owner. - */ - modifier onlyOwner() { - require(msg.sender == owner); - _; - } - - /** - * @dev Allows the current owner to relinquish control of the contract. - * @notice Renouncing to ownership will leave the contract without an owner. - * It will not be possible to call the functions with the `onlyOwner` - * modifier anymore. - */ - function renounceOwnership() public onlyOwner { - emit OwnershipRenounced(owner); - owner = address(0); - } - - /** - * @dev Allows the current owner to transfer control of the contract to a newOwner. - * @param _newOwner The address to transfer ownership to. - */ - function transferOwnership(address _newOwner) public onlyOwner { - _transferOwnership(_newOwner); - } - - /** - * @dev Transfers control of the contract to a newOwner. - * @param _newOwner The address to transfer ownership to. - */ - function _transferOwnership(address _newOwner) internal { - require(_newOwner != address(0)); - emit OwnershipTransferred(owner, _newOwner); - owner = _newOwner; - } -} diff --git a/migrations/1_initial_migration.js b/migrations/1_initial_migration.js index 4d5f3f9..36a949b 100644 --- a/migrations/1_initial_migration.js +++ b/migrations/1_initial_migration.js @@ -1,4 +1,4 @@ -var Migrations = artifacts.require("./Migrations.sol"); +const Migrations = artifacts.require("./Migrations.sol"); module.exports = function(deployer) { deployer.deploy(Migrations); diff --git a/migrations/2_deploy_susu.js b/migrations/2_deploy_susu.js deleted file mode 100644 index b70d302..0000000 --- a/migrations/2_deploy_susu.js +++ /dev/null @@ -1,5 +0,0 @@ -var Susu = artifacts.require("./Susu.sol"); - -module.exports = function (deployer) { - deployer.deploy(Susu, 2, "Test", 1); -}; diff --git a/migrations/2_deploy_susu_orig.js b/migrations/2_deploy_susu_orig.js new file mode 100644 index 0000000..c9d559b --- /dev/null +++ b/migrations/2_deploy_susu_orig.js @@ -0,0 +1,5 @@ +var SusuOrig = artifacts.require("./SusuOrig.sol"); + +module.exports = function (deployer) { + deployer.deploy(SusuOrig, 2, "Test", 1); +}; diff --git a/migrations/3_deploy_susu_registry.js b/migrations/3_deploy_susu_registry.js new file mode 100644 index 0000000..1c2a928 --- /dev/null +++ b/migrations/3_deploy_susu_registry.js @@ -0,0 +1,5 @@ +const SusuRegistry = artifacts.require("SusuRegistry"); + +module.exports = function (deployer) { + deployer.deploy(SusuRegistry); +}; diff --git a/migrations/4_deploy_susu_parent.js b/migrations/4_deploy_susu_parent.js new file mode 100644 index 0000000..756f42e --- /dev/null +++ b/migrations/4_deploy_susu_parent.js @@ -0,0 +1,7 @@ +const SusuParent = artifacts.require("SusuParent"); +const SusuRegistry = artifacts.require("SusuRegistry"); +const LATEST_REGISTRY_ADDRESS = SusuRegistry.address; + +module.exports = function (deployer) { + deployer.deploy(SusuParent, LATEST_REGISTRY_ADDRESS); +}; diff --git a/src/App.js b/src/App.js index ec0c0f7..e166725 100644 --- a/src/App.js +++ b/src/App.js @@ -21,7 +21,7 @@ class App extends Component {
- +
diff --git a/src/components/ActionButtons.js b/src/components/ActionButtons.js index 907d189..2ae8f70 100644 --- a/src/components/ActionButtons.js +++ b/src/components/ActionButtons.js @@ -17,6 +17,7 @@ class ActionButtons extends Component { let contributeBtn = ; let pullPayOutBtn = ; let joinBtn = ; + let upgradeBtn = ; if(!this.props.isGroupFull && !this.props.isMember){ btns.push(joinBtn); @@ -27,6 +28,9 @@ class ActionButtons extends Component { if(this.props.isMemberToPayNext) { btns.push(pullPayOutBtn); } + if(this.props.isOwner) { + btns.push(upgradeBtn); + } } return ( @@ -47,7 +51,7 @@ class ActionButtons extends Component { from:this.props.myAddress, to:this.props.susuContract.address, value:this.props.web3.toWei(this.props.contribAmt, "ether"), - gas:60000 + gas:2000000 }, (err)=>{ this.setState({isLoading:false}); @@ -81,7 +85,7 @@ class ActionButtons extends Component { e.preventDefault(); this.setState({isLoading:true}); this.props.susuContract.joinGroup( - {from:this.props.myAddress, gas:60000}, + {from:this.props.myAddress, gas:200000}, (err)=>{ this.setState({isLoading:false}); if(typeof err === 'undefined' || !err) { @@ -93,6 +97,17 @@ class ActionButtons extends Component { } ); } + + clickUpgrade(e) { + e.preventDefault(); + this.setState({isLoading:true}); + this.props.susuParentContract.deployed().then((instance)=>{ + const options = { from: this.props.myAddress, gas: 2000000 }; + return instance.upgradeSusu(this.props.groupName, options); + }).then((a,b)=>{ + location.reload(); + }); + } } ActionButtons.defaultProps = { @@ -101,12 +116,14 @@ ActionButtons.defaultProps = { isGroupTerminated: false, isMember: false, susuContract: null, + susuParentContract: null, myAddress: '', web3:null, contribAmt:0.0, myContrib:0.0, isReadyToPayout:false, isMemberToPayNext:false, + groupName: '', }; export default ActionButtons diff --git a/src/components/DeployPage.js b/src/components/DeployPage.js index 177c20a..d45939c 100644 --- a/src/components/DeployPage.js +++ b/src/components/DeployPage.js @@ -1,10 +1,13 @@ import React, { Component } from 'react' -import contract from 'truffle-contract' +// import contract from 'truffle-contract' -import SusuContract from '../../build/contracts/Susu.json' import getWeb3 from '../utils/getWeb3' import '../App.css' +import SusuParentContract from "../../build/contracts/SusuParent"; +// import SusuOrigContract from "../../build/contracts/SusuOrig"; +// import SusuContract from "../../build/contracts/Susu"; +// import {BigNumber} from "bignumber.js"; class DeployPage extends Component { @@ -14,6 +17,10 @@ class DeployPage extends Component { this.state = { web3: null, isLoading: false, + susuParentContract: null, + susuContract: null, + key:'key', + contribAmtWei:0, } } @@ -23,12 +30,22 @@ class DeployPage extends Component { this.setState({ web3: results.web3 }); + + this.instantiateSusuContract(); }) .catch(() => { console.log('Error finding web3.'); }) } + instantiateSusuContract() { + const contract = require("truffle-contract"); + const susuParentContract = contract(SusuParentContract); + const provider = new this.state.web3.providers.HttpProvider("http://127.0.0.1:7545"); + susuParentContract.setProvider(provider); + this.setState({susuParentContract: susuParentContract}); + } + render() { const disabled = this.state.isLoading ? 'btn-disabled' : ''; const btnClasses = `${disabled} btn-contribute`; @@ -36,6 +53,20 @@ class DeployPage extends Component {
+ {/**/} + {/**/} + {/**/} + {/**/} + {/**/} + {/**/} + {/**/} + {/**/} + {/**/} + {/**/} + {/**/} + {/**/} + {/**/} + {/**/} @@ -63,29 +94,155 @@ class DeployPage extends Component { ); }// render() + // createSusu(e) { + // e.preventDefault(); + // this.state.susuParentContract.deployed().then((instance)=>{ + // const options = { from: this.state.web3.eth.accounts[0], gas: 2000000 }; + // return instance.createSusu(this.state.key, 2, 'name_'+this.state.key, this.state.web3.toWei(1.0, "ether"), options); + // }).then((result)=>{console.log('createSusu result:',result);}); + // } + // + // setSusu(e) { + // e.preventDefault(); + // this.state.susuParentContract.deployed().then((instance)=>{ + // return instance.getSusu.call(this.state.key); + // }).then((susuContractAddress)=>{ + // console.log('susuContractAddress:',susuContractAddress); + // const susuContract = this.state.web3.eth.contract(SusuContract.abi).at(susuContractAddress); + // this.setState({susuContract: susuContract}); + // }); + // } + // + // upgradeSusu(e) { + // e.preventDefault(); + // this.state.susuParentContract.deployed().then((instance)=>{ + // const options = { from: this.state.web3.eth.accounts[0], gas: 2000000 }; + // return instance.upgradeSusu(this.state.key, options); + // }).then((result)=>{console.log('upgradeSusu result:',result);}); + // } + // + // version(e) { + // e.preventDefault(); + // this.state.susuContract.version((err, version)=>{ + // console.log('err:',err, ' version:', version); + // }); + // } + // + // groupName(e) { + // e.preventDefault(); + // this.state.susuContract.groupName((err, groupName)=>{ + // console.log('err:',err, ' groupName:', groupName); + // }); + // } + // + // getManyMembers(e) { + // e.preventDefault(); + // this.state.susuContract.getManyMembers((err, getManyMembersBig)=>{ + // let bigNumber = new BigNumber(getManyMembersBig); + // const getManyMembers = bigNumber.toNumber(); + // console.log('err:',err, ' getManyMembers:', getManyMembers); + // }); + // } + // + // contribute(e) { + // e.preventDefault(); + // + // this.state.web3.eth.sendTransaction( + // { + // from: this.state.web3.eth.accounts[0], + // to: this.state.susuContract.address, + // value:this.state.contribAmtWei, + // gas:2000000 + // }, + // (err)=>{ + // if(typeof err === 'undefined' || !err) { + // console.log('contribute done'); + // } else { + // console.error(err); + // } + // }); + // } + // + // contribAmtWei(e) { + // e.preventDefault(); + // this.state.susuContract.contribAmtWei((err, contribAmtWeiBig)=>{ + // let bigNumber = new BigNumber(contribAmtWeiBig); + // const contribAmtWei = bigNumber.toNumber(); + // console.log('err:',err, ' contribAmtWei:', contribAmtWei); + // this.setState({contribAmtWei: contribAmtWei}); + // }); + // } + // + // getContributionForMember(e) { + // e.preventDefault(); + // this.state.susuContract.getContributionForMember(this.state.web3.eth.accounts[0], (err, getContributionForMemberBig)=>{ + // let bigNumber = new BigNumber(getContributionForMemberBig); + // const getContributionForMember = this.state.web3.fromWei(bigNumber, 'ether').toNumber(); + // console.log('err:',err, ' getContributionForMember:', getContributionForMember); + // }); + // } + // + // getMemberAtIndex0(e) { + // e.preventDefault(); + // this.state.susuContract.getMemberAtIndex(0, (err, memberAddress)=>{ + // console.log('err:',err, ' memberAddress0:', memberAddress); + // }); + // } + // + // getMemberAtIndex1(e) { + // e.preventDefault(); + // this.state.susuContract.getMemberAtIndex(1, (err, memberAddress)=>{ + // console.log('err:',err, ' memberAddress1:', memberAddress); + // }); + // } + // + // owner(e) { + // e.preventDefault(); + // const options = {from: this.state.web3.eth.accounts[0]}; + // this.state.susuContract.owner(options, (err, owner)=>{ + // console.log('err:',err, ' owner:', owner, ' =?me', (owner===this.state.web3.eth.accounts[0])); + // }); + // } + // + // amIOwner(e) { + // e.preventDefault(); + // const options = {from: this.state.web3.eth.accounts[0]}; + // this.state.susuContract.amIOwner(options, (err, amIOwner)=>{ + // console.log('err:',err, ' amIOwner:', amIOwner); + // }); + // } + // + // joinGroup(e) { + // e.preventDefault(); + // const options = {from: this.state.web3.eth.accounts[0], gas: 2000000}; + // this.state.susuContract.joinGroup(options, (err, resp)=>{console.log('err:',err, ' resp:', resp);}); + // } + clickCreate(e) { e.preventDefault(); this.setState({isLoading:true}); - const susuContract = contract(SusuContract); - const { unlinked_binary, abi } = susuContract; - const newContract = this.state.web3.eth.contract(abi) - const options = { from: this.state.web3.eth.accounts[0], data: unlinked_binary, gas: 2000000 } + const options = { from: this.state.web3.eth.accounts[0], gas: 2000000 }; const groupSize = document.getElementById('group_size').value; const groupName = document.getElementById('group_name').value; const contribAmtEth = document.getElementById('contrib_amt').value; const contribAmtWei = this.state.web3.toWei(contribAmtEth, 'ether'); - newContract.new(groupSize, groupName, contribAmtWei, options, this.newContractCallback()) - } - newContractCallback() { - return (err, newContract) => { - const { address } = newContract; - if(typeof address !== 'undefined' ) { - window.location.href = '/'+address; - } - } + this.state.susuParentContract.deployed().then((instance)=>{ + return instance.createSusu(groupName, groupSize, groupName, contribAmtWei, options); + }).then(()=>{ + window.location.href = '/'+groupName; + }); } + + // newContractCallback() { + // return (err, newContract) => { + // const { address } = newContract; + // if(typeof address !== 'undefined' ) { + // + // } + // } + // } } DeployPage.defaultProps = { diff --git a/src/components/GroupInfo.js b/src/components/GroupInfo.js index 21cce29..140fb61 100644 --- a/src/components/GroupInfo.js +++ b/src/components/GroupInfo.js @@ -16,6 +16,18 @@ class GroupInfo extends Component { + + + + + + + + + + + +
Contribution Amt (eth): {this.props.contribAmt}
Contract Address:{this.props.contractAddress}
Contract Version:{this.props.contractVersion}
Contract Balance:{this.props.contractBalance}
); @@ -26,6 +38,9 @@ class GroupInfo extends Component { GroupInfo.defaultProps = { groupName: 'groupName not set', contribAmt: -1, + contractAddress: 'contractAddress not set', + contractVersion: 'contractVersion not set', + contractBalance: 0, }; export default GroupInfo diff --git a/src/components/GroupPage.js b/src/components/GroupPage.js index 6fef4a9..781a889 100644 --- a/src/components/GroupPage.js +++ b/src/components/GroupPage.js @@ -1,6 +1,6 @@ import React, { Component } from 'react' -import SusuContract from '../../build/contracts/Susu.json' +// import SusuOrigContract from '../../build/contracts/SusuOrig.json' import getWeb3 from '../utils/getWeb3' import {BigNumber} from 'bignumber.js'; @@ -12,6 +12,8 @@ import PartnerRow from "./PartnerRow"; import GroupInfo from "./GroupInfo"; import ActionButtons from "./ActionButtons"; import PartnerRowEmpty from "./PartnerRowEmpty"; +import SusuParentContract from "../../build/contracts/SusuParent"; +import SusuContract from "../../build/contracts/Susu"; class GroupPage extends Component { @@ -21,17 +23,20 @@ class GroupPage extends Component { this.state = { web3: null, susuContract: null, + susuParentContract: null, myAddress: '', contribAmt: 0, - groupName: '---', payoutFrequency: 'monthly', manyMembers: 0, groupSize: 0, ownerAddress: '', partnerObjects: [], - contractAddress: props.match.params.contractAddress, + groupName: props.match.params.groupName, myContrib:0.0, memberAddrToPayNext:0, + susuContractAddress: '', + susuContractVersion: '', + susuContractBalance: 0, } } @@ -51,14 +56,28 @@ class GroupPage extends Component { } instantiateContract() { - const susuContract = this.state.web3.eth.contract(SusuContract.abi).at(this.state.contractAddress); - this.setState({susuContract:susuContract}); + const contract = require("truffle-contract"); + const susuParentContract = contract(SusuParentContract); + const provider = new this.state.web3.providers.HttpProvider("http://127.0.0.1:7545"); + susuParentContract.setProvider(provider); + this.setState({susuParentContract: susuParentContract}); + this.state.susuParentContract.deployed().then((instance)=>{ + return instance.getSusu.call(this.state.groupName); + }).then((susuContractAddress)=>{ + const susuContract = this.state.web3.eth.contract(SusuContract.abi).at(susuContractAddress); + this.setState({susuContract: susuContract}); + this.setState({susuContractAddress: susuContractAddress}); + this.initState(); + }); + } + + initState() { let _this = this; this.state.web3.eth.getAccounts(function(error, accounts) { const myAddress = accounts[0]; _this.setState({myAddress: myAddress}); - susuContract.getContributionForMember(myAddress, (err, contribAmtWei)=>{ + _this.state.susuContract.getContributionForMember(myAddress, (err, contribAmtWei)=>{ let bigNumber = new BigNumber(contribAmtWei); const contribAmt = _this.state.web3.fromWei(bigNumber, 'ether').toNumber(); _this.setState({myContrib:contribAmt}); @@ -72,35 +91,43 @@ class GroupPage extends Component { this.setState({ partnerObjects: partnerObjects }); } - susuContract.groupName((err, groupName)=>{ + this.state.web3.eth.getBalance(this.state.susuContractAddress, (err, balanceBig)=>{ + let bigNumber = new BigNumber(balanceBig); + const susuContractBalance = _this.state.web3.fromWei(bigNumber, 'ether').toNumber(); + this.setState({susuContractBalance:susuContractBalance}); + }); + + this.state.susuContract.groupName((err, groupName)=>{ this.setState({groupName:groupName}); }); - susuContract.memberIdxToPayNext((err, memberIdxToPayNextBig)=>{ + this.state.susuContract.version((err, susuContractVersion)=>{ + this.setState({susuContractVersion:susuContractVersion}); + }); + + this.state.susuContract.memberIdxToPayNext((err, memberIdxToPayNextBig)=>{ let bigNumber = new BigNumber(memberIdxToPayNextBig); const memberIdxToPayNext = bigNumber.toNumber(); - console.log('memberIdxToPayNext:',memberIdxToPayNext); - susuContract.getMemberAtIndex(memberIdxToPayNext, (err, memberAddrToPayNext)=>{ - console.log('memberAddrToPayNext:',memberAddrToPayNext); + this.state.susuContract.getMemberAtIndex(memberIdxToPayNext, (err, memberAddrToPayNext)=>{ this.setState({memberAddrToPayNext:memberAddrToPayNext}); }); }); - susuContract.contribAmtWei((err, contribAmtWei)=>{ + this.state.susuContract.contribAmtWei((err, contribAmtWei)=>{ let bigNumber = new BigNumber(contribAmtWei); const contribAmt = this.state.web3.fromWei(bigNumber, 'ether').toNumber(); this.setState({contribAmt:contribAmt}); }); - susuContract.owner((err, ownerAddress)=>{ + this.state.susuContract.owner((err, ownerAddress)=>{ this.setState({ownerAddress:ownerAddress}); - susuContract.getManyMembers((err, manyMembersBig)=>{ + this.state.susuContract.getManyMembers((err, manyMembersBig)=>{ let bigNumber = new BigNumber(manyMembersBig); const manyMembers = bigNumber.toNumber(); this.setState({manyMembers:manyMembers}); - susuContract.groupSize((err, groupSizeBig)=>{ + this.state.susuContract.groupSize((err, groupSizeBig)=>{ let bigNumber = new BigNumber(groupSizeBig); const groupSize = bigNumber.toNumber(); this.setState({groupSize:groupSize}); @@ -123,12 +150,21 @@ class GroupPage extends Component { let isGroupTerminated = false; let isMember = this.isMember(); let isMemberToPayNext = this.state.memberAddrToPayNext===this.state.myAddress; + let contractAddress = this.state.susuContractAddress; + let contractVersion = this.state.susuContractVersion; + let contractBalance = this.state.susuContractBalance; return (
- + {this.createPartnerRows()} @@ -143,12 +179,14 @@ class GroupPage extends Component { isGroupTerminated={isGroupTerminated} isMember={isMember} susuContract={this.state.susuContract} + susuParentContract={this.state.susuParentContract} myAddress={this.state.myAddress} web3={this.state.web3} contribAmt={this.state.contribAmt} myContrib={this.state.myContrib} isReadyToPayout={this.isReadyToPayout()} isMemberToPayNext={isMemberToPayNext} + groupName={this.state.groupName} /> diff --git a/test/TestSusu.js b/test/TestSusu.js index 2db17b1..cb2fd7d 100644 --- a/test/TestSusu.js +++ b/test/TestSusu.js @@ -1,4 +1,4 @@ -const Susu = artifacts.require('./Susu.sol') +const Susu = artifacts.require('./SusuOrig.sol') contract('Susu', function ([owner, donor]) { let susu