diff --git a/README.md b/README.md index 4f0bfb456..e42e79021 100644 --- a/README.md +++ b/README.md @@ -24,12 +24,15 @@ With ocean.js, you can: ocean.js is part of the [Ocean Protocol](https://oceanprotocol.com) toolset. -This is in alpha state and you can expect running into problems. If you run into them, please open up a [new issue](/issues). +This is in alpha state and you can expect running into problems. If you run into them, please open up a [new issue](https://github.com/oceanprotocol/ocean.js/issues/new?assignees=&labels=bug&template=bug_report.md&title=). +- [πŸ“š Prerequisites](#-prerequisites) - [πŸ— Installation](#-installation) - [πŸ„ Quickstart](#-quickstart) + - [Beginners Guide](#beginners-guide) - [Simple Flow](#simple-flow) - [Marketplace Flow](#marketplace-flow) + - [πŸ“– Learn More](#learn-more) - [πŸ¦‘ Development](#-development) - [✨ Code Style](#-code-style) - [πŸ‘©β€πŸ”¬ Testing](#-testing) @@ -40,6 +43,11 @@ This is in alpha state and you can expect running into problems. If you run into - [Production](#production) - [πŸ› License](#-license) +## πŸ“š Prerequisites +- node.js ([Install from here](https://nodejs.org/en/download/)) +- Docker ([Managed as a non-root user](https://docs.docker.com/engine/install/linux-postinstall/)) +- A Unix based operating system (Mac or Linux) + ## πŸ— Installation ```bash @@ -67,6 +75,11 @@ async function init() { return ocean } ``` +### Beginners Guide + +This introduction is aimed at developers who are completely new to blockchain, no coding experience is required. + +[Go to beginners guide](docs/beginners_guide.md) ### Simple Flow @@ -80,6 +93,18 @@ This batteries-included flow includes metadata, multiple services for one datato [Go to marketplace flow](docs/quickstart_marketplace.md) +### πŸ“– Learn more + +- [Get test OCEAN](docs/get-test-OCEAN.md) - from rinkeby +- [Understand config parameters](docs/parameters.md) - envvars vs files +- [Learn about off-chain services](docs/services.md) - Ocean Provider for data services, Aquarius metadata store +- [Learn about wallets](docs/wallets.md) - on generating, storing, and accessing private keys +- [Get an overview of ocean.js](docs/overview.md) - key modules and functions + +If you have any difficulties with the quickstarts, or if you have further questions about how to use ocean.js please reach out to us on [Discord](https://discord.gg/TnXjkR5). + +If you notice any bugs or issues with Ocean.js please [open an issue on github](https://github.com/oceanprotocol/ocean.js/issues/new?assignees=&labels=bug&template=bug_report.md&title=). + ## πŸ¦‘ Development The project is authored with TypeScript and compiled with `tsc`. @@ -87,6 +112,7 @@ The project is authored with TypeScript and compiled with `tsc`. To start compiler in watch mode: ```bash +npm install npm start ``` diff --git a/docs/beginners_guide.md b/docs/beginners_guide.md new file mode 100644 index 000000000..aa2934346 --- /dev/null +++ b/docs/beginners_guide.md @@ -0,0 +1,338 @@ +# A beginners guide to selling data over the blockchain + +This is a beginners guide to selling a dataset over the blockchain. The process involves creating a datatoken, which will be used to purchase the dataset, and listing it on a marketplace. This guide provides all the code you need and no prior knowledge is required. It is helpful if you have some experience with javascript but it is not necessary. + +Selling your data over the blockchain puts you in charge of how it is used and can be a great source of passive income. There are many AI startups that have deep expertise in machine learning but need more data to improve their models. Selling your data via the blockchain gives you a level of security that you would be unable to achieve if you were selling via a centralised marketplace. + +In this guide we'll be making use of the Ocean.js library. Ocean Protocol provides you with everything you need to quickly get setup and start selling data over the blockchain. + +If you have any questions or issues at any point while following along to this article please reach out to us on [discord](https://discord.gg/TnXjkR5). + +Here are the steps we will be following throughout the article: + +0. Prerequisites +1. Initialize services +2. Create a new node.js project +3. Install dependencies +4. Create a config file and update contract addresses +5. Publish a new datatoken +6. Mint 200 tokens +7. Publish a dataset +8. Allow the marketplace to sell your datatokens + +Let's go through each step: + +## 0. Prerequisites +Before we start it is important that you have all of the necessary prerequisites installed on your computer. +- **A Unix based operating system (Linux or Mac)**. If you are a Windows user you can try to run linux inside a virtual machine but this is outside of the scope of this article. +- **Git**. Instructions for installing Git can be found here: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git +- **Node.js** can be downloaded from here: https://nodejs.org/en/download/ +- **Docker** can be installed from here: https://docs.docker.com/get-docker/. Please note that Docker must run as a non-root user, you can set this up by following these instructions: https://docs.docker.com/engine/install/linux-postinstall/ + + + +## 1. Initialize services +For this tutorial we will be setting up and running a local blockchain and then the datatokens will be published to your local blockchain. This isn’t as difficult as it sounds, fortunately Ocean Protocol provides the services to start your local blockchain with a couple of commands. Deploying your datatoken to a locally run blockchain is a great way to start as it is quick and avoids any costs. + +Simply copy the following commands into your terminal and your own local blockchain will start running: + +```Bash +git clone https://github.com/oceanprotocol/barge.git +cd barge/ +./start_ocean.sh --with-provider2 --no-dashboard +``` + +These commands clone the Ocean Protocol Barge service and start running it. This does more than just start a local blockchain, it also deploys the Ocean Protocol smart contracts to your local blockchain and starts a local off-chain database for saving the metadata. + +A smart contract is a self executing piece of code that is stored on the blockchain. They essentially provide the backend logic for managing your datasets and datatokens. They are not contracts in the legal sense. + +You can read more about each of the services that barge runs via these links: +- **Aquarius** provides the off-chain database: https://github.com/oceanprotocol/aquarius +- **Ganache** is part of the Truffle Suite of blockchain tools and it sets up and runs the local blockchain: https://github.com/trufflesuite/ganache-cli +- Ocean Protocol **smart contracts**: https://github.com/oceanprotocol/ocean-contracts +- **Provider**: https://github.com/oceanprotocol/provider-py + +You need to leave the Barge services running throughout the rest of the tutorial so make sure you keep this terminal window open. All of the remaining terminal commands will be done in a new terminal window. + +## 2. Create a new node.js project + +You are now ready to start your project. We start by creating a new folder and initiating a new Node.js project. Open a new terminal and enter the following commands: + +```bash +mkdir quickstart +cd quickstart +npm init +# Answer the questions in the command line prompt +cat > index.js +# On linux press CTRL + D to save +``` + +## 3. Install dependencies + +Next we need to set up the Node.js project so that it installs the necessary dependencies. These include the Ocean Protocol libaries and contracts, as well as web3.js which is a javascript library for interacting with the Blockchain. If you would like to learn more about web3.js, you can read the documentation here: https://web3js.readthedocs.io/en/v1.3.4/ + +Open the package.json file in a text editor and update the dependencies to include the following: +```JSON + "dependencies": { + "@oceanprotocol/contracts": "^0.5.6", + "@oceanprotocol/lib": "^0.6.5", + "web3": "^1.3.0" + } +``` + +Now in your terminal run the following command: + +```bash +npm install +``` + +At this point you may get some warning messages but there shouldn’t be any errors. As long as you don’t have any errors, you are ready to proceed. + +## 4. Create a config file and update contract addresses +Now we need to set up a configuration file that will determine where your datatoken and dataset are published to. We will enter the local addresses where the Ocean Protocol services are running. When you are ready to deploy your datatoken on the Ethereum mainnet you will need to update these addresses, the process of live deploying your dataset and datatokens will be covered in a later blog post. + +Start by creating a new config.js file. In your terminal, enter the following command. + +```bash +cat > config.js +``` + +Make sure that this config.js file has been created inside your quickstart directory. Now open the config.js in your code editor and enter the following code: + +```Javascript +const { ConfigHelper } = require("@oceanprotocol/lib"); +const Web3 = require("web3"); +const defaultConfig = new ConfigHelper().getConfig("development"); + +const urls = { + networkUrl: "http://localhost:8545", + aquarius: "http://localhost:5000", + providerUri: "http://localhost:8030", +}; + +const contracts = { + "DTFactory": "0x_YOUR_DTFactory_ADDRESS_", + "BFactory": "0x_YOUR_DTFactory_ADDRESS_", + "FixedRateExchange": "0x_YOUR_DTFactory_ADDRESS_", + "Metadata": "0x_YOUR_Metadata_ADDRESS_", + "Ocean": "0x_YOUR_Ocean_ADDRESS_" +}; + +const config = { + ...defaultConfig, + metadataCacheUri: urls.aquarius, + providerUri: urls.providerUri, + web3Provider: new Web3(urls.networkUrl), +}; + +module.exports = { + config, + contracts, + urls, +}; +``` + +You will notice that the code currently contains placeholders for the contract addresses (e.g. `β€œ0x_YOUR_DTFactory_ADDRESS_"`). This is because we need to update these addresses with the addresses of the Ocean Protocol smart contracts on your local blockchain. + +When the Barge service started running it automatically saved contract addresses in a JSON file in a hidden folder under your home directory. We can check what these contract addresses are by running the following command into your terminal: + +```bash +cat ~/.ocean/ocean-contracts/artifacts/address.json +``` +You should get an output that looks like this: + +```JSON +{ + "development": { + "DTFactory": "0x27F7b0C827596C7355ee39EdFd3235F8b47C2862", + "BFactory": "0xCe7c408C56f8BFF8EF616F5CE3E7868486de3748", + "FixedRateExchange": "0xf4C7B100cECA95Badc583bdBd10F6CA8D9123B09", + "Metadata": "0x2c11A9763AaCb838fDFD6Ee01fD1179196ee20f5", + "Ocean": "0x11570aE63B4fDe21d213Bc1A9BF61eEA51d13D56" + } +} +``` + +Now we need to remove the placeholder contract addresses from the config.js file and replace them with the contract addresses that were outputted to your terminal. When you have done this, save the file. + +## 5. Publish a new datatoken +Now you are ready to publish your first datatoken! + +The datatoken that we will be deploying is an ERC20 token. ERC20 is standard for fungible tokens (meaning each token is identical and interchangeable), the standard contains a list of required and optional functions that form the smart contract which manages the token balances and transfers. ERC20 is the most popular standard for tokens deployed on the Ethereum Blockchain and many of the popular tokens that you will have heard of (Tether, USDC, Dai, Binance token) all follow the ERC20 standard. You can read more about the ERC20 token standard here: https://ethereum.org/en/developers/docs/standards/tokens/erc-20/ + +Security is incredibly important for any blockchain token (as they are a potential target for attacks) and for this reason it is not best practice to write an ERC20 from scratch. This would introduce unnecessary complexity and would require an in depth security audit. In general, complexity is the enemy of security. Instead of writing our own ERC20 token, the code we deploy will inherit from the OpenZepplin ERC20 library. This library has been thoroughly battle tested in live environments and is used to underpin millions of dollars. You can read more about the OpenZepplin ERC20 contract libraries here: https://docs.openzeppelin.com/contracts/2.x/api/token/erc20 + +The process of creating and deploying the ERC20 datatokens has been automated by Ocean Protocol. All we need to do is open the `index.js` file in your text editor and enter the following code: + +```Javascript +const Web3 = require("web3"); +const { Ocean, DataTokens } = require("@oceanprotocol/lib"); + +const { factoryABI } = require("@oceanprotocol/contracts/artifacts/DTFactory.json"); +const { datatokensABI } = require("@oceanprotocol/contracts/artifacts/DataTokenTemplate.json"); +const { config, contracts, urls } = require("./config"); + +const init = async () => { + const ocean = await Ocean.getInstance(config); + const blob = `http://localhost:8030/api/v1/services/consume`; + + const accounts = await ocean.accounts.list(); + const alice = accounts[0].id; + console.log('Alice account address:', alice) + + const datatoken = new DataTokens( + contracts.DTFactory, + factoryABI, + datatokensABI, + new Web3(urls.networkUrl) + ); + const tokenAddress = await datatoken.create(blob, alice); + console.log(`Deployed datatoken address: ${tokenAddress}`); +}; + +init(); +``` + +This is all the code you need to deploy your first datatoken. Now save the file and run it. In your terminal, run the following command: + +```Bash +node index.js +``` + +You should see the console log message stating the address of your datatoken. Congratulations, you've created your first Ocean datatoken! πŸŒŠπŸ‹ + +## 6. Mint 200 tokens + +Next, we will edit the code in `index.js` to mint 200 datatokens. These 200 datatokens are minted and sent to Alice's Address. + +At the end of the `init() { ... }` function (after `console.log('Deployed datatoken address: ${tokenAddress}')`) add the following line of code: + +```Javascript + await datatoken.mint(tokenAddress, alice, '200', alice) + let aliceBalance = await datatoken.balance(tokenAddress, alice) + console.log('Alice token balance:', aliceBalance) +``` + +Now run the `index.js` file again: + +```bash +node index.js +``` + +You should now see in the console output that Alice has a token balance of 200. + +## 7. Publish a dataset + +Now we will publish your dataset so that it can be sold over the blockchain. We start by creating a new file called data.js. In your terminal enter these commands: + +```Bash +cat > data.js +``` + +Now open the data.js file in your text editor. Enter the following code and save the file: + +```Javascript +const testData = { + main: { + type: "dataset", + name: "test-dataset", + dateCreated: new Date(Date.now()).toISOString().split(".")[0] + "Z", + author: "test", + license: "MIT", + files: [ + { + url: + "https://file-examples-com.github.io/uploads/2017/02/file_example_XLS_10.xls", + contentType: "xlsx", + }, + ], + }, +}; + +module.exports = { testData }; +``` + +If you already have a dataset hosted somewhere you can replace the example link url to the address where your dataset is hosted. You should also update the contentType field with the file extension of your dataset. If you haven’t yet hosted your dataset, you can continue with the example link in place. + +Now, we need to import the dataset into the index.js file. Open your your `index.js` in your text editor and add the following line of code at the top of the file under the other `require()` statements: + +```Javascript +const { testData } = require("./data"); +``` + +Next we add the code for publishing the dataset. This includes important information about your dataset such as the price, the publishing date and the timeout. At the end of the `init() { ... }` function (after `console.log('Bob token balance:', bobBalance)`) add the following code: + + ```Javascript + dataService = await ocean.assets.createAccessServiceAttributes( + accounts[0], + 10, // set the price in datatoken + new Date(Date.now()).toISOString().split(".")[0] + "Z", // publishedDate + 0 // timeout + ); + + // publish asset + const createData = await ocean.assets.create( + testData, + accounts[0], + [dataService], + tokenAddress + ); + + const dataId = createData.id; + console.log('Data ID:', dataId); +``` + +Now save and run the `index.js` file: + +```Bash +node index.js +``` + +In the terminal output you should now see the Data ID (did) output. + +Congratulations, you have published your first dataset! 🌊🐠 + +## 8. Allow the marketplace to sell you datatokens + +Finally we will go through the steps for allowing a marketplace to sell your dataset. For this demonstration we will use the address of a marketplace on your local blockchain but in a live environment you would need to use the address of the actual marketplace that will be selling your data. It is important to double check that this address is correct because if it isn’t you could permanently lose your datatokens. + +We start by saving the address of the marketplace. On the line after `const alice = accounts[0].id` add the following code: + +```Javascript + const marketplace = accounts[1].id; + console.log('Marketplace account address:', marketplace); +``` + +Next we will initiate a transaction that approves the marketplace to send your tokens (to buyers) on your behalf. We then make a call on the datatoken contract to check the allowance that the marketplace now has. + +At the end of the `init() { ... }` function (after `console.log('Data ID:', dataId)`) add the following code: + +```Javascript +await datatoken.approve( + tokenAddress, + marketplace, // marketplace address, + '100', // marketplaceAllowance + alice +) + +const marketplaceAllowance = await datatoken.allowance( + tokenAddress, + alice, + marketplace, // marketplace address, +); + +console.log("Marketplace Allowance:", marketplaceAllowance); +``` + +Now save the file and run it: + +```Bash +node index.js +``` + +You should see in the terminal output that the marketplace has an allowance of 100 datatokens. + +Well done, you have now completed the tutorial! + +There are many more things that you can do with Ocean.js which will be the subject of future blog posts. If you have any questions or if you would like you learn more about Ocean Protocol, please reach out to us on [Discord](https://discord.gg/TnXjkR5) or if you have found any issues with Ocean.js please raise them on [GitHub](https://github.com/oceanprotocol/ocean.js/issues/new?assignees=&labels=bug&template=bug_report.md&title=). + diff --git a/docs/get-test-OCEAN.md b/docs/get-test-OCEAN.md new file mode 100644 index 000000000..f0d260d22 --- /dev/null +++ b/docs/get-test-OCEAN.md @@ -0,0 +1,24 @@ + + +# Get Test OCEAN and Verify It + +As you develop on Ocean, you'll often need to use the OCEAN token. It's an ERC20 token on Ethereum mainnet, along with testnet deployments. + +Here, let's get some OCEAN for the Rinkeby testnet. + +## Setup + +This builds on the setup in the following. Please do it first. + +- [Datatokens tutorial](quickstart_simple.md) + +## Get OCEAN + +[Get Rinkeby OCEAN via this faucet](https://faucet.rinkeby.oceanprotocol.com/). + +It will tell you that test OCEAN are on the way, and report the transaction id (txid). Copy this txid. + +Go to https://rinkeby.etherscan.io, and search for the txid. You will see the tx that sent you OCEAN. \ No newline at end of file diff --git a/docs/overview.md b/docs/overview.md new file mode 100644 index 000000000..37676c37c --- /dev/null +++ b/docs/overview.md @@ -0,0 +1,303 @@ +# Overview + +Here is an overview of all ot the main functions and submodules: + +### Ocean instance +Create/get datatoken, get dtfactory, user orders (history) + +``` +import { Ocean } from '@oceanprotocol/lib' +const ocean = await Ocean.getInstance(config) +``` + +Then use the following submodules... + +# Assets +Publish, get, list, search, order, consume/download +```Typescript +ocean.asset.getInstance(config: InstantiableConfig): Promise; +``` +```Typescript +ocean.asset.create(metadata: Metadata, publisher: Account, services?: Service[], dtAddress?: string, cap?: string, name?: string, symbol?: string, providerUri?: string): SubscribablePromise; +``` +```Typescript +ocean.asset.ownerAssets(owner: string): Promise; +``` +```Typescript +ocean.asset.resolve(did: string): Promise; +``` +```Typescript +ocean.asset.resolveByDTAddress(dtAddress: string, offset?: number, page?: number, sort?: number): Promise; +``` +```Typescript +ocean.asset.editMetadata(ddo: DDO, newMetadata: EditableMetadata): Promise; +``` +```Typescript +ocean.asset.updateMetadata(ddo: DDO, consumerAccount: string): Promise; +``` +```Typescript +ocean.asset.editServiceTimeout(ddo: DDO, serviceIndex: number, timeout: number): Promise; +``` +```Typescript +ocean.asset.creator(did: string): Promise; +``` +```Typescript +ocean.asset.query(query: SearchQuery): Promise; +``` +```Typescript +ocean.asset.search(text: string): Promise; +``` +```Typescript +ocean.asset.getServiceByType(did: string, serviceType: string): Promise; +``` +```Typescript +ocean.asset.getServiceByIndex(did: string, serviceIndex: number): Promise; +``` +```Typescript +ocean.asset.createAccessServiceAttributes(creator: Account, cost: string, datePublished: string, timeout?: number, providerUri?: string): Promise; +``` +```Typescript +ocean.asset.initialize(did: string, serviceType: string, consumerAddress: string, serviceIndex: number, serviceEndpoint: string): Promise; +``` +```Typescript +ocean.asset.order(did: string, serviceType: string, payerAddress: string, serviceIndex?: number, mpAddress?: string, consumerAddress?: string, searchPreviousOrders?: boolean): Promise; +``` +```Typescript +ocean.asset.download(did: string, txId: string, tokenAddress: string, consumerAccount: Account, destination: string): Promise; +``` +```Typescript +ocean.asset.simpleDownload(dtAddress: string, serviceEndpoint: string, txId: string, account: string): Promise; +``` +```Typescript +ocean.asset.getOrderHistory(account: Account, serviceType?: string, fromBlock?: number): Promise; +``` + +# Datatoken Pool +Create, add/remove liquidity, check liquidity, price, buy datatokens + +```Typescript +ocean.pool. +``` +```Typescript +ocean.pool.createDTPool(account: string, token: string, amount: string, weight: string, fee: string): Promise; +``` +```Typescript +ocean.pool.getDTAddress(poolAddress: string): Promise; +``` +```Typescript +ocean.pool.getOceanReserve(poolAddress: string): Promise; +``` +```Typescript +ocean.pool.getDTReserve(poolAddress: string): Promise; +``` +```Typescript +ocean.pool.getMaxBuyQuantity(poolAddress: string, tokenAddress: string): Promise; +``` +```Typescript +ocean.pool.getOceanMaxBuyQuantity(poolAddress: string): Promise; +``` +```Typescript +ocean.pool.getDTMaxBuyQuantity(poolAddress: string): Promise; +``` +```Typescript +ocean.pool.calcInGivenOut(poolAddress: string, tokenInAddress: string, tokenOutAddress: string, tokenOutAmount: string): Promise; +``` +```Typescript +ocean.pool.calcOutGivenIn(poolAddress: string, tokenInAddress: string, tokenOutAddress: string, tokenInAmount: string): Promise; +``` +```Typescript +ocean.pool.calcPoolOutGivenSingleIn(poolAddress: string, tokenInAddress: string, tokenInAmount: string): Promise; +``` +```Typescript +ocean.pool.calcSingleInGivenPoolOut(poolAddress: string, tokenInAddress: string, poolShares: string): Promise; +``` +```Typescript +ocean.pool.calcSingleOutGivenPoolIn(poolAddress: string, tokenOutAddress: string, poolShares: string): Promise; +``` +```Typescript +ocean.pool.calcPoolInGivenSingleOut(poolAddress: string, tokenOutAddress: string, tokenOutAmount: string): Promise; +``` +```Typescript +ocean.pool.getPoolSharesRequiredToRemoveDT(poolAddress: string, dtAmount: string): Promise; +``` +```Typescript +ocean.pool.getDTRemovedforPoolShares(poolAddress: string, poolShares: string): Promise; +``` +```Typescript +ocean.pool.getPoolSharesRequiredToRemoveOcean(poolAddress: string, oceanAmount: string): Promise; +``` +```Typescript +ocean.pool.getOceanRemovedforPoolShares(poolAddress: string, poolShares: string): Promise; +``` +```Typescript +ocean.pool.getTokensRemovedforPoolShares(poolAddress: string, poolShares: string): Promise; +``` +```Typescript +ocean.pool.getDTMaxAddLiquidity(poolAddress: string): Promise; +``` +```Typescript +ocean.pool.getOceanMaxAddLiquidity(poolAddress: string): Promise; +``` +```Typescript +ocean.pool.getMaxAddLiquidity(poolAddress: string, tokenAddress: string): Promise; +``` +```Typescript +ocean.pool.getMaxRemoveLiquidity(poolAddress: string, tokenAddress: string): Promise; +``` +```Typescript +ocean.pool.getDTMaxRemoveLiquidity(poolAddress: string): Promise; +``` +```Typescript +ocean.pool.getOceanMaxRemoveLiquidity(poolAddress: string): Promise; +``` +```Typescript +ocean.pool.buyDT(account: string, poolAddress: string, dtAmountWanted: string, maxOceanAmount: string, maxPrice?: string): Promise; +``` +```Typescript +ocean.pool.sellDT(account: string, poolAddress: string, dtAmount: string, oceanAmountWanted: string, maxPrice?: string): Promise; +``` +```Typescript +ocean.pool.addDTLiquidity(account: string, poolAddress: string, amount: string): Promise; +``` +```Typescript +ocean.pool.removeDTLiquidity(account: string, poolAddress: string, amount: string, maximumPoolShares: string): Promise; +``` +```Typescript +ocean.pool.addOceanLiquidity(account: string, poolAddress: string, amount: string): Promise; +``` +```Typescript +ocean.pool.removeOceanLiquidity(account: string, poolAddress: string, amount: string, maximumPoolShares: string): Promise; +``` +```Typescript +ocean.pool.removePoolLiquidity(account: string, poolAddress: string, poolShares: string, minDT?: string, minOcean?: string): Promise; +``` +```Typescript +ocean.pool.getDTPrice(poolAddress: string): Promise; +``` +```Typescript +ocean.pool.searchPoolforDT(dtAddress: string): Promise; +``` +```Typescript +ocean.pool.getOceanNeeded(poolAddress: string, dtRequired: string): Promise; +``` +```Typescript +ocean.pool.getOceanReceived(poolAddress: string, dtSold: string): Promise; +``` +```Typescript +ocean.pool.getDTNeeded(poolAddress: string, OceanRequired: string): Promise; +``` +```Typescript +ocean.pool.getPoolsbyCreator(account?: string): Promise; +``` +```Typescript +ocean.pool.getPoolDetails(poolAddress: string): Promise; +``` +```Typescript +ocean.pool.getPoolLogs(poolAddress: string, account?: string): Promise; +``` +```Typescript +ocean.pool.getAllPoolLogs(account: string): Promise; +``` + +# Fixed rate exchange +Create, price, buy datatokens + +```Typescript +ocean.exchange.create(dataToken: string, rate: string, address: string): Promise; +``` +```Typescript +ocean.exchange.generateExchangeId(dataToken: string, owner: string): Promise; +``` +```Typescript +ocean.exchange.buyDT(exchangeId: string, dataTokenAmount: string, address: string): Promise; +``` +```Typescript +ocean.exchange.getNumberOfExchanges(): Promise; +``` +```Typescript +ocean.exchange.setRate(exchangeId: string, newRate: number, address: string): Promise; +``` +```Typescript +ocean.exchange.activate(exchangeId: string, address: string): Promise; +``` +```Typescript +ocean.exchange.deactivate(exchangeId: string, address: string): Promise; +``` +```Typescript +ocean.exchange.getRate(exchangeId: string): Promise; +``` +```Typescript +ocean.exchange.getSupply(exchangeId: string): Promise; +``` +```Typescript +ocean.exchange.getOceanNeeded(exchangeId: string, dataTokenAmount: string): Promise; +``` +```Typescript +ocean.exchange.getExchange(exchangeId: string): Promise; +``` +```Typescript +ocean.exchange.getExchanges(): Promise; +``` +```Typescript +ocean.exchange.isActive(exchangeId: string): Promise; +``` +```Typescript +ocean.exchange.CalcInGivenOut(exchangeId: string, dataTokenAmount: string): Promise; +``` +```Typescript +ocean.exchange.searchforDT(dataTokenAddress: string, minSupply: string): Promise; +``` +```Typescript +ocean.exchange.getExchangesbyCreator(account?: string): Promise; +``` +```Typescript +ocean.exchange.getExchangeSwaps(exchangeId: string, account?: string): Promise; +``` +```Typescript +ocean.exchange.getAllExchangesSwaps(account: string): Promise; +``` + +# Compute-to-data +consume/start, stop, results, status, define-service + +```Typescript +ocean.compute.start(did: string, txId: string, tokenAddress: string, consumerAccount: Account, algorithmDid?: string, algorithmMeta?: MetadataAlgorithm, output?: Output, serviceIndex?: string, serviceType?: string, algorithmTransferTxId?: string, algorithmDataToken?: string): Promise; +``` +```Typescript +ocean.compute.stop(consumerAccount: Account, did: string, jobId: string): Promise; +``` +```Typescript +ocean.compute.delete(consumerAccount: Account, did: string, jobId: string): Promise; +``` +```Typescript +ocean.compute.status(consumerAccount: Account, did?: string, jobId?: string): Promise; +``` +```Typescript +ocean.compute.result(consumerAccount: Account, did: string, jobId: string): Promise; +``` +```Typescript +ocean.compute.createServerAttributes(serverId: string, serverType: string, cost: string, cpu: string, gpu: string, memory: string, disk: string, maxExecutionTime: number): Server; +``` +```Typescript +ocean.compute.createContainerAttributes(image: string, tag: string, checksum: string): Container; +``` +```Typescript +ocean.compute.createClusterAttributes(type: string, url: string): Cluster; +``` +```Typescript +ocean.compute.createProviderAttributes(type: string, description: string, cluster: Cluster, containers: Container[], servers: Server[]): { + type: string; + description: string; + environment: { + cluster: Cluster; + supportedServers: Server[]; + supportedContainers: Container[]; + }; + }; +``` +```Typescript +ocean.compute.createComputeService(consumerAccount: Account, cost: string, datePublished: string, providerAttributes: any, computePrivacy?: ServiceComputePrivacy, timeout?: number, providerUri?: string): ServiceCompute; +``` +```Typescript +ocean.compute.order(consumerAccount: string, datasetDid: string, serviceIndex: number, algorithmDid?: string, algorithmMeta?: MetadataAlgorithm, mpAddress?: string): SubscribablePromise; +``` \ No newline at end of file diff --git a/docs/parameters.md b/docs/parameters.md new file mode 100644 index 000000000..f046e0457 --- /dev/null +++ b/docs/parameters.md @@ -0,0 +1,63 @@ + + +# On Config Parameters + +We can set any config parameter (a) via an envvar, or (b) via a config file. Envvar values override config file values. + +An `Ocean` instance will hold a `Config` instance that holds various config parameters. These parameters need to get set. This is set based on what's input to `Ocean` constructor: + +1. dict input: `Ocean({'network':..})` +2. Config object input: `Ocean(Config('config.ini'))` +3. no input, so it uses CONFIG_FILE envvar + +Here are examples. + +## 1. dict input, filled from envvars + +First, in console: + +```console +export NETWORK_URL=https://rinkeby.infura.io/v3/ +export AQUARIUS_URL=https://aquarius.rinkeby.oceanprotocol.com +export PROVIDER_URL=https://provider.rinkeby.oceanprotocol.com +``` + +For legacy support, you can also use `metadataStoreUri` instead of `metadataCacheUri`. + +## 1a. Unsetting envvars + +Recall that parameters set by envvars override config file values. So, to use a config value in a file, we must remove its envvar. + +Here's how. In the console: + +```console + unset NETWORK_URL AQUARIUS_URL PROVIDER_URL +``` + +## 2. Config object input, filled from config file + +First, in your working directory, create `config.ini` file and fill as follows: + +```console + [eth-network] + network = https://rinkeby.infura.io/v3/ + + [resources] + aquarius.url = https://provider.rinkeby.oceanprotocol.com + provider.url = https://aquarius.rinkeby.oceanprotocol.com +``` + + +## 3. No input, so it uses CONFIG_FILE envvar + +We'll use the `config.ini` file created from the previous example. + +Then, set an envvar for the config file. In the console: + +```console +export CONFIG_FILE=config.ini +``` + diff --git a/docs/quickstart_marketplace.md b/docs/quickstart_marketplace.md index 21f7d613c..9ad123e22 100644 --- a/docs/quickstart_marketplace.md +++ b/docs/quickstart_marketplace.md @@ -7,167 +7,416 @@ It focuses on Alice's experience as a publisher, and Bob's experience as a buyer Here's the steps. 1. Initialize services -1. Alice publishes assets for data services (= publishes a datatoken contract and metadata) -1. Alice mints 100 tokens -1. Alice allows marketplace to sell her datatokens -1. Marketplace posts asset for sale -1. Value swap: Bob buys datatokens from marketplace -1. Bob uses a service he just purchased (download) +2. Create a new node.js project +3. Install dependancies +4. Create a config file and update contract addresses +5. Publish a new data token +6. Mint 200 tokens +7. Publish a dataset +8. Alice allows marketplace to sell her datatokens +9. Marketplace withdraws Alice's datatokens from allowance +10. Marketplace posts asset for sale +11. Bob acquires datatokens (value swap) +12. Bob downloads the dataset +13. Extensions + Let's go through each step. ## 1. Initialize services -This quickstart treats the publisher service, ganache-cli, metadata store, and marketplace as -externally-run services. For convenience, we run barge locally in default settings. +We start by initializing the services. To do this, we clone the Barge repository and run it. This will run the current default versions of [Aquarius](https://github.com/oceanprotocol/aquarius), [Provider](https://github.com/oceanprotocol/provider-py), and [Ganache](https://github.com/trufflesuite/ganache-cli) with [our contracts](https://github.com/oceanprotocol/ocean-contracts) deployed to it. ```bash git clone https://github.com/oceanprotocol/barge.git cd barge/ -export PROVIDER_VERSION=latest -./start_ocean.sh --no-dashboard +./start_ocean.sh --with-provider2 --no-dashboard ``` -## 2. Alice publishes assets for data services (= publishes a datatoken contract) +## 2. Create a new node.js project -1. Create DataToken +Start by creating a new Node.js project. Open a new terminal and enter the following commands: + +```bash +mkdir marketplace-quickstart +cd marketplace-quickstart +npm init +# Answer the questions in the command line prompt +cat > marketplace.js +# On linux press CTRL + D to save +``` + +## 3. Install dependancies + +Open the package.json file in a text editor and update the dependancies to include the following: + +```JSON + "dependencies": { + "@oceanprotocol/contracts": "^0.5.6", + "@oceanprotocol/lib": "^0.6.5", + "web3": "^1.3.0" + } +``` + +Now in your terminal run the following command: + +```bash +npm install +``` + +## 4. Create a config file and update contract addresses + +Create a new config.js file: + +```bash +cat > config.js +``` + +Now open the config.js in your code editor and enter the following: + +```Javascript +const { ConfigHelper } = require("@oceanprotocol/lib"); +const Web3 = require("web3"); +const defaultConfig = new ConfigHelper().getConfig("development"); + +const urls = { + networkUrl: "http://localhost:8545", + aquarius: "http://localhost:5000", + providerUri: "http://localhost:8030", +}; + +const contracts = { + "DTFactory": "0x_YOUR_DTFactory_ADDRESS_", + "BFactory": "0x_YOUR_DTFactory_ADDRESS_", + "FixedRateExchange": "0x_YOUR_DTFactory_ADDRESS_", + "Metadata": "0x_YOUR_Metadata_ADDRESS_", + "Ocean": "0x_YOUR_Ocean_ADDRESS_" +}; -```javascript -import { TestContractHandler } from '../TestContractHandler' -import { DataTokens } from '../../src/datatokens/Datatokens' -import { Ocean } from '../../src/ocean/Ocean' -import { LoggerInstance } from '../../src/utils' -const Web3 = require('web3') -const web3 = new Web3('http://127.0.0.1:8545') -const factory = require('@oceanprotocol/contracts/artifacts/DTFactory.json') -const datatokensTemplate = require('@oceanprotocol/contracts/artifacts/DataTokenTemplate.json') - -// Alice's config const config = { - metadataCacheUri: 'http://aquarius:5000', - providerUri: 'http://localhost:8030', - nodeUri: `http://localhost:${process.env.ETH_PORT || 8545}`, - verbose: LogLevel.Error, - web3Provider: web3, - factoryAddress: '0x123456789...' -} -const ocean = await Ocean.getInstance(config) -const alice = (await ocean.accounts.list())[0] - -const datatoken = new DataTokens( - config.factoryAddress, - factory.abi, - datatokensTemplate.abi, - web3, - LoggerInstance -) -const data = { t: 1, url: ocean.config.metadataCacheUri } -const blob = JSON.stringify(data) + ...defaultConfig, + metadataCacheUri: urls.aquarius, + providerUri: urls.providerUri, + web3Provider: new Web3(urls.networkUrl), +}; + +module.exports = { + config, + contracts, + urls, +}; -const dataTokenAddress = await datatoken.create(blob, alice.getId()) ``` -2. Publish asset(s) +Now check what your contract addresses are locally. In your terminal run: -```javascript -const asset = { +```bash +cat ~/.ocean/ocean-contracts/artifacts/address.json +``` + +Next, update the contract addresses in your config.js file. Replace each of the place holders with the actual addresses that were outputted into your terminal. + +## 5. Publish a new data token +Now open the `marketplace.js` file in your text editor. Enter the following code and save the file: + +```Javascript +const Web3 = require("web3"); +const { Ocean, DataTokens } = require("@oceanprotocol/lib"); + +const { factoryABI } = require("@oceanprotocol/contracts/artifacts/DTFactory.json"); +const { datatokensABI } = require("@oceanprotocol/contracts/artifacts/DataTokenTemplate.json"); +const { config, contracts, urls } = require("./config"); + + + +const init = async () => { + const ocean = await Ocean.getInstance(config); + const blob = `http://localhost:8030/api/v1/services/consume`; + + const accounts = await ocean.accounts.list(); + const alice = accounts[0].id; + console.log('Alice account address:', alice) + + const datatoken = new DataTokens( + contracts.DTFactory, + factoryABI, + datatokensABI, + new Web3(urls.networkUrl) + ); + const tokenAddress = await datatoken.create(blob, alice); + console.log(`Deployed datatoken address: ${tokenAddress}`); +}; + +init(); +``` + +Now in your terminal, run the following command: + +```bash +node marketplace.js +``` + +Congratulations, you've created your first Ocean datatoken! πŸŒŠπŸ‹ + +## 6. Mint 200 tokens + +Next, we will edit the code in `marketplace.js` to mint 200 datatokens. These 200 data tokens are minted and sent to Alice's Address. + +At the end of the `init() { ... }` function (after `console.log('Deployed datatoken address: ${tokenAddress}')`) add the following line of code: + +```Javascript + await datatoken.mint(tokenAddress, alice, '200', alice) + let aliceBalance = await datatoken.balance(tokenAddress, alice) + console.log('Alice token balance:', aliceBalance) +``` + +Now run the `marketplace.js` file again: + +```bash +node marketplace.js +``` + +You should now see in the console output that Alice has a token balance of 200. + +## 7. Publish a dataset + +Create a new file called data.js. In your terminal enter these commands: + +```Bash +cat > data.js +``` + +Open the data.js file in your text editor. Enter the following code and save the file: + +```Javascript +const testData = { main: { - type: 'dataset', - name: 'test-dataset', - dateCreated: new Date(Date.now()).toISOString().split('.')[0] + 'Z', // remove milliseconds - author: 'oceanprotocol-team', - license: 'MIT', + type: "dataset", + name: "test-dataset", + dateCreated: new Date(Date.now()).toISOString().split(".")[0] + "Z", + author: "test", + license: "MIT", files: [ { url: - 'https://raw.githubusercontent.com/tbertinmahieux/MSongsDB/master/Tasks_Demos/CoverSongs/shs_dataset_test.txt', - checksum: 'efb2c764274b745f5fc37f97c6b0e761', - contentLength: '4535431', - contentType: 'text/csv', - encoding: 'UTF-8', - compression: 'zip' - } - ] - } -} - -// create a service -service1 = await ocean.assets.createAccessServiceAttributes( - alice, - 10, // set the price in datatoken - new Date(Date.now()).toISOString().split('.')[0] + 'Z', // publishedDate - 0 // timeout -) + "https://file-examples-com.github.io/uploads/2017/02/file_example_XLS_10.xls", + contentType: "xlsx", + }, + ], + }, +}; + +module.exports = { testData }; +``` -// publish asset -const ddo = await ocean.assets.create(asset, alice, [downloadService], dataTokenAddress) +Now, in your `marketplace.js` file import the test data. Add the following line of code at the top of the file under the other `require()` statements: -const did = ddo.id +```Javascript +const { testData } = require("./data"); ``` -## 3. Alice mints 100 tokens +At the end of the `init() { ... }` function (after `console.log('Bob token balance:', bobBalance)`) add the following code: + +```Javascript + dataService = await ocean.assets.createAccessServiceAttributes( + accounts[0], + 10, // set the price in datatoken + new Date(Date.now()).toISOString().split(".")[0] + "Z", // publishedDate + 0 // timeout + ); + + // publish asset + const createData = await ocean.assets.create( + testData, + accounts[0], + [dataService], + tokenAddress + ); + + const dataId = createData.id; + console.log('Data ID:', dataId); +``` -```javascript -await datatoken.mint(tokenAddress, alice.getId(), 100) +Now save and run the `marketplace.js` file: + +```Bash +node marketplace.js ``` -## 4. Alice allows marketplace to sell her datatokens +In the terminal output you should now see the Data ID (did) outputed. -```javascript +Congratulations, you have published your first dataset! 🌊🐠 + +## 8. Alice allows marketplace to sell her datatokens + +On the line after `const alice = accounts[0].id` add the following code: + +```Javascript + const marketplace = accounts[1].id; + console.log('Marketplace account address:', marketplace); +``` + +At the end of the `init() { ... }` function (after `console.log('Data ID:', dataId)`) add the following code: + +```Javascript await datatoken.approve( - dataTokenAddress, - '0x068ed00cf0441e4829d9784fcbe7b9e26d4bd8d0', // marketplace address, - 20, // marketplaceAllowance - alice.getId() + tokenAddress, + marketplace, // marketplace address, + '100', // marketplaceAllowance + alice ) + + const marketplaceAllowance = await datatoken.allowance( + tokenAddress, + alice, + marketplace, // marketplace address, + ); + + console.log("Marketplace Allowance:", marketplaceAllowance); +``` + +You should see in the terminal output that the marketplace has a datatoken allowance of 100 tokens. + +Now save the file and run it: + +```Bash +node marketplace.js ``` -## 5. Marketplace posts asset for sale +You should see in the terminal output that the marketplace has an allowance of 100 datatokens. + +## 9. Marketplace withdraws Alice's datatokens from allowance -Now, you're the marketplace:) +Now, you're the marketplace :) At the end of the `init() { ... }` function (after `console.log("Marketplace Allowance:", marketplaceAllowance)`) add the following code: + +```Javascript +await datatoken.transferFrom(tokenAddress, alice, '100', marketplace) +const marketplaceBalance = await datatoken.balance(tokenAddress, marketplace) +aliceBalance = await datatoken.balance(tokenAddress, alice) + +console.log("Marketplace balance:", marketplaceBalance) +console.log("Alice balance:", aliceBalance) +``` + +Now save and run the file: + +```Bash +node marketplace.js +``` + +You should see in the terminal output that the Markeplace now has a datatoken balance of 100 and Alice now has a balance of 100. + +## 10. Marketplace posts asset for sale + +In this section we show how the maketplace can post the dataset for sale. + +First, in the same terminal that you are running your files, enter the following command: + +```bash +export ADDRESS_FILE="${HOME}/.ocean/ocean-contracts/artifacts/address.json" +``` +This tells ocean.js the location of the contract addresses. + +At the end of the `init() { ... }` function (after `console.log("Alice balance:", aliceBalance)`) add the following code: ```javascript -// Market's config -const marketOcean = await Ocean.getInstance(config) -const marketplace = (await ocean.accounts.list())[1] +// Wait for datatoken to be published +await new Promise(r => setTimeout(r, 15000)); -const asset = await ocean.assets.resolve(ddo.id) +const asset = await ocean.assets.resolve(dataId) const accessService = await ocean.assets.getServiceByType(asset.id, 'access') -price = 20 // in USD per dataToken -assert(accessService.attributes.main.cost * price === 200) +console.log("accessService", accessService) +``` + +Now save and run the file: + +```Bash +node marketplace.js +``` + +In the terminal output you should see the atributes of your dataset, including the cost, creator address and published date. + +## 11. Bob acquires datatokens (value swap) + +Now, you're Bob :) In production environment, Bob would visit the marketplace website and purchase datatokends with USD via a payment gateway such as Stripe. In this example we demonstrate Alice sending Bob datatokens so that he is able to consume the dataset. + +First we will edit the `init() { ... }` function to create an address for Bob. On the line after `const marketplace = accounts[1].id;` add the following code: + +```Javascript + const bob = accounts[2].id; + console.log('Bob account address:', bob); ``` -## 6. Value swap: Bob buys datatokens from marketplace +Now at the end of the `init() { ... }` function (after `console.log('transactionId', transactionId)`) add the following code: + +```Javascript + const transaction = await datatoken.transfer(tokenAddress, bob, '50', alice) + const transactionId = transaction['transactionHash'] + console.log('transactionId', transactionId) + + let bobBalance = await datatoken.balance(tokenAddress, bob) + aliceBalance = await datatoken.balance(tokenAddress, alice) + + console.log('Alice token balance:', aliceBalance) + console.log('Bob token balance:', bobBalance) +``` + +Save the `marketplace.js` file and run it again. In your terminal enter: + +```bash +node marketplace.js +``` +You should see in the terminal output that both Bob and Alice have a balance of 50 tokens. + +## 12. Bob downloads the dataset + +Finally, Bob downloads the dataset. This is is a two part process where he first orders the dataset and then downloads it. + +At the end of the `init() { ... }` function (after `console.log("bobTransaction", bobTransaction)`) add the following code: ```javascript -// Not shown: in marketplace GUI, Bob uses Stripe to send USD to marketplace (or other methods / currencies). +const bobTransaction = await ocean.assets.order(asset.id, accessService.type, bob) +console.log("bobTransaction", bobTransaction) + +const data = await ocean.assets.download( + asset.id, + bobTransaction, + tokenAddress, + accounts[2], + './datafiles' +) +bobBalance = await datatoken.balance(tokenAddress, bob) +console.log("Bob token balance:", bobBalance) ``` -## 7. Bob uses a service he just purchased (download) +Save the `marketplace.js` file and run it again. In your terminal enter: + +You should see in the terminal output that Bob's balance has now been reduce to 40 tokens, as he has spent 10 on the dataset. You can confirm in the terminal that the data has been downloaded with the following commands: + +```bash +cd datafiles +ls +``` +In the terminal output you should see a new directory has been created that contains your data. -Now, you're Bob:) + +To view Bob's previous orders you can enter the following code at the end of the `init() { ... }` function (after `console.log("Bob token balance:", bobBalance)`): ```javascript -const accessService = await ocean.assets.getServiceByType(asset.id, 'access') -const bob = (await ocean.accounts.list())[2] -await ocean.assets - .order(ddo.id, accessService.type, bob.getId()) - .then(async (res: string) => { - res = JSON.parse(res) - return await datatoken.transfer( - res['dataToken'], - res['to'], - res['numTokens'], - res['from'] - ) - }) - .then(async (tx) => { - await ocean.assets.download( - ddo.id, - tx.transactionHash, - dataTokenAddress, - bob, - '~/my-datasets' - ) - }) +const history = await ocean.assets.getOrderHistory(accounts[2]) +console.log("Bob's history", history) ``` + +If you save the file and run it again you should see all of Bob's previous orders. + +## 13. Extensions + +Congratulations on completing the Oceon.js Marketplace tutorial πŸŒŠπŸ‹πŸ . This has given you a solid foundation upon which you can start using Ocean.js. There is still a lot more you can do with Ocean.js, here are some suggestions for next steps to continue learning: + +1. Check Alice's order history using `ocean.assets.getOrderHistory(accounts[0])` +2. List all of Alice's assets with `ocean.assets.ownerAssets(alice)` +3. Update metadata for Alice's dataset using `ocean.assets.editMetadata(ddo, newMetaData)` +4. Update the new metadata onchain with `ocean.onChainMetadata.update(newDdo.id, newDdo, alice)` +5. Check the metadata with `ocean.assets.getServiceByType(ddo.id, 'metadata')` +6. Update the timeout for the dataset with `ocean.assets.editServiceTimeout(ddo, serviceIndex, newTimeout)` + diff --git a/docs/quickstart_simple.md b/docs/quickstart_simple.md index 9cd7937c4..c19dbd1ca 100644 --- a/docs/quickstart_simple.md +++ b/docs/quickstart_simple.md @@ -1,78 +1,270 @@ # Quickstart: Simple Flow -This section describes a flow with the simplest transfer of value, for static data. +This section describes how to create a datatoken and host a dataset using the simplest flow. -Here's the steps. +Here are the the steps: -1. Alice publishes a dataset (= publishes a datatoken contract) -2. Alice mints 100 tokens -3. Alice transfers 1 token to Bob -4. Bob consumes dataset +1. Initialize services +2. Create a new node.js project +3. Install dependancies +4. Create a config file and update contract addresses +5. Publish a new data token +6. Mint 100 tokens +7. Transfer tokens between users. +8. Host a dataset Let's go through each of these in detail. -## 1. Alice publishes a dataset (= publishes a datatoken contract) +## 1. Initialize services -For now, you're Alice:) Let's proceed. +We start by initializing the services. To do this, we clone the Barge repository and run it. This will run the current default versions of [Aquarius](https://github.com/oceanprotocol/aquarius), [Provider](https://github.com/oceanprotocol/provider-py), and [Ganache](https://github.com/trufflesuite/ganache-cli) with [our contracts](https://github.com/oceanprotocol/ocean-contracts) deployed to it. -Run `ganache-cli` locally: +```bash +git clone https://github.com/oceanprotocol/barge.git +cd barge/ +./start_ocean.sh --with-provider2 --no-dashboard +``` +## 2. Create a new node.js project + +Start by creating a new Node.js project. Open a new terminal and enter the following commands: ```bash -ganache-cli +mkdir ocean-quickstart +cd ocean-quickstart +npm init +# Answer the questions in the command line prompt +cat > index.js +# On linux press CTRL + D to save ``` -Then proceed in with your code: +## 3. Install dependancies -```javascript -const tokenAmount = 100 -const transferAmount = 1 -const blob = 'http://localhost:8030/api/v1/provider/services' +Open the package.json file in a text editor and update the dependancies to include the following: -const alice = await ocean.accounts.list()[0] -const bob = await ocean.accounts.list()[0] -// create datatoken class -const datatoken = new DataTokens( - contracts.factoryAddress, - factoryABI, - datatokensABI, - web3, - Logger -) -// deploy datatoken -const tokenAddress = await datatoken.create(blob, alice) +```JSON + "dependencies": { + "@oceanprotocol/contracts": "^0.5.6", + "@oceanprotocol/lib": "^0.6.5", + "web3": "^1.3.0" + } ``` -## 2. Alice hosts the dataset +Now in your terminal run the following command: -Clone [provider-py](https://github.com/oceanprotocol/provider-py) and update your local environment variables: +```bash +npm install +``` + +## 4. Create a config file and update contract addresses + +Create a new config.js file: ```bash -export FLASK_APP=ocean_provider/run.py -export PROVIDER_ADDRESS=your_provider_address -export PROVIDER_KEY=your_provider_key -export CONFIG='{"File": "https://raw.githubusercontent.com/oceanprotocol/barge/master/README.md"}' +cat > config.js ``` -## 3. Alice mints 100 tokens +Now open the config.js in your code editor and enter the following: + +```Javascript +const { ConfigHelper } = require("@oceanprotocol/lib"); +const Web3 = require("web3"); +const defaultConfig = new ConfigHelper().getConfig("development"); + +const urls = { + networkUrl: "http://localhost:8545", + aquarius: "http://localhost:5000", + providerUri: "http://localhost:8030", +}; + +const contracts = { + "DTFactory": "0x_YOUR_DTFactory_ADDRESS_", + "BFactory": "0x_YOUR_DTFactory_ADDRESS_", + "FixedRateExchange": "0x_YOUR_DTFactory_ADDRESS_", + "Metadata": "0x_YOUR_Metadata_ADDRESS_", + "Ocean": "0x_YOUR_Ocean_ADDRESS_" +}; + +const config = { + ...defaultConfig, + metadataCacheUri: urls.aquarius, + providerUri: urls.providerUri, + web3Provider: new Web3(urls.networkUrl), +}; + +module.exports = { + config, + contracts, + urls, +}; -```javascript -datatoken.mint(tokenAddress, alice, tokenAmount) ``` -## 4. Alice transfers 1 token to Bob +Now check what your contract addresses are locally. In your terminal run: -```javascript -const ts = await datatoken.transfer(tokenAddress, bob, transferAmount, alice) -const transactionId = ts['transactionHash'] +```bash +cat ~/.ocean/ocean-contracts/artifacts/address.json ``` -## 5. Bob consumes dataset +Next, update the contract addresses in your config.js file. Replace each of the place holders with the actual addresses that were outputted into your terminal. + +## 5. Publish a new data token +Now open the `index.js` file in your text editor. Enter the following code and save the file: + +```Javascript +const Web3 = require("web3"); +const { Ocean, DataTokens } = require("@oceanprotocol/lib"); + +const { factoryABI } = require("@oceanprotocol/contracts/artifacts/DTFactory.json"); +const { datatokensABI } = require("@oceanprotocol/contracts/artifacts/DataTokenTemplate.json"); +const { config, contracts, urls } = require("./config"); + + -Now, you are Bob :) +const init = async () => { + const ocean = await Ocean.getInstance(config); + const blob = `http://localhost:8030/api/v1/services/consume`; -```javascript -const config = new Config() -const ocean = await Ocean.getInstance() + const accounts = await ocean.accounts.list(); + const alice = accounts[0].id; + console.log('Alice account address:', alice) -await ocean.assets.download(tokenAddress, blob, transactionId, bob) + const datatoken = new DataTokens( + contracts.DTFactory, + factoryABI, + datatokensABI, + new Web3(urls.networkUrl) + ); + const tokenAddress = await datatoken.create(blob, alice); + console.log(`Deployed datatoken address: ${tokenAddress}`); +}; + +init(); +``` + +Now in your terminal, run the following command: + +```bash +node index.js ``` + +Congratulations, you've created your first Ocean datatoken! πŸŒŠπŸ‹ + +## 6. Mint 100 tokens + +Next, we will edit the code in `index.js` to mint 100 datatokens. These 100 data tokens are minted and sent to Alice's Address. + +At the end of the `init() { ... }` function (after `console.log('Deployed datatoken address: ${tokenAddress}')`) add the following line of code: + +```Javascript + await datatoken.mint(tokenAddress, alice, '100', alice) + let aliceBalance = await datatoken.balance(tokenAddress, alice) + console.log('Alice token balance:', aliceBalance) +``` + +Now run the `index.js` file again: + +```bash +node index.js +``` + +You should now see in the console output that Alice has a token balance of 100. + +## 7. Transfer tokens between users. + +Next we will transfer tokens from Alice to Bob. First we will edit the `init() { ... }` function to create an address for Bob. On the line after `const alice = accounts[0].id` add the following code: + +```Javascript + const bob = accounts[1].id; + console.log('Bob account address:', bob); +``` + +Now at the end of the `init() { ... }` function (after `console.log('Alice token balance:', aliceBalance)`) add the following code: + +```Javascript + const transaction = await datatoken.transfer(tokenAddress, bob, '50', alice) + const transactionId = transaction['transactionHash'] + console.log('transactionId', transactionId) + + const bobBalance = await datatoken.balance(tokenAddress, bob) + aliceBalance = await datatoken.balance(tokenAddress, alice) + + console.log('Alice token balance:', aliceBalance) + console.log('Bob token balance:', bobBalance) +``` + +Save the `index.js` file and run it again. In your terminal enter: + +```bash +node index.js +``` + +You should now see in the terminal output that both Alice and Bob have a token balance of 50. + +## 8. Publish a dataset + +Create a new file called data.js. In your terminal enter these commands: + +```Bash +cat > data.js +``` + +Open the data.js file in your text editor. Enter the following code and save the file: + +```Javascript +const testData = { + main: { + type: "dataset", + name: "test-dataset", + dateCreated: new Date(Date.now()).toISOString().split(".")[0] + "Z", + author: "test", + license: "MIT", + files: [ + { + url: + "https://file-examples-com.github.io/uploads/2017/02/file_example_XLS_10.xls", + contentType: "xlsx", + }, + ], + }, +}; + +module.exports = { testData }; +``` + +Now, in your `index.js` file import the test data. Add the following line of code at the top of the file under the other `require()` statements: + +```Javascript +const { testData } = require("./data"); +``` + +At the end of the `init() { ... }` function (after `console.log('Bob token balance:', bobBalance)`) add the following code: + +```Javascript + dataService = await ocean.assets.createAccessServiceAttributes( + accounts[0], + 10, // set the price in datatoken + new Date(Date.now()).toISOString().split(".")[0] + "Z", // publishedDate + 0 // timeout + ); + + // publish asset + const createData = await ocean.assets.create( + testData, + accounts[0], + [dataService], + tokenAddress + ); + + const dataId = createData.id; + console.log('Data ID:', dataId); +``` + +Now save and run the `index.js` file: + +```Bash +node index.js +``` + +In the terminal output you should now see the Data ID (did) outputed. + +Congratulations, you have published your first dataset! 🌊🐠🐑 Now you are ready for the [marketplace flow](docs/quickstart_marketplace.md). diff --git a/docs/services.md b/docs/services.md new file mode 100644 index 000000000..5607367c5 --- /dev/null +++ b/docs/services.md @@ -0,0 +1,94 @@ + + +# About Ocean off-chain services + +## Introduction + +Ocean uses these off-chain services: + +- [Ocean Provider](https://github.com/oceanprotocol/provider) is for data services. Specifically, it's a REST API serving requests for two types of data services: static urls (for downloading data) and compute services. It's run by the marketplace or the data publisher. +- [Ocean Aquarius](https://github.com/oceanprotocol/aquarius) is metadata cache REST API. This helps to aid search in marketplaces. + +We now describe how to use these. + +## 1. Set config values for services + +Here we use a file to set config values. + +In your working directory, create a file `config.ini` and fill it with the following. It will use pre-existing services running for rinkeby testnet. + + [eth-network] + network = https://rinkeby.infura.io/v3/ + + [resources] + aquarius.url = AQUARIUS_URL=https://aquarius.rinkeby.oceanprotocol.com + provider.url = PROVIDER_URL=https://provider.rinkeby.oceanprotocol.com + +Ensure that envvars don't override the config file values: + +```console +unset NETWORK_URL AQUARIUS_URL PROVIDER_URL +``` + +Create an envvar to point to the new config file. In the console: + +```console +export CONFIG_FILE=config.ini +``` + +## 2. Use the services within Javascrtipt + +In Javascript, import and configure the components / services: + +```javascript +const Web3 = require('web3') +const web3 = new Web3('http://127.0.0.1:8545') + +#configure the components +const config = { + metadataCacheUri: 'http://aquarius:5000', + providerUri: 'http://localhost:8030', + nodeUri: `http://localhost:${process.env.ETH_PORT || 8545}`, + verbose: LogLevel.Error, + web3Provider: web3, + factoryAddress: '0x123456789...' +} +``` + +Now you're ready to use the services! 🐳 The marketplace tutorial will use them in more detail. + +## Alternatives on Services + +Above, we described a specific flow to go through configuring services. Here are some variants of that flow. + +### Point to services in other networks + +The service urls above are for rinkeby. [Ocean's docs have urls](https://docs.oceanprotocol.com/concepts/networks) for Ethereum mainnet and other supported networks. + +### Run your own services, separately + +Above, we pointed to existing services. Alternatively, you can run your own. Here's how. + +Open a new console, and get provider running: + +```console +docker run oceanprotocol/provider:latest +``` + +Open another new console, and get aquarius running: + +```console +docker run oceanprotocol/aquarius:latest +``` + +Here are the urls for the local services, for use in `config.ini` etc. + +- Provider url: `http://127.0.0.1:8030` +- Aquarius url: `http://127.0.0.1:5000` + +### Run your own services, all at once + +Above, we ran all services separately. You can also run [Ocean Barge](https://github.com/oceanprotocol/barge) to conveniently run them all at once. diff --git a/docs/wallets.md b/docs/wallets.md new file mode 100644 index 000000000..ca8830007 --- /dev/null +++ b/docs/wallets.md @@ -0,0 +1,98 @@ + + +# Wallets + +This page describes some basic approaches to Ethereum wallets and accounts. + +All you really need is a private key. From that, you can derive the Ethereum address. An Ethereum "account" is a combination of private key and Eth address. + +A "wallet" is a thing that stores private keys (and maybe signs transactions). This includes Metamask (browser plugin), Trezor (hardware wallet), and more. [Ocean docs on wallets](https://docs.oceanprotocol.com/tutorials/wallets/) has more information. + +Here we describe: + +1. How to generate private keys +2. Where to store private keys +3. How your software might access accounts + +## 1. How to generate private keys + +### Generate in browser with Metamask + +The datatokens tutorial described how to install Metamask, then use one the Ethereum accounts it auto-generates (along with the private key). + +### Generate in Javascript + +ocean-lib includes the [web3.js library](https://web3js.readthedocs.io/en/) which can generate private keys. +Here's how. In Javascript: + +```javascript +const Web3 = require('web3'); +const web3Provider = new Web3js.providers.WebsocketProvider(`wss://mainnet.infura.io/ws/v3/INFURA_PROJECT_ID`); +const web3 = new Web3(web3Provider); +const account = await web3.eth.accounts.create(); +const privateKey = account.privateKey; +``` + +## 2. Where to store private keys + +The _whole point_ of crypto wallets is store private keys. Wallets have various tradeoffs of cost, convienence, and security. For example, hardware wallets tend to be more secure but less convenient and not free. + +It can also be useful to store private keys locally on your machine, for testing, though only with a small amount of value at stake (keep the risk down πŸ™). + +Do _not_ store your private keys on anything public, unless you want your tokens to disappear. For example, don't store your private keys in GitHub or expose them on frontend webpage code. + +## 3. How your software might access Ethereum accounts + +There are two main ways: (a) directly load private key, and (b) as a keyfile JSON object. Let's review each. + +### 3a. Directly load private key + +You could grab this from a locally-stored file, or from an envvar that you copy in for a new session. Here we focus on the latter. + +First, make your key available as an envvar. Here's an example key. From your console: + +```console +export MY_TEST_KEY=0xaefd8bc8725c4b3d15fbe058d0f58f4d852e8caea2bf68e0f73acb1aeec19baa +``` + +The Ethereum address that gets computed from the example key is `0x281269C18376010B196a928c335E495bd05eC32F`. + + +### 3b. Keyfile JSON object, aka EncryptedKey + +Here's an example JSON object. This example has the same private key as above, and password `OceanProtocol` to encrypt/decrypt the private key. The private key is stored as parameter `ciphertext` (in encrypted form, of course). + +```json + { + "address": "281269c18376010b196a928c335e495bd05ec32f", + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "ac0b74c5100bd319030d983029256250" + }, + "ciphertext": "6e003d25869a8f84c3d055d4bda3fd0e83b89769b6513b58b2b76d0738f2ab1c", + "kdf": "pbkdf2", + "kdfparams": { + "c": 1000000, + "dklen": 32, + "prf": "hmac-sha256", + "salt": "423c1be88c1fadd926c1b668a5d93f74" + }, + "mac": "6b90720ddc10d457c2e3e7e1b61550d7a7fa75e6051cb1ed4f1516fba4f0a45f" + }, + "id": "7954ec59-6819-4e3c-b065-e6f3a9c1fe6c", + "version": 3 + } +``` + +Here's how you use the JSON object. In your console, export the EncryptedKey and password: + +```console +export MY_TEST_ENCRYPTED_KEY='{"address": "281269c18376010b196a928c335e495bd05ec32f", "crypto": {"cipher": "aes-128-ctr", "cipherparams": {"iv": "ac0b74c5100bd319030d983029256250"}, "ciphertext": "6e003d25869a8f84c3d055d4bda3fd0e83b89769b6513b58b2b76d0738f2ab1c", "kdf": "pbkdf2", "kdfparams": {"c": 1000000, "dklen": 32, "prf": "hmac-sha256", "salt": "423c1be88c1fadd926c1b668a5d93f74"}, "mac": "6b90720ddc10d457c2e3e7e1b61550d7a7fa75e6051cb1ed4f1516fba4f0a45f"}, "id": "7954ec59-6819-4e3c-b065-e6f3a9c1fe6c", "version": 3}' +export MY_TEST_PASSWORD=OceanProtocol +``` + +