From 1924546d40dea5a980c2cb2b8dd88c856639dc2f Mon Sep 17 00:00:00 2001 From: Till Kolter Date: Tue, 5 Dec 2017 00:27:16 +0100 Subject: [PATCH 1/6] Refactor dapp scratch to manage multiple contracts --- lib/contracts/index.js | 14 ++++ lib/dapp-scratch.js | 20 ++++-- lib/index.js | 147 +++++++++++++++++++++++++++++++++++++++++ lib/starterTemplate.js | 123 ++-------------------------------- package.json | 6 +- 5 files changed, 184 insertions(+), 126 deletions(-) create mode 100644 lib/contracts/index.js create mode 100644 lib/index.js diff --git a/lib/contracts/index.js b/lib/contracts/index.js new file mode 100644 index 0000000..f987548 --- /dev/null +++ b/lib/contracts/index.js @@ -0,0 +1,14 @@ +/** + * The file enables `/dapp-scratch-wrapper/index.js` to import all contract modules + * in a one-shot manner. There should not be any reason to edit this file. + */ + +const files = require.context('.', false, /\.js$/) +const modules = {} + +files.keys().forEach(key => { + if (key === './index.js') return + modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default +}) + +export default modules diff --git a/lib/dapp-scratch.js b/lib/dapp-scratch.js index 08a9fe7..8a1c236 100644 --- a/lib/dapp-scratch.js +++ b/lib/dapp-scratch.js @@ -67,7 +67,8 @@ class DappScratch { .then(this.wrapperReplaceName.bind(this)) .then(this.buildFunctions.bind(this)) .then(this.writeWrapper.bind(this)) - .then(resolve) + .then(this.copyStaticFiles.bind(this)) + // .then(resolve) .catch((err) => { reject(new Error(err)) }) @@ -94,10 +95,8 @@ class DappScratch { writeWrapper () { return new Promise((resolve, reject) => { - if (fs.existsSync('./dapp-scratch-wrapper')) fs.removeSync('./dapp-scratch-wrapper') - fs.mkdirSync('./dapp-scratch-wrapper') - fs.mkdirSync('./dapp-scratch-wrapper/' + this.projectName) - this.path = './dapp-scratch-wrapper/' + this.projectName + '/index.js' + fs.mkdirsSync('./dapp-scratch-wrapper/contracts') + this.path = `./dapp-scratch-wrapper/contracts/${this.projectName}.js` fs.writeFile(this.path, this.wrapperFile, (err) => { if (err) { reject(new Error(err)) @@ -108,6 +107,13 @@ class DappScratch { }) } + copyStaticFiles () { + return new Promise((resolve, reject) => { + fs.copySync(`${__dirname}/index.js`, './dapp-scratch-wrapper/index.js') + fs.copySync(`${__dirname}/contracts/index.js`, './dapp-scratch-wrapper/contracts/index.js') + resolve() + }) + } getAbi () { @@ -248,7 +254,7 @@ class DappScratch { all += functions + ') {\n' if (info.stateMutability === 'nonpayable') { - all += ' if (!this.account) return new Error(\'Unlock Wallet\')\n' + all += ' if (!this.contractManager.account) return new Error(\'Unlock Wallet\')\n' } all += ' return this.' + this.projectName + '.methods.' + info.name + '(' + typedFunctions + ')' @@ -257,7 +263,7 @@ class DappScratch { all += '.call()\n' break case('nonpayable'): - all += '.send({from: this.account})\n' + all += '.send({from: this.contractManager.account})\n' all += ' .on(\'transactionHash\', (hash) => {\n' all += ' console.log(hash)\n' all += ' this.loading = true\n' diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..d4c579f --- /dev/null +++ b/lib/index.js @@ -0,0 +1,147 @@ + +import Web3 from 'web3' +const BN = Web3.utils.BN +import ZeroClientProvider from 'web3-provider-engine/zero.js' +import IdManagerProvider from '@aeternity/id-manager-provider' +import contracts from './contracts' + +class ContractManager { + constructor (options) { + this.pollingInterval = null + this.account = null + this.unlocked = false + this.balanceWei = 0 + this.balance = 0 + this.genesisBlock = 0 + this.loading = false + this.options = { + autoInit: true, + connectionRetries: 3 + } + Object.assign(this.options, options) + if (this.options.autoInit) this.initWeb3() + } + + /* + * Connect + */ + + initWeb3 () { + return new Promise((resolve, reject) => { + + let web3Provider = false + let idManager = new IdManagerProvider({ + rpcUrl: 'http://localhost:9545', + skipSecurity: true, + + }) + + idManager.checkIdManager().then((idManagerPresent)=>{ + // check for aedentity app + if (idManagerPresent) { + web3Provider = idManager.web3.currentProvider + + // check for metamask + } else if (global.web3) { + web3Provider = web3.currentProvider + + // attempt to try again if no aedentity app or metamask + } else if (this.options.connectionRetries > 0){ + this.options.connectionRetries -= 1 + setTimeout(() => { + this.initWeb3().then(resolve).catch((error) => { + reject(new Error(error)) + }) + }, 1000) + // revert to a read only version using infura endpoint + } else { + this.readOnly = true + web3Provider = ZeroClientProvider({ + getAccounts: function(){}, + rpcUrl: 'https://mainnet.infura.io', + // rpcUrl: 'https://testnet.infura.io', + // rpcUrl: 'https://rinkeby.infura.io', + // rpcUrl: 'https://kovan.infura.io', + }) + } + + if (web3Provider) { + global.web3 = new Web3(web3Provider) + this.startChecking() + + if (this.options.getPastEvents) this.getPastEvents() + if (this.options.watchFutureEvents) this.watchFutureEvents() + } + }) + }) + } + + /* + * Check every second for switching network or wallet + */ + + startChecking () { + if (this.pollingInterval) clearInterval(this.pollingInterval) + this.getGenesisBlock() + .then(() => { + this.pollingInterval = setInterval(this.check.bind(this), 1000) + }) + .catch((err) => { + throw new Error(err) + }) + } + + check () { + this.checkNetwork() + .then(this.checkAccount.bind(this)) + .catch((error) => { + console.error(error) + throw new Error(error) + }) + } + + checkNetwork () { + return global.web3.eth.net.getId((err, netId) => { + if (err) console.error(err) + if (!err && this.network !== netId) { + this.network = netId + return this.deployContracts() + } + }) + } + + deployContracts () { + for (const contract in contracts) { + if (contracts.hasOwnProperty(contract)) { + this[contract] = new contracts[contract](this) + } + } + } + + checkAccount () { + return global.web3.eth.getAccounts((error, accounts) => { + if (error) throw new Error(error) + if (accounts.length && this.account !== accounts[0]) { + this.unlocked = true + this.account = accounts[0] + } else if (!accounts.length) { + this.unlocked = false + this.account = null + } + }) + } + + + /* + * Not Yet Implemented vvvv + */ + + getGenesisBlock () { + return new Promise((resolve, reject) => { + resolve() + }) + } + +} + +export default ContractManager diff --git a/lib/starterTemplate.js b/lib/starterTemplate.js index 5b9a1c6..c0579bd 100644 --- a/lib/starterTemplate.js +++ b/lib/starterTemplate.js @@ -7,135 +7,26 @@ import ZeroClientProvider from 'web3-provider-engine/zero.js' import IdManagerProvider from '@aeternity/id-manager-provider' class __NAME__ { - constructor (options) { + constructor (contractManager, options) { - this.__NAME__ = null - - this.pollingInterval = null - this.account = null - this.unlocked = false - this.balanceWei = 0 - this.balance = 0 + this.contractManager = contractManager this.address = __ADDRESS__ this.genesisBlock = 0 - this.loading = false this.options = { - autoInit: true, getPastEvents: false, - watchFutureEvents: false, - connectionRetries: 3 + watchFutureEvents: false } Object.assign(this.options, options) - if (this.options.autoInit) this.initWeb3() - } - - // hello world : ) - helloWorld () { - console.log('hello world!') - } - - /* - * Connect - */ - - initWeb3 () { - return new Promise((resolve, reject) => { - - let web3Provider = false - let idManager = new IdManagerProvider() - - idManager.checkIdManager().then((idManagerPresent)=>{ - // check for aedentity app - if (idManagerPresent) { - web3Provider = idManager.web3.currentProvider - - // check for metamask - } else if (global.web3) { - web3Provider = web3.currentProvider - - // attempt to try again if no aedentity app or metamask - } else if (this.options.connectionRetries > 0){ - this.options.connectionRetries -= 1 - setTimeout(() => { - this.initWeb3().then(resolve).catch((error) => { - reject(new Error(error)) - }) - }, 1000) - // revert to a read only version using infura endpoint - } else { - this.readOnly = true - web3Provider = ZeroClientProvider({ - getAccounts: function(){}, - rpcUrl: 'https://mainnet.infura.io', - // rpcUrl: 'https://testnet.infura.io', - // rpcUrl: 'https://rinkeby.infura.io', - // rpcUrl: 'https://kovan.infura.io', - }) - } - - if (web3Provider) { - global.web3 = new Web3(web3Provider) - this.startChecking() - - if (this.options.getPastEvents) this.getPastEvents() - if (this.options.watchFutureEvents) this.watchFutureEvents() - } - }) - }) - } - - /* - * Check every second for switching network or wallet - */ - - startChecking () { - if (this.pollingInterval) clearInterval(this.pollingInterval) - this.getGenesisBlock() - .then(() => { - this.pollingInterval = setInterval(this.check.bind(this), 1000) - }) - .catch((err) => { - throw new Error(err) - }) - } - check () { - this.checkNetwork() - .then(this.checkAccount.bind(this)) - .catch((error) => { - console.error(error) - throw new Error(error) - }) - } - - checkNetwork () { - return global.web3.eth.net.getId((err, netId) => { - if (err) console.error(err) - if (!err && this.network !== netId) { - this.network = netId - return this.deployContract() - } - }) - } - - deployContract () { if (!this.address || this.address === 'REPLACE_WITH_CONTRACT_ADDRESS') return new Error('Please provide a contract address') this.__NAME__ = new global.web3.eth.Contract(__NAME__Artifacts.abi, this.address) - } - checkAccount () { - return global.web3.eth.getAccounts((error, accounts) => { - if (error) throw new Error(error) - if (accounts.length && this.account !== accounts[0]) { - this.unlocked = true - this.account = accounts[0] - } else if (!accounts.length) { - this.unlocked = false - this.account = null - } - }) } + // hello world : ) + helloWorld () { + console.log('hello world!') + } /* * Not Yet Implemented vvvv diff --git a/package.json b/package.json index 7deda15..47c5297 100644 --- a/package.json +++ b/package.json @@ -8,15 +8,15 @@ "author": "Billy Rennekamp <hello@okw.me>", "license": "ISC", "dependencies": { + "@aeternity/id-manager-provider": "github:aeternity/id-manager-provider", "colors": "^1.1.2", "commander": "^2.11.0", "fs-extra": "^4.0.2", "inquirer": "^4.0.0", "shelljs": "^0.7.8" }, - "devDependencies": { - }, + "devDependencies": {}, "scripts": { - + "dev": "node bin/dapp-scratch.js" } } From fca626e9c1c2b6e6631db8328d0c1c859151f25f Mon Sep 17 00:00:00 2001 From: Till Kolter Date: Tue, 5 Dec 2017 08:14:57 +0100 Subject: [PATCH 2/6] Add dapp-scratch-wrapper to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 3c3629e..0a919e2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules +dapp-scratch-wrapper From 71bcdfbf1d09a780ac84ac7e248ce8af7382dfcd Mon Sep 17 00:00:00 2001 From: Till Kolter Date: Tue, 5 Dec 2017 08:17:52 +0100 Subject: [PATCH 3/6] Remove aeternity dependency --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 47c5297..14b2828 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,6 @@ "author": "Billy Rennekamp <hello@okw.me>", "license": "ISC", "dependencies": { - "@aeternity/id-manager-provider": "github:aeternity/id-manager-provider", "colors": "^1.1.2", "commander": "^2.11.0", "fs-extra": "^4.0.2", From 3de9ab1d96b1aa1750a34e7cca521f7c5e49a7fd Mon Sep 17 00:00:00 2001 From: Till Kolter Date: Wed, 6 Dec 2017 08:27:28 +0100 Subject: [PATCH 4/6] Remove unused imports --- lib/starterTemplate.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/starterTemplate.js b/lib/starterTemplate.js index c0579bd..9e41721 100644 --- a/lib/starterTemplate.js +++ b/lib/starterTemplate.js @@ -1,11 +1,4 @@ -import __NAME__Artifacts from '../../build/contracts/__NAME__.json' - -import Web3 from 'web3' -const BN = Web3.utils.BN -import ZeroClientProvider from 'web3-provider-engine/zero.js' -import IdManagerProvider from '@aeternity/id-manager-provider' - class __NAME__ { constructor (contractManager, options) { From a91fa25423f9eccb30df64740f98d868362d96c0 Mon Sep 17 00:00:00 2001 From: Till Kolter Date: Wed, 6 Dec 2017 08:28:20 +0100 Subject: [PATCH 5/6] Remove genesis block related code snippets --- lib/starterTemplate.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/starterTemplate.js b/lib/starterTemplate.js index 9e41721..1e1a4ee 100644 --- a/lib/starterTemplate.js +++ b/lib/starterTemplate.js @@ -4,7 +4,6 @@ class __NAME__ { this.contractManager = contractManager this.address = __ADDRESS__ - this.genesisBlock = 0 this.options = { getPastEvents: false, watchFutureEvents: false @@ -25,12 +24,6 @@ class __NAME__ { * Not Yet Implemented vvvv */ - getGenesisBlock () { - return new Promise((resolve, reject) => { - resolve() - }) - } - getPastEvents () { return new Promise((resolve, reject) => { resolve() From 277abb460d9882f457cf2c7ba95a7a8cdd967d1f Mon Sep 17 00:00:00 2001 From: Till Kolter Date: Thu, 7 Dec 2017 03:52:45 +0100 Subject: [PATCH 6/6] Add identity observer pattern --- lib/index.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/index.js b/lib/index.js index d4c579f..b3645cc 100644 --- a/lib/index.js +++ b/lib/index.js @@ -18,6 +18,8 @@ class ContractManager { autoInit: true, connectionRetries: 3 } + this.identityObservers = [] + Object.assign(this.options, options) if (this.options.autoInit) this.initWeb3() } @@ -124,6 +126,11 @@ class ContractManager { if (accounts.length && this.account !== accounts[0]) { this.unlocked = true this.account = accounts[0] + + for (let i in this.identityObservers) { + this.identityObservers[i](this.account) + } + } else if (!accounts.length) { this.unlocked = false this.account = null @@ -142,6 +149,13 @@ class ContractManager { }) } + addIdentityObserver (observer) { + this.identityObservers.push(observer) + if (this.address) { + observer(this.account) + } + } + } export default ContractManager