diff --git a/contracts/Core.sol b/contracts/Core.sol index 678e11bd..4583bc41 100644 --- a/contracts/Core.sol +++ b/contracts/Core.sol @@ -23,6 +23,9 @@ pragma solidity ^0.4.17; import "./CoreInterface.sol"; + +/// @dev Core is a minimal stub that will become the anchoring and consensus point for +/// the utility chain to validate itself against contract Core is CoreInterface { /* diff --git a/contracts/OpenSTUtility.sol b/contracts/OpenSTUtility.sol index d939673d..007d124a 100644 --- a/contracts/OpenSTUtility.sol +++ b/contracts/OpenSTUtility.sol @@ -514,6 +514,22 @@ contract OpenSTUtility is Hasher, OpsManaged { return tokenAddress; } + /* + * Public view functions + */ + function registeredTokenProperties( + bytes32 _uuid) + external + view + returns ( + address /* token */, + address /* registrar */) + { + RegisteredToken storage registeredToken = registeredTokens[_uuid]; + return ( + address(registeredToken.token), + registeredToken.registrar); + } /* * Operation functions diff --git a/contracts/OpenSTValue.sol b/contracts/OpenSTValue.sol index b587c4b2..12f392e9 100644 --- a/contracts/OpenSTValue.sol +++ b/contracts/OpenSTValue.sol @@ -264,7 +264,7 @@ contract OpenSTValue is OpsManaged, Hasher { bytes32 redemptionIntentHash = hashRedemptionIntent( _uuid, _redeemer, - nonces[_redeemer]++, + nonces[_redeemer], _amountUT, _redemptionUnlockHeight ); @@ -329,11 +329,45 @@ contract OpenSTValue is OpsManaged, Hasher { address _account) public view - returns (uint256 nextNonce) + returns (uint256 /* nextNonce */) { return (nonces[_account] + 1); } + function core( + uint256 _chainIdUtility) + external + view + returns (address /* core address */ ) + { + return address(cores[_chainIdUtility]); + } + + function utilityTokenProperties( + bytes32 _uuid) + external + view + returns ( + string symbol, + string name, + uint256 conversionRate, + uint8 decimals, + uint256 chainIdUtility, + address simpleStake, + address stakingAccount + /* utility token struct */ ) + { + UtilityToken storage utilityToken = utilityTokens[_uuid]; + return ( + utilityToken.symbol, + utilityToken.name, + utilityToken.conversionRate, + utilityToken.decimals, + utilityToken.chainIdUtility, + address(utilityToken.simpleStake), + utilityToken.stakingAccount); + } + /* * Registrar functions */ diff --git a/package.json b/package.json index 2d0dd561..ac34c072 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,11 @@ "moment": "^2.19.2", "web3": "^0.20.2" }, - "devDependencies": {}, + "devDependencies": { + "truffle": "^4.0.1", + "ganache-cli": "^6.0.3", + "abi-decoder": "^1.0.9" + }, "author": "OpenST Foundation Ltd.", "license": "Apache v2.0" } diff --git a/test/OpenST.js b/test/OpenST.js deleted file mode 100644 index 1e8bbc2e..00000000 --- a/test/OpenST.js +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2017 OpenST Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// ---------------------------------------------------------------------------- -// Test: OpenST.js -// -// http://www.simpletoken.org/ -// -// ---------------------------------------------------------------------------- - diff --git a/test/OpenSTUtility.js b/test/OpenSTUtility.js index 350e47fb..20b32eb3 100644 --- a/test/OpenSTUtility.js +++ b/test/OpenSTUtility.js @@ -473,6 +473,9 @@ contract('OpenSTUtility', function(accounts) { }) }) +/** + * note: code is removed from contracts as unused and space needed for protocol completion + TODO: remove once certain no such logic is required describe('AddNameReservation', async () => { before(async () => { @@ -577,4 +580,5 @@ contract('OpenSTUtility', function(accounts) { await Utils.expectThrow(openSTUtility.removeSymbolRoute(hashSymbol, { from: accounts[2] })); }) }) +*/ }) diff --git a/test/OpenSTUtility_utils.js b/test/OpenSTUtility_utils.js index cf7e39e7..7f114171 100644 --- a/test/OpenSTUtility_utils.js +++ b/test/OpenSTUtility_utils.js @@ -56,6 +56,19 @@ module.exports.checkProposedBrandedTokenEvent = (event, _requester, _uuid, _symb assert.equal(event.args._conversionRate.toNumber(), _conversionRate.toNumber()); } +module.exports.validateProposedBrandedTokenEvent = (event, _requester, _symbol, _name, _conversionRate) => { + if (Number.isInteger(_conversionRate)) { + _conversionRate = new BigNumber(_conversionRate); + } + + assert.equal(event.event, "ProposedBrandedToken"); + assert.equal(event.args._requester, _requester); + assert.equal(event.args._symbol, _symbol); + assert.equal(event.args._name, _name); + assert.equal(event.args._conversionRate.toNumber(), _conversionRate.toNumber()); +} + + module.exports.checkRegisteredBrandedTokenEvent = (event, _registrar, _token, _uuid, _symbol, _name, _conversionRate, _requester) => { assert.equal(event.event, "RegisteredBrandedToken"); if (Number.isInteger(_conversionRate)) { @@ -71,6 +84,25 @@ module.exports.checkRegisteredBrandedTokenEvent = (event, _registrar, _token, _u assert.equal(event.args._requester, _requester); } +module.exports.checkRegisteredBrandedTokenEventOnProtocol = (formattedDecodedEvents, _registrar, _token, _uuid, _symbol, _name, _conversionRate, _requester) => { + + var event = formattedDecodedEvents['RegisteredBrandedToken']; + + assert.notEqual(event, null); + + if (Number.isInteger(_conversionRate)) { + _conversionRate = new BigNumber(_conversionRate); + } + + assert.equal(event._registrar, _registrar); + assert.equal(event._token, _token); + assert.equal(event._uuid, _uuid); + assert.equal(event._symbol, _symbol); + assert.equal(event._name, _name); + assert.equal(event._conversionRate, _conversionRate.toNumber()); + assert.equal(event._requester, _requester); +} + module.exports.checkStakingIntentConfirmedEvent = (event, _uuid, _stakingIntentHash, _staker, _beneficiary, _amountST, _amountUT, _expirationHeight) => { if (Number.isInteger(_amountST)) { _amountST = new BigNumber(_amountST); @@ -94,6 +126,29 @@ module.exports.checkStakingIntentConfirmedEvent = (event, _uuid, _stakingIntentH assert.equal(event.args._expirationHeight.toNumber(), _expirationHeight.toNumber()); } +module.exports.checkStakingIntentConfirmedEventOnProtocol = (formattedDecodedEvents, _uuid, _stakingIntentHash, + _staker, _beneficiary, _amountST, _amountUT) => { + + var event = formattedDecodedEvents['StakingIntentConfirmed']; + + assert.notEqual(event, null); + + if (Number.isInteger(_amountST)) { + _amountST = new BigNumber(_amountST); + } + + if (Number.isInteger(_amountUT)) { + _amountUT = new BigNumber(_amountUT); + } + + assert.equal(event._uuid, _uuid); + assert.equal(event._stakingIntentHash, _stakingIntentHash); + assert.equal(event._staker, _staker); + assert.equal(event._beneficiary, _beneficiary); + assert.equal(event._amountST, _amountST.toNumber()); + assert.equal(event._amountUT, _amountUT.toNumber()); +} + module.exports.checkProcessedMintEvent = (event, _uuid, _stakingIntentHash, _token, _staker, _beneficiary, _amount) => { if (Number.isInteger(_amount)) { _amount = new BigNumber(_amount); diff --git a/test/OpenSTValue_utils.js b/test/OpenSTValue_utils.js index 839ce1ea..8cd14431 100644 --- a/test/OpenSTValue_utils.js +++ b/test/OpenSTValue_utils.js @@ -55,6 +55,8 @@ module.exports.checkUtilityTokenRegisteredEvent = (event, _uuid, _symbol, _name, assert.equal(event.event, "UtilityTokenRegistered"); assert.equal(event.args._uuid, _uuid); + // TODO: re-evaluate checking + // assert.equal(event.args.stake, stake); assert.equal(event.args._symbol, _symbol); assert.equal(event.args._name, _name); assert.equal(event.args._decimals.toNumber(), _decimals); @@ -63,6 +65,30 @@ module.exports.checkUtilityTokenRegisteredEvent = (event, _uuid, _symbol, _name, assert.equal(event.args._stakingAccount, _stakingAccount); } +module.exports.checkUtilityTokenRegisteredEventOnProtocol = (formattedDecodedEvents, _uuid, _symbol, _name, _decimals, _conversionRate, _chainIdUtility, _stakingAccount) => { + + var event = formattedDecodedEvents['UtilityTokenRegistered']; + + assert.notEqual(event, null); + + if (Number.isInteger(_decimals)) { + _decimals = new BigNumber(_decimals); + } + + if (Number.isInteger(_conversionRate)) { + _conversionRate = new BigNumber(_conversionRate); + } + + assert.equal(event._uuid, _uuid); + // assert.equal(event.args.stake, stake); + assert.equal(event._symbol, _symbol); + assert.equal(event._name, _name); + assert.equal(event._decimals, _decimals); + assert.equal(event._conversionRate, _conversionRate); + assert.equal(event._chainIdUtility, _chainIdUtility); + assert.equal(event._stakingAccount, _stakingAccount); +} + module.exports.checkStakingIntentDeclaredEvent = (event, _uuid, _staker, _stakerNonce, _beneficiary, _amountST, _amountUT, _unlockHeight, _stakingIntentHash, _chainIdUtility) => { if (Number.isInteger(_stakerNonce)) { _stakerNonce = new BigNumber(_stakerNonce); @@ -92,6 +118,66 @@ module.exports.checkStakingIntentDeclaredEvent = (event, _uuid, _staker, _staker assert.equal(event.args._chainIdUtility, _chainIdUtility); } +module.exports.checkStakingIntentDeclaredEventProtocol = (formattedDecodedEvents, _uuid, _staker, _stakerNonce, _beneficiary, + _amountST, _amountUT, _chainIdUtility) => { + + var event = formattedDecodedEvents['StakingIntentDeclared']; + + if (Number.isInteger(_stakerNonce)) { + _stakerNonce = new BigNumber(_stakerNonce); + } + + if (Number.isInteger(_amountST)) { + _amountST = new BigNumber(_amountST); + } + + if (Number.isInteger(_amountUT)) { + _amountUT = new BigNumber(_amountUT); + } + + if (Number.isInteger(_chainIdUtility)) { + _chainIdUtility = new BigNumber(_chainIdUtility); + } + + assert.equal(event.event, "StakingIntentDeclared"); + assert.equal(event._uuid, _uuid); + assert.equal(event._staker, _staker); + assert.equal(event._stakerNonce, _stakerNonce.toNumber()); + assert.equal(event._beneficiary, _beneficiary); + assert.equal(event._amountST, _amountST.toNumber()); + assert.equal(event._amountUT, _amountUT.toNumber()); + assert.equal(event._chainIdUtility, _chainIdUtility.toNumber()); +} + +module.exports.checkStakingIntentDeclaredEventProtocol = (event, _uuid, _staker, _stakerNonce, _beneficiary, + _amountST, _amountUT, _chainIdUtility) => { + + if (Number.isInteger(_stakerNonce)) { + _stakerNonce = new BigNumber(_stakerNonce); + } + + if (Number.isInteger(_amountST)) { + _amountST = new BigNumber(_amountST); + } + + if (Number.isInteger(_amountUT)) { + _amountUT = new BigNumber(_amountUT); + } + + if (Number.isInteger(_chainIdUtility)) { + _chainIdUtility = new BigNumber(_chainIdUtility); + } + + assert.equal(event.event, "StakingIntentDeclared"); + assert.equal(event.args._uuid, _uuid); + assert.equal(event.args._staker, _staker); + assert.equal(event.args._stakerNonce.toNumber(), _stakerNonce.toNumber()); + assert.equal(event.args._beneficiary, _beneficiary); + assert.equal(event.args._amountST.toNumber(), _amountST.toNumber()); + assert.equal(event.args._amountUT.toNumber(), _amountUT.toNumber()); + assert.equal(event.args._chainIdUtility.toNumber(), _chainIdUtility.toNumber()); +} + module.exports.checkProcessedStakeEvent = (event, _uuid, _stakingIntentHash, _stake, _staker, _amountST, _amountUT) => { if (Number.isInteger(_amountST)) { _amountST = new BigNumber(_amountST); @@ -132,6 +218,28 @@ module.exports.checkRedemptionIntentConfirmedEvent = (event, _uuid, _redemptionI assert.equal(event.args._unlockHeight.toNumber(), _unlockHeight.toNumber()); } +module.exports.checkRedemptionIntentConfirmedEventOnProtocol = (formattedDecodedEvents, uuid, _redemptionIntentHash, _redeemer, _amountST, _amountUT) => { + var event = formattedDecodedEvents['RedemptionIntentConfirmed']; + assert.notEqual(event, null); + + if (Number.isInteger(_amountST)) { + _amountST = new BigNumber(_amountST); + } + + if (Number.isInteger(_amountUT)) { + _amountUT = new BigNumber(_amountUT); + } + + var _unlockHeight = new BigNumber(event._expirationHeight); + + assert.equal(event._uuid, uuid); + assert.equal(event._redemptionIntentHash, _redemptionIntentHash); + assert.equal(event._redeemer, _redeemer); + assert.equal(event._amountST, _amountST.toNumber()); + assert.equal(event._amountUT, _amountUT.toNumber()); + assert.isAbove(_unlockHeight.toNumber(), 0); +} + module.exports.checkProcessedUnstakeEvent = (event, _uuid, _redemptionIntentHash, stake, _redeemer, _amountST) => { if (Number.isInteger(_amountST)) { _amountST = new BigNumber(_amountST); @@ -143,4 +251,4 @@ module.exports.checkProcessedUnstakeEvent = (event, _uuid, _redemptionIntentHash assert.equal(event.args.stake, stake); assert.equal(event.args._redeemer, _redeemer); assert.equal(event.args._amountST.toNumber(), _amountST.toNumber()); -} +} \ No newline at end of file diff --git a/test/Protocol.js b/test/Protocol.js new file mode 100644 index 00000000..22631de2 --- /dev/null +++ b/test/Protocol.js @@ -0,0 +1,484 @@ +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// Test: initialise and stake Simple Token Prime +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +const Assert = require('assert'); +const BigNumber = require('bignumber.js'); +const utils = require("./lib/utils.js"); +const openSTValueUtils = require("./OpenSTValue_utils.js"); +const openSTUtilityUtils = require('./OpenSTUtility_utils.js'); +const web3EventsDecoder = require('./lib/event_decoder.js'); + +const ProtocolUtils = require('./Protocol_utils.js'); + +const openSTUtilityArtifacts = artifacts.require("./OpenSTUtility.sol"); +const openSTValueArtifacts = artifacts.require("./OpenSTValue.sol"); +const BrandedToken = artifacts.require("./BrandedToken.sol"); + +const CHAINID_VALUE = new BigNumber(2001); +const CHAINID_UTILITY = new BigNumber(2002); + +contract('OpenST', function(accounts) { + + const DECIMALSFACTOR = new BigNumber('10').pow('18'); + const TOKEN_SYMBOL = "ST"; + const TOKEN_NAME = "Simple Token"; + const TOKEN_DECIMALS = 18; + const TOKENS_MAX = new BigNumber('800000000').mul(DECIMALSFACTOR); + + const STPRIME_SYMBOL = "STP"; + const STPRIME_NAME = "SimpleTokenPrime"; + const STPRIME_CONVERSION_RATE = new BigNumber(1); + + // Member Details + const symbol = "BT"; + const name = "Branded Token"; + const conversionRate = 10; + + const deployMachine = accounts[0]; + const owner = accounts[1]; + const admin = accounts[2]; + const ops = accounts[3]; + const intercommVC = accounts[4]; + const intercommUC = accounts[5]; + const requester = accounts[6]; + const staker = accounts[7]; + const redeemer = accounts[8]; + + const AMOUNT_ST = new BigNumber(1000).mul(DECIMALSFACTOR); + const AMOUNT_BT = new BigNumber(AMOUNT_ST*conversionRate); + const REDEEM_AMOUNT_BT = new BigNumber(5).mul(DECIMALSFACTOR); + const REDEEM_AMOUNT_STPRIME = new BigNumber(15).mul(DECIMALSFACTOR); + + describe('Setup Utility chain with Simple Token Prime', function () { + + + var simpleToken = null; + var registrarVC = null; + var registrarUC = null; + var coreVC = null; + var openSTValue = null; + var openSTUtility = null; + var stPrime = null; + var brandedToken = null + + var stpContractAddress = null; + var simpleStakeContractAddress = null; + var registeredBrandedTokenUuid = null; + var registeredBrandedToken = null; + var nonceBT = null; + var uuidSTP = null; + var nonceSTP = null; + var stakingIntentHash = null; + var unlockHeight = null; + var redemptionIntentHash = null; + + //- [x] truffle complete deployment process + + before(async () => { + var contracts = await ProtocolUtils.deployOpenSTProtocol(artifacts, accounts); + simpleToken = contracts.token; + registrarVC = contracts.registrarVC; + registrarUC = contracts.registrarUC; + openSTValue = contracts.openSTValue; + openSTUtility = contracts.openSTUtility; + // core on VC to represent UC + coreVC = contracts.coreVC; + stPrime = contracts.stPrime; + }); + + context('deploy and configure utility chain', function() { + + it("add core to represent utility chain", async () => { + const o = await registrarVC.addCore(openSTValue.address, coreVC.address, { from: intercommVC }); + Assert.ok(o); + utils.logResponse(o, "RegistrarVC.addCore"); + Assert.equal(await openSTValue.core.call(CHAINID_UTILITY), coreVC.address); + }); + + it("register Simple Token Prime", async () => { + const uuidSTP = await openSTUtility.uuidSTPrime.call(); + Assert.notEqual(uuidSTP, ""); + const o = await registrarVC.registerUtilityToken(openSTValue.address, STPRIME_SYMBOL, STPRIME_NAME, + STPRIME_CONVERSION_RATE, CHAINID_UTILITY, 0, uuidSTP, { from: intercommVC }); + utils.logResponse(o, "RegistrarVC.registerUtilityToken (STP)"); + Assert.notEqual((await openSTValue.utilityTokenProperties.call(uuidSTP))[5], utils.NullAddress); + }); + + // Initialize Transfer to ST' Contract Address + it("initialize transfer to Simple Token Prime contract address", async () => { + Assert.equal(await web3.eth.getBalance(stPrime.address), 0); + + const o = await stPrime.initialize({ from: deployMachine, value: TOKENS_MAX}); + utils.logResponse(o, "STPrime.initialize"); + var stPrimeContractBalanceAfterTransfer = await web3.eth.getBalance(stPrime.address).toNumber(); + Assert.equal(stPrimeContractBalanceAfterTransfer, TOKENS_MAX); + }); + + it("report gas usage: deployment and setup", async () => { + utils.printGasStatistics(); + utils.clearReceipts(); + }); + }); + + context('stake Simple Token for Simple Token Prime', function() { + + it("stake Simple Token", async () => { + uuidSTP = await openSTUtility.uuidSTPrime.call(); + // transfer ST to staker account + Assert.ok(await simpleToken.transfer(staker, AMOUNT_ST, { from: deployMachine })); + // staker sets allowance for OpenSTValue + Assert.ok(await simpleToken.approve(openSTValue.address, AMOUNT_ST, { from: staker })); + // for testing purpose query nonceSTP in advance + nonceSTP = await openSTValue.getNextNonce.call(staker); + Assert.equal(nonceSTP, 1); + // staker calls OpenSTValue.stake to initiate the staking for ST' with uuidSTP + // with staker as the beneficiary + const o = await openSTValue.stake(uuidSTP, AMOUNT_ST, staker, { from: staker }); + utils.logResponse(o, "OpenSTValue.stake"); + openSTValueUtils.checkStakingIntentDeclaredEventProtocol(o.logs[0], uuidSTP, staker, nonceSTP, staker, + AMOUNT_ST, AMOUNT_ST, CHAINID_UTILITY); + stakingIntentHash = o.logs[0].args._stakingIntentHash; + unlockHeight = o.logs[0].args._unlockHeight; + }); + + it("confirm staking intent for Simple Token Prime", async () => { + // registrar registers staking intent on utility chain + const o = await registrarUC.confirmStakingIntent(openSTUtility.address, uuidSTP, staker, nonceSTP, + staker, AMOUNT_ST, AMOUNT_ST, unlockHeight, stakingIntentHash, { from: intercommUC }); + utils.logResponse(o, "OpenSTUtility.confirmStakingIntent"); + }); + + it("process staking", async () => { + const o = await openSTValue.processStaking(stakingIntentHash, { from: staker }); + utils.logResponse(o, "OpenSTValue.processStaking"); + }); + + it("process minting", async () => { + const o = await openSTUtility.processMinting(stakingIntentHash, { from: staker }); + utils.logResponse(o, "OpenSTUtility.processMinting"); + }); + + it("claim Simple Token Prime", async () => { + var balanceBefore = await web3.eth.getBalance(staker); + const o = await stPrime.claim(staker, { from: intercommUC }); + utils.logResponse(o, "STPrime.claim"); + var balanceAfter = await web3.eth.getBalance(staker); + var totalSupply = await stPrime.totalSupply.call(); + Assert.equal(totalSupply.toNumber(), AMOUNT_ST.toNumber()); + Assert.equal(balanceAfter.sub(balanceBefore).toNumber(), AMOUNT_ST.toNumber()); + }); + + it("report gas usage: staking Simple Token Prime", async () => { + utils.printGasStatistics(); + utils.clearReceipts(); + }); + }); + + context('propose and register branded token', function() { + // propose branded token + + it("propose branded token for member company", async() => { + + const result = await openSTUtility.proposeBrandedToken(symbol, name, conversionRate, {from: requester}); + var eventLog = result.logs[0]; + + openSTUtilityUtils.validateProposedBrandedTokenEvent(eventLog, requester, symbol, name, conversionRate); + + registeredBrandedTokenUuid = eventLog.args._uuid; + registeredBrandedToken = eventLog.args._token; + brandedToken = BrandedToken.at(registeredBrandedToken); + + utils.logResponse(result, "OpenSTUtility.proposeBrandedToken"); + + }); + + // register Branded Token on Utility Chain + + it("register branded token on utility chain", async() => { + + const result = await registrarUC.registerBrandedToken(openSTUtility.address, symbol, name, + conversionRate, requester, registeredBrandedToken, registeredBrandedTokenUuid, { from: intercommUC }); + + var formattedDecodedEvents = web3EventsDecoder.perform(result.receipt, openSTUtility.address, openSTUtilityArtifacts.abi); + + openSTUtilityUtils.checkRegisteredBrandedTokenEventOnProtocol(formattedDecodedEvents, registrarUC.address, + registeredBrandedToken, registeredBrandedTokenUuid, symbol, name, conversionRate, requester); + + utils.logResponse(result, "OpenSTUtility.registerBrandedToken"); + + }); + + // register Utility Token on Value Chain + + it("register utility token on value chain", async() => { + + const result = await registrarVC.registerUtilityToken(openSTValue.address, symbol, name, conversionRate, + CHAINID_UTILITY, requester, registeredBrandedTokenUuid, { from: intercommVC }); + + var formattedDecodedEvents = web3EventsDecoder.perform(result.receipt, openSTValue.address, openSTValueArtifacts.abi); + + openSTValueUtils.checkUtilityTokenRegisteredEventOnProtocol(formattedDecodedEvents, registeredBrandedTokenUuid, + symbol, name, 18, conversionRate, CHAINID_UTILITY, requester); + + var event = formattedDecodedEvents['UtilityTokenRegistered']; + + simpleStakeContractAddress = event.stake; + + utils.logResponse(result, "OpenSTValue.registerUtilityToken"); + + }); + + it("report gas usage: register and propose branded token", async () => { + utils.printGasStatistics(); + utils.clearReceipts(); + }); + + }); + + // stake ST for BT + context('stake Simple Token for Branded Token', function() { + + it("approve and stake Simple Token", async() => { + // transfer ST to requester account + Assert.ok(await simpleToken.transfer(requester, AMOUNT_ST, { from: deployMachine })); + // Check for requester simpleToken Balance + var balanceOfRequester = await simpleToken.balanceOf(requester); + Assert.equal(balanceOfRequester, AMOUNT_ST.toNumber()); + // requester sets allowance for OpenSTValue + Assert.ok(await simpleToken.approve(openSTValue.address, AMOUNT_ST, { from: requester })); + + // Query nonceBT in advance + nonceBT = await openSTValue.getNextNonce.call(requester); + Assert.equal(nonceBT, 1); + // requester calls OpenSTValue.stake to initiate the staking for Branded Token with registeredBrandedTokenUuid + // with requester as the beneficiary + var stakeResult = await openSTValue.stake(registeredBrandedTokenUuid, AMOUNT_ST, requester, { from: requester }); + + openSTValueUtils.checkStakingIntentDeclaredEventProtocol(stakeResult.logs[0], registeredBrandedTokenUuid, requester, nonceBT, + requester, AMOUNT_ST, AMOUNT_BT, CHAINID_UTILITY); + + stakingIntentHash = stakeResult.logs[0].args._stakingIntentHash; + unlockHeight = stakeResult.logs[0].args._unlockHeight; + nonceBT = stakeResult.logs[0].args._stakerNonce; + + utils.logResponse(stakeResult, "OpenSTUtility.approveAndStake"); + + }); + + it("confirm staking intent for Branded Token", async() => { + + const result = await registrarUC.confirmStakingIntent(openSTUtility.address, registeredBrandedTokenUuid, + requester, nonceBT, requester, AMOUNT_ST, AMOUNT_BT, unlockHeight, stakingIntentHash, { from: intercommUC }); + + var formattedDecodedEvents = web3EventsDecoder.perform(result.receipt, openSTUtility.address, openSTUtilityArtifacts.abi); + + openSTUtilityUtils.checkStakingIntentConfirmedEventOnProtocol(formattedDecodedEvents, registeredBrandedTokenUuid, + stakingIntentHash, requester, requester, AMOUNT_ST, AMOUNT_BT); + + utils.logResponse(result, "OpenSTUtility.confirmStakingIntent"); + + }); + + it("process staking", async() => { + const result = await openSTValue.processStaking(stakingIntentHash, { from: requester }); + + openSTValueUtils.checkProcessedStakeEvent(result.logs[0], registeredBrandedTokenUuid, stakingIntentHash, + simpleStakeContractAddress, requester, AMOUNT_ST, AMOUNT_BT); + + utils.logResponse(result, "OpenSTValue.processStaking"); + + }); + + it("process minting", async() => { + const result = await openSTUtility.processMinting(stakingIntentHash, { from: requester }); + + openSTUtilityUtils.checkProcessedMintEvent(result.logs[0], registeredBrandedTokenUuid, stakingIntentHash, + registeredBrandedToken, requester, requester, AMOUNT_BT); + + utils.logResponse(result, "OpenSTValue.processminting"); + }); + + it("claim Branded Token", async() => { + var balanceBefore = await brandedToken.balanceOf(requester); + const o = await brandedToken.claim(requester, { from: intercommUC }); + var balanceAfter = await brandedToken.balanceOf(requester); + var totalSupply = await brandedToken.totalSupply.call(); + Assert.equal(totalSupply.toNumber(), AMOUNT_BT.toNumber()); + Assert.equal(balanceAfter.sub(balanceBefore).toNumber(), AMOUNT_BT.toNumber()); + }); + + it("report gas usage: stake Simple Token for Branded Token", async () => { + utils.printGasStatistics(); + utils.clearReceipts(); + }); + + }); + + context('Transfer Branded Token and STPrime to Redeemer', function() { + + it("transfer branded token from Requester to Redeemer", async() => { + + var result = await brandedToken.transfer(redeemer, REDEEM_AMOUNT_BT, { from: requester }); + Assert.ok(result); + var balanceOfRedeemer = await brandedToken.balanceOf(redeemer); + Assert.equal(balanceOfRedeemer, REDEEM_AMOUNT_BT.toNumber()); + + utils.logResponse(result, "brandedToken.transfer"); + + }); + + it("transfer STPrime to Redeemer", async() => { + + var redeemerBalanceBeforeTransfer = await web3.eth.getBalance(redeemer).toNumber(); + result = await web3.eth.sendTransaction({ from: staker, to: redeemer, value: REDEEM_AMOUNT_STPRIME ,gasPrice: '0x12A05F200' }); + var redeemerBalanceAfterTransfer = await web3.eth.getBalance(redeemer).toNumber(); + Assert.equal((redeemerBalanceBeforeTransfer+(REDEEM_AMOUNT_STPRIME.toNumber())), redeemerBalanceAfterTransfer); + + }); + + it("report gas usage: Transfer Branded Token and STPrime to Redeemer", async () => { + + utils.printGasStatistics(); + utils.clearReceipts(); + + }); + + }); + + // unstake BT by Redeemer + context('Redeem and Unstake Branded Token', function() { + + it("approve branded token", async() => { + + var approveResult = await brandedToken.approve(openSTUtility.address, REDEEM_AMOUNT_BT, { from: redeemer }) + Assert.ok(approveResult); + utils.logResponse(approveResult, "OpenSTUtility.approveResult"); + + }); + + it("call redeem", async() => { + + nonce = await openSTValue.getNextNonce.call(redeemer); + var redeemResult = await openSTUtility.redeem(registeredBrandedTokenUuid, REDEEM_AMOUNT_BT, nonce, { from: redeemer }); + redemptionIntentHash = redeemResult.logs[0].args._redemptionIntentHash; + unlockHeight = redeemResult.logs[0].args._unlockHeight; + openSTUtilityUtils.checkRedemptionIntentDeclaredEvent(redeemResult.logs[0], registeredBrandedTokenUuid, redemptionIntentHash, brandedToken.address, + redeemer, nonce, REDEEM_AMOUNT_BT, unlockHeight, CHAINID_VALUE); + utils.logResponse(redeemResult, "OpenSTUtility.redeem"); + + }); + + it("confirm redemption intent", async() => { + + var confirmRedemptionResult = await registrarVC.confirmRedemptionIntent( openSTValue.address, registeredBrandedTokenUuid, + redeemer, nonce, REDEEM_AMOUNT_BT, unlockHeight, redemptionIntentHash, { from: intercommVC }); + + var formattedDecodedEvents = web3EventsDecoder.perform(confirmRedemptionResult.receipt, openSTValue.address, openSTValueArtifacts.abi); + var redeemedAmountST = (REDEEM_AMOUNT_BT/conversionRate); + openSTValueUtils.checkRedemptionIntentConfirmedEventOnProtocol(formattedDecodedEvents, registeredBrandedTokenUuid, + redemptionIntentHash, redeemer, redeemedAmountST, REDEEM_AMOUNT_BT); + + utils.logResponse(confirmRedemptionResult, "OpenSTValue.confirmRedemptionIntent"); + + }); + + it("process redemption", async() => { + + var processRedeemingResult = await openSTUtility.processRedeeming(redemptionIntentHash, { from: redeemer }); + + utils.logResponse(processRedeemingResult, "openSTUtility.processRedeeming"); + + }); + + it("process unstake", async() => { + + var processUnstakeResult = await openSTValue.processUnstaking(redemptionIntentHash, { from: redeemer }); + + utils.logResponse(processUnstakeResult, "openSTValue.processUnstaking"); + + }); + + it("report gas usage: Redeem and Unstake Branded Token", async () => { + + utils.printGasStatistics(); + utils.clearReceipts(); + + }); + + }); + + // unstake STPrime by Redeemer + context('Redeem and Unstake STPrime', function() { + + it("call redeem", async() => { + + nonce = await openSTValue.getNextNonce.call(redeemer); + var redeemResult = await openSTUtility.redeemSTPrime(nonce, { from: redeemer, value: REDEEM_AMOUNT_STPRIME }); + redemptionIntentHash = redeemResult.logs[0].args._redemptionIntentHash; + unlockHeight = redeemResult.logs[0].args._unlockHeight; + openSTUtilityUtils.checkRedemptionIntentDeclaredEvent(redeemResult.logs[0], uuidSTP, redemptionIntentHash, stPrime.address, + redeemer, nonce, REDEEM_AMOUNT_STPRIME, unlockHeight, CHAINID_VALUE); + + utils.logResponse(redeemResult, "OpenSTUtility.STPrime.redeem"); + + }); + + it("confirm redemption intent", async() => { + + var confirmRedemptionResult = await registrarVC.confirmRedemptionIntent( openSTValue.address, uuidSTP, redeemer, nonce, + REDEEM_AMOUNT_STPRIME, unlockHeight, redemptionIntentHash, { from: intercommVC }); + + var formattedDecodedEvents = web3EventsDecoder.perform(confirmRedemptionResult.receipt, + openSTValue.address, openSTValueArtifacts.abi); + openSTValueUtils.checkRedemptionIntentConfirmedEventOnProtocol(formattedDecodedEvents, uuidSTP, + redemptionIntentHash, redeemer, REDEEM_AMOUNT_STPRIME, REDEEM_AMOUNT_STPRIME); + + utils.logResponse(confirmRedemptionResult, "OpenSTValue.STPrime.confirmRedemptionIntent"); + + }); + + it("process redemption", async() => { + + + var processRedeemingResult = await openSTUtility.processRedeeming(redemptionIntentHash, { from: redeemer }); + + utils.logResponse(processRedeemingResult, "openSTUtility.STPrime.processRedeeming"); + + }); + + it("process unstake", async() => { + + var processUnstakeResult = await openSTValue.processUnstaking(redemptionIntentHash, { from: redeemer }); + utils.logResponse(processUnstakeResult, "openSTValue.STPrime.processUnstaking"); + + }); + + it("report gas usage: Redeem and Unstake Simple Token Prime", async () => { + + utils.printGasStatistics(); + utils.clearReceipts(); + + }); + + }); + + }); +}); \ No newline at end of file diff --git a/test/Protocol_utils.js b/test/Protocol_utils.js new file mode 100644 index 00000000..68851596 --- /dev/null +++ b/test/Protocol_utils.js @@ -0,0 +1,123 @@ +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// Test: Protocol_utils.js +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +const BigNumber = require('bignumber.js'); +const Assert = require('assert'); + +const utils = require("./lib/utils.js"); +const openSTValueUtils = require("./OpenSTValue_utils.js"); + +var SimpleToken = artifacts.require("./SimpleToken/SimpleToken.sol"); +var Registrar = artifacts.require("./Registrar.sol"); +var Core = artifacts.require("./Core.sol"); +var OpenSTValue = artifacts.require("./OpenSTValue.sol"); +var OpenSTUtility = artifacts.require("./OpenSTUtility.sol"); +var STPrime = artifacts.require("./STPrime.sol"); + +const CHAINID_VALUE = 2001; +const CHAINID_UTILITY = 2002; + +module.exports.deployOpenSTProtocol = async (artifacts, accounts) => { + + const deployMachine = accounts[0]; + const owner = accounts[1]; + const admin = accounts[2]; + const ops = accounts[3]; + const intercommVC = accounts[4]; + const intercommUC = accounts[5]; + + var res = null; + + const simpleToken = await SimpleToken.new({ from: deployMachine }); + await utils.logTransaction(simpleToken.transactionHash, "SimpleToken.new"); + // finalize the tokens + utils.logResponse(await simpleToken.setAdminAddress(admin, { from: deployMachine }), + "SimpleToken.setAdminAddress"); + utils.logResponse(await simpleToken.finalize({ from: admin }), + "SimpleToken.finalize"); + // transfer ownership + utils.logResponse(await simpleToken.initiateOwnershipTransfer(owner, { from: deployMachine }), + "SimpleToken.initiateOwnershipTransfer"); + utils.logResponse(await simpleToken.completeOwnershipTransfer({ from: owner }), + "SimpleToken.completeOwnershipTransfer"); + + const registrarVC = await Registrar.new({ from: deployMachine }); + await utils.logTransaction(registrarVC.transactionHash, "RegistrarVC.new"); + // set Ops of registrar to Intercom account on value chain + utils.logResponse(await registrarVC.setOpsAddress(intercommVC, { from: deployMachine }), + "Registrar.setOpsAddress"); + utils.logResponse(await registrarVC.initiateOwnershipTransfer(owner, { from: deployMachine }), + "Registrar.initiateOwnershipTransfer"); + utils.logResponse(await registrarVC.completeOwnershipTransfer({ from: owner }), + "Registrar.completeOwnershipTransfer"); + + const registrarUC = await Registrar.new({ from: deployMachine }); + await utils.logTransaction(registrarUC.transactionHash, "RegistrarUC.new"); + // set Ops of registrar to Intercom account on utility chain + utils.logResponse(await registrarUC.setOpsAddress(intercommUC, { from: deployMachine }), + "Registrar.setOpsAddress"); + utils.logResponse(await registrarUC.initiateOwnershipTransfer(owner, { from: deployMachine }), + "Registrar.initiateOwnershipTransfer"); + utils.logResponse(await registrarUC.completeOwnershipTransfer({ from: owner }), + "Registrar.completeOwnershipTransfer"); + + const openSTValue = await OpenSTValue.new(CHAINID_VALUE, simpleToken.address, + registrarVC.address); + await utils.logTransaction(openSTValue.transactionHash, "OpenSTValue.new"); + utils.logResponse(await openSTValue.initiateOwnershipTransfer(owner, { from: deployMachine }), + "OpenSTValue.initiateOwnershipTransfer"); + utils.logResponse(await openSTValue.completeOwnershipTransfer({ from: owner }), + "OpenSTValue.completeOwnershipTransfer"); + + const openSTUtility = await OpenSTUtility.new(CHAINID_VALUE, CHAINID_UTILITY, + registrarUC.address, { from: deployMachine, gas: 8500000 }); + await utils.logTransaction(openSTUtility.transactionHash, "OpenSTUtility.new"); + utils.logResponse(await openSTUtility.initiateOwnershipTransfer(owner, { from: deployMachine }), + "OpenSTUtility.initiateOwnershipTransfer"); + utils.logResponse(await openSTUtility.completeOwnershipTransfer({ from: owner }), + "OpenSTUtility.completeOwnershipTransfer"); + + // only setup a core for the Value Chain to track the Utility Chain for v0.9.1 + const coreVC = await Core.new(registrarVC.address, CHAINID_VALUE, CHAINID_UTILITY, + openSTUtility.address); + await utils.logTransaction(coreVC.transactionHash, "CoreVC.new"); + + const stpContractAddress = await openSTUtility.simpleTokenPrime.call(); + Assert.notEqual(stpContractAddress, utils.NullAddress); + const stPrime = STPrime.at(stpContractAddress); + + // console.log("Simple Token:", simpleToken.address); + // console.log("Registrar VC:", registrarVC.address); + // console.log("Registrar UC:", registrarUC.address); + // console.log("OpenSTValue:", openSTValue.address); + // console.log("OpenSTUtility:", openSTUtility.address); + // console.log("CoreVC:", coreVC.address); + + return { + token : simpleToken, + registrarVC : registrarVC, + registrarUC : registrarUC, + openSTValue : openSTValue, + openSTUtility : openSTUtility, + coreVC : coreVC, + stPrime : stPrime + }; +} \ No newline at end of file diff --git a/test/lib/event_decoder.js b/test/lib/event_decoder.js new file mode 100644 index 00000000..bfe65e46 --- /dev/null +++ b/test/lib/event_decoder.js @@ -0,0 +1,89 @@ +"use strict"; + +const web3EventsDecoder = function () {}; + +web3EventsDecoder.prototype = { + + getFormattedEvents: function(eventsData) { + var formattedEvents = {}; + + var eventDataValues = {}; + + for (var i = 0; i < eventsData.length; i++) { + var currEvent = eventsData[i] + , currEventName = currEvent.name + , currEventAddr = currEvent.address + , currEventParams = currEvent.events; + + formattedEvents[currEventName] = {address: currEventAddr}; + + for (var j = 0; j < currEventParams.length; j++) { + var p = currEventParams[j]; + formattedEvents[currEventName][p.name] = p.value; + } + + } + + return formattedEvents; + }, + + // decode logs from a transaction receipt + perform: function(txReceipt, contractAddr, contractAbi) { + //console.log(txReceipt); + //console.log(contractAddr); + //console.log(contractAbi); + var decodedEvents = []; + + // Transaction receipt not found + if (!txReceipt) { + console.error(" Transaction receipt was not found."); + return; + } + + // Block not yet mined + if (!txReceipt.blockNumber) { + console.error(" Transaction not yet mined. Please try after some time. "); + return; + } + + const toAddr = txReceipt.to; + + // if the address is a known address + if ( txReceipt.logs.length > 0) { + + var abiDecoder = require('abi-decoder') + , relevantLogs = []; + + for (var i = 0; i < txReceipt.logs.length; i++) { + + var currContractAddrFromReciept = txReceipt.logs[i].address; + + console.debug('**** contract address: ' + txReceipt.logs[i].address + ' at log index(' + i + ') in TxHash: ' + txReceipt.transactionHash + ''); + + if (!currContractAddrFromReciept) { + console.error('**** No contract found for contract address: ' + txReceipt.logs[i].address + ' at log index(' + i + ') in TxHash: ' + txReceipt.transactionHash + ''); + continue; + } + + // ABI not found + if (!contractAbi) { + console.error("ABI not found for contract: "+contractAddr); + return; + } + + relevantLogs.push(txReceipt.logs[i]); + abiDecoder.addABI(contractAbi); + } + + if(relevantLogs.length > 0) { + decodedEvents = abiDecoder.decodeLogs(relevantLogs); + } + + } + + return this.getFormattedEvents(decodedEvents); + } + +}; + +module.exports = new web3EventsDecoder(); \ No newline at end of file diff --git a/test/lib/utils.js b/test/lib/utils.js index 85cb138c..d5f738e6 100644 --- a/test/lib/utils.js +++ b/test/lib/utils.js @@ -28,18 +28,68 @@ var SimpleToken = artifacts.require("./SimpleToken/SimpleToken.sol") const NullAddress = "0x0000000000000000000000000000000000000000"; +/* + * Tracking Gas Usage + */ + +const receipts = []; + +module.exports.logResponse = (response, description) => { + receipts.push({ + receipt : response.receipt, + description : description, + response : response + }); +} + +module.exports.logReceipt = (receipt, description) => { + receipts.push({ + receipt : receipt, + description : description, + response : null + }) +} + +module.exports.logTransaction = async (hash, description) => { + const receipt = await web3.eth.getTransactionReceipt(hash) + await this.logReceipt(receipt, description) +} + +module.exports.printGasStatistics = () => { + var totalGasUsed = 0 + + console.log(" -----------------------------------------------------"); + console.log(" Report gas usage\n"); + + for (i = 0; i < receipts.length; i++) { + const entry = receipts[i] + + totalGasUsed += entry.receipt.gasUsed + + console.log(" " + entry.description.padEnd(45) + entry.receipt.gasUsed) + } + + console.log(" -----------------------------------------------------") + console.log(" " + "Total gas logged: ".padEnd(45) + totalGasUsed + "\n") +} + +module.exports.clearReceipts = () => { + receipts.splice( 0, receipts.length ); +} + + /// @dev Deploy SimpleToken and other contracts /// to test full protocol -module.exports.deployContracts = async (artifacts, accounts) => { +// module.exports.deployContracts = async (artifacts, accounts) => { - const token = await SimpleToken.new({ from: accounts[0], gas: 3500000 }); +// const token = await SimpleToken.new({ from: accounts[0], gas: 3500000 }); - // to be extended +// // to be extended - return { - token : token - } -} +// return { +// token : token +// } +// } /* * General event checks diff --git a/tools/runTestRpc.sh b/tools/runTestRpc.sh index 054a92ff..3fffd0d0 100755 --- a/tools/runTestRpc.sh +++ b/tools/runTestRpc.sh @@ -1,2 +1,2 @@ #!/bin/bash -testrpc --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea0,100000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea1,100000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea2,100000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea3,100000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea4,100000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea5,100000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea6,100000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea7,100000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea8,100000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea9,0" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb0,100000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb1,100000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb2,100000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb3,100000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb4,100000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb5,100000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb6,100000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb7,100000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb8,100000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb9,100000000000000000000000000000" --gasLimit 10000000 +testrpc --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea0,10000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea1,10000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea2,10000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea3,10000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea4,10000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea5,10000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea6,10000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea7,10000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea8,10000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea9,0" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb0,10000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb1,10000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb2,10000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb3,10000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb4,10000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb5,10000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb6,10000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb7,10000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb8,10000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb9,10000000000000000000000000000" --gasLimit 12000000