Skip to content

Commit

Permalink
feat: improved transactions (#32)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: removed helpers isDropped, isFailure, isTxDropped, waitForCompletion
  • Loading branch information
Juan Cazala authored and nicosantangelo committed Sep 21, 2018
1 parent a74c13a commit b7f3a21
Show file tree
Hide file tree
Showing 8 changed files with 234 additions and 179 deletions.
10 changes: 2 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,8 @@
"type": "git",
"url": "https://github.com/decentraland/decentraland-eth.git"
},
"files": [
"dist"
],
"keywords": [
"common",
"modules",
"decentraland"
],
"files": ["dist"],
"keywords": ["common", "modules", "decentraland"],
"author": "Decentraland",
"license": "ISC",
"dependencies": {
Expand Down
3 changes: 2 additions & 1 deletion specs/Contracts.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ function doTest() {
expect(typeof txRecipt.contractAddress).to.eq('string')
expect(txRecipt.contractAddress.length).to.be.greaterThan(0)

const x = await txUtils.getTransaction(contract.transactionHash)
/* tslint:disable-next-line:no-unnecessary-type-assertion */
const x = (await txUtils.getTransaction(contract.transactionHash)) as txUtils.ConfirmedTransaction
expect(typeof x).eq('object')
expect(x.hash).eq(contract.transactionHash)
expect(typeof x.receipt).eq('object')
Expand Down
29 changes: 29 additions & 0 deletions specs/eth.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { expect } from 'chai'
import { NodeConnectionFactory } from './NodeConnectionFactory'
import { eth, txUtils } from '../dist'

describe('Eth tests', () => {
const nodeConnectionFactory = new NodeConnectionFactory()
let provider

before(() => {
provider = nodeConnectionFactory.createProvider()
return eth.connect({ provider })
})

describe('.getTransactionsByAccount', function() {
it('should return the right amount of txs', async function() {
const account = eth.getAccount()

const txId = await eth.wallet.sendTransaction({ from: account })
await txUtils.getConfirmedTransaction(txId)
const txs = await eth.getTransactionsByAccount(account)
expect(txs.length).to.be.equal(1)

const txId2 = await eth.wallet.sendTransaction({ from: account })
await txUtils.getConfirmedTransaction(txId2)
const txs2 = await eth.getTransactionsByAccount(account)
expect(txs2.length).to.be.equal(2)
})
})
})
7 changes: 4 additions & 3 deletions specs/remoteProvider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,12 @@ describe('ETH using url provider (mainnet)', function() {
expect(invalidTx).to.be.equal(null)
})

it('should work and identify failed transactions', async function() {
it('should work and identify reverted transactions', async function() {
this.timeout(30000)
const failedTx = await txUtils.getTransaction(mainnetFailedTransaction)
/* tslint:disable-next-line:no-unnecessary-type-assertion */
const failedTx = (await txUtils.getTransaction(mainnetFailedTransaction)) as txUtils.RevertedTransaction

expect(txUtils.isFailure(failedTx)).to.be.equal(true)
expect(failedTx.type).to.be.equal('reverted')
})

it('should get the status of a transaction', async function() {
Expand Down
77 changes: 15 additions & 62 deletions specs/txUtils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { deployContract } from './deployContract'
import { eth, txUtils } from '../dist'

describe('txUtils tests', () => {
const DEFAULT_FETCH_DELAY = txUtils.TRANSACTION_FETCH_DELAY
const nodeConnectionFactory = new NodeConnectionFactory()
let provider

Expand All @@ -14,13 +13,17 @@ describe('txUtils tests', () => {
})

describe('.getTransaction', function() {
it('should return the transaction status and its receipt', async function() {
it('should return the confirmed transaction status and its receipt', async function() {
this.timeout(100000)

const contract = await deployContract(eth.wallet, 'MANA', require('./fixtures/MANAToken.json'))
const { receipt, ...tx } = await txUtils.getTransaction(contract.transactionHash)
/* tslint:disable-next-line:no-unnecessary-type-assertion */
const { receipt, ...tx } = (await txUtils.getTransaction(
contract.transactionHash
)) as txUtils.ConfirmedTransaction

expect(Object.keys(tx)).to.be.deep.equal([
'type',
'hash',
'nonce',
'blockHash',
Expand Down Expand Up @@ -50,68 +53,18 @@ describe('txUtils tests', () => {
expect(receipt.transactionHash).to.be.equal('0x505d58d5b6a38304deaad305ff2d773354cc939afc456562ba6bddbbf201e27f')
})

it('should return null if the tx hash is invalid or dropped', async () => {
const invalidTx = await txUtils.getTransaction(
'0xc15c7dda554711eac29d4a983e53aa161dd1bdc6e1d013bb29da1f607916de1'
)
expect(invalidTx).to.be.equal(null)

const droppedTx = await txUtils.getTransaction(
'0x24615f57f5754f2479d6657f7ac9a56006d8d6f634c6955310a5af1c79f4969'
)
expect(droppedTx).to.be.equal(null)
})
})

describe('.isTxDropped', function() {
it('should wait TRANSACTION_FETCH_DELAY for each retry attempts', async function() {
txUtils.TRANSACTION_FETCH_DELAY = 50
const retryAttemps = 5
const totalTime = 5 * 50

const begining = Date.now()
await txUtils.isTxDropped('0x24615f57f5754f2479d6657f7ac9a56006d8d6f634c6955310a5af1c79f4969', retryAttemps)
const end = Date.now()
const delay = end - begining

expect(delay).to.be.within(totalTime, totalTime + 100) // give it 100ms of leway
})

afterEach(() => {
txUtils.TRANSACTION_FETCH_DELAY = DEFAULT_FETCH_DELAY
it('should return null for an unknown transaction', async function() {
const tx = await txUtils.getTransaction('0xfaceb00c')
expect(tx).to.be.null // tslint:disable-line
})
})

describe('.waitForCompletion', function() {
it('should return a failed tx for a dropped hash', async function() {
txUtils.TRANSACTION_FETCH_DELAY = 10

const droppedTx = await txUtils.waitForCompletion(
'0x24615f57f5754f2479d6657f7ac9a56006d8d6f634c6955310a5af1c79f4969'
)

expect(droppedTx).to.be.deep.equal({
hash: '0x24615f57f5754f2479d6657f7ac9a56006d8d6f634c6955310a5af1c79f4969',
status: txUtils.TRANSACTION_STATUS.failed,
isDropped: true
})
})

it('should return the full transaction after it finishes', async function() {
txUtils.TRANSACTION_FETCH_DELAY = 10

// compiled solidity source code
const code =
'603d80600c6000396000f3007c01000000000000000000000000000000000000000000000000000000006000350463c6888fa18114602d57005b6007600435028060005260206000f3'
const txHash = await eth.wallet.sendTransaction({ data: code })
const tx = await txUtils.waitForCompletion(txHash)

expect(tx.hash).to.be.equal(txHash)
expect(tx.receipt).not.to.be.equal(undefined)
})

afterEach(() => {
txUtils.TRANSACTION_FETCH_DELAY = DEFAULT_FETCH_DELAY
describe('.getConfirmedTransaction', function() {
it('should return the confirmed transaction', async function() {
const account = eth.getAccount()
const txId = await eth.wallet.sendTransaction({ from: account })
const tx = await txUtils.getConfirmedTransaction(txId)
expect(tx.type).to.be.equal('confirmed')
})
})
})
58 changes: 56 additions & 2 deletions src/ethereum/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { NodeWallet } from './wallets'
import { ethUtils } from './ethUtils'
import { promisify } from '../utils/index'
import { Contract } from './Contract'
import { Wallet } from './wallets/Wallet'
import { Wallet, TransactionStatus } from './wallets/Wallet'

export type ConnectOptions = {
/** An array of objects defining contracts or Contract subclasses to use. Check {@link eth#setContracts} */
Expand Down Expand Up @@ -96,6 +96,11 @@ export namespace eth {
return getAccount()
}

export async function getCurrentNonce(): Promise<number> {
const address = getAddress()
return promisify(wallet.getWeb3().eth.getTransactionCount)(address)
}

export function getAccount() {
return wallet.getAccount()
}
Expand Down Expand Up @@ -150,7 +155,7 @@ export namespace eth {
return contracts[name]
}

export async function sign(payload): Promise<{message: string; signature: string}> {
export async function sign(payload): Promise<{ message: string; signature: string }> {
const message = ethUtils.toHex(payload)
const signature = await wallet.sign(message)
return { message, signature }
Expand Down Expand Up @@ -207,4 +212,53 @@ export namespace eth {
}
return network
}

/**
* Interface for the web3 `getBlockNumber` method.
* @return {number} - The number of the latest block
*/
export async function getBlockNumber(): Promise<number> {
return promisify(wallet.getWeb3().eth.getBlockNumber)()
}

/**
* Interface for the web3 `getBlock` method.
* @return {object} - An ehtereum block
*/
export async function getBlock(blockHashOrBlockNumber: string | number, returnTransactionObjects: boolean = false) {
return promisify(wallet.getWeb3().eth.getBlock)(blockHashOrBlockNumber, returnTransactionObjects)
}

/**
* A helper method to get all the transactions from/to a particular address or * (between start/end block numbers)
* @return {array} - An array of transactions
*/
export async function getTransactionsByAccount(
address: string,
startBlockNumber: number = null,
endBlockNumber: number = null
): Promise<TransactionStatus[]> {
if (endBlockNumber == null) {
endBlockNumber = await this.getBlockNumber()
}
if (startBlockNumber == null) {
startBlockNumber = endBlockNumber - 1000
}

startBlockNumber = startBlockNumber < 0 ? 0 : startBlockNumber
endBlockNumber = endBlockNumber < startBlockNumber ? startBlockNumber : endBlockNumber

const accountTransactions = []
for (let i = startBlockNumber; i <= endBlockNumber; i++) {
let block = await this.getBlock(i, true)
if (block != null && block.transactions != null) {
block.transactions.forEach(function(tx: TransactionStatus) {
if (address === '*' || address === tx.from || address === tx.to) {
accountTransactions.push(tx)
}
})
}
}
return accountTransactions
}
}
Loading

0 comments on commit b7f3a21

Please sign in to comment.