import { generateStore } from './generateStore' import defaultOptions from './defaultOptions' import merge from './mergeOptions' import DrizzleContract from './DrizzleContract' import * as ContractActions from './contracts/constants' import * as DrizzleActions from './drizzleStatus/drizzleActions' // Load as promise so that async Drizzle initialization can still resolve const isEnvReadyPromise = new Promise((resolve) => { const hasNavigator = typeof navigator !== 'undefined' const hasWindow = typeof window !== 'undefined' const hasDocument = typeof document !== 'undefined' if (hasNavigator && navigator.product === 'ReactNative') { return resolve() } if (hasWindow) { return window.addEventListener('load', resolve) } // resolve in any case if we missed the load event and the document is already loaded if (hasDocument && document.readyState === `complete`) { return resolve() } }) export const getOrCreateWeb3Contract = (store, contractConfig, web3) => { if (contractConfig.web3Contract) { return contractConfig.web3Contract } const state = store.getState() const networkId = state.web3 && state.web3.networkId const selectedAccount = state.accounts[0] const { abi, networks, deployedBytecode } = contractConfig return ( new web3.eth.Contract(abi, networks[networkId].address, { from: selectedAccount, data: deployedBytecode }) ) } class Drizzle { constructor (givenOptions, store) { const options = merge(defaultOptions, givenOptions) // Variables this.contracts = {} this.contractList = [] this.options = options this.store = store || this.generateStore(options) this.web3 = {} this.loadingContract = {} // Wait for window load event in case of injected web3. isEnvReadyPromise.then(() => { // Begin Drizzle initialization. this.store.dispatch({ type: DrizzleActions.DRIZZLE_INITIALIZING, drizzle: this, options }) }) } addContract (contractConfig, events = []) { const web3Contract = getOrCreateWeb3Contract( this.store, contractConfig, this.web3 ) const drizzleContract = new DrizzleContract( web3Contract, this.web3, contractConfig.contractName, this.store, events ) if (this.contracts[drizzleContract.contractName]) { throw new Error( `Contract already exists: ${drizzleContract.contractName}` ) } this.store.dispatch({ type: ContractActions.CONTRACT_INITIALIZING, contractConfig }) this.contracts[drizzleContract.contractName] = drizzleContract this.contractList.push(drizzleContract) this.store.dispatch({ type: ContractActions.CONTRACT_INITIALIZED, name: contractConfig.contractName }) } deleteContract (contractName) { // Deleting a contract means removing it from this instance's // `contractList`, `contracts`, and `loadingContract` if (!this.contracts[contractName]) { throw new Error(`Contract does not exist: ${contractName}`) } this.contractList = this.contractList.filter( contract => contract.contractName !== contractName ) const { [contractName]: omittedContract, ...restContracts } = this.contracts this.contracts = restContracts const { [contractName]: omittedLoading, ...restLoadingContract } = this.loadingContract this.loadingContract = restLoadingContract this.store.dispatch({ type: ContractActions.DELETE_CONTRACT, contractName }) } findContractByAddress (address) { return this.contractList.find(contract => { return contract.address.toLowerCase() === address.toLowerCase() }) } /* * NOTE * This strangeness is for backward compatibility with < v1.2.4 * Future versions will have generateStore's contents here */ generateStore (options) { return generateStore(options) } } export default Drizzle