Skip to content
This repository has been archived by the owner on Jul 13, 2022. It is now read-only.

Events Support #451

Merged
merged 10 commits into from
Sep 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/api/.gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
coverage
.tmp
src/generated
src/contracts/models
dist
lib
node_modules
Expand Down
8 changes: 7 additions & 1 deletion packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,20 @@
"tsc": "rm -rf dist/* && tsc --outDir './dist'",
"test": "jest",
"tsc:watch": "rm -rf dist; tsc -w --outDir './dist'",
"ganache": "npx ganache-cli -p 8545 -i 5777 -m \"${npm_package_config_mnemonic}\"",
"lint": "eslint src --ext ts --ignore-path .gitignore --fix",
"abi-gen": "node src/abi-extractor.js && rm -rf src/generated && ts-node abi-gen.ts --abis './.tmp/*.json' -o src/generated --template './template.handlebars' --partials './partials/*.handlebars'"
"abi-gen": "rm -rf src/contracts/models && ts-node abi-gen.ts --abis './.tmp/*.json' -o src/contracts/models --template './template.handlebars' --partials './partials/*.handlebars'"
},
"config": {
"mnemonic": "lemon scrub wasp bracket town boat property sadness layer taxi butter audit"
},
"files": [
"dist"
],
"devDependencies": {
"@0xproject/abi-gen": "^1.0.9",
"@types/jest": "^23.3.2",
"@types/web3": "^1.0.6",
"bignumber.js": "^7.2.1",
"chalk": "^2.4.1",
"eslint-plugin-typescript": "^0.12.0",
Expand All @@ -36,6 +41,7 @@
"@rigoblock/contracts": "^0.3.2",
"@types/node": "^9.6.6",
"bignumber.js": "^5.0.0",
"ganache-cli": "7.0.0-beta.0",
"handlebars": "^4.0.12",
"typechain": "^0.2.2",
"web3": "1.0.0-beta.36",
Expand Down
9 changes: 5 additions & 4 deletions packages/api/partials/event.handlebars
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export interface {{@root.contractName}}{{name}}EventArgs extends DecodedLogArgs {
{{#each inputs}}
{{name}}: {{#returnType type components}}{{/returnType}};
{{/each}}
public {{this.name}}Event(
options?: EventOptions,
cb?: Function
) {
return this.rawWeb3Contract.events.{{this.name}}(options || {}, cb)
}
2 changes: 1 addition & 1 deletion packages/api/partials/txFunction.handlebars
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
public async {{this.tsName}}({{> typed_params inputs=inputs}}): Promise<{{> return_type outputs=outputs}}> {
public async {{this.tsName}}({{> typed_params inputs=inputs}}): Promise<TransactionObject<{{> return_type outputs=outputs}}>> {
return this.rawWeb3Contract.methods.{{this.tsName}}({{> params inputs=inputs}})
}
2 changes: 1 addition & 1 deletion packages/api/partials/typed_params.handlebars
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{{#each inputs}}
{{name}}: {{#parameterType type components}}{{/parameterType}},
{{name}}: {{#parameterType type components}}{{/parameterType}}{{#if @last}}{{else}},{{/if}}
{{/each}}
46 changes: 42 additions & 4 deletions packages/api/src/baseContract.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,44 @@
export default class BaseContract {
public readonly rawWeb3Contract: any
constructor(web3: any, address: string, abi: any) {
this.rawWeb3Contract = new web3.eth.Contract(abi, address)
import { EventEmitter, EventLog } from 'web3/types'

export interface TransactionObject {
call
send
encodeABI
estimateGas
}
export interface EventFilter {
[key: string]: string
}

export interface EventOptions {
filter?: EventFilter
fromBlock?: number
toBlock?: number | 'latest'
topics?: string[]
}

export default class BaseContract<Events> {
public rawWeb3Contract: any

public getPastEvents(
eventName: Events,
options?: EventOptions
): Promise<EventLog[]> {
return this.rawWeb3Contract.getPastEvents(eventName, options || {})
}

public allEvents(
options?: EventOptions,
cb?: Function
): Promise<EventEmitter> {
return this.rawWeb3Contract.events.allEvents(options || {}, cb)
}

public once(
eventName: Events,
options?: EventOptions,
cb?: Function
): Promise<EventEmitter> {
return this.rawWeb3Contract.once(eventName, options || {}, cb)
}
}
37 changes: 21 additions & 16 deletions packages/api/template.handlebars
Original file line number Diff line number Diff line change
@@ -1,29 +1,28 @@
/* eslint-disable */
import { BigNumber } from 'bignumber.js'
import BaseContract from '../baseContract'

{{!-- TODO: add events --}}
{{!-- {{#if events}}
export type {{contractName}}EventArgs =
{{#each events}}
| {{@root.contractName}}{{name}}EventArgs{{#if @last}};{{/if}}
{{/each}}
import BaseContract, { EventOptions } from '../../baseContract'
import { TransactionObject } from 'web3/eth/types'

{{#if events}}
export enum {{contractName}}Events {
{{#each events}}
{{name}} = '{{name}}',
{{/each}}
}
{{/if}}

{{#if events}}
export class {{contractName}} extends BaseContract<{{contractName}}Events> {
public constructor(web3: any, address: string) {
super()
{{else}}
export class {{contractName}} {
public rawWeb3Contract: any
public constructor(web3: any, address: string) {
{{/if}}

{{#each events}}
{{> event}}

{{/each}}
{{/if}} --}}
export class {{contractName}} extends BaseContract {
public constructor(web3: any, address: string) {
const abi = {{{getAbi ctor events methods}}}
super(web3, address, abi);
this.rawWeb3Contract = new web3.eth.Contract(abi, address)
}

static async createAndValidate(
Expand All @@ -42,6 +41,7 @@ export class {{contractName}} extends BaseContract {
}

public static address: string

{{#each methods}}
{{#this.constant}}
{{> constantFunction contractName=../contractName}}
Expand All @@ -50,6 +50,11 @@ export class {{contractName}} extends BaseContract {
{{> txFunction contractName=../contractName}}
{{/this.constant}}
{{/each}}
{{#if events}}
{{#each events}}
{{> event contractName=../contractName}}
{{/each}}
{{/if}}

static isDeployed() {
return !!this.address
Expand Down
191 changes: 140 additions & 51 deletions packages/api/test/contracts.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import Web3 = require('web3')
import { Vault } from '../src/generated/vault'
import { VaultFactory } from '../src/generated/vault_factory'
import { Vault } from '../src/contracts/models/vault'
import {
VaultFactory,
VaultFactoryEvents
} from '../src/contracts/models/vault_factory'

describe('generated contract', () => {
let web3
let extendedExpect
let accounts
let txOptions
beforeAll(async () => {
web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'))
web3 = new Web3(new Web3.providers.WebsocketProvider('ws://localhost:8545'))
accounts = await web3.eth.getAccounts()
extendedExpect = expect as any
})
Expand All @@ -32,60 +35,146 @@ describe('generated contract', () => {
'0x7ce6e371085cb611fb46d5065397223ef2f952ff'
)
})
it('constant method', async () => {
const registry = '0xf7cBB0849d4a8ec5aB4650030FA776c00Eb52dA7'
const res = await vaultFactory.getRegistry()
expect(res).toEqual(registry)
describe('constant methods', () => {
it('reads a constant property of the contract from the blockChain ', async () => {
const registry = '0xf7cBB0849d4a8ec5aB4650030FA776c00Eb52dA7'
const res = await vaultFactory.getRegistry()
expect(res).toEqual(registry)
})
})
it('createVault', async () => {
const vaultName = Math.random()
.toString(36)
.substring(2, 7)
const vaultOptions = [vaultName, 'ASD']
const gasPrice = await web3.eth.getGasPrice()
const gasEstimate = await vaultFactory // 2538415
.createVault(...vaultOptions)
.then(obj => obj.estimateGas(txOptions))
const receipt = await vaultFactory
.createVault(...vaultOptions)
.then(obj =>
// adding value to gas estimate as it is not always correct
obj.send({ ...txOptions, gasPrice, gas: gasEstimate + 5000 })
)

extendedExpect(receipt.transactionHash).toBeHash()
describe('non constant methods', () => {
describe('non payable methods', () => {
it("writes to the blockChain, but doesn't accept ether", async () => {
const vaultName = Math.random()
.toString(36)
.substring(2, 7)
const vaultOptions = [vaultName, 'ASD']
const gasPrice = await web3.eth.getGasPrice()
const gasEstimate = await vaultFactory // 2538415
.createVault(...vaultOptions)
.then(obj => {
return obj.estimateGas(txOptions)
})
const receipt = await vaultFactory
.createVault(...vaultOptions)
.then(obj =>
// adding value to gas estimate as it is not always correct
obj.send({ ...txOptions, gasPrice, gas: gasEstimate + 5000 })
)

extendedExpect(receipt.transactionHash).toBeHash()
})
})
describe('payable methods', async () => {
let vaultInstance
beforeAll(async () => {
const vaultName = Math.random()
.toString(36)
.substring(2, 7)
const vaultOptions = [vaultName, 'VLT']
const gasPrice = await web3.eth.getGasPrice()
const gasEstimate = await vaultFactory
.createVault(...vaultOptions)
.then(obj => obj.estimateGas(txOptions))
const receipt = await vaultFactory
.createVault(...vaultOptions)
.then(obj =>
// adding value to gas estimate as it is not always correct
obj.send({ ...txOptions, gasPrice, gas: gasEstimate + 5000 })
)
const vaultAddress = receipt.events.VaultCreated.returnValues.vault
vaultInstance = await Vault.createAndValidate(web3, vaultAddress)
})
it('accepts an ether value', async () => {
const options = { ...txOptions, value: web3.utils.toWei('1') }
const gasPrice = await web3.eth.getGasPrice()
const gasEstimate = await vaultInstance
.buyVault()
.then(obj => obj.estimateGas(options))
await vaultInstance
.buyVault()
.then(obj =>
obj.send({ ...options, gasPrice, gas: gasEstimate + 5000 })
)
})
})
})
})
describe('contract events', () => {
describe('getPastEvents', () => {
it('returns past logs for the specified event', async () => {
const vaultFactory = await VaultFactory.createAndValidate(
web3,
'0x7ce6e371085cb611fb46d5065397223ef2f952ff'
)
const eventLog = await vaultFactory.getPastEvents(
VaultFactoryEvents.VaultCreated,
{ fromBlock: 0, toBlock: 'latest' }
)
expect(Array.isArray(eventLog)).toBe(true)
})
})
describe('payable methods', async () => {
let vaultInstance
beforeAll(async () => {
const vaultName = Math.random()
.toString(36)
.substring(2, 7)
const vaultOptions = [vaultName, 'VLT']
const gasPrice = await web3.eth.getGasPrice()
const gasEstimate = await vaultFactory
.createVault(...vaultOptions)
.then(obj => obj.estimateGas(txOptions))
const receipt = await vaultFactory
.createVault(...vaultOptions)
.then(obj =>
// adding value to gas estimate as it is not always correct
obj.send({ ...txOptions, gasPrice, gas: gasEstimate + 5000 })
describe('websocket events', () => {
const allEventsSpy = jest.fn()
const onceSpy = jest.fn()
const VaultCreatedSpy = jest.fn()
const filterOptions = {
fromBlock: 0,
toBlock: 'latest'
}
const filterCallback = (err, event) =>
err ? console.error(err) : console.log(event)

class ContractMock {
public once(...args) {
return onceSpy(...args)
}
public events = {
allEvents: allEventsSpy,
VaultCreated: VaultCreatedSpy
}
}
const Web3Mock = {
eth: {
getCode: () => Promise.resolve('0x12345'),
Contract: ContractMock
}
}
let mockedVaultFactory
beforeEach(async () => {
const VaultFactory = require('../src/contracts/models/vault_factory')
.VaultFactory

mockedVaultFactory = await VaultFactory.createAndValidate(
Web3Mock,
'0x7ce6e371085cb611fb46d5065397223ef2f952ff'
)
})
describe('allEvents', () => {
it('calls the web3 allEvents method with the specified options', async () => {
await mockedVaultFactory.allEvents(filterOptions, filterCallback)
expect(allEventsSpy).toHaveBeenCalledWith(
filterOptions,
filterCallback
)
const vaultAddress = receipt.events.VaultCreated.returnValues.vault
vaultInstance = await Vault.createAndValidate(web3, vaultAddress)
})
})
describe('once', () => {
it('calls the web3 once function with specified options', async () => {
const options = ['VaultCreated', filterOptions, filterCallback]
await mockedVaultFactory.once(...options)
expect(onceSpy).toHaveBeenCalledWith(...options)
})
})
it('buyVault', async () => {
const options = { ...txOptions, value: web3.utils.toWei('1') }
const gasPrice = await web3.eth.getGasPrice()
const gasEstimate = await vaultInstance
.buyVault()
.then(obj => obj.estimateGas(options))
await vaultInstance
.buyVault()
.then(obj =>
obj.send({ ...options, gasPrice, gas: gasEstimate + 5000 })
describe('contract event', () => {
it('calls the web3 event function to subscribe to a specific event', async () => {
await mockedVaultFactory.VaultCreatedEvent(filterOptions)
expect(VaultCreatedSpy).toHaveBeenCalledWith(
filterOptions,
undefined // callback is optional
)
})
})
})
})
Expand Down
3 changes: 1 addition & 2 deletions packages/api/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
"noImplicitReturns": true,
"declaration": true,
"sourceMap": true,
"pretty": true,
"typeRoots": ["node_modules/@0xproject/typescript-typings/types", "node_modules/@types"],
"pretty": true
}
}
Loading