Browse Source

Version 0.2.0

master v0.2.0
Ezerous 4 years ago
parent
commit
9d212ecb3b
  1. 2
      package.json
  2. 8
      src/Drizzle.js
  3. 2
      src/accountBalances/accountBalancesMiddleware.js
  4. 2
      src/accountBalances/accountBalancesSaga.js
  5. 6
      src/accounts/accountsMiddleware.js
  6. 2
      src/blocks/blocksSaga.js
  7. 1
      src/contractStateUtils.js
  8. 1
      src/contracts/constants.js
  9. 11
      src/contracts/contractsReducer.js
  10. 18
      src/contracts/contractsSaga.js
  11. 9
      src/drizzle-middleware.js
  12. 20
      src/drizzleStatus/drizzleStatusSaga.js
  13. 2
      src/generateStore.js
  14. 40
      src/index.js
  15. 2
      src/mergeOptions.js
  16. 10
      src/web3/web3Actions.js
  17. 23
      src/web3/web3Middleware.js
  18. 5
      src/web3/web3Reducer.js
  19. 17
      src/web3/web3Saga.js

2
package.json

@ -1,6 +1,6 @@
{
"name": "@ezerous/drizzle",
"version": "0.1.0",
"version": "0.2.0",
"description": "A reactive data-store for web3 and smart contracts.",
"license": "MIT",
"author": "Ezerous <ezerous@gmail.com>",

8
src/Drizzle.js

@ -6,7 +6,7 @@ import * as ContractActions from './contracts/constants'
import * as DrizzleActions from './drizzleStatus/drizzleActions'
// Load as promise so that async Drizzle initialization can still resolve
var isEnvReadyPromise = new Promise((resolve, reject) => {
const isEnvReadyPromise = new Promise((resolve) => {
const hasNavigator = typeof navigator !== 'undefined'
const hasWindow = typeof window !== 'undefined'
const hasDocument = typeof document !== 'undefined'
@ -81,12 +81,6 @@ class Drizzle {
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

2
src/accountBalances/accountBalancesMiddleware.js

@ -1,4 +1,4 @@
import { WEB3_INITIALIZED } from '../web3/constants'
import { WEB3_INITIALIZED } from '../web3/web3Actions'
import { accountBalancesFetching } from './accountBalancesActions'
import { ACCOUNTS_FETCHED } from '../accounts/accountsActions'

2
src/accountBalances/accountBalancesSaga.js

@ -1,7 +1,7 @@
import { call, put, select, takeLatest } from 'redux-saga/effects'
import {
ACCOUNT_BALANCES_FAILED,
ACCOUNT_BALANCE_FETCHED,
ACCOUNT_BALANCES_FAILED,
ACCOUNT_BALANCES_FETCHED,
ACCOUNT_BALANCES_FETCHING
} from './accountBalancesActions'

6
src/accounts/accountsMiddleware.js

@ -1,14 +1,14 @@
import { WEB3_INITIALIZED } from '../web3/constants'
import { WEB3_INITIALIZED } from '../web3/web3Actions'
import { accountsFetched, accountsListening } from './accountsActions'
export const accountsMiddleware = () => store => next => action => {
export const accountsMiddleware = web3 => store => next => action => {
const { type } = action
if (type === WEB3_INITIALIZED) {
if(!window.ethereum)
console.warn('No Metamask detected, not subscribed to account changes!')
else {
const { web3 } = action;
web3 = action.web3;
window.ethereum.on('accountsChanged', accounts => {
// For some reason accounts here are returned with lowercase letters, so we need to patch them
let patchedAccounts = Array.from(accounts);

2
src/blocks/blocksSaga.js

@ -1,5 +1,5 @@
import { END, eventChannel } from 'redux-saga'
import { call, put, take, takeEvery, takeLatest, all } from 'redux-saga/effects'
import { all, call, put, take, takeEvery, takeLatest } from 'redux-saga/effects'
import * as BlocksActions from './blockActions'
/*

1
src/contractStateUtils.js

@ -22,6 +22,7 @@ export const generateContractInitialState = contractConfig => {
return {
initialized: false,
synced: false,
deployed: true,
...objectOfConstants
}
}

1
src/contracts/constants.js

@ -4,6 +4,7 @@ export const EVENT_ERROR = 'EVENT_ERROR'
export const LISTEN_FOR_EVENT = 'LISTEN_FOR_EVENT'
export const CONTRACT_INITIALIZING = 'CONTRACT_INITIALIZING'
export const CONTRACT_INITIALIZED = 'CONTRACT_INITIALIZED'
export const CONTRACT_NOT_DEPLOYED = 'CONTRACT_NOT_DEPLOYED'
export const GOT_CONTRACT_VAR = 'GOT_CONTRACT_VAR'
export const DELETE_CONTRACT = 'DELETE_CONTRACT'
export const CONTRACT_SYNCING = 'CONTRACT_SYNCING'

11
src/contracts/contractsReducer.js

@ -29,6 +29,17 @@ const contractsReducer = (state = initialState, action) => {
}
}
// Contract not found on the current network
if (action.type === ContractActions.CONTRACT_NOT_DEPLOYED) {
return {
...state,
[action.name]: {
...state[action.name],
deployed: false
}
}
}
if (action.type === ContractActions.DELETE_CONTRACT) {
const { [action.contractName]: omitted, ...rest } = state
return rest

18
src/contracts/contractsSaga.js

@ -199,7 +199,7 @@ function * callCallContractFn ({
} catch (error) {
console.error(error)
var errorArgs = {
const errorArgs = {
name: contract.contractName,
variable: contract.abi[fnIndex].name,
argsHash: argsHash,
@ -230,8 +230,8 @@ function * callSyncContract (action) {
delete contractFnsState.events
// Iterate over functions and hashes
for (var fnName in contractFnsState) {
for (var argsHash in contractFnsState[fnName]) {
for (let fnName in contractFnsState) {
for (let argsHash in contractFnsState[fnName]) {
const fnIndex = contractFnsState[fnName][argsHash].fnIndex
const args = contractFnsState[fnName][argsHash].args
@ -271,6 +271,18 @@ function isSendOrCallOptions (options) {
return false
}
export function * isContractDeployed ({ web3, contractConfig }) {
const networkId = yield call(web3.eth.net.getId);
if(contractConfig.networks[networkId]){
const contractAddress = contractConfig.networks[networkId].address;
const fetchedByteCode = yield call(web3.eth.getCode, contractAddress);
if(fetchedByteCode === contractConfig.deployedBytecode)
return true;
}
return false;
}
function * contractsSaga () {
yield takeEvery(ContractActions.SEND_CONTRACT_TX, callSendContractTx)
yield takeEvery(ContractActions.CALL_CONTRACT_FN, callCallContractFn)

9
src/drizzle-middleware.js

@ -1,6 +1,7 @@
import * as DrizzleActions from './drizzleStatus/drizzleActions'
import * as ContractActions from './contracts/constants'
import { ACCOUNTS_FETCHED } from './accounts/accountsActions'
import { NETWORK_ID_CHANGED } from './web3/web3Actions'
export const drizzleMiddleware = drizzleInstance => store => next => action => {
const { type } = action
@ -9,6 +10,14 @@ export const drizzleMiddleware = drizzleInstance => store => next => action => {
drizzleInstance = action.drizzle
}
if (type === NETWORK_ID_CHANGED) {
store.dispatch({
type: DrizzleActions.DRIZZLE_INITIALIZING,
drizzle: drizzleInstance,
options: drizzleInstance.options
})
}
if (
type === ACCOUNTS_FETCHED &&
drizzleInstance &&

20
src/drizzleStatus/drizzleStatusSaga.js

@ -1,13 +1,15 @@
import { call, put, takeLatest } from 'redux-saga/effects'
// Initialization Functions
import { initializeWeb3, getNetworkId } from '../web3/web3Saga'
import { getNetworkId, initializeWeb3 } from '../web3/web3Saga'
import { getAccounts } from '../accounts/accountsSaga'
import { getAccountBalances } from '../accountBalances/accountBalancesSaga'
import * as DrizzleActions from './drizzleActions'
import * as BlocksActions from '../blocks/blockActions'
import { NETWORK_IDS, NETWORK_MISMATCH } from '../web3/constants'
import { NETWORK_IDS, NETWORK_MISMATCH } from '../web3/web3Actions'
import { CONTRACT_NOT_DEPLOYED } from '../contracts/constants'
import { isContractDeployed } from '../contracts/contractsSaga'
export function * initializeDrizzle (action) {
try {
@ -35,15 +37,19 @@ export function * initializeDrizzle (action) {
yield call(getAccountBalances, { web3 })
// Instantiate contracts passed through via options.
for (var i = 0; i < options.contracts.length; i++) {
var contractConfig = options.contracts[i]
var events = []
var contractName = contractConfig.contractName
for (let i = 0; i < options.contracts.length; i++) {
const contractConfig = options.contracts[i]
let events = []
const contractName = contractConfig.contractName;
if (contractName in options.events) {
events = options.events[contractName]
}
if(!(yield call(isContractDeployed, { web3, contractConfig }))){
yield put({ type: CONTRACT_NOT_DEPLOYED, name: contractName })
throw `Contract ${contractName} not deployed on this network`
}
yield call([drizzle, drizzle.addContract], contractConfig, events)
}

2
src/generateStore.js

@ -1,5 +1,5 @@
import { all, fork } from 'redux-saga/effects'
import { createStore, applyMiddleware, compose, combineReducers } from 'redux'
import { applyMiddleware, combineReducers, compose, createStore } from 'redux'
import createSagaMiddleware from 'redux-saga'
import drizzleSagas from './rootSaga'
import drizzleReducers from './reducer'

40
src/index.js

@ -7,53 +7,27 @@ import * as EventActions from './contracts/constants'
import * as AccountActions from './accounts/accountsActions'
// Reducers
import accountsReducer from './accounts/accountsReducer'
import accountBalancesReducer from './accountBalances/accountBalancesReducer'
import blocksReducer from './blocks/blocksReducer'
import contractsReducer from './contracts/contractsReducer'
import drizzleStatusReducer from './drizzleStatus/drizzleStatusReducer'
import transactionsReducer from './transactions/transactionsReducer'
import transactionStackReducer from './transactions/transactionStackReducer'
import web3Reducer from './web3/web3Reducer'
import drizzleReducers from './reducer'
// Middleware
import drizzleMiddleware from './drizzle-middleware'
import accountsMiddleware from './accounts/accountsMiddleware'
import accountBalancesMiddleware from './accountBalances/accountBalancesMiddleware'
import web3Middleware from './web3/web3Middleware'
// Sagas
import accountBalancesSaga from './accountBalances/accountBalancesSaga'
import blocksSaga from './blocks/blocksSaga'
import contractsSaga from './contracts/contractsSaga'
import drizzleStatusSaga from './drizzleStatus/drizzleStatusSaga'
const drizzleReducers = {
accounts: accountsReducer,
accountBalances: accountBalancesReducer,
contracts: contractsReducer,
currentBlock: blocksReducer,
drizzleStatus: drizzleStatusReducer,
transactions: transactionsReducer,
transactionStack: transactionStackReducer,
web3: web3Reducer
}
import drizzleSagas from './rootSaga'
const drizzleMiddlewares = [
drizzleMiddleware,
accountsMiddleware,
accountBalancesMiddleware
]
const drizzleSagas = [
accountBalancesSaga,
blocksSaga,
contractsSaga,
drizzleStatusSaga
accountBalancesMiddleware,
web3Middleware
]
const drizzleActions = {
AccountActions,
EventActions
account: AccountActions,
event: EventActions
}
export {

2
src/mergeOptions.js

@ -1,5 +1,5 @@
const merge = require('deepmerge');
import isPlainObject from 'is-plain-object';
import isPlainObject from 'is-plain-object'
export default function (defaultOptions, newOptions) {
return merge(defaultOptions, newOptions, {

10
src/web3/constants.js → src/web3/web3Actions.js

@ -3,7 +3,9 @@ export const WEB3_INITIALIZED = 'WEB3_INITIALIZED'
export const WEB3_FAILED = 'WEB3_FAILED'
export const WEB3_USER_DENIED = 'WEB3_USER_DENIED'
export const NETWORK_ID_FETCHING = 'NETWORK_ID_FETCHING'
export const NETWORK_ID_FETCHED = 'NETWORK_ID_FETCHED'
export const NETWORK_ID_CHANGED = 'NETWORK_ID_CHANGED'
export const NETWORK_ID_FAILED = 'NETWORK_ID_FAILED'
export const NETWORK_MISMATCH = 'NETWORK_MISMATCH'
@ -15,3 +17,11 @@ export const NETWORK_IDS = {
kovan: 42,
ganache: 5777
}
export function networkIdChanged (web3, networkId) {
return {
type: NETWORK_ID_CHANGED,
web3,
networkId
}
}

23
src/web3/web3Middleware.js

@ -0,0 +1,23 @@
import { networkIdChanged, WEB3_INITIALIZED } from './web3Actions'
export const web3Middleware = web3 => store => next => action => {
const { type } = action
if (type === WEB3_INITIALIZED) {
if(!window.ethereum)
console.warn('No Metamask detected, not subscribed to network changes!')
else {
web3 = action.web3;
window.ethereum.on('networkChanged', (networkId) => {
// Warning: 'networkChanged' is deprecated (EIP-1193)
const storedNetworkId = store.getState().web3.networkId;
if(storedNetworkId && networkId !== storedNetworkId)
store.dispatch(networkIdChanged(web3, networkId));
});
}
}
return next(action)
}
const initializedMiddleware = web3Middleware(undefined)
export default initializedMiddleware

5
src/web3/web3Reducer.js

@ -1,4 +1,4 @@
import * as Action from './constants'
import * as Action from './web3Actions'
const initialState = {
status: ''
@ -33,7 +33,8 @@ const web3Reducer = (state = initialState, action) => {
}
}
if (action.type === Action.NETWORK_ID_FETCHED) {
if (action.type === Action.NETWORK_ID_FETCHED
|| action.type === Action.NETWORK_ID_CHANGED) {
return {
...state,
networkId: action.networkId

17
src/web3/web3Saga.js

@ -1,12 +1,11 @@
import { call, put } from 'redux-saga/effects'
import * as Action from './constants'
import * as Action from './web3Actions'
const Web3 = require('web3');
/*
* Initialization
*/
export function * initializeWeb3 (options) {
try {
let web3 = {}
@ -35,7 +34,6 @@ export function * initializeWeb3 (options) {
} catch (error) {
console.error(error)
yield put({ type: Action.WEB3_FAILED })
return
}
} else if (typeof window.web3 !== 'undefined') {
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
@ -71,20 +69,19 @@ export function * initializeWeb3 (options) {
}
/*
* Network ID
* Fetch Network ID
*/
export function * getNetworkId ({ web3 }) {
try {
const networkId = yield call(web3.eth.net.getId)
const networkId = yield call(web3.eth.net.getId);
yield put({ type: Action.NETWORK_ID_FETCHED, networkId })
yield put({ type: Action.NETWORK_ID_FETCHED, networkId });
return networkId
} catch (error) {
yield put({ type: Action.NETWORK_ID_FAILED, error })
yield put({ type: Action.NETWORK_ID_FAILED, error });
console.error('Error fetching network ID:')
console.error(error)
console.error('Error fetching network ID:');
console.error(error);
}
}

Loading…
Cancel
Save