bnc-assist
Advanced tools
Comparing version 0.7.3 to 0.7.4
{ | ||
"name": "bnc-assist", | ||
"version": "0.7.3", | ||
"version": "0.7.4", | ||
"description": "Blocknative Assist js library for Dapp developers", | ||
@@ -5,0 +5,0 @@ "main": "lib/assist.min.js", |
@@ -46,3 +46,3 @@ # Assist.js | ||
The library uses [semantic versioning](https://semver.org/spec/v2.0.0.html). | ||
The current version is 0.7.3. | ||
The current version is 0.7.4. | ||
There are minified and non-minified versions. | ||
@@ -52,7 +52,7 @@ Put this script at the top of your `<head>` | ||
```html | ||
<script src="https://assist.blocknative.com/0-7-3/assist.js"></script> | ||
<script src="https://assist.blocknative.com/0-7-4/assist.js"></script> | ||
<!-- OR... --> | ||
<script src="https://assist.blocknative.com/0-7-3/assist.min.js"></script> | ||
<script src="https://assist.blocknative.com/0-7-4/assist.min.js"></script> | ||
``` | ||
@@ -135,2 +135,14 @@ | ||
### `promiEvent` | ||
If using web3 versions 1.0 and you would like to listen for the events triggered on the `promiEvent` that is normally returned from a transaction call, Assist returns the `promiEvent`, but it is wrapped in an object to prevent it from resolving internally in Assist. | ||
```javascript | ||
const { promiEvent } = await decoratedContract.myMethod(param).send(txOptions) | ||
promiEvent.on('receipt', () => { | ||
// ... | ||
}) | ||
``` | ||
## API Reference | ||
@@ -137,0 +149,0 @@ |
@@ -0,1 +1,2 @@ | ||
import truffleContract from 'truffle-contract' | ||
import { Server } from 'mock-socket' | ||
@@ -5,12 +6,30 @@ import abi from '~/__tests__/res/dstoken.json' | ||
import * as web3Helpers from '~/js/helpers/web3' | ||
import { initialState, updateState } from '~/js/helpers/state' | ||
import { state, initialState, updateState } from '~/js/helpers/state' | ||
import convertLibJson from '~/__tests__/res/ConvertLib.json' | ||
import { convertLibAddress, port } from '../../../../internals/ganacheConfig' | ||
const multidepRequire = require('multidep')('multidep.json') | ||
// truffle contracts require an old web3 | ||
const Web3v0p20 = multidepRequire('web3', '0.20.6') | ||
const someAddress = '0x0000000000000000000000000000000000000000' | ||
const getTruffleContract = async () => { | ||
const contractDef = truffleContract(convertLibJson) | ||
contractDef.setProvider( | ||
new Web3v0p20.providers.HttpProvider(`http://localhost:${port}`) | ||
) | ||
return contractDef.at(convertLibAddress) | ||
} | ||
const getWeb3Contract = async web3 => | ||
web3.eth.contract | ||
? web3.eth.contract(abi).at(someAddress) // 0.20 | ||
: new web3.eth.Contract(abi, someAddress) // 1.0.0-beta | ||
// multidep docs: https://github.com/joliss/node-multidep | ||
multidepRequire.forEachVersion('web3', (version, Web3) => { | ||
describe(`using web3 ${version}`, () => { | ||
describe('Contract function is called', () => { | ||
describe('Contract is called', () => { | ||
let assistInstance | ||
@@ -25,5 +44,2 @@ let web3 | ||
web3 = new Web3(fakeURL) | ||
contract = web3.eth.contract | ||
? web3.eth.contract(abi).at(someAddress) | ||
: new web3.eth.Contract(abi, someAddress) | ||
assistInstance = da.init(config) | ||
@@ -34,81 +50,108 @@ }) | ||
}) | ||
test(`it doesn't fail and returns the expected decorated contract`, () => { | ||
const assistInstance = da.init({ dappId: '123', web3, networkId: '1' }) | ||
const decoratedContract = assistInstance.Contract(contract) | ||
const contracts = [ | ||
['truffle', getTruffleContract], | ||
['web3', getWeb3Contract] | ||
] | ||
contracts.forEach(([name, getContract]) => { | ||
describe(`with a ${name} contract`, () => { | ||
beforeEach(async () => { | ||
if (name === 'truffle') state.config.truffleContract = true | ||
contract = await getContract(web3) | ||
}) | ||
afterEach(() => { | ||
if (name === 'truffle') state.config.truffleContract = false | ||
}) | ||
test(`it doesn't fail and returns the expected decorated contract`, () => { | ||
const assistInstance = da.init({ | ||
dappId: '123', | ||
web3, | ||
networkId: '1' | ||
}) | ||
const decoratedContract = assistInstance.Contract(contract) | ||
if (decoratedContract.methods) { | ||
// test web3js v1.0.0-beta.X | ||
expect({ | ||
givenProvider: decoratedContract.givenProvider, | ||
BatchRequest: decoratedContract.BatchRequest, | ||
options: decoratedContract.options, | ||
methods: decoratedContract.methods, | ||
abiModel: decoratedContract.abiModel, | ||
_jsonInterface: decoratedContract._jsonInterface, | ||
events: { | ||
...decoratedContract.events, | ||
contract: null | ||
if (decoratedContract.methods) { | ||
// test web3js v1.0.0-beta.X | ||
expect({ | ||
givenProvider: decoratedContract.givenProvider, | ||
BatchRequest: decoratedContract.BatchRequest, | ||
options: decoratedContract.options, | ||
methods: decoratedContract.methods, | ||
abiModel: decoratedContract.abiModel, | ||
_jsonInterface: decoratedContract._jsonInterface, | ||
events: { | ||
...decoratedContract.events, | ||
contract: null | ||
} | ||
}).toMatchSnapshot() | ||
} else { | ||
// test web3js v0.20.X | ||
expect({ | ||
...decoratedContract, | ||
_eth: null | ||
}).toMatchSnapshot() | ||
} | ||
}).toMatchSnapshot() | ||
} else { | ||
// test web3js v0.20.X | ||
expect({ | ||
...decoratedContract, | ||
_eth: null | ||
}).toMatchSnapshot() | ||
} | ||
}) | ||
describe(`when user doesn't have a validApiKey`, () => { | ||
beforeEach(() => { | ||
updateState({ validApiKey: false }) | ||
}) | ||
test('should throw the expected error', () => { | ||
expect(() => { | ||
assistInstance.Contract(abi, someAddress) | ||
}).toThrowError('Your API key is not valid') | ||
}) | ||
}) | ||
describe(`when state.supportedNetwork is falsy`, () => { | ||
beforeEach(() => { | ||
updateState({ supportedNetwork: null }) | ||
}) | ||
test('should throw the expected error', () => { | ||
expect(() => { | ||
assistInstance.Contract(abi, someAddress) | ||
}).toThrowError('This network is not supported') | ||
}) | ||
}) | ||
describe('when user is on mobile and mobile is not blocked', () => { | ||
beforeEach(() => { | ||
updateState({ mobileDevice: true, config: { mobileBlocked: false } }) | ||
}) | ||
test('it should return the contract unmodified', () => { | ||
const decoratedContract = assistInstance.Contract(contract) | ||
expect(decoratedContract).toEqual(contract) | ||
}) | ||
}) | ||
describe('when state.web3Instance is falsy', () => { | ||
beforeEach(() => { | ||
updateState({ web3Instance: undefined }) | ||
}) | ||
describe('and window.web3 exists', () => { | ||
beforeEach(() => { | ||
window.web3 = web3 | ||
}) | ||
afterEach(() => { | ||
delete window.web3 | ||
describe(`when user doesn't have a validApiKey`, () => { | ||
beforeEach(() => { | ||
updateState({ validApiKey: false }) | ||
}) | ||
test('should throw the expected error', () => { | ||
expect(() => { | ||
assistInstance.Contract(abi, someAddress) | ||
}).toThrowError('Your API key is not valid') | ||
}) | ||
}) | ||
test('configureWeb3 should be called', () => { | ||
const configureWeb3Mock = jest.spyOn(web3Helpers, 'configureWeb3') | ||
assistInstance.Contract(contract) | ||
expect(configureWeb3Mock).toHaveBeenCalledTimes(1) | ||
configureWeb3Mock.mockRestore() | ||
describe(`when state.supportedNetwork is falsy`, () => { | ||
beforeEach(() => { | ||
updateState({ supportedNetwork: null }) | ||
}) | ||
test('should throw the expected error', () => { | ||
expect(() => { | ||
assistInstance.Contract(abi, someAddress) | ||
}).toThrowError('This network is not supported') | ||
}) | ||
}) | ||
}) | ||
describe('and window.web3 is falsy', () => { | ||
test('it should throw the expected error', () => { | ||
expect(() => { | ||
assistInstance.Contract(contract) | ||
}).toThrowError('A web3 instance is needed to decorate contract') | ||
describe('when user is on mobile and mobile is not blocked', () => { | ||
beforeEach(() => { | ||
updateState({ | ||
mobileDevice: true, | ||
config: { mobileBlocked: false } | ||
}) | ||
}) | ||
test('it should return the contract unmodified', () => { | ||
const decoratedContract = assistInstance.Contract(contract) | ||
expect(decoratedContract).toEqual(contract) | ||
}) | ||
}) | ||
describe('when state.web3Instance is falsy', () => { | ||
beforeEach(() => { | ||
updateState({ web3Instance: undefined }) | ||
}) | ||
describe('and window.web3 exists', () => { | ||
beforeEach(() => { | ||
window.web3 = web3 | ||
}) | ||
afterEach(() => { | ||
delete window.web3 | ||
}) | ||
test('configureWeb3 should be called', () => { | ||
const configureWeb3Mock = jest.spyOn( | ||
web3Helpers, | ||
'configureWeb3' | ||
) | ||
assistInstance.Contract(contract) | ||
expect(configureWeb3Mock).toHaveBeenCalledTimes(1) | ||
configureWeb3Mock.mockRestore() | ||
}) | ||
}) | ||
describe('and window.web3 is falsy', () => { | ||
test('it should throw the expected error', () => { | ||
expect(() => { | ||
assistInstance.Contract(contract) | ||
}).toThrowError( | ||
'A web3 instance is needed to decorate contract' | ||
) | ||
}) | ||
}) | ||
}) | ||
}) | ||
@@ -115,0 +158,0 @@ }) |
@@ -34,19 +34,25 @@ import { state, updateState } from './state' | ||
export function isDuplicateTransaction({ value, to }, contract = {}) { | ||
export function isDuplicateTransaction({ value, to }, contract) { | ||
const { transactionQueue } = state | ||
let duplicate = transactionQueue.find(txObj => { | ||
const sameMethod = contract | ||
? contract.methodName === txObj.contract.methodName | ||
: true | ||
const sameParams = contract | ||
? argsEqual(contract.parameters, txObj.contract.parameters) | ||
: true | ||
return Boolean( | ||
transactionQueue.find(txObj => { | ||
const { methodName, parameters } = contract | ||
return ( | ||
sameMethod && | ||
sameParams && | ||
txObj.transaction.value === value && | ||
txObj.transaction.to === to | ||
) | ||
}) | ||
if ( | ||
methodName === (txObj.contract && txObj.contract.methodName) && | ||
argsEqual(parameters, txObj.contract && txObj.contract.parameters) | ||
) { | ||
return txObj.transaction.value === value && txObj.transaction.to === to | ||
} | ||
if (duplicate && duplicate.transaction.status === 'confirmed') { | ||
duplicate = false | ||
} | ||
return false | ||
}) | ||
) | ||
return Boolean(duplicate) | ||
} | ||
@@ -53,0 +59,0 @@ |
@@ -122,2 +122,5 @@ import { state, updateState } from './state' | ||
case 'confirmed': | ||
// handle race condition: https://github.com/blocknative/assist/issues/174 | ||
if (eventCode === 'txConfirmedClient') return | ||
txObj = getTxObjFromQueue(transaction.id) | ||
@@ -124,0 +127,0 @@ |
@@ -88,5 +88,10 @@ import { promisify } from 'bluebird' | ||
export function modernSend(method, name, args) { | ||
const innerMethod = method(...args).send | ||
const returnObject = {} | ||
const originalReturnObject = method(...args) | ||
const innerMethod = originalReturnObject.send | ||
const returnObject = Object.keys(originalReturnObject).reduce((obj, key) => { | ||
obj[key] = originalReturnObject[key] | ||
return obj | ||
}, {}) | ||
returnObject.send = (...innerArgs) => { | ||
@@ -93,0 +98,0 @@ const { callback, txObject, inlineCustomMsgs } = separateArgs(innerArgs, 0) |
@@ -218,16 +218,17 @@ import { state } from '~/js/helpers/state' | ||
} else { | ||
txPromise | ||
.on('transactionHash', async hash => { | ||
onTxHash(transactionId, hash, categoryCode) | ||
resolve(hash) | ||
callback && callback(null, hash) | ||
}) | ||
.on('receipt', async () => { | ||
onTxReceipt(transactionId, categoryCode) | ||
}) | ||
.on('error', async errorObj => { | ||
onTxError(transactionId, errorObj, categoryCode) | ||
handleError({ resolve, reject, callback })(errorObj) | ||
}) | ||
new Promise(confirmed => { | ||
// resolve the promiEvent so that "on" methods can be used by dev | ||
resolve({ txPromise }) | ||
txPromise | ||
.on('transactionHash', async hash => { | ||
onTxHash(transactionId, hash, categoryCode) | ||
callback && callback(null, hash) | ||
}) | ||
.on('receipt', confirmed) | ||
.once('confirmation', confirmed) | ||
.on('error', async errorObj => { | ||
onTxError(transactionId, errorObj, categoryCode) | ||
handleError({ resolve, reject, callback })(errorObj) | ||
}) | ||
}).then(() => onTxReceipt(transactionId, categoryCode)) | ||
} | ||
@@ -261,7 +262,11 @@ }) | ||
const txObj = getTxObjFromQueue(id) | ||
if (!txObj) return | ||
const { | ||
transaction: { status } | ||
} = txObj | ||
if ( | ||
txObj && | ||
txObj.transaction.status !== 'pending' && | ||
state.socketConnection | ||
state.socketConnection && | ||
(status === 'approved' || status === 'pending') | ||
) { | ||
@@ -268,0 +273,0 @@ updateTransactionInQueue(id, { status: 'stalled' }) |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
2294089
10310
535