UNPKG

@gnosis.pm/pm-js

Version:

A javascript library for building applications on top of Gnosis, the Ethereum prediction market platform

322 lines (262 loc) 12.9 kB
import assert from 'assert' import ganache from 'ganache-cli' import apolloArtifacts from '@gnosis.pm/pm-apollo-contracts' import Gnosis from '../src/index' import { description, options, requireRejection, multiWeb3It, } from './test_utils' const { requireEventFromTXResult } = Gnosis describe('Gnosis', function () { this.timeout(120000) it('exists', () => { assert(Gnosis) }) it('initializes with defaults', async () => { let gnosis = await Gnosis.create() assert(gnosis) }) it('initializes with options', async () => { let gnosis = await Gnosis.create({ ethereum: 'http://localhost:8545', ipfs: '', gnosisdb: 'https:/db.gnosis.pm', defaultAccount: '0x61315aec47febe497554a2a0f3a3e7ef287d052f', }) assert(gnosis) assert.equal(gnosis.defaultAccount, '0x61315aec47febe497554a2a0f3a3e7ef287d052f') }) it('initializes with a provider', async () => { let gnosis = await Gnosis.create({ ethereum: ganache.provider(), }) assert(gnosis) }) it('initializes with a provider that has a mangled name', async () => { class Mangled extends ganache.provider().constructor {} const provider = new Mangled() assert(provider.constructor.name === 'Mangled') let gnosis = await Gnosis.create({ ethereum: provider, }) assert(gnosis) }) multiWeb3It('initializes with a Web3 HTTP provider', async (Web3) => { const gnosis = await Gnosis.create({ ethereum: new Web3.providers.HttpProvider('http://localhost:8545'), }) assert(gnosis) }) it('initializes with contracts containing gas stats', async () => { let gnosis = await Gnosis.create() assert(gnosis.contracts) assert(gnosis.contracts.CentralizedOracle.gasStats) assert(Object.keys(gnosis.contracts.CentralizedOracle.gasStats) .every((k) => gnosis.contracts.CentralizedOracle.abi .find(({ type, name }) => type === 'function' && name === k))) assert(gnosis.standardMarketFactory.gasStats) }) it('initializes with contract aliases for ERC20 and WETH9', async () => { let gnosis = await Gnosis.create() assert.strictEqual(gnosis.contracts.ERC20, gnosis.contracts.Token) assert.strictEqual(gnosis.contracts.WETH9, gnosis.contracts.EtherToken) }) it('initializes on the mainnet with an etherToken instance that points to the maker WETH', async () => { let gnosis = await Gnosis.create({ ethereum: ganache.provider({ network_id: 1 }), }) assert(gnosis.etherToken) assert.equal(gnosis.etherToken.address, '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2') }) it('can import custom deployed smart contract sets with config data', async () => { let gnosis = await Gnosis.create(options) await gnosis.importContracts(apolloArtifacts, { OlympiaToken: 'olympiaToken', AddressRegistry: 'olympiaAddressRegistry', }) assert.notEqual(gnosis.contracts.PlayToken, null) assert.equal(gnosis.olympiaToken, undefined) assert.equal(gnosis.olympiaAddressRegistry, undefined) await gnosis.setWeb3Provider(ganache.provider({ network_id: 4 })) assert.equal(gnosis.olympiaToken.address, '0x979861df79c7408553aaf20c01cfb3f81ccf9341') assert.equal(gnosis.olympiaAddressRegistry.address, '0x6427d856450b20f6fab88be18d949faf9c4da512') // TODO: Truffle contracts cache the address last used to represent deployed contracts // even after the provider has changed to provide for another network. // When this behavior is no longer the case, the next bit can be uncommented and should pass. // await gnosis.setWeb3Provider(ganache.provider()) // assert.equal(gnosis.olympiaToken, undefined) // assert.equal(gnosis.olympiaAddressRegistry, undefined) }) it('reports more informative error messages and logs messages', async () => { const logs = [] const gnosis = await Gnosis.create({ logger: (s) => { logs.push(s) }, }) const netOutcomeTokensSold = [0, 0] const feeFactor = 5000 // 0.5% const participants = gnosis.web3.eth.accounts.slice(0, 4) const ipfsHash = await gnosis.publishEventDescription(description) assert.equal(logs.length, 1) assert(/\b\w{46}\b/.test(logs[0]), 'no IPFS hash found in log message ' + logs[0]) const oracle = await gnosis.createCentralizedOracle(ipfsHash) assert.equal(logs.length, 3) assert(/\b0x[a-f0-9]{64}\b/i.test(logs[1]), 'no transaction hash found in log message ' + logs[1]) assert(logs[2].indexOf(oracle.address) !== -1, 'oracle address not found in log message ' + logs[2]) let errorString = (await requireRejection(gnosis.createCategoricalEvent({ collateralToken: gnosis.etherToken, oracle, outcomeCount: 1, // < wrong outcomeCount }))).toString() assert( errorString.indexOf('EventFactory') !== -1 && errorString.indexOf('createCategoricalEvent') !== -1 && errorString.indexOf(gnosis.etherToken.address) !== -1 && errorString.indexOf(oracle.address) !== -1 && /\b1\b/.test(errorString), 'could not find call info in error message' ) // ^ depending on whether we're running geth or ganache, the above might have generated 0 or 1 logs assert(logs.length === 3 || logs.length === 4) logs.length = 3 const event = await gnosis.createCategoricalEvent({ collateralToken: gnosis.etherToken, oracle: oracle, outcomeCount: netOutcomeTokensSold.length }) assert.equal(logs.length, 5) assert(/\b0x[a-f0-9]{64}\b/i.test(logs[3]), 'no transaction hash found in log message ' + logs[3]) assert(logs[4].indexOf(event.address) !== -1, 'event address not found in log message ' + logs[4]) const market = await gnosis.createMarket({ event: event, marketMaker: gnosis.lmsrMarketMaker, fee: feeFactor, // 0% }) assert.equal(logs.length, 7) assert(/\b0x[a-f0-9]{64}\b/i.test(logs[5]), 'no transaction hash found in log message ' + logs[5]) assert(logs[6].indexOf(market.address) !== -1, 'market address not found in log message ' + logs[6]) requireEventFromTXResult(await gnosis.etherToken.deposit({ value: 8e18 }), 'Deposit') const funding = 1e18 requireEventFromTXResult(await gnosis.etherToken.approve(market.address, funding), 'Approval') requireEventFromTXResult(await market.fund(funding), 'MarketFunding') errorString = (await requireRejection(gnosis.buyOutcomeTokens({ market, outcomeTokenIndex: 0, outcomeTokenCount: 1e18, cost: 1 }))).toString() assert( errorString.indexOf('Market') !== -1 && errorString.indexOf('buy') !== -1 && /\b0\b/.test(errorString), `could not find call info in error message ${errorString}` ) // ^ depending on whether we're running geth or ganache, the above might have generated 1 to 3 logs // the approve should go through, but a transaction hash may or may not be generated for the buy // also there is a race condition on the all promise which may or may not let a log through for the approve assert(logs.length >= 8 || logs.length <= 10) logs.length = 7 await gnosis.buyOutcomeTokens({ market, outcomeTokenIndex: 0, outcomeTokenCount: 1e18 }) assert.equal(logs.length, 11) for(let i = 7; i < 11; ++i) assert(/\b0x[a-f0-9]{64}\b/i.test(logs[i]), 'no transaction hash found in log message ' + logs[i]) // same deal for selling errorString = (await requireRejection(gnosis.sellOutcomeTokens({ market, outcomeTokenIndex: 0, outcomeTokenCount: 1, minProfit: gnosis.web3.toBigNumber(2).pow(256).sub(1) }))).toString() assert( errorString.indexOf('Market') !== -1 && errorString.indexOf('sell') !== -1 && /\b0\b/.test(errorString), `could not find call info in error message ${errorString}` ) // ^ depending on whether we're running geth or ganache, the above might have generated 1 to 3 logs assert(logs.length >= 12 && logs.length <= 14) logs.length = 11 await gnosis.sellOutcomeTokens({ market, outcomeTokenIndex: 0, outcomeTokenCount: 1e18 }) assert.equal(logs.length, 15) for(let i = 11; i < 15; ++i) assert(/\b0x[a-f0-9]{64}\b/i.test(logs[i]), 'no transaction hash found in log message ' + logs[i]) }) it('supports custom options to be passed to provider', async () => { let gnosis = await Gnosis.create(options) const txParamObjects = [] const _sendAsync = gnosis.web3.currentProvider.sendAsync gnosis.web3.currentProvider.sendAsync = function() { const rpcMessage = arguments[0] if(rpcMessage.method === 'eth_sendTransaction') { txParamObjects.push(rpcMessage.params[0]) } return _sendAsync.apply(this, arguments) } let ipfsHash = await gnosis.publishEventDescription(description) let centralizedOracleFactory = await gnosis.contracts.CentralizedOracleFactory.deployed() let oracle = await gnosis.createCentralizedOracle(ipfsHash) let event = await gnosis.createCategoricalEvent({ collateralToken: gnosis.etherToken, oracle: oracle, outcomeCount: 2, }) let market = await gnosis.createMarket({ event, marketMaker: gnosis.lmsrMarketMaker, marketFactory: gnosis.standardMarketFactory, fee: 5000, }) assert.equal(txParamObjects[txParamObjects.length - 1].event, undefined) assert.equal(txParamObjects[txParamObjects.length - 1].marketFactory, undefined) requireEventFromTXResult(await gnosis.etherToken.deposit({ value: 2e18 }), 'Deposit') requireEventFromTXResult(await gnosis.etherToken.approve(market.address, 1e18), 'Approval') requireEventFromTXResult(await market.fund(1e18), 'MarketFunding') const numOutcomeTokens = await gnosis.buyOutcomeTokens({ market, outcomeTokenIndex: 0, outcomeTokenCount: 1e18, custom: 'foo', customApproveOverloaded: 'bar', customBuyOverloaded: 'baz', approveTxOpts: { customApproveOverloaded: 'overbarred' }, buyTxOpts: { customBuyOverloaded: 'overbazzed' }, }) let approveTxParamObj = txParamObjects[txParamObjects.length - 2] const buyTxParamObj = txParamObjects[txParamObjects.length - 1] assert.equal(approveTxParamObj.custom, 'foo') assert.equal(approveTxParamObj.approveTxOpts, undefined) assert.equal(approveTxParamObj.buyTxOpts, undefined) assert.equal(approveTxParamObj.customApproveOverloaded, 'overbarred') assert.equal(buyTxParamObj.custom, 'foo') assert.equal(buyTxParamObj.approveTxOpts, undefined) assert.equal(buyTxParamObj.buyTxOpts, undefined) assert.equal(buyTxParamObj.customBuyOverloaded, 'overbazzed') await gnosis.sellOutcomeTokens({ market, outcomeTokenIndex: 0, outcomeTokenCount: numOutcomeTokens, custom2: 'foo', custom2ApproveOverloaded: 'bar', custom2BuyOverloaded: 'baz', approveTxOpts: { custom2ApproveOverloaded: 'overbarred' }, sellTxOpts: { custom2BuyOverloaded: 'overbazzed' }, }) approveTxParamObj = txParamObjects[txParamObjects.length - 2] const sellTxParamObj = txParamObjects[txParamObjects.length - 1] assert.equal(approveTxParamObj.custom2, 'foo') assert.equal(approveTxParamObj.approveTxOpts, undefined) assert.equal(approveTxParamObj.sellTxOpts, undefined) assert.equal(approveTxParamObj.custom2ApproveOverloaded, 'overbarred') assert.equal(sellTxParamObj.custom2, 'foo') assert.equal(sellTxParamObj.approveTxOpts, undefined) assert.equal(sellTxParamObj.sellTxOpts, undefined) assert.equal(sellTxParamObj.custom2BuyOverloaded, 'overbazzed') }) })