A reactive data-store for web3 and smart contracts
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

159 lines
4.2 KiB

import * as ContractActions from './contracts/constants'
import * as TransactionsActions from './transactions/constants'
import { isGetterFunction, isSetterFunction } from './contractStateUtils'
class DrizzleContract {
constructor (
web3Contract,
web3,
name,
store,
events = [],
contractArtifact = {}
) {
this.abi = web3Contract.options.jsonInterface
this.address = web3Contract.options.address
this.web3 = web3
this.contractName = name
this.contractArtifact = contractArtifact
this.store = store
// Merge web3 contract instance into DrizzleContract instance.
Object.assign(this, web3Contract)
for (var i = 0; i < this.abi.length; i++) {
var item = this.abi[i]
if (isGetterFunction(item)) {
this.methods[item.name].cacheCall = this.cacheCallFunction(item.name, i)
}
if (isSetterFunction(item)) {
this.methods[item.name].cacheSend = this.cacheSendFunction(item.name, i)
}
}
// Register event listeners if any events.
if (events.length > 0) {
for (i = 0; i < events.length; i++) {
const event = events[i]
if (typeof event === 'object') {
store.dispatch({
type: ContractActions.LISTEN_FOR_EVENT,
contract: this,
eventName: event.eventName,
eventOptions: event.eventOptions
})
} else {
store.dispatch({
type: ContractActions.LISTEN_FOR_EVENT,
contract: this,
eventName: event
})
}
}
}
}
cacheCallFunction (fnName, fnIndex, fn) {
var contract = this
return function () {
// Collect args and hash to use as key, 0x0 if no args
var argsHash = '0x0'
var args = arguments
if (args.length > 0) {
argsHash = contract.generateArgsHash(args)
}
const contractName = contract.contractName
const functionState = contract.store.getState().contracts[contractName][
fnName
]
// If call result is in state and fresh, return value instead of calling
if (argsHash in functionState) {
if (contract.store.getState().contracts[contractName].synced === true) {
return argsHash
}
}
// Otherwise, call function and update store
contract.store.dispatch({
type: ContractActions.CALL_CONTRACT_FN,
contract,
fnName,
fnIndex,
args,
argsHash
})
// Return nothing because state is currently empty.
return argsHash
}
}
cacheSendFunction (fnName, fnIndex, fn) {
// NOTE: May not need fn index
var contract = this
return function () {
var args = arguments
// Generate temporary ID
const transactionStack = contract.store.getState().transactionStack
const stackId = transactionStack.length
const stackTempKey = `TEMP_${new Date().getTime()}`
// Add ID to "transactionStack" with temp value, will be overwritten on TX_BROADCASTED
contract.store.dispatch({ type: TransactionsActions.PUSH_TO_TXSTACK, stackTempKey })
// Dispatch tx to saga
// When txhash received, will be value of stack ID
contract.store.dispatch({
type: ContractActions.SEND_CONTRACT_TX,
contract,
fnName,
fnIndex,
args,
stackId,
stackTempKey
})
// return stack ID
return stackId
}
}
generateArgsHash (args) {
var web3 = this.web3
var hashString = ''
for (var i = 0; i < args.length; i++) {
if (typeof args[i] !== 'function') {
var argToHash = args[i]
// Stringify objects to allow hashing
if (typeof argToHash === 'object') {
argToHash = JSON.stringify(argToHash)
}
// Convert number to strong to allow hashing
if (typeof argToHash === 'number') {
argToHash = argToHash.toString()
}
// This check is in place for web3 v0.x
if ('utils' in web3) {
var hashPiece = web3.utils.sha3(argToHash)
} else {
var hashPiece = web3.sha3(argToHash)
}
hashString += hashPiece
}
}
return web3.utils.sha3(hashString)
}
}
export default DrizzleContract