Skip to content

Commit

Permalink
feat: New txUtils object for handling transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
nicosantangelo committed Feb 8, 2018
1 parent 3aa9b2f commit 0367e3e
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 78 deletions.
6 changes: 5 additions & 1 deletion src/ethereum/eth.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const eth = {
* @return {boolean} - True if the connection was successful
*/
async connect(options = {}) {
if (this.wallet && this.wallet.isConnected()) {
if (this.isConnected()) {
this.disconnect()
}

Expand Down Expand Up @@ -63,6 +63,10 @@ export const eth = {
return wallet
},

isConnected() {
return (this.wallet && this.wallet.isConnected()) || !!web3
},

disconnect() {
if (this.wallet) {
this.wallet.disconnect()
Expand Down
2 changes: 1 addition & 1 deletion src/ethereum/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { eth } from './eth'
export { Contract } from './Contract'
export { SignedMessage } from './SignedMessage'
export { tx } from './tx'
export { txUtils } from './txUtils'
76 changes: 0 additions & 76 deletions src/ethereum/tx.js

This file was deleted.

120 changes: 120 additions & 0 deletions src/ethereum/txUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { Log } from '../log'
import { sleep } from '../utils'
import { eth } from './eth'

const log = new Log('txUtils')

/**
* Some utility functions to work with Ethereum transactions.
* @namespace
*/
export const txUtils = {
DUMMY_TX_ID: '0xdeadbeef',

TRANSACTION_FETCH_DELAY: 10 * 1000,

TRANSACTION_STATUS: Object.freeze({
pending: 'pending',
confirmed: 'confirmed',
failed: 'failed'
}),

/**
* Waits until the transaction finishes. Returns if it was successfull.
* Throws if the transaction fails or if it lacks any of the supplied events
* @param {string} txId - Transaction id to watch
* @param {Array<string>|string} events - Events to watch. See {@link txUtils#getLogEvents}
* @return {object} data - Current transaction data. See {@link txUtils#getTransaction}
*/
async getConfirmedTransaction(txId, events) {
const tx = await txUtils.waitForCompletion(txId)

if (this.isFailure(tx)) {
throw new Error(`Transaction "${txId}" failed`)
}

if (!this.hasLogEvents(tx, events)) {
throw new Error(`Missing events for transaction "${txId}": ${events}`)
}

return tx
},

/**
* Wait until a transaction finishes by either being mined or failing
* @param {string} txId - Transaction id to watch
* @return {object} data - Current transaction data. See {@link txUtils#getTransaction}
*/
async waitForCompletion(txId) {
const tx = await this.getTransaction(txId)

if (this.isPending(tx) || !tx.recepeit) {
log.debug(`"${txId}" pending, wait ${this.TRANSACTION_FETCH_DELAY}ms`)
await sleep(this.TRANSACTION_FETCH_DELAY)
return this.waitForCompletion(txId)
}

log.debug(`"${txId}" completed`)
return tx
},

/**
* Get the transaction status and recepeit
* @param {string} txId - Transaction id
* @return {object} data - Current transaction data. See {@link https://github.com/ethereum/wiki/wiki/JavaScript-API#web3ethgettransaction}
* @return {object.recepeit} transaction - Transaction recepeit
*/
async getTransaction(txId) {
const [tx, recepeit] = await Promise.all([
eth.fetchTxStatus(txId),
eth.fetchTxReceipt(txId)
])

return { ...tx, recepeit }
},

/**
* Expects the result of getTransaction's geth command and returns true if the transaction is still pending.
* It'll also check for a pending status prop against {@link txUtils#TRANSACTION_STATUS}
* @param {object} tx - The transaction object
* @return boolean
*/
isPending(tx) {
return (
tx &&
(tx.blockNumber === null || tx.status === this.TRANSACTION_STATUS.pending)
)
},

/**
* Expects the result of getTransactionRecepeit's geth command and returns true if the transaction failed.
* It'll also check for a failed status prop against {@link txUtils#TRANSACTION_STATUS}
* @param {object} tx - The transaction object
* @return boolean
*/
isFailure(tx) {
return (
tx &&
(!tx.recepeit ||
tx.recepeit.status === '0x0' ||
tx.status === this.TRANSACTION_STATUS.failed)
)
},

/**
* Returns true if a transaction contains an event
* @param {Array<object>} tx - Transaction with a decoded recepeit
* @param {Array<string>|string} eventNames - A string or array of strings with event names you want to search for
* @return boolean
*/
hasLogEvents(tx, eventNames) {
if (!eventNames || eventNames.length === 0) return true
if (!tx.recepit) return false

if (!Array.isArray(eventNames)) eventNames = [eventNames]

return tx.recepeit
.filter(log => log && log.name)
.every(log => eventNames.includes(log.name))
}
}

0 comments on commit 0367e3e

Please sign in to comment.