diff --git a/examples/categoricalMarketCreation.js b/examples/categoricalMarketCreation.js deleted file mode 100644 index ad96e23..0000000 --- a/examples/categoricalMarketCreation.js +++ /dev/null @@ -1,138 +0,0 @@ -/****************************************************************** -* This scripts aims to demonstrate how to create a categorical prediction market -* using GnosisJS. -* Prerequisites: -* - Have a testnet (TestRPC, Ganache) up and running on localhost port 8545 (testrpc -d -i 437894314312) -* - Have the Gnosis contracts migrated on your local testnet -* cd gnosis.js/node_modules/@gnosis.pm/gnosis-core-contracts/ -* npm install -* npm run migrate -* - Take a look at the config.json file -* -/*****************************************************************/ - -let Gnosis; -try { - Gnosis = require('../'); -} catch (err) { - console.error(err) - process.exit(); -} - -const Web3 = require('web3'); -const HDWalletProvider = require("truffle-hdwallet-provider"); -let config -if (process.argv.length == 2){ - config = require('./config.json'); -} -else{ - config = require(process.argv[2]) -} -const provider_url = config.blockchain.protocol + "://" + config.blockchain.host + ":" + config.blockchain.port; -const hd_provider = new HDWalletProvider(config.mnemonic, provider_url); -const etherTokenAddress = config.etherTokenAddress; -const gasPrice = config.gasPrice; - -const options = { - ethereum: new Web3(hd_provider).currentProvider, - ipfs: config.ipfs -}; - -// Market dictionary definition -const marketJSON = { - 'title': 'My first categorical prediction market', - 'description': 'This market was created by using GnosisJS library', - 'outcomes': ['Yes', 'No'], - 'resolutionDate': new Date().toISOString(), - 'fee': 0, - 'funding': 1e18 // 1 Ether token -}; - -let gnosisInstance; -let ipfsHash; -let oracle; -let market; - -return Gnosis.create(options) -.then(result => { - gnosisInstance = result; - console.info('[GnosisJS] > Connection established'); - console.info("[GnosisJS] > Creation started..."); -}).then(() => { - return new Promise((resolve, reject) => { - let eventDescription = { - title: marketJSON.title, - description: marketJSON.description, - resolutionDate: marketJSON.resolutionDate, - }; - // Publish the event description to IPFS - return gnosisInstance.publishEventDescription(eventDescription).then(result => { - ipfsHash = result; - console.info("[GnosisJS] > Event description hash: " + ipfsHash); - console.info("[GnosisJS] > Creating Centralized Oracle..."); - return gnosisInstance.createCentralizedOracle(ipfsHash, { gasPrice }); - }).then(result => { - oracle = result; - console.info("[GnosisJS] > Centralized Oracle was created"); - console.info("[GnosisJS] > Creating categorical Event..."); - // Create a centralized oracle - return gnosisInstance.createCategoricalEvent({ - collateralToken: etherTokenAddress, - oracle, - // Note the outcomeCount must match the length of the outcomes array published on IPFS - outcomeCount: marketJSON.outcomes.length, - gasPrice - }); - }).then(result => { - event = result; - console.info("[GnosisJS] > Categorical event was created"); - console.info("[GnosisJS] > Creating market..."); - // Create the market - return gnosisInstance.createMarket({ - event: event, - marketMaker: gnosisInstance.lmsrMarketMaker, - marketFactory: gnosisInstance.standardMarketFactory, - fee: marketJSON.fee, - gasPrice - }) - }).then(response => { - market = response; // market instance - console.info("[GnosisJS] > Market was created, address " + market.address); - console.info("[GnosisJS] > Funding market..."); - const etherToken = gnosisInstance.contracts.EtherToken.at(etherTokenAddress); - return new Promise((resolve, reject) => { - // Approve tokens transferral - etherToken.approve(market.address, marketJSON.funding).then(result => { - console.info('[GnosisJS] > Market tokens were approved'); - console.info('[GnosisJS] > Sending funds: ' + marketJSON.funding + '...'); - // Provide funds to the market - market.fund(marketJSON.funding).then(result => { - console.info("[GnosisJS] > Market creation done successfully"); - resolve(); - }) - .catch(error => { - console.warn('[GnosisJS] > Error while sending tokens'); - console.warn(error); - reject(error); - }); - }) - .catch(error => { - console.warn('[GnosisJS] > Error while approving tokens'); - console.warn(error); - reject(error); - }); - }); - }).then(values => { - console.info("[GnosisJS] > All done!"); - resolve(market.address); - }) - .catch(error => { - console.warn(error); - reject(error); - }); - }) -}) -.catch(error => { - console.warn('[GnosisJS] > ERROR'); - console.warn(error); -}); diff --git a/examples/config.json b/examples/config.json deleted file mode 100644 index b309a3f..0000000 --- a/examples/config.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "accountAddress": "", - "etherTokenAddress": "0x59d3631c86bbe35ef041872d502f218a39fba150", - "mnemonic": "", - "gasPrice": "4000000001", - "blockchain": { - "host": "rinkeby.infura.io", - "protocol": "https", - "port": 443 - }, - "gnosisdb": { - "protocol": "https", - "host": "gnosisdb.staging.gnosisdev.com", - "port": "443" - }, - "ipfs": { - "host": "ipfs.infura.io", - "port": 5001, - "protocol": "https" - } -} diff --git a/examples/etherTokenIssuance.js b/examples/etherTokenIssuance.js deleted file mode 100644 index 3cde614..0000000 --- a/examples/etherTokenIssuance.js +++ /dev/null @@ -1,63 +0,0 @@ -/****************************************************************** -* This scripts aims to demonstrate how to issue Ether Tokens -* using GnosisJS. -* Prerequisites: -* - Have a testnet (TestRPC, Ganache) up and running on localhost port 8545 (testrpc -d -i 437894314312) -* - Have the Gnosis contracts migrated on your local testnet: -* cd gnosis.js/node_modules/@gnosis.pm/gnosis-core-contracts/ -* npm install -* npm run migrate -* - Take a look at the config.json file -* -/*****************************************************************/ - -let Gnosis; -try { - Gnosis = require('../'); -} catch (err) { - console.error(err) - process.exit(); -} - -const Web3 = require('web3'); -const HDWalletProvider = require("truffle-hdwallet-provider"); -let config; -if (process.argv.length == 2){ - config = require('./config.json'); -} -else{ - config = require(process.argv[2]) -} -const etherTokenAddress = config.etherTokenAddress; -const provider_url = config.blockchain.protocol + "://" + config.blockchain.host + ":" + config.blockchain.port; -const hd_provider = new HDWalletProvider(config.mnemonic, provider_url); -const web3Instance = new Web3(hd_provider); -const tokensAmount = 4e18; // Amount of tokens to issue/deposit - -const options = { - ethereum: web3Instance.currentProvider, - ipfs: config.ipfs -}; - -return Gnosis.create(options) -.then(result => { - gnosisInstance = result; - console.info('[GnosisJS] > connection established'); - console.info("[GnosisJS] > Issuance started..."); - etherTokenContract = gnosisInstance.contracts.EtherToken.at(etherTokenAddress); - return new Promise((resolve, reject) => { - etherTokenContract.deposit({ value: tokensAmount }).then(result => { - console.info('[GnosisJS] > Issuance ended successfully.'); - resolve(); - }) - .catch(error => { - console.warn('[GnosisJS] > Error while sending tokens'); - console.warn(error); - reject(error); - }); - }); -}) -.catch(error => { - console.warn('[GnosisJS] > ERROR'); - console.warn(error); -}); diff --git a/examples/marketResolution.js b/examples/marketResolution.js deleted file mode 100644 index 6df4c7b..0000000 --- a/examples/marketResolution.js +++ /dev/null @@ -1,105 +0,0 @@ -/****************************************************************** -* This scripts aims to demonstrate how to resolve an existing -* prediction market using GnosisJS. -* Prerequisites: -* - Have a testnet (TestRPC, Ganache) up and running on localhost port 8545 (testrpc -d -i 437894314312) -* - Have the Gnosis contract migrated on your local testnet -* cd gnosis.js/node_modules/@gnosis.pm/gnosis-core-contracts/ -* npm install -* npm run migrate -* - Have created a prediction market using either categorical_market_creation.js -* or scalar_market_creation.js -* - Take a look at the config.json file -* -/*****************************************************************/ - -let Gnosis; -try { - Gnosis = require('../'); -} catch (err) { - console.error(err) - process.exit(); -} - -const Web3 = require('web3'); -const HDWalletProvider = require("truffle-hdwallet-provider"); -let config; -if (process.argv.length == 2){ - config = require('./config.json'); -} -else{ - config = require(process.argv[2]) -} -const provider_url = config.blockchain.protocol + "://" + config.blockchain.host + ":" + config.blockchain.port; -const hd_provider = new HDWalletProvider(config.mnemonic, provider_url); -const web3Instance = new Web3(hd_provider); - -const options = { - ethereum: web3Instance.currentProvider, - ipfs: config.ipfs -}; - -// Market properties -const marketAddress = "0x487ce41d066352bbc34916ac90664aa416bf6e53"; -const eventOutcome = "1"; - -// Market stages are defined in market contract -const marketStages = { - created: 0, - funded: 1, - closed: 2 -} - -let gnosisInstance; -let eventAddress; -let marketInstance; -let executionStep; - -return Gnosis.create(options) -.then(result => { - gnosisInstance = result; - console.info('[GnosisJS] > connection established'); - console.info("[GnosisJS] > Market resolution for address " + marketAddress + " started..."); -}) -.then(() => { - marketInstance = gnosisInstance.contracts.StandardMarket.at(marketAddress); - return marketInstance.stage().then((stage) => { - // Market has to have fund and not be closed! - switch (stage.toNumber()) { - case marketStages.created: - return Promise.reject('Market cannot be resolved. It must be in funded stage (current is CREATED stage)'); - case marketStages.closed: - return Promise.reject('Market cannot be resolved. It must be in funded stage (current is CLOSED stage)'); - default: - return Promise.resolve(); - } - }); -}) -.then(() => { - return marketInstance.close().then(() => { - console.info('[GnosisJS] > Market resolved'); - return marketInstance.stage().then((stage) => { - if (stage.toNumber() !== marketStages.closed) { - return Promise.reject("[GnosisJS] > OOPS, Market hasn't passed to closed stage. Check it manually.") - } - }); - }) -}) -.then(() => { - console.info('[GnosisJS] > Obtaining event address...'); - return marketInstance.eventContract().then((address) => { - eventAddress = address - }); -}) -.then(() => { - return gnosisInstance.resolveEvent({event: eventAddress, outcome: eventOutcome}).then(() => { - console.info('[GnosisJS] > Event resolved'); - }); -}) -.then(() => { - console.log('[GnosisJS] > Market resolved successfully.'); -}) -.catch(error => { - console.warn('[GnosisJS] > ERROR'); - console.warn(error); -}); diff --git a/examples/marketSellShares.js b/examples/marketSellShares.js deleted file mode 100644 index 1a0e2b1..0000000 --- a/examples/marketSellShares.js +++ /dev/null @@ -1,73 +0,0 @@ -/****************************************************************** -* This scripts is to demonstrate how to sell shares on a prediction market -* using GnosisJS. -* Prerequisites: -* - Have a testnet (TestRPC, Ganache) up and running on localhost port 8545 (testrpc -d -i 437894314312) -* - Have the Gnosis contracts migrated on your local testnet -* cd gnosis.js/node_modules/@gnosis.pm/gnosis-core-contracts/ -* npm install -* npm run migrate -* - Have created a prediction market using either categorical_market_creation.js -* or scalar_market_creation.js -* - Take a look at the config.json file -* -/*****************************************************************/ - -let Gnosis; -try { - Gnosis = require('../'); -} catch (err) { - console.error(err) - process.exit(); -} - -const Web3 = require('web3'); -const HDWalletProvider = require("truffle-hdwallet-provider"); -let config; -if (process.argv.length == 2){ - config = require('./config.json'); -} -else{ - config = require(process.argv[2]) -} -// CHANGE THE MNEMONIC WITH THE ONE FROM YOUR TESTNET -const provider_url = config.blockchain.protocol + "://" + config.blockchain.host + ":" + config.blockchain.port; -const hd_provider = new HDWalletProvider(config.mnemonic, provider_url); -const gasPrice = config.gasPrice; - -const options = { - ethereum: new Web3(hd_provider).currentProvider, - ipfs: config.ipfs -}; - -const marketAddress = "0x471978edf16a410377cf3e10add939e366e29556"; -const outcomeIndex = "1"; // outcome to sell, is the index taken from the market outcomes array -const outcomeAmount = 0.5e18; // amount of shares to sell - -let gnosisInstance; - -return Gnosis.create(options) -.then(result => { - gnosisInstance = result; - console.info('[GnosisJS] > connection established'); - console.info("[GnosisJS] > Started selling shares on market with address " + marketAddress); - return new Promise((resolve, reject) => { - gnosisInstance.sellOutcomeTokens({ - market: marketAddress, - outcomeTokenIndex: outcomeIndex, - outcomeTokenCount: outcomeAmount, - gasPrice - }).then(response => { - resolve(response) - }).catch(error => { - reject(error); - }); - }); -}) -.then(() => { - console.info("[GnosisJS] > Shares were sold successfully."); -}) -.catch(error => { - console.warn('[GnosisJS] > ERROR'); - console.warn(error); -}); diff --git a/examples/marketsBuyShares.js b/examples/marketsBuyShares.js deleted file mode 100644 index 4c68088..0000000 --- a/examples/marketsBuyShares.js +++ /dev/null @@ -1,72 +0,0 @@ -/****************************************************************** -* This scripts aims to demonstrate how to buy shares on a prediction market -* using GnosisJS. -* Prerequisites: -* - Have a testnet (TestRPC, Ganache) up and running on localhost port 8545 (testrpc -d -i 437894314312) -* - Have the Gnosis contracts migrated on your local testnet -* cd gnosis.js/node_modules/@gnosis.pm/gnosis-core-contracts/ -* npm install -* npm run migrate -* - Have created a prediction market using either categorical_market_creation.js -* or scalar_market_creation.js -* - Take a look at the config.json file -* -/*****************************************************************/ - -let Gnosis; -try { - Gnosis = require('../'); -} catch (err) { - console.error(err) - process.exit(); -} - -const Web3 = require('web3'); -const HDWalletProvider = require("truffle-hdwallet-provider"); -let config; -if (process.argv.length == 2){ - config = require('./config.json'); -} -else{ - config = require(process.argv[2]) -} -const provider_url = config.blockchain.protocol + "://" + config.blockchain.host + ":" + config.blockchain.port; -const hd_provider = new HDWalletProvider(config.mnemonic, provider_url); -const gasPrice = config.gasPrice; - -const options = { - ethereum: new Web3(hd_provider).currentProvider, - ipfs: config.ipfs -}; - -const marketAddress = "0x471978edf16a410377cf3e10add939e366e29556"; -const outcomeIndex = "1"; // outcome to buy, is the index taken from the market outcomes array -const outcomeAmount = 0.5e18; // amount of shares to buy - -let gnosisInstance; - -return Gnosis.create(options) -.then(result => { - gnosisInstance = result; - console.info('[GnosisJS] > connection established'); - console.info("[GnosisJS] > Started buying shares on market with address " + marketAddress); - return new Promise((resolve, reject) => { - gnosisInstance.buyOutcomeTokens({ - market: marketAddress, - outcomeTokenIndex: outcomeIndex, - outcomeTokenCount: outcomeAmount, - gasPrice - }).then(response => { - resolve(response) - }).catch(error => { - reject(error); - }); - }); -}) -.then(() => { - console.info("[GnosisJS] > Shares were bought successfully."); -}) -.catch(error => { - console.warn('[GnosisJS] > ERROR'); - console.warn(error); -}); diff --git a/examples/scalarMarketCreation.js b/examples/scalarMarketCreation.js deleted file mode 100644 index aee195e..0000000 --- a/examples/scalarMarketCreation.js +++ /dev/null @@ -1,136 +0,0 @@ -/****************************************************************** -* This scripts aims to demonstrate how to create a scalar prediction market -* using GnosisJS. -* Prerequisites: -* - Have a testnet (TestRPC, Ganache) up and running on localhost port 8545 (testrpc -d -i 437894314312) -* - Have the Gnosis contracts migrated on your local testnet -* cd gnosis.js/node_modules/@gnosis.pm/gnosis-core-contracts/ -* npm install -* npm run migrate -* - Take a look at the config.json file -* -/*****************************************************************/ - -let Gnosis; -try { - Gnosis = require('../'); -} catch (err) { - console.error(err) - process.exit(); -} - -const Web3 = require('web3'); -const HDWalletProvider = require("truffle-hdwallet-provider"); -let config; -if (process.argv.length == 2){ - config = require('./config.json'); -} -else{ - config = require(process.argv[2]) -} -const provider_url = config.blockchain.protocol + "://" + config.blockchain.host + ":" + config.blockchain.port; -const hd_provider = new HDWalletProvider(config.mnemonic, provider_url); -const etherTokenAddress = config.etherTokenAddress; -const gasPrice = config.gasPrice; - -const options = { - ethereum: new Web3(hd_provider).currentProvider, - ipfs: config.ipfs -}; - -const marketJSON = { - 'title': 'My first scalar prediction market', - 'description': 'This market was created by using GnosisJS library', - 'resolutionDate': new Date().toISOString(), - 'lowerBound': '100', - 'upperBound': '200', - 'decimals': 0, - 'unit': 'EUR', - 'fee': 0, - 'funding': 1e18 // 1 Ether token -}; - -let gnosisInstance; -let ipfsHash; -let oracle; -let market; - -return Gnosis.create(options) -.then(result => { - gnosisInstance = result; - console.info('[GnosisJS] > connection established'); - console.info("[GnosisJS] > Creation started..."); -}).then(() => { - return new Promise((resolve, reject) => { - let eventDescription = { - title: marketJSON.title, - description: marketJSON.description, - resolutionDate: marketJSON.resolutionDate, - }; - - return gnosisInstance.publishEventDescription(eventDescription).then(result => { - ipfsHash = result; - console.info("[GnosisJS] > Event description hash: " + ipfsHash); - console.info("[GnosisJS] > Creating Centralized Oracle..."); - return gnosisInstance.createCentralizedOracle(ipfsHash, { gasPrice }); - }).then(result => { - oracle = result; - console.info("[GnosisJS] > Centralized Oracle was created"); - console.info("[GnosisJS] > Creating scalar Event..."); - return gnosisInstance.createScalarEvent({ - collateralToken: etherTokenAddress, - oracle, - lowerBound: marketJSON.lowerBound, - upperBound: marketJSON.upperBound, - gasPrice - }) - }).then(result => { - event = result; - console.info("[GnosisJS] > Scalar event was created"); - console.info("[GnosisJS] > Creating market..."); - return gnosisInstance.createMarket({ - event: event, - marketMaker: gnosisInstance.lmsrMarketMaker, - marketFactory: gnosisInstance.standardMarketFactory, - fee: marketJSON.fee, - gasPrice - }) - }).then(response => { - market = response; // market instance - console.info("[GnosisJS] > Market was created, address " + market.address); - console.info("[GnosisJS] > Funding market..."); - const etherToken = gnosisInstance.contracts.EtherToken.at(etherTokenAddress); - return new Promise((resolve, reject) => { - etherToken.approve(market.address, marketJSON.funding).then(result => { - console.info('[GnosisJS] > Market tokens were approved'); - console.info('[GnosisJS] > Sending funds: ' + marketJSON.funding + '...'); - market.fund(marketJSON.funding).then(result => { - console.info("[GnosisJS] > Market creation done successfully"); - resolve(); - }) - .catch(error => { - console.warn('[GnosisJS] > Error while sending tokens'); - console.warn(error); - reject(error); - }); - }) - .catch(error => { - console.warn('[GnosisJS] > Error while approving tokens'); - console.warn(error); - reject(error); - }); - }); - }).then(values => { - console.info("[GnosisJS] > All done!"); - resolve(market.address); - }) - .catch(error => { - console.warn(error); - reject(error); - }); - }) -}) -.catch(error => { - console.warn('[GnosisJS] > ERROR'); - console.warn(error); -}); diff --git a/tutorials/events-oracles-and-markets.md b/tutorials/events-oracles-and-markets.md index d4f038b..33443db 100644 --- a/tutorials/events-oracles-and-markets.md +++ b/tutorials/events-oracles-and-markets.md @@ -10,18 +10,23 @@ Who will win the U.S. presidential election of 2016? To ask this question with a prediction market on Gnosis, you must first upload the event description onto IPFS via {@link Gnosis#publishEventDescription}. This will asynchronously provide you with a hash value which can be used to locate the file on IPFS: ```js -const gnosis = await Gnosis.create() -const ipfsHash = await gnosis.publishEventDescription({ - title: 'Who will win the U.S. presidential election of 2016?', - description: 'Every four years, the citizens of the United States vote for their next president...', - resolutionDate: '2016-11-08T23:00:00-05:00', - outcomes: ['Clinton', 'Trump', 'Other'], -}) -// now the event description has been uploaded to ipfsHash and can be used -assert.equal( - (await gnosis.loadEventDescription(ipfsHash)).title, - 'Who will win the U.S. presidential election of 2016?' -) +let gnosis, ipfsHash +async function createDescription () { + gnosis = await Gnosis.create() + ipfsHash = await gnosis.publishEventDescription({ + title: 'Who will win the U.S. presidential election of 2016?', + description: 'Every four years, the citizens of the United States vote for their next president...', + resolutionDate: '2016-11-08T23:00:00-05:00', + outcomes: ['Clinton', 'Trump', 'Other'], + }) + // now the event description has been uploaded to ipfsHash and can be used + console.assert( + (await gnosis.loadEventDescription(ipfsHash)).title === + 'Who will win the U.S. presidential election of 2016?', + ) + console.info(`Ipfs hash: https://ipfs.infura.io/api/v0/cat?stream-channels=true&arg=${ipfsHash}`) +} +createDescription() ``` Of course, future events will come to pass, and once they do, the outcome should be determinable. Oracles report on the outcome of events. The simplest oracle contract provided by Gnosis is a [`CentralizedOracle`](https://gnosis.github.io/gnosis-contracts/docs/CentralizedOracle/), and it is controlled by a single entity: the `owner` of the contract, which is a single Ethereum address, and which will from this point forward in this guide be referred to as the centralized oracle itself. @@ -30,7 +35,12 @@ To create a centralized oracle, use {@link Gnosis#createCentralizedOracle}: ```js // After obtaining an instance of {@link Gnosis} as "gnosis" and "ipfsHash" from {@link Gnosis#publishEventDescription} -const oracle = await gnosis.createCentralizedOracle(ipfsHash) +let oracle +async function createOracle() { + oracle = await gnosis.createCentralizedOracle(ipfsHash) + console.info(`Oracle created with address ${oracle.address}`) +} +createOracle() ``` After `createCentralizedOracle` finishes, the owner of the CentralizedOracle contract instance created will be the message sender, or the default account for all transactions in the Gnosis instance (which is normally set to the first account exposed by the Web3 provider). @@ -46,52 +56,69 @@ Note that ether is *not* an ERC20-compliant token at the moment of this writing. In order to create a categorical event contract instance backed by an specific `oracle`, use {@link Gnosis#createCategoricalEvent}. For example, a categorical event with three outcomes like the earlier example can be made like this: ```js -const event = await gnosis.createCategoricalEvent({ - collateralToken: gnosis.etherToken, - oracle, - // Note the outcomeCount must match the length of the outcomes array published on IPFS - outcomeCount: 3, -}) +let event +async function createCategoricalEvent() { + event = await gnosis.createCategoricalEvent({ + collateralToken: gnosis.etherToken, + oracle, + // Note the outcomeCount must match the length of the outcomes array published on IPFS + outcomeCount: 3, + }) + console.info(`Categorical event created with address ${event.address}`) +} +createCategoricalEvent() ``` Note that EtherToken is traded with this particular event instance. +*If you are using the Rinkeby network, you can check that your event has been indexed by [GnosisDB](https://github.com/gnosis/gnosisdb) https://gnosisdb.rinkeby.gnosis.pm/api/events/`event.address`* + When an event has been created, users can convert their collateral into sets of outcome tokens. For example, suppose a user buys 4 ETH worth of outcome tokens from `event`: ```js -const txResults = await Promise.all([ - gnosis.etherToken.deposit({ value: 4e18 }), - gnosis.etherToken.approve(event.address, 4e18), - event.buyAllOutcomes(4e18), -]) - -// Make sure everything worked -const expectedEvents = [ - 'Deposit', - 'Approval', - 'OutcomeTokenSetIssuance', -] -txResults.forEach((txResult, i) => { - Gnosis.requireEventFromTXResult(txResult, expectedEvents[i]) -}) +async function buyAllOutcomes() { + const txResults = await Promise.all([ + gnosis.etherToken.deposit({ value: 4e18 }), + gnosis.etherToken.approve(event.address, 4e18), + event.buyAllOutcomes(4e18), + ]) + + // Make sure everything worked + const expectedEvents = [ + 'Deposit', + 'Approval', + 'OutcomeTokenSetIssuance', + ] + txResults.forEach((txResult, i) => { + Gnosis.requireEventFromTXResult(txResult, expectedEvents[i]) + }) +} +buyAllOutcomes() ``` +*Note that dependending on the wallet your are using tx's can be sorted in the inverse order. The first transaction should have a value of 4 ETH, and the others 0 ETH, just data* That user would then have `4e18` units of each [`OutcomeToken`](https://gnosis.github.io/gnosis-contracts/docs/OutcomeToken/): ```js -const { Token } = gnosis.contracts -const outcomeCount = (await event.getOutcomeCount()).valueOf() - -for(let i = 0; i < outcomeCount; i++) { - const outcomeToken = await Token.at(await event.outcomeTokens(i)) - console.log('Have', (await outcomeToken.balanceOf(gnosis.defaultAccount)).valueOf(), 'units of outcome', i) +async function checkBalances() { + const { Token } = gnosis.contracts + const outcomeCount = (await event.getOutcomeCount()).valueOf() + + for(let i = 0; i < outcomeCount; i++) { + const outcomeToken = await Token.at(await event.outcomeTokens(i)) + console.log('Have', (await outcomeToken.balanceOf(gnosis.defaultAccount)).valueOf(), 'units of outcome', i) + } } +checkBalances() ``` Finally, if you are the centralized oracle for an `event` contract which refers to the 2016 U.S. presidential election as set up above, you can report the outcome of the event as "Trump" and allow stakeholders to settle their claims with {@link Gnosis#resolveEvent}: ```js -await gnosis.resolveEvent({ event, outcome: 1 }) +async function resolve() { + await gnosis.resolveEvent({ event, outcome: 1 }) +} +resolve() ``` Note that you must pass in the 0-based index of the outcome corresponding to the event description published on IPFS ("Trump" has index 1 in the example `['Clinton', 'Trump', 'Other']`), @@ -99,7 +126,10 @@ Note that you must pass in the 0-based index of the outcome corresponding to the If you are a stakeholder in this `event` contract instance, you can redeem your winnings with [`CategoricalEvent.redeemWinnings`](https://gnosis.github.io/gnosis-contracts/docs/CategoricalEvent/): ```js -Gnosis.requireEventFromTXResult(await event.redeemWinnings(), 'WinningsRedemption') +async function redeem() { + Gnosis.requireEventFromTXResult(await event.redeemWinnings(), 'WinningsRedemption') +} +redeem() ``` ### Markets and Automated Market Makers @@ -111,32 +141,40 @@ However, it may be difficult to coordinate the trade. In order to create liquidi Gnosis contracts contain market and market maker contract interfaces, a [standard market implementation](https://gnosis.github.io/gnosis-contracts/docs/StandardMarket/), and an [implementation](https://gnosis.github.io/gnosis-contracts/docs/LMSRMarketMaker/) of the [logarithmic market scoring rule (LMSR)](http://mason.gmu.edu/~rhanson/mktscore.pdf), an automated market maker. This can be leveraged with the {@link Gnosis#createMarket} method. For example, given an `event`, you can create a [`StandardMarket`](https://gnosis.github.io/gnosis-contracts/docs/StandardMarket/) operated by the LMSR market maker with the following: ```js -const market = await gnosis.createMarket({ - event, - marketMaker: gnosis.lmsrMarketMaker, - 50000, // signifies a 5% fee on transactions - // see docs at {@link Gnosis#createMarket} for more info -}) +let market +async function createMarket() { + market = await gnosis.createMarket({ + event, + marketMaker: gnosis.lmsrMarketMaker, + fee: 50000 // signifies a 5% fee on transactions + // see docs at {@link Gnosis#createMarket} for more info + }) + console.info(`Market created with address ${market.address}`) +} +createMarket() ``` Once a `market` has been created, it needs to be funded with the collateral token in order for it to provide liquidity. The market creator funds the market according to the maximum loss acceptable to them, which is possible since LMSR guarantees a bounded loss: ```js -// Fund the market with 4 ETH -await Promise.all([ - gnosis.etherToken.deposit({ value: 4e18 }), - gnosis.etherToken.approve(event.address, 4e18), - market.fund(4e18), -]) - -const expectedEvents = [ - 'Deposit', - 'Approval', - 'MarketFunding', -] -txResults.forEach((txResult, i) => { - Gnosis.requireEventFromTXResult(txResult, expectedEvents[i]) -}) +async function fund() { + // Fund the market with 4 ETH + const txResults = await Promise.all([ + gnosis.etherToken.deposit({ value: 4e18 }), + gnosis.etherToken.approve(market.address, 4e18), + market.fund(4e18), + ]) + + const expectedEvents = [ + 'Deposit', + 'Approval', + 'MarketFunding', + ] + txResults.forEach((txResult, i) => { + Gnosis.requireEventFromTXResult(txResult, expectedEvents[i]) + }) +} +fund() ``` Furthermore, the outcome tokens sold by the market are guaranteed to be backed by collateral because the ultimate source of these outcome tokens are from the event contract, which only allow buying collateral-backed sets of outcome tokens. @@ -144,35 +182,48 @@ Furthermore, the outcome tokens sold by the market are guaranteed to be backed b Let's suppose there is a `market` on the 2016 presidential election as indicated above, and that you are wondering if "Other" outcome tokens (which have index 2) are worth it at its price point. You can estimate how much it would cost to buy `1e18` units of those outcome tokens with [`LMSRMarketMaker.calcCost`](https://gnosis.github.io/gnosis-contracts/docs/LMSRMarketMaker/): ```js -const cost = await gnosis.lmsrMarketMaker.calcCost(market.address, 2, 1e18) -console.log(cost.valueOf()) +async function calcCost() { + const cost = await gnosis.lmsrMarketMaker.calcCost(market2.address, 2, 1e18) + console.info(`Buy 1 Outcome Token with index 2 costs ${cost.valueOf()/1e18} ETH tokens`) +} +calcCost() ``` Let's say now that you've decided that these outcome tokens are worth it. Gnosis.js contains convenience functions for buying and selling outcome tokens from a market backed by an LMSR market maker. They are {@link Gnosis#buyOutcomeTokens} and {@link Gnosis#sellOutcomeTokens}. To buy these outcome tokens, you can use the following code: ```js -await gnosis.buyOutcomeTokens({ - market, - outcomeTokenIndex: 2, - outcomeTokenCount: 1e18, -}) +async function buyOutcomeTokens() { + await gnosis.buyOutcomeTokens({ + market, + outcomeTokenIndex: 2, + outcomeTokenCount: 1e18, + }) + console.info('Bought 1 Outcome Token of Outcome with index 2') +} +buyOutcomeTokens() ``` Similarly, you can see how much these outcome tokens are worth to the `market` with [`LMSRMarketMaker.calcProfit`](https://gnosis.github.io/gnosis-contracts/docs/LMSRMarketMaker/): ```js -const profit = await gnosis.lmsrMarketMaker.calcProfit(market.address, 2, 1e18) -console.log(calcProfit.valueOf()) +async function calcProfit() { + const profit = await gnosis.lmsrMarketMaker.calcProfit(market.address, 2, 1e18) + console.info(`Sell 1 Outcome Token with index 2 gives ${profit.valueOf()/1e18} ETH tokens of profit`) +} +calcProfit() ``` If you want to sell the outcome tokens you have bought, you can do the following: ```js -await gnosis.sellOutcomeTokens({ - market, - outcomeTokenIndex: 2, - outcomeTokenCount: 1e18, -}) +async function sellOutcomeTokens() { + await gnosis.sellOutcomeTokens({ + market, + outcomeTokenIndex: 2, + outcomeTokenCount: 1e18, + }) +} +sellOutcomeTokens() ``` Oftentimes prediction markets aggregate predictions into more accurate predictions. Because of this, without a fee, the investor can expect to take a loss on their investments. However, too high of a fee would discourage participation in the market. Discerning the best fee factor for markets is outside the scope of this document. @@ -180,8 +231,11 @@ Oftentimes prediction markets aggregate predictions into more accurate predictio Finally, if you are the creator of a [`StandardMarket`](https://gnosis.github.io/gnosis-contracts/docs/StandardMarket/), you can close the market and obtain all of its outcome token holdings with `StandardMarket.close` and/or withdraw the trading fees paid with `StandardMarket.withdrawFees`: ```js -Gnosis.requireEventFromTXResult(await market.close(), 'MarketClose') -Gnosis.requireEventFromTXResult(await market.withdrawFees(), 'MarketFeeWithdrawal') +async function closeAndWithdraw() { + Gnosis.requireEventFromTXResult(await market.close(), 'MarketClose') + Gnosis.requireEventFromTXResult(await market.withdrawFees(), 'MarketFeeWithdrawal') +} +closeAndWithdraw() ``` ### Events with Scalar Outcomes @@ -192,25 +246,36 @@ The discussion up to this point has been about an instance of an event with cate const lowerBound = '80' const upperBound = '100' -const ipfsHash = await gnosis.publishEventDescription({ - title: 'What will be the annual global land and ocean temperature anomaly for 2017?', - description: 'The anomaly is with respect to the average temperature for the 20th century and is as reported by the National Centers for Environmental Services...', - resolutionDate: '2017-01-01T00:00:00+00:00', - lowerBound, - upperBound, - decimals: 2, - unit: '°C', -}) +let ipfsHash, oracle, event + +async function createScalarEvent() { + ipfsHash = await gnosis.publishEventDescription({ + title: 'What will be the annual global land and ocean temperature anomaly for 2017?', + description: 'The anomaly is with respect to the average temperature for the 20th century and is as reported by the National Centers for Environmental Services...', + resolutionDate: '2017-01-01T00:00:00+00:00', + lowerBound, + upperBound, + decimals: 2, + unit: '°C', + }) -const oracle = await gnosis.createCentralizedOracle(ipfsHash) + console.info(`Ipfs hash: https://ipfs.infura.io/api/v0/cat?stream-channels=true&arg=${ipfsHash}`) -const event = await gnosis.createScalarEvent({ - collateralToken: gnosis.etherToken, - oracle, - // Note that these bounds should match the values published on IPFS - lowerBound, - upperBound, -}) + oracle = await gnosis.createCentralizedOracle(ipfsHash) + + console.info(`Oracle created with address ${oracle.address}`) + + event = await gnosis.createScalarEvent({ + collateralToken: gnosis.etherToken, + oracle, + // Note that these bounds should match the values published on IPFS + lowerBound, + upperBound, + }) + + console.info(`Event created with address ${event.address}`) +} +createScalarEvent() ``` This sets up an event with a lower bound of 0.80°C and an upper bound of 1.00°C. Note that the values are passed in as whole integers and adjusted to the right order of magnitude according to the `decimals` property of the event description. @@ -220,7 +285,9 @@ There are two outcome tokens associated with this event: a short token for the l Now let's say that the NCES reports that the average global temperature anomaly for 2017 is 0.89°C. If you are the centralized oracle for this event as above, you can report this result to the chain like so: ```js -await gnosis.resolveEvent({ event, outcome: '89' }) +async function resolve() { + await gnosis.resolveEvent({ event, outcome: '89' }) +} ``` This will value each unit of the short outcome at \\(1 - {0.89 - 0.80 \over 1.00 - 0.80} = 0.55\\) units of the collateral, and the long outcome at \\(0.45\\) units of the collateral. Thus, if you held 50 units of the short outcome and 100 units of the long outcome, [`ScalarEvent.redeemWinnings`](https://gnosis.github.io/gnosis-contracts/docs/ScalarEvent/) would net you \\(\lfloor 50 \times 0.55 + 100 \times 0.45 \rfloor = 72\\) units of collateral. Hopefully you'll have paid less than that for those outcomes. diff --git a/tutorials/installation.md b/tutorials/installation.md index eb11a86..1ca78fb 100644 --- a/tutorials/installation.md +++ b/tutorials/installation.md @@ -29,6 +29,11 @@ const Gnosis = require('@gnosis.pm/gnosisjs') This will import the transpiled library through the `dist/index` entry point, which exports the {@link Gnosis} class. +If you are playing around with gnosis.js directly in the project folder, you can import it from dist +```js +const Gnosis = require('.') +``` + ### Browser use The `gnosis.js` file and its minified version `gnosis.min.js` are self-contained and can be used directly in a webpage. For example, you may copy `gnosis.min.js` into a folder or onto your server, and in an HTML page, use the following code to import the library: