Skip to content
This repository has been archived by the owner on Jan 14, 2020. It is now read-only.

Commit

Permalink
Sparrow dom/tranaction hash (#546)
Browse files Browse the repository at this point in the history
* add transaction hash event to contract call

* simpify api

* add transactionHash and confirmationCallback to deploy function as well

* handle null calllbacks

* add possible probleme explanation

* remove unneeded options

* adding comments and abstracting constant

* add test for transactionHash and onComplete callbacks

* handle possible race condition in test
  • Loading branch information
sparrowDom authored and Franck committed Sep 27, 2018
1 parent f216c87 commit 8e6f238
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 11 deletions.
4 changes: 2 additions & 2 deletions src/contractInterface/users/resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ class UsersResolver {
this.currentAdapter = this.adapters[this.currentVersion]
}

async set({ profile, attestations = [] }) {
return this.currentAdapter.set({ profile, attestations })
async set({ profile, attestations = [], options = {} }) {
return this.currentAdapter.set({ profile, attestations, options })
}

async get(address) {
Expand Down
11 changes: 6 additions & 5 deletions src/contractInterface/users/v00_adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ class V00_UsersAdapter {
this.contractName = 'V00_UserRegistry'
}

async set({ profile, attestations = [] }) {
async set({ profile, attestations = [], options = {}}) {
if (profile) {
const selfAttestation = await this.profileAttestation(profile)
attestations.push(selfAttestation)
}
const newAttestations = await this.newAttestations(attestations)
return await this.addAttestations(newAttestations)
return await this.addAttestations(newAttestations, options)
}

async get(address) {
Expand Down Expand Up @@ -91,7 +91,7 @@ class V00_UsersAdapter {
})
}

async addAttestations(attestations) {
async addAttestations(attestations, options) {
const account = await this.contractService.currentAccount()
const userRegistry = await this.contractService.deployed(
this.contractService.contracts[this.contractName]
Expand Down Expand Up @@ -123,7 +123,7 @@ class V00_UsersAdapter {
'ClaimHolderRegistered',
'addClaims',
[topics, issuers, sigs, data, dataOffsets],
{ from: account, gas: 400000, contractAddress: identityAddress }
{ ...options, from: account, gas: 400000, contractAddress: identityAddress }
)
} else {
// create identity with presigned claims
Expand All @@ -138,7 +138,8 @@ class V00_UsersAdapter {
data,
dataOffsets
],
{ from: account, gas }
{ from: account, gas },
options
)
}
} else if (!identityAddress) {
Expand Down
8 changes: 6 additions & 2 deletions src/resources/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ class Users {
this.resolver = new UsersResolver({ contractService, ipfsService })
}

async set({ profile, attestations = [] }) {
return this.resolver.set({ profile, attestations })
/* possible options values:
* - confirmationCallback(confirmationCount, transactionReceipt) -> called repeatedly after a transaction is mined
* - transactionHashCallback(hash) -> called immediately when the transaction hash is received
*/
async set({ profile, attestations = [], options = {}}) {
return this.resolver.set({ profile, attestations, options })
}

async get(address) {
Expand Down
43 changes: 41 additions & 2 deletions src/services/contract-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import bs58 from 'bs58'
import Web3 from 'web3'

const emptyAddress = '0x0000000000000000000000000000000000000000'
const NUMBER_CONFIRMATIONS_TO_REPORT = 20
const SUPPORTED_ERC20 = [
{ symbol: 'OGN', decimals: 18, contractName: 'OriginToken' }
]
Expand Down Expand Up @@ -192,7 +193,7 @@ class ContractService {
return withLibraryAddresses
}

async deploy(contract, args, options) {
async deploy(contract, args, options, { confirmationCallback, transactionHashCallback } = {} ) {
const bytecode = await this.getBytecode(contract)
const deployed = await this.deployed(contract)
const txReceipt = await new Promise((resolve, reject) => {
Expand All @@ -205,16 +206,53 @@ class ContractService {
.on('receipt', receipt => {
resolve(receipt)
})
//.on('confirmation', confirmationCallback)
//.on('transactionHash', transactionHashCallback)
// Workaround for "confirmationCallback" not being triggered with web3 version:1.0.0-beta.34
.on('transactionHash', (hash) => {
if (transactionHashCallback)
transactionHashCallback(hash)
if (confirmationCallback)
this.checkForDeploymentCompletion(hash, confirmationCallback)
})
.on('error', err => reject(err))
})
return txReceipt
}

/* confirmation callback does not get triggered in current version of web3 version:1.0.0-beta.34
* so this function perpetually (until 20 confirmations) checks for presence of deployed contract.
*
* This could also be a problem in Ethereum node: https://github.com/ethereum/web3.js/issues/1255
*/
async checkForDeploymentCompletion(hash, confirmationCallback) {
const transactionInfo = await this.web3.eth.getTransaction(hash)

// transaction not mined
if (transactionInfo.blockNumber === null){
setTimeout(() => {
this.checkForDeploymentCompletion(hash, confirmationCallback)
}, 1500)
} else {
const currentBlockNumber = await this.web3.eth.getBlockNumber()
const confirmations = currentBlockNumber - transactionInfo.blockNumber
confirmationCallback(confirmations, {
transactionHash: transactionInfo.hash
})
// do checks until NUMBER_CONFIRMATIONS_TO_REPORT block confirmations
if (confirmations < NUMBER_CONFIRMATIONS_TO_REPORT) {
setTimeout(() => {
this.checkForDeploymentCompletion(hash, confirmationCallback)
}, 1500)
}
}
}

async call(
contractName,
functionName,
args = [],
{ contractAddress, from, gas, value, confirmationCallback, additionalGas = 0 } = {}
{ contractAddress, from, gas, value, confirmationCallback, transactionHashCallback, additionalGas = 0 } = {}
) {
const contractDefinition = this.contracts[contractName]
if (typeof contractDefinition === 'undefined') {
Expand All @@ -239,6 +277,7 @@ class ContractService {
.send(opts)
.on('receipt', resolve)
.on('confirmation', confirmationCallback)
.on('transactionHash', transactionHashCallback)
.on('error', reject)
})
const block = await this.web3.eth.getBlock(transactionReceipt.blockNumber)
Expand Down
31 changes: 31 additions & 0 deletions test/resource_users.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import chai from 'chai'
import chaiString from 'chai-string'
import chaiAsPromised from 'chai-as-promised'
import Web3 from 'web3'

Expand All @@ -9,6 +10,7 @@ import ContractService from '../src/services/contract-service'
import IpfsService from '../src/services/ipfs-service'

chai.use(chaiAsPromised)
chai.use(chaiString)
const expect = chai.expect

const issuerPrivatekey =
Expand Down Expand Up @@ -220,6 +222,35 @@ describe('User Resource', function() {
const badProfile = { profile: { bad: 'profile' } }
return expect(users.set(badProfile)).to.eventually.be.rejectedWith(Error)
})

it('should be able to receive transactionHash and onComplete callbacks', (done) => {
let transactionHash
let doneCalled = false

users.set({
profile: {
firstName: 'Wonder',
lastName: 'Woman',
},
options: {
transactionHashCallback: (hash) => {
transactionHash = hash
},
confirmationCallback: (confirmationCount, transactionReceipt) => {
expect(confirmationCount).is.a('number')
expect(transactionReceipt).is.a('object')
// transactionHashCallback should always execute before confirmationCallback
expect(transactionHash).to.startsWith('0x')

// prevent done being called multiple times
if (!doneCalled){
doneCalled = true
done()
}
}
}
})
})
})

describe('get', () => {
Expand Down

0 comments on commit 8e6f238

Please sign in to comment.