mirror of
https://github.com/oceanprotocol-archive/squid-js.git
synced 2024-02-02 15:31:51 +01:00
Merge pull request #20 from oceanprotocol/feature/new-ocean-interface
Squid v0.1.0
This commit is contained in:
commit
56e4a74483
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,2 +1,7 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
dist/
|
dist/
|
||||||
|
.nyc_output/
|
||||||
|
coverage/
|
||||||
|
doc/
|
||||||
|
test/**/*.js
|
||||||
|
src/**/*.js
|
||||||
|
11
.npmignore
11
.npmignore
@ -1 +1,10 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
|
coverage/
|
||||||
|
.github
|
||||||
|
.nyc_output
|
||||||
|
.travis.yml
|
||||||
|
test/
|
||||||
|
src/
|
||||||
|
tsconfig.json
|
||||||
|
tslint.json
|
||||||
|
oceanprotocol-squid-*.tgz
|
||||||
|
17
.travis.yml
17
.travis.yml
@ -1,6 +1,6 @@
|
|||||||
language: node_js
|
language: node_js
|
||||||
node_js:
|
node_js:
|
||||||
- "8"
|
- "10"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
- docker
|
- docker
|
||||||
@ -13,18 +13,21 @@ matrix:
|
|||||||
fast_finish: true
|
fast_finish: true
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- npm install -g npm
|
- npm install -g npm
|
||||||
- npm install -g release-it greenkeeper-lockfile
|
- npm install -g release-it greenkeeper-lockfile ganache-cli
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- greenkeeper-lockfile-update
|
- greenkeeper-lockfile-update
|
||||||
|
- ganache-cli > ganache-cli.log &
|
||||||
|
- sleep 2
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- npm test
|
- npm run test:cover
|
||||||
- npm run build
|
- npm run build
|
||||||
|
- npm run doc
|
||||||
|
|
||||||
after_script:
|
after_script:
|
||||||
- greenkeeper-lockfile-upload
|
- greenkeeper-lockfile-upload
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
email: false
|
email: false
|
1
SQUID_INTERFACE.md
Normal file
1
SQUID_INTERFACE.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
this document has been moved to [here](https://github.com/oceanprotocol/dev-ocean/blob/master/doc/development/squid.md).
|
8452
package-lock.json
generated
8452
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
59
package.json
59
package.json
@ -1,17 +1,39 @@
|
|||||||
{
|
{
|
||||||
"name": "@oceanprotocol/squid",
|
"name": "@oceanprotocol/squid",
|
||||||
"version": "0.0.12",
|
"version": "0.1.0-beta.16",
|
||||||
"description": "JavaScript client library for Ocean Protocol",
|
"description": "JavaScript client library for Ocean Protocol",
|
||||||
"main": "dist/squid.js",
|
"main": "dist/squid.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "tslint -c tslint.json 'src/**/*.ts'",
|
"test": "mocha",
|
||||||
"start": "tsc -w",
|
"test:watch": "mocha -w --watch-extensions js,ts",
|
||||||
"build": "tsc",
|
"test:cover": "nyc mocha",
|
||||||
|
"lint": "tslint -c tslint.json 'src/**/*.ts'",
|
||||||
|
"start": "npm link @oceanprotocol/keeper-contracts && npm run build:watch",
|
||||||
|
"build": "npm run lint && tsc && npm run doc",
|
||||||
|
"build:watch": "tsc -w",
|
||||||
|
"doc": "typedoc --mode modules --out ./doc/ src/",
|
||||||
"release": "./node_modules/release-it/bin/release-it.js --src.tagName='v%s' --github.release --npm.publish --non-interactive",
|
"release": "./node_modules/release-it/bin/release-it.js --src.tagName='v%s' --github.release --npm.publish --non-interactive",
|
||||||
"release-minor": "./node_modules/release-it/bin/release-it.js minor --src.tagName='v%s' --github.release --npm.publish --non-interactive",
|
"release-minor": "./node_modules/release-it/bin/release-it.js minor --src.tagName='v%s' --github.release --npm.publish --non-interactive",
|
||||||
"release-major": "./node_modules/release-it/bin/release-it.js major --src.tagName='v%s' --github.release --npm.publish --non-interactive",
|
"release-major": "./node_modules/release-it/bin/release-it.js major --src.tagName='v%s' --github.release --npm.publish --non-interactive",
|
||||||
"prepublishOnly": "npm run build"
|
"prepublishOnly": "npm run build"
|
||||||
},
|
},
|
||||||
|
"nyc": {
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts"
|
||||||
|
],
|
||||||
|
"extension": [
|
||||||
|
".ts"
|
||||||
|
],
|
||||||
|
"require": [
|
||||||
|
"ts-node/register"
|
||||||
|
],
|
||||||
|
"reporter": [
|
||||||
|
"text-summary",
|
||||||
|
"html"
|
||||||
|
],
|
||||||
|
"sourceMap": true,
|
||||||
|
"instrument": true
|
||||||
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/oceanprotocol/squid-js.git"
|
"url": "git+https://github.com/oceanprotocol/squid-js.git"
|
||||||
@ -23,18 +45,31 @@
|
|||||||
"url": "https://github.com/oceanprotocol/squid-js/issues"
|
"url": "https://github.com/oceanprotocol/squid-js/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/oceanprotocol/squid-js#readme",
|
"homepage": "https://github.com/oceanprotocol/squid-js#readme",
|
||||||
"dependencies": {
|
|
||||||
"@oceanprotocol/keeper-contracts": "^0.2.0",
|
|
||||||
"bignumber.js": "^7.2.1",
|
|
||||||
"truffle-contract": "^3.0.6",
|
|
||||||
"web3": "0.20.6"
|
|
||||||
},
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8 <10"
|
"node": ">=8 <10"
|
||||||
},
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@oceanprotocol/keeper-contracts": "^0.3.3",
|
||||||
|
"bignumber.js": "^7.2.1",
|
||||||
|
"eth-crypto": "^1.2.4",
|
||||||
|
"eth-ecies": "^1.0.3",
|
||||||
|
"ethereumjs-util": "^6.0.0",
|
||||||
|
"jsonwebtoken": "^8.3.0",
|
||||||
|
"node-fetch": "^2.2.0",
|
||||||
|
"web3": "1.0.0-beta.36",
|
||||||
|
"web3-utils": "1.0.0-beta.36"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/web3": "^1.0.6",
|
"@types/chai": "^4.1.6",
|
||||||
|
"@types/mocha": "^5.2.5",
|
||||||
|
"@types/node": "^8.10.36",
|
||||||
|
"chai": "^4.2.0",
|
||||||
|
"mocha": "^5.2.0",
|
||||||
|
"nyc": "^13.1.0",
|
||||||
|
"source-map-support": "^0.5.9",
|
||||||
|
"ts-node": "^7.0.1",
|
||||||
"tslint": "^5.11.0",
|
"tslint": "^5.11.0",
|
||||||
"typescript": "^3.0.3"
|
"typedoc": "^0.13.0",
|
||||||
|
"typescript": "^3.1.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
15
src/ConfigProvider.ts
Normal file
15
src/ConfigProvider.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import Config from "./models/Config"
|
||||||
|
|
||||||
|
export default class ConfigProvider {
|
||||||
|
|
||||||
|
public static getConfig() {
|
||||||
|
return ConfigProvider.config
|
||||||
|
}
|
||||||
|
|
||||||
|
public static configure(config: Config) {
|
||||||
|
|
||||||
|
ConfigProvider.config = config
|
||||||
|
}
|
||||||
|
|
||||||
|
private static config: Config
|
||||||
|
}
|
47
src/keeper/Auth.ts
Normal file
47
src/keeper/Auth.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import {Receipt} from "web3-utils"
|
||||||
|
import AccessStatus from "../models/AccessStatus"
|
||||||
|
import Asset from "../ocean/Asset"
|
||||||
|
import Order from "../ocean/Order"
|
||||||
|
import ContractBase from "./ContractBase"
|
||||||
|
|
||||||
|
export default class OceanAuth extends ContractBase {
|
||||||
|
|
||||||
|
public static async getInstance(): Promise<OceanAuth> {
|
||||||
|
const auth: OceanAuth = new OceanAuth("OceanAuth")
|
||||||
|
await auth.init()
|
||||||
|
return auth
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getOrderStatus(orderId: string): Promise<AccessStatus> {
|
||||||
|
return this.call("statusOfAccessRequest", [orderId])
|
||||||
|
.then((status: string) => {
|
||||||
|
const statusInt = parseInt(status, 10)
|
||||||
|
const statusString = AccessStatus[statusInt]
|
||||||
|
return AccessStatus[statusString]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getEncryptedAccessToken(orderId: string, consumerAddress: string): Promise<Receipt> {
|
||||||
|
return this.call("getEncryptedAccessToken", [orderId], consumerAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async initiateAccessRequest(asset: Asset, publicKey: string,
|
||||||
|
timeout: number, buyerAddress: string): Promise<Receipt> {
|
||||||
|
const args = [asset.getId(), asset.publisher.getId(), publicKey, timeout]
|
||||||
|
return this.sendTransaction("initiateAccessRequest", buyerAddress, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async commitAccessRequest(order: Order, publisherAddress: string) {
|
||||||
|
const args = [order.getId(), true, 9999999999, "discovery", "read", "slaLink", "slaType"]
|
||||||
|
return this.sendTransaction("commitAccessRequest", publisherAddress, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getTempPubKey(orderId: string) {
|
||||||
|
return this.call("getTempPubKey", [orderId])
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deliverAccessToken(orderId: string, accessToken: string, publisherAddress: string) {
|
||||||
|
return this.sendTransaction("deliverAccessToken", publisherAddress, [orderId, accessToken])
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
82
src/keeper/ContractBase.ts
Normal file
82
src/keeper/ContractBase.ts
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import Event from "web3"
|
||||||
|
import Contract from "web3-eth-contract"
|
||||||
|
import Logger from "../utils/Logger"
|
||||||
|
import ContractHandler from "./ContractHandler"
|
||||||
|
|
||||||
|
export default abstract class ContractBase {
|
||||||
|
|
||||||
|
protected static instance = null
|
||||||
|
|
||||||
|
private contract: Contract = null
|
||||||
|
private contractName: string
|
||||||
|
|
||||||
|
constructor(contractName) {
|
||||||
|
this.contractName = contractName
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
public async listenToEventOnce(eventName: string, options: any): Promise<any> {
|
||||||
|
return new Promise<any>((resolve, reject) => {
|
||||||
|
if (!this.contract.events[eventName]) {
|
||||||
|
throw new Error(`Event ${eventName} not found on contract ${this.contractName}`)
|
||||||
|
}
|
||||||
|
this.contract.events[eventName](options, (err) => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
.on("data", (eventData: any) => {
|
||||||
|
resolve(eventData)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
public async getEventData(eventName: any, options: any): Promise<Event[]> {
|
||||||
|
if (!this.contract.events[eventName]) {
|
||||||
|
throw new Error(`Event ${eventName} not found on contract ${this.contractName}`)
|
||||||
|
}
|
||||||
|
return this.contract.getPastEvents(eventName, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
public getAddress() {
|
||||||
|
return this.contract.options.address
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async init() {
|
||||||
|
this.contract = await ContractHandler.get(this.contractName)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async sendTransaction(name: string, from: string, args: any[]) {
|
||||||
|
if (!this.contract.methods[name]) {
|
||||||
|
throw new Error(`Method ${name} is not part of contract ${this.contractName}`)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const tx = this.contract.methods[name](...args)
|
||||||
|
const gas = await tx.estimateGas(args, {
|
||||||
|
from,
|
||||||
|
})
|
||||||
|
return tx.send({
|
||||||
|
from,
|
||||||
|
gas,
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
const argString = JSON.stringify(args, null, 2)
|
||||||
|
Logger.error(`Sending transaction ${name} on contract ${this.contractName} failed.`)
|
||||||
|
Logger.error(`Args: ${argString} From: ${from}`)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async call(name: string, args: any[], from?: string) {
|
||||||
|
if (!this.contract.methods[name]) {
|
||||||
|
throw new Error(`Method ${name} is not part of contract ${this.contractName}`)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const method = this.contract.methods[name](...args)
|
||||||
|
return method.call(from ? {from} : null)
|
||||||
|
} catch (err) {
|
||||||
|
Logger.error(`Calling method ${name} on contract ${this.contractName} failed. Args: ${args}`, err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
128
src/keeper/ContractHandler.ts
Normal file
128
src/keeper/ContractHandler.ts
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
import Contract from "web3-eth-contract"
|
||||||
|
import Logger from "../utils/Logger"
|
||||||
|
import Keeper from "./Keeper"
|
||||||
|
import Web3Provider from "./Web3Provider"
|
||||||
|
|
||||||
|
const contracts: Map<string, Contract> = new Map<string, Contract>()
|
||||||
|
|
||||||
|
export default class ContractHandler {
|
||||||
|
|
||||||
|
public static async get(what: string): Contract {
|
||||||
|
const where = (await (await Keeper.getInstance()).getNetworkName()).toLowerCase()
|
||||||
|
try {
|
||||||
|
return contracts.get(what) || await ContractHandler.load(what, where)
|
||||||
|
} catch (err) {
|
||||||
|
Logger.error("Failed to load", what, "from", where, err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async deployContracts() {
|
||||||
|
Logger.log("Trying to deploy contracts")
|
||||||
|
|
||||||
|
const web3 = Web3Provider.getWeb3()
|
||||||
|
|
||||||
|
const deployerAddress = (await web3.eth.getAccounts())[0]
|
||||||
|
|
||||||
|
// deploy libs
|
||||||
|
/* not part of trilobite
|
||||||
|
const dll = await ContractHandler.deployContract("DLL", deployerAddress)
|
||||||
|
const attributeStore = await ContractHandler.deployContract("AttributeStore", deployerAddress)
|
||||||
|
*/
|
||||||
|
// deploy contracts
|
||||||
|
const token = await ContractHandler.deployContract("OceanToken", deployerAddress)
|
||||||
|
/* not part of trilobite
|
||||||
|
const plcrVoting = await ContractHandler.deployContract("PLCRVoting", deployerAddress, {
|
||||||
|
args: [token.options.address],
|
||||||
|
tokens: [
|
||||||
|
{
|
||||||
|
name: "DLL", address: dll.options.address,
|
||||||
|
}, {
|
||||||
|
name: "AttributeStore", address: attributeStore.options.address,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
/* not part of trilobite
|
||||||
|
const registry = await ContractHandler.deployContract("OceanRegistry", deployerAddress, {
|
||||||
|
args: [token.options.address, plcrVoting.options.address],
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
const market = await ContractHandler.deployContract("OceanMarket", deployerAddress, {
|
||||||
|
args: [token.options.address],
|
||||||
|
})
|
||||||
|
/* not part of trilobite
|
||||||
|
const dispute = await ContractHandler.deployContract("OceanDispute", deployerAddress, {
|
||||||
|
args: [market.options.address, registry.options.address, plcrVoting.options.address],
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
await ContractHandler.deployContract("OceanAuth", deployerAddress, {
|
||||||
|
args: [market.options.address],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async load(what: string, where: string): Promise<Contract> {
|
||||||
|
const web3 = Web3Provider.getWeb3()
|
||||||
|
// Logger.log("Loading", what, "from", where)
|
||||||
|
const artifact = require(`@oceanprotocol/keeper-contracts/artifacts/${what}.${where}`)
|
||||||
|
// Logger.log('Loaded artifact', artifact)
|
||||||
|
const code = await web3.eth.getCode(artifact.address)
|
||||||
|
if (code === "0x0") {
|
||||||
|
// no code in the blockchain dude
|
||||||
|
throw new Error(`No code deployed at address ${artifact.address}, sorry.`)
|
||||||
|
}
|
||||||
|
// Logger.log("Getting instance of", what, "from", where, "at", artifact.address)
|
||||||
|
const contract = new web3.eth.Contract(artifact.abi, artifact.address)
|
||||||
|
Logger.log("Loaded", what, "from", where)
|
||||||
|
contracts.set(what, contract)
|
||||||
|
return contracts.get(what)
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: reactivate for tethys
|
||||||
|
private static replaceTokens(bytecode: string, tokens: any[]) {
|
||||||
|
|
||||||
|
for (const token of tokens) {
|
||||||
|
|
||||||
|
bytecode = bytecode.replace(
|
||||||
|
new RegExp(`_+${token.name}_+`, "g"),
|
||||||
|
token.address.replace("0x", ""))
|
||||||
|
}
|
||||||
|
// Logger.log(bytecode)
|
||||||
|
|
||||||
|
return bytecode.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async deployContract(name: string, from: string, params?): Promise<Contract> {
|
||||||
|
|
||||||
|
// dont redeploy if there is already something loaded
|
||||||
|
if (contracts.has(name)) {
|
||||||
|
return contracts.get(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
const web3 = Web3Provider.getWeb3()
|
||||||
|
|
||||||
|
let contractInstance: Contract
|
||||||
|
try {
|
||||||
|
Logger.log("Deploying", name)
|
||||||
|
|
||||||
|
const artifact = require(`@oceanprotocol/keeper-contracts/artifacts/${name}.development.json`)
|
||||||
|
const tempContract = new web3.eth.Contract(artifact.abi, artifact.address)
|
||||||
|
contractInstance = await tempContract.deploy({
|
||||||
|
data: params && params.tokens ?
|
||||||
|
ContractHandler.replaceTokens(artifact.bytecode.toString(), params.tokens) :
|
||||||
|
artifact.bytecode,
|
||||||
|
arguments: params && params.args ? params.args : null,
|
||||||
|
}).send({
|
||||||
|
from,
|
||||||
|
gas: 3000000,
|
||||||
|
gasPrice: 10000000000,
|
||||||
|
})
|
||||||
|
contracts.set(name, contractInstance)
|
||||||
|
// Logger.log("Deployed", name, "at", contractInstance.options.address);
|
||||||
|
} catch (err) {
|
||||||
|
Logger.error("Deployment failed for", name, "with params", JSON.stringify(params, null, 2), err.message)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
|
||||||
|
return contractInstance
|
||||||
|
}
|
||||||
|
}
|
53
src/keeper/Keeper.ts
Normal file
53
src/keeper/Keeper.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import OceanAuth from "./Auth"
|
||||||
|
import OceanMarket from "./Market"
|
||||||
|
import OceanToken from "./Token"
|
||||||
|
import Web3Provider from "./Web3Provider"
|
||||||
|
|
||||||
|
export default class Keeper {
|
||||||
|
|
||||||
|
public static async getInstance() {
|
||||||
|
|
||||||
|
if (Keeper.instance === null) {
|
||||||
|
Keeper.instance = new Keeper()
|
||||||
|
|
||||||
|
Keeper.instance.market = await OceanMarket.getInstance()
|
||||||
|
Keeper.instance.auth = await OceanAuth.getInstance()
|
||||||
|
Keeper.instance.token = await OceanToken.getInstance()
|
||||||
|
}
|
||||||
|
return Keeper.instance
|
||||||
|
}
|
||||||
|
|
||||||
|
private static instance: Keeper = null
|
||||||
|
|
||||||
|
public token: OceanToken
|
||||||
|
public market: OceanMarket
|
||||||
|
public auth: OceanAuth
|
||||||
|
|
||||||
|
public async getNetworkName(): Promise<string> {
|
||||||
|
return Web3Provider.getWeb3().eth.net.getId()
|
||||||
|
.then((networkId) => {
|
||||||
|
let network: string = "unknown"
|
||||||
|
|
||||||
|
switch (networkId) {
|
||||||
|
case 1:
|
||||||
|
network = "Main"
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
network = "Morden"
|
||||||
|
break
|
||||||
|
case 3:
|
||||||
|
network = "Ropsten"
|
||||||
|
break
|
||||||
|
case 4:
|
||||||
|
network = "Rinkeby"
|
||||||
|
break
|
||||||
|
case 42:
|
||||||
|
network = "Kovan"
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
network = "development"
|
||||||
|
}
|
||||||
|
return network
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
47
src/keeper/Market.ts
Normal file
47
src/keeper/Market.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import BigNumber from "bignumber.js"
|
||||||
|
import {Receipt} from "web3-utils"
|
||||||
|
import Order from "../ocean/Order"
|
||||||
|
import ContractBase from "./ContractBase"
|
||||||
|
|
||||||
|
export default class OceanMarket extends ContractBase {
|
||||||
|
|
||||||
|
public static async getInstance(): Promise<OceanMarket> {
|
||||||
|
const market: OceanMarket = new OceanMarket("OceanMarket")
|
||||||
|
await market.init()
|
||||||
|
return market
|
||||||
|
}
|
||||||
|
|
||||||
|
// call functions (costs no gas)
|
||||||
|
public async isAssetActive(assetId: string): Promise<boolean> {
|
||||||
|
return this.call("checkAsset", [assetId])
|
||||||
|
}
|
||||||
|
|
||||||
|
public async verifyOrderPayment(orderId: string): Promise<boolean> {
|
||||||
|
return this.call("verifyPaymentReceived", [orderId])
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getAssetPrice(assetId: string): Promise<number> {
|
||||||
|
return this.call("getAssetPrice", [assetId])
|
||||||
|
.then((price: string) => new BigNumber(price).toNumber())
|
||||||
|
}
|
||||||
|
|
||||||
|
public async requestTokens(amount: number, receiverAddress: string): Promise<Receipt> {
|
||||||
|
return this.sendTransaction("requestTokens", receiverAddress, [amount])
|
||||||
|
}
|
||||||
|
|
||||||
|
public async generateId(input: string): Promise<string> {
|
||||||
|
return this.call("generateId", [input])
|
||||||
|
}
|
||||||
|
|
||||||
|
public async register(assetId: string, price: number, publisherAddress: string): Promise<Receipt> {
|
||||||
|
return this.sendTransaction("register", publisherAddress, [assetId, price])
|
||||||
|
}
|
||||||
|
|
||||||
|
public async payOrder(order: Order, publisherAddress: string,
|
||||||
|
price: number, consumerAddress: string,
|
||||||
|
timeout: number): Promise<Receipt> {
|
||||||
|
return this.sendTransaction("sendPayment", consumerAddress, [
|
||||||
|
order.getId(), publisherAddress, price, timeout,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
21
src/keeper/Token.ts
Normal file
21
src/keeper/Token.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import BigNumber from "bignumber.js"
|
||||||
|
import {Receipt} from "web3-utils"
|
||||||
|
import ContractBase from "./ContractBase"
|
||||||
|
|
||||||
|
export default class OceanToken extends ContractBase {
|
||||||
|
|
||||||
|
public static async getInstance(): Promise<OceanToken> {
|
||||||
|
const token: OceanToken = new OceanToken("OceanToken")
|
||||||
|
await token.init()
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
|
public async approve(marketAddress: string, price: number, buyerAddress: string): Promise<Receipt> {
|
||||||
|
return this.sendTransaction("approve", buyerAddress, [marketAddress, price])
|
||||||
|
}
|
||||||
|
|
||||||
|
public async balanceOf(address: string): Promise<number> {
|
||||||
|
return this.call("balanceOf", [address])
|
||||||
|
.then((balance: string) => new BigNumber(balance).toNumber())
|
||||||
|
}
|
||||||
|
}
|
19
src/keeper/Web3Provider.ts
Normal file
19
src/keeper/Web3Provider.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import * as Web3 from "web3"
|
||||||
|
import ConfigProvider from "../ConfigProvider"
|
||||||
|
import Logger from "../utils/Logger"
|
||||||
|
|
||||||
|
Logger.log("using web3", Web3.version)
|
||||||
|
|
||||||
|
export default class Web3Provider {
|
||||||
|
|
||||||
|
public static getWeb3() {
|
||||||
|
if (Web3Provider.web3 === null) {
|
||||||
|
const config = ConfigProvider.getConfig()
|
||||||
|
const web3Provider = config.web3Provider || new Web3.providers.HttpProvider(config.nodeUri)
|
||||||
|
Web3Provider.web3 = new Web3(Web3.givenProvider || web3Provider)
|
||||||
|
}
|
||||||
|
return Web3Provider.web3
|
||||||
|
}
|
||||||
|
|
||||||
|
private static web3: Web3 = null
|
||||||
|
}
|
@ -1,30 +0,0 @@
|
|||||||
import Config from "../utils/config";
|
|
||||||
import Web3Helper from "../utils/Web3Helper";
|
|
||||||
import ContractLoader from "./contractLoader";
|
|
||||||
import KeeperBase from "./keeper-base";
|
|
||||||
|
|
||||||
export default class OceanAuth extends KeeperBase {
|
|
||||||
|
|
||||||
public static async getInstance(config: Config, web3Helper) {
|
|
||||||
const auth = new OceanAuth(config, web3Helper);
|
|
||||||
|
|
||||||
auth.contract = await ContractLoader.load("OceanAuth", auth.web3Helper);
|
|
||||||
return auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
private constructor(config: Config, web3Helper: Web3Helper) {
|
|
||||||
super(config, web3Helper);
|
|
||||||
}
|
|
||||||
|
|
||||||
public cancelAccessRequest(orderId: string, senderAddress: string) {
|
|
||||||
return this.contract.cancelAccessRequest(orderId, {from: senderAddress});
|
|
||||||
}
|
|
||||||
|
|
||||||
public getOrderStatus(orderId: string) {
|
|
||||||
return this.contract.statusOfAccessRequest(orderId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getEncryptedAccessToken(orderId: string, senderAddress: string) {
|
|
||||||
return this.contract.getEncryptedAccessToken(orderId, {from: senderAddress});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
import * as TruffleContract from "truffle-contract";
|
|
||||||
import Logger from "../utils/logger";
|
|
||||||
import Web3Helper from "../utils/Web3Helper";
|
|
||||||
|
|
||||||
const contracts: Map<string, object> = new Map<string, object>();
|
|
||||||
|
|
||||||
export default class ContractLoader {
|
|
||||||
public static async load(what: string, web3Helper: Web3Helper) {
|
|
||||||
return contracts.get(what) || await ContractLoader._doLoad(what, web3Helper);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async _doLoad(what: string, web3Helper: Web3Helper): Promise<object> {
|
|
||||||
const where = (await web3Helper.getNetworkName()).toLowerCase();
|
|
||||||
Logger.log("Loading", what, "from", where);
|
|
||||||
try {
|
|
||||||
/* eslint-disable-next-line security/detect-non-literal-require */
|
|
||||||
const artifact = require(`@oceanprotocol/keeper-contracts/artifacts/${what}.${where}`);
|
|
||||||
// Logger.log('Loaded artifact', artifact)
|
|
||||||
|
|
||||||
const contract = TruffleContract(artifact);
|
|
||||||
Logger.log("Getting instance of", what, "from", where, "at", artifact.address);
|
|
||||||
contract.setProvider(web3Helper.web3.currentProvider);
|
|
||||||
contracts.set(what, await contract.at(artifact.address));
|
|
||||||
return contracts.get(what);
|
|
||||||
} catch (err) {
|
|
||||||
Logger.error("Failed to load", what, "from", where);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
import Config from "../utils/config";
|
|
||||||
import Web3Helper from "../utils/Web3Helper";
|
|
||||||
|
|
||||||
export default class KeeperBase {
|
|
||||||
|
|
||||||
public contract: any = null;
|
|
||||||
|
|
||||||
protected config: Config;
|
|
||||||
protected web3Helper: Web3Helper;
|
|
||||||
|
|
||||||
constructor(config: Config, web3Helper: Web3Helper) {
|
|
||||||
this.config = config;
|
|
||||||
this.web3Helper = web3Helper;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
import BigNumber from "bignumber.js";
|
|
||||||
import Config from "../utils/config";
|
|
||||||
import Logger from "../utils/logger";
|
|
||||||
import Web3Helper from "../utils/Web3Helper";
|
|
||||||
import ContractLoader from "./contractLoader";
|
|
||||||
import KeeperBase from "./keeper-base";
|
|
||||||
|
|
||||||
export default class OceanMarket extends KeeperBase {
|
|
||||||
|
|
||||||
public static async getInstance(config: Config, web3Helper: Web3Helper) {
|
|
||||||
|
|
||||||
const market = new OceanMarket(config, web3Helper);
|
|
||||||
market.contract = await ContractLoader.load("OceanMarket", market.web3Helper);
|
|
||||||
return market;
|
|
||||||
}
|
|
||||||
|
|
||||||
private constructor(config: Config, web3Helper: Web3Helper) {
|
|
||||||
super(config, web3Helper);
|
|
||||||
}
|
|
||||||
|
|
||||||
// call functions (costs no gas)
|
|
||||||
public checkAsset(assetId: string) {
|
|
||||||
return this.contract.checkAsset(assetId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public verifyOrderPayment(orderId: string): boolean {
|
|
||||||
return this.contract.verifyPaymentReceived(orderId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getAssetPrice(assetId: string) {
|
|
||||||
return this.contract.getAssetPrice(assetId)
|
|
||||||
.then((price: BigNumber) => price.toNumber());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transactions with gas cost
|
|
||||||
public requestTokens(amount: number, address: string) {
|
|
||||||
return this.contract.requestTokens(amount, {from: address});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async registerAsset(name: string, description: string, price: number, publisherAddress: string) {
|
|
||||||
const assetId = await this.contract.generateId(name + description);
|
|
||||||
const result = await this.contract.register(
|
|
||||||
assetId,
|
|
||||||
price, {
|
|
||||||
from: publisherAddress,
|
|
||||||
gas: this.config.defaultGas,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
Logger.log("registered: ", result);
|
|
||||||
return assetId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async payAsset(assetId: string, order: any, publisherAddress: string, senderAddress: string) {
|
|
||||||
const assetPrice = await this.contract.getAssetPrice(assetId)
|
|
||||||
.then((price: BigNumber) => price.toNumber());
|
|
||||||
this.contract.sendPayment(order.id, publisherAddress, assetPrice, order.timeout, {
|
|
||||||
from: senderAddress,
|
|
||||||
gas: 2000000,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
import Config from "../utils/config";
|
|
||||||
import Logger from "../utils/logger";
|
|
||||||
import Web3Helper from "../utils/Web3Helper";
|
|
||||||
import ContractLoader from "./contractLoader";
|
|
||||||
import KeeperBase from "./keeper-base";
|
|
||||||
|
|
||||||
export default class OceanToken extends KeeperBase {
|
|
||||||
|
|
||||||
public static async getInstance(config: Config, web3Helper: Web3Helper) {
|
|
||||||
const token = new OceanToken(config, web3Helper);
|
|
||||||
token.contract = await ContractLoader.load("OceanToken", token.web3Helper);
|
|
||||||
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
private constructor(config: Config, web3Helper: Web3Helper) {
|
|
||||||
super(config, web3Helper);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getTokenBalance(accountAddress: string) {
|
|
||||||
return this.contract.balanceOf.call(accountAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getEthBalance(account: string): Promise<number> {
|
|
||||||
return new Promise<number>((resolve, reject) => {
|
|
||||||
// Logger.log("getting balance for", account);
|
|
||||||
this.web3Helper.web3.eth.getBalance(account, "latest", (err: any, balance: number) => {
|
|
||||||
if (err) {
|
|
||||||
return reject(err);
|
|
||||||
}
|
|
||||||
// Logger.log("balance", balance);
|
|
||||||
resolve(balance);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
import Config from "./utils/config";
|
|
||||||
import Logger from "./utils/logger";
|
|
||||||
|
|
||||||
declare var fetch;
|
|
||||||
|
|
||||||
export default class MetaData {
|
|
||||||
|
|
||||||
private assetsUrl: string;
|
|
||||||
|
|
||||||
constructor(config: Config) {
|
|
||||||
const providerUri = config.providerUri || null;
|
|
||||||
|
|
||||||
this.assetsUrl = providerUri + "/assets";
|
|
||||||
}
|
|
||||||
|
|
||||||
public getAssetsMetadata() {
|
|
||||||
return fetch(this.assetsUrl + "/metadata", {method: "GET"})
|
|
||||||
.then((res) => res.json())
|
|
||||||
.then((data) => JSON.parse(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
public publishDataAsset(asset: object) {
|
|
||||||
return fetch(this.assetsUrl + "/metadata",
|
|
||||||
{
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify(asset),
|
|
||||||
headers: {"Content-type": "application/json"},
|
|
||||||
})
|
|
||||||
.then((response: any) => {
|
|
||||||
Logger.log("Success:", response);
|
|
||||||
if (response.ok) {
|
|
||||||
Logger.log("Success:", response);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
Logger.log("Failed: ", response.status, response.statusText);
|
|
||||||
return false;
|
|
||||||
// throw new Error(response.statusText ? response.statusText :
|
|
||||||
// `publish asset failed with status ${response.status}`)
|
|
||||||
})
|
|
||||||
.catch((error: Error) => {
|
|
||||||
Logger.log(`Publish asset to ocean database could not be completed: ${error.message}`);
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
9
src/models/AccessStatus.ts
Normal file
9
src/models/AccessStatus.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
enum AccessStatus {
|
||||||
|
Requested,
|
||||||
|
Committed,
|
||||||
|
Delivered,
|
||||||
|
Verified,
|
||||||
|
Revoked,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AccessStatus
|
4
src/models/Balance.ts
Normal file
4
src/models/Balance.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export default class Balance {
|
||||||
|
public eth: number
|
||||||
|
public ocn: number
|
||||||
|
}
|
5
src/models/Config.ts
Normal file
5
src/models/Config.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export default class Config {
|
||||||
|
public providerUri: string
|
||||||
|
public nodeUri: string
|
||||||
|
public web3Provider: any
|
||||||
|
}
|
153
src/ocean.ts
153
src/ocean.ts
@ -1,153 +0,0 @@
|
|||||||
import BigNumber from "bignumber.js";
|
|
||||||
import OceanAuth from "./keeper/auth";
|
|
||||||
import OceanMarket from "./keeper/market";
|
|
||||||
import OceanToken from "./keeper/token";
|
|
||||||
import MetaData from "./metadata";
|
|
||||||
import Config from "./utils/config";
|
|
||||||
import Logger from "./utils/logger";
|
|
||||||
import Web3Helper from "./utils/Web3Helper";
|
|
||||||
|
|
||||||
export default class Ocean {
|
|
||||||
|
|
||||||
public static async getInstance(config) {
|
|
||||||
|
|
||||||
const ocean = new Ocean(config);
|
|
||||||
|
|
||||||
ocean.market = await OceanMarket.getInstance(config, ocean.helper);
|
|
||||||
ocean.auth = await OceanAuth.getInstance(config, ocean.helper);
|
|
||||||
ocean.token = await OceanToken.getInstance(config, ocean.helper);
|
|
||||||
|
|
||||||
return ocean;
|
|
||||||
}
|
|
||||||
|
|
||||||
public token: OceanToken;
|
|
||||||
public market: OceanMarket;
|
|
||||||
public auth: OceanAuth;
|
|
||||||
public helper: Web3Helper;
|
|
||||||
public metadata: MetaData;
|
|
||||||
|
|
||||||
private config: Config;
|
|
||||||
|
|
||||||
private constructor(config: Config) {
|
|
||||||
|
|
||||||
this.config = config;
|
|
||||||
|
|
||||||
this.helper = new Web3Helper(config);
|
|
||||||
this.metadata = new MetaData(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getAccounts() {
|
|
||||||
return Promise.all((await this.helper.getAccounts()).map(async (account: string) => {
|
|
||||||
// await ocean.market.requestTokens(account, 1000)
|
|
||||||
const {token} = this;
|
|
||||||
return {
|
|
||||||
name: account,
|
|
||||||
balance: {
|
|
||||||
eth: await token.getEthBalance(account),
|
|
||||||
ocn: await token.getTokenBalance(account),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getOrdersByConsumer(consumerAddress: string) {
|
|
||||||
const {auth} = this;
|
|
||||||
const accessConsentEvent = auth.contract.AccessConsentRequested({_consumer: consumerAddress}, {
|
|
||||||
fromBlock: 0,
|
|
||||||
toBlock: "latest",
|
|
||||||
});
|
|
||||||
|
|
||||||
let outerResolve = null;
|
|
||||||
let outerReject = null;
|
|
||||||
const promise = new Promise<any[]>((resolve, reject) => {
|
|
||||||
outerResolve = resolve;
|
|
||||||
outerReject = reject;
|
|
||||||
});
|
|
||||||
|
|
||||||
const getEvents = (): Promise<any[]> => {
|
|
||||||
accessConsentEvent.get((error: any, logs: any[]) => {
|
|
||||||
if (error) {
|
|
||||||
outerReject(error);
|
|
||||||
throw new Error(error);
|
|
||||||
} else {
|
|
||||||
outerResolve(logs);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return promise;
|
|
||||||
};
|
|
||||||
const events = await getEvents().then((result) => result);
|
|
||||||
// let orders = await this.buildOrdersFromEvents(events, consumerAddress).then((result) => result)
|
|
||||||
const orders = events
|
|
||||||
.filter((obj: any) => (obj.args._consumer === consumerAddress))
|
|
||||||
.map(async (event: any) => ({
|
|
||||||
...event.args,
|
|
||||||
timeout: event.args._timeout.toNumber(),
|
|
||||||
status: await this.auth.getOrderStatus(event.args._id)
|
|
||||||
.then((status: BigNumber) => status.toNumber()),
|
|
||||||
paid: this.market.verifyOrderPayment(event.args._id),
|
|
||||||
key: null,
|
|
||||||
}));
|
|
||||||
Logger.debug("got orders: ", orders);
|
|
||||||
return orders;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async purchaseAsset(
|
|
||||||
assetId: string, publisherId: string, price: number, privateKey: string, publicKey: string, timeout: number,
|
|
||||||
senderAddress: string, initialRequestEventHandler, accessCommittedEventHandler, tokenPublishedEventHandler) {
|
|
||||||
const {token, market, auth, config} = this;
|
|
||||||
try {
|
|
||||||
// Allow market contract to transfer funds on the consumer"s behalf
|
|
||||||
await token.contract.approve(market.contract.address, price, {from: senderAddress, gas: config.defaultGas});
|
|
||||||
} catch (err) {
|
|
||||||
Logger.log("token approve", err);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// Submit the access request
|
|
||||||
await auth.contract.initiateAccessRequest(
|
|
||||||
assetId, publisherId, publicKey,
|
|
||||||
timeout, {from: senderAddress, gas: 1000000},
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
Logger.log("initiateAccessRequest", err);
|
|
||||||
}
|
|
||||||
const resourceFilter = {_resourceId: assetId, _consumer: senderAddress};
|
|
||||||
const initRequestEvent = auth.contract.AccessConsentRequested(resourceFilter);
|
|
||||||
let order: any = {};
|
|
||||||
this._listenOnce(
|
|
||||||
initRequestEvent,
|
|
||||||
"AccessConsentRequested",
|
|
||||||
(result: any, error: any) => {
|
|
||||||
order = initialRequestEventHandler(result, error);
|
|
||||||
const requestIdFilter = {_id: order.id};
|
|
||||||
const accessCommittedEvent = auth.contract.AccessRequestCommitted(requestIdFilter);
|
|
||||||
const tokenPublishedEvent = auth.contract.EncryptedTokenPublished(requestIdFilter);
|
|
||||||
this._listenOnce(
|
|
||||||
accessCommittedEvent,
|
|
||||||
"AccessRequestCommitted",
|
|
||||||
(accessRequestCommittedResult: any, accessRequestCommittedError: any) => {
|
|
||||||
accessCommittedEventHandler(accessRequestCommittedResult, order, accessRequestCommittedError);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
this._listenOnce(
|
|
||||||
tokenPublishedEvent,
|
|
||||||
"EncryptedTokenPublished",
|
|
||||||
(encryptedTokenPublishedResult: any, encryptedTokenPublishedError: any) => {
|
|
||||||
tokenPublishedEventHandler(encryptedTokenPublishedResult, order, encryptedTokenPublishedError);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return order;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper functions (private)
|
|
||||||
public _listenOnce(event: any, eventName: string, callback) {
|
|
||||||
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
||||||
event.watch((error: any, result: any) => {
|
|
||||||
event.stopWatching();
|
|
||||||
if (error) {
|
|
||||||
Logger.log(`Error in keeper ${eventName} event: `, error);
|
|
||||||
}
|
|
||||||
callback(result, error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
41
src/ocean/Account.ts
Normal file
41
src/ocean/Account.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import BigNumber from "bignumber.js"
|
||||||
|
import Keeper from "../keeper/Keeper"
|
||||||
|
import Web3Provider from "../keeper/Web3Provider"
|
||||||
|
import Balance from "../models/Balance"
|
||||||
|
import OceanBase from "./OceanBase"
|
||||||
|
|
||||||
|
export default class Account extends OceanBase {
|
||||||
|
private balance: Balance
|
||||||
|
|
||||||
|
public async getOceanBalance(): Promise<number> {
|
||||||
|
return (await Keeper.getInstance()).token.balanceOf(this.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getEtherBalance(): Promise<number> {
|
||||||
|
// Logger.log("getting balance for", account);
|
||||||
|
return Web3Provider.getWeb3().eth
|
||||||
|
.getBalance(this.id, "latest")
|
||||||
|
.then((balance: string): number => {
|
||||||
|
// Logger.log("balance", balance);
|
||||||
|
return new BigNumber(balance).toNumber()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getBalance(): Promise<Balance> {
|
||||||
|
|
||||||
|
if (!this.balance) {
|
||||||
|
this.balance = {
|
||||||
|
eth: await this.getEtherBalance(),
|
||||||
|
ocn: await this.getOceanBalance(),
|
||||||
|
} as Balance
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.balance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transactions with gas cost
|
||||||
|
public async requestTokens(amount: number): Promise<number> {
|
||||||
|
await (await Keeper.getInstance()).market.requestTokens(amount, this.id)
|
||||||
|
return amount
|
||||||
|
}
|
||||||
|
}
|
56
src/ocean/Asset.ts
Normal file
56
src/ocean/Asset.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import * as EthCrypto from "eth-crypto"
|
||||||
|
import * as EthjsUtil from "ethereumjs-util"
|
||||||
|
import Keeper from "../keeper/Keeper"
|
||||||
|
import Logger from "../utils/Logger"
|
||||||
|
import Account from "./Account"
|
||||||
|
import OceanBase from "./OceanBase"
|
||||||
|
import Order from "./Order"
|
||||||
|
|
||||||
|
export default class Asset extends OceanBase {
|
||||||
|
|
||||||
|
constructor(public name: string,
|
||||||
|
public description: string,
|
||||||
|
public price: number,
|
||||||
|
public publisher: Account) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
public async purchase(consumer: Account, timeout: number): Promise<Order> {
|
||||||
|
const {token, market, auth} = await Keeper.getInstance()
|
||||||
|
|
||||||
|
const key = EthCrypto.createIdentity()
|
||||||
|
const publicKey = EthjsUtil.privateToPublic(key.privateKey).toString("hex")
|
||||||
|
const price = await market.getAssetPrice(this.getId())
|
||||||
|
const isValid = await market.isAssetActive(this.getId())
|
||||||
|
|
||||||
|
Logger.log("The asset:", this.getId(), "is it valid?", isValid, "it's price is:", price)
|
||||||
|
|
||||||
|
if (!isValid) {
|
||||||
|
throw new Error("The Asset is not valid!")
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const marketAddr = market.getAddress()
|
||||||
|
// Allow market contract to transfer funds on the consumer"s behalf
|
||||||
|
await token.approve(marketAddr, price, consumer.getId())
|
||||||
|
Logger.log(`${price} tokens approved on market with id: ${marketAddr}`)
|
||||||
|
} catch (err) {
|
||||||
|
Logger.error("token.approve failed", err)
|
||||||
|
}
|
||||||
|
let order: Order
|
||||||
|
try {
|
||||||
|
// Submit the access request
|
||||||
|
const initiateAccessRequestReceipt = await auth.initiateAccessRequest(this,
|
||||||
|
publicKey, timeout, consumer.getId())
|
||||||
|
|
||||||
|
const {returnValues} = initiateAccessRequestReceipt.events.AccessConsentRequested
|
||||||
|
Logger.log(`Keeper AccessConsentRequested event received on asset: ${this.getId()}`)
|
||||||
|
order = new Order(this, returnValues._timeout, returnValues._pubKey, key)
|
||||||
|
order.setId(returnValues._id)
|
||||||
|
} catch (err) {
|
||||||
|
Logger.error("auth.initiateAccessRequest failed", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return order
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
93
src/ocean/Ocean.ts
Normal file
93
src/ocean/Ocean.ts
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import ConfigProvider from "../ConfigProvider"
|
||||||
|
import Keeper from "../keeper/Keeper"
|
||||||
|
import Web3Provider from "../keeper/Web3Provider"
|
||||||
|
import Provider from "../provider/Provider"
|
||||||
|
import ProviderProvider from "../provider/ProviderProvider"
|
||||||
|
import Logger from "../utils/Logger"
|
||||||
|
import Account from "./Account"
|
||||||
|
import Asset from "./Asset"
|
||||||
|
import Order from "./Order"
|
||||||
|
|
||||||
|
export default class Ocean {
|
||||||
|
|
||||||
|
public static async getInstance(config) {
|
||||||
|
|
||||||
|
if (!Ocean.instance) {
|
||||||
|
ConfigProvider.configure(config)
|
||||||
|
ProviderProvider.setProvider(Provider)
|
||||||
|
Ocean.instance = new Ocean(await Keeper.getInstance())
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ocean.instance
|
||||||
|
}
|
||||||
|
|
||||||
|
private static instance = null
|
||||||
|
private keeper: Keeper
|
||||||
|
|
||||||
|
private constructor(keeper: Keeper) {
|
||||||
|
this.keeper = keeper
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getAccounts(): Promise<Account[]> {
|
||||||
|
|
||||||
|
// retrieve eth accounts
|
||||||
|
const ethAccounts = await Web3Provider.getWeb3().eth.getAccounts()
|
||||||
|
|
||||||
|
return ethAccounts.map((address: string) => new Account(address))
|
||||||
|
}
|
||||||
|
|
||||||
|
public async register(asset: Asset): Promise<string> {
|
||||||
|
const {market} = this.keeper
|
||||||
|
|
||||||
|
// generate an id
|
||||||
|
const assetId = await market.generateId(asset.name + asset.description)
|
||||||
|
Logger.log(`Registering: ${assetId} with price ${asset.price} for ${asset.publisher.getId()}`)
|
||||||
|
asset.setId(assetId)
|
||||||
|
const isAssetActive = await market.isAssetActive(assetId)
|
||||||
|
// register asset in the market
|
||||||
|
if (!isAssetActive) {
|
||||||
|
const result = await market.register(asset.getId(), asset.price, asset.publisher.getId())
|
||||||
|
Logger.log("Registered:", assetId, "in block", result.blockNumber)
|
||||||
|
} else {
|
||||||
|
throw new Error("Asset already registered")
|
||||||
|
}
|
||||||
|
return assetId
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getOrdersByAccount(consumer: Account): Promise<Order[]> {
|
||||||
|
const {auth} = this.keeper
|
||||||
|
|
||||||
|
Logger.log("Getting orders")
|
||||||
|
|
||||||
|
const accessConsentRequestedData = await auth.getEventData(
|
||||||
|
"AccessConsentRequested", {
|
||||||
|
filter: {
|
||||||
|
_consumer: consumer.getId(),
|
||||||
|
},
|
||||||
|
fromBlock: 0,
|
||||||
|
toBlock: "latest",
|
||||||
|
})
|
||||||
|
|
||||||
|
const orders = await Promise.all(
|
||||||
|
accessConsentRequestedData
|
||||||
|
.map(async (event: any) => {
|
||||||
|
|
||||||
|
const {returnValues} = event
|
||||||
|
|
||||||
|
const order: Order = new Order(
|
||||||
|
null,
|
||||||
|
parseInt(returnValues._timeout, 10),
|
||||||
|
null, null)
|
||||||
|
|
||||||
|
order.setId(returnValues._id)
|
||||||
|
|
||||||
|
return order
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Logger.log("Got orders:", JSON.stringify(orders, null, 2))
|
||||||
|
Logger.log(`Got ${Object.keys(orders).length} orders`)
|
||||||
|
|
||||||
|
return orders
|
||||||
|
}
|
||||||
|
}
|
18
src/ocean/OceanBase.ts
Normal file
18
src/ocean/OceanBase.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
export default abstract class OceanBase {
|
||||||
|
|
||||||
|
protected id = "0x00"
|
||||||
|
|
||||||
|
constructor(id?) {
|
||||||
|
if (id) {
|
||||||
|
this.id = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getId() {
|
||||||
|
return this.id
|
||||||
|
}
|
||||||
|
|
||||||
|
public setId(id) {
|
||||||
|
this.id = id
|
||||||
|
}
|
||||||
|
}
|
97
src/ocean/Order.ts
Normal file
97
src/ocean/Order.ts
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import * as EthEcies from "eth-ecies"
|
||||||
|
import * as JWT from "jsonwebtoken"
|
||||||
|
import Keeper from "../keeper/Keeper"
|
||||||
|
import Web3Provider from "../keeper/Web3Provider"
|
||||||
|
import AccessStatus from "../models/AccessStatus"
|
||||||
|
import ProviderProvider from "../provider/ProviderProvider"
|
||||||
|
import Logger from "../utils/Logger"
|
||||||
|
import Account from "./Account"
|
||||||
|
import Asset from "./Asset"
|
||||||
|
import OceanBase from "./OceanBase"
|
||||||
|
|
||||||
|
export default class Order extends OceanBase {
|
||||||
|
|
||||||
|
constructor(private asset: Asset, private timeout: number,
|
||||||
|
private pubkey: string, private key: any) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getStatus(): Promise<AccessStatus> {
|
||||||
|
const {auth} = await Keeper.getInstance()
|
||||||
|
return auth.getOrderStatus(this.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async pay(consumer: Account): Promise<string> {
|
||||||
|
const {market} = await Keeper.getInstance()
|
||||||
|
Logger.log(
|
||||||
|
`Sending payment: ${this.getId()} ${this.asset.publisher.getId()} ${this.asset.price} ${this.timeout}`,
|
||||||
|
)
|
||||||
|
const payReceipt =
|
||||||
|
await market.payOrder(this, this.asset.publisher.getId(), this.asset.price, consumer.getId(), this.timeout)
|
||||||
|
|
||||||
|
return payReceipt.events.PaymentReceived.returnValues._paymentId
|
||||||
|
}
|
||||||
|
|
||||||
|
public async commit(accessToken: string): Promise<boolean> {
|
||||||
|
const {auth} = await Keeper.getInstance()
|
||||||
|
const commitAccessRequestReceipt = await auth.commitAccessRequest(this, this.asset.publisher.getId())
|
||||||
|
if (commitAccessRequestReceipt.events.AccessRequestRejected) {
|
||||||
|
|
||||||
|
const {returnValues} = commitAccessRequestReceipt.events.AccessRequestRejected
|
||||||
|
throw new Error(`commitAccessRequest failed ${JSON.stringify(returnValues, null, 2)}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const pubKey = await auth.getTempPubKey(this.getId())
|
||||||
|
|
||||||
|
if (this.pubkey !== pubKey) {
|
||||||
|
throw new Error("Pubkey missmatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
const encryptedAccessToken =
|
||||||
|
EthEcies.encrypt(new Buffer(pubKey, "hex"), new Buffer(accessToken)).toString("hex")
|
||||||
|
|
||||||
|
await auth.deliverAccessToken(this.getId(), `0x${encryptedAccessToken}`, this.asset.publisher.getId())
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
public async consume(consumer: Account): Promise<string> {
|
||||||
|
const {auth} = await Keeper.getInstance()
|
||||||
|
|
||||||
|
const encryptedAccessToken = await auth.getEncryptedAccessToken(this.getId(), consumer.getId())
|
||||||
|
|
||||||
|
// grab the access token from acl contract
|
||||||
|
const tokenNo0x = encryptedAccessToken.slice(2)
|
||||||
|
const encryptedTokenBuffer = Buffer.from(tokenNo0x, "hex")
|
||||||
|
|
||||||
|
const privateKey = this.key.privateKey.slice(2)
|
||||||
|
const accessTokenEncoded: string =
|
||||||
|
EthEcies.decrypt(Buffer.from(privateKey, "hex"), encryptedTokenBuffer).toString()
|
||||||
|
const accessToken = JWT.decode(accessTokenEncoded) // Returns a json object
|
||||||
|
|
||||||
|
if (!accessToken) {
|
||||||
|
throw new Error(`AccessToken is not an jwt: ${accessTokenEncoded}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const signature = Web3Provider.getWeb3().eth.sign(encryptedAccessToken, consumer.getId())
|
||||||
|
const encryptedAccessTokenSha3 = Web3Provider.getWeb3().utils.sha3(encryptedAccessToken)
|
||||||
|
|
||||||
|
// Download the data set from the provider using the url in the access token
|
||||||
|
// decode the access token, grab the service_endpoint, request_id,
|
||||||
|
|
||||||
|
// payload keys: ['consumerId', 'fixed_msg', 'sigEncJWT', 'jwt']
|
||||||
|
const payload = JSON.stringify({
|
||||||
|
consumerId: consumer.getId(),
|
||||||
|
fixed_msg: encryptedAccessTokenSha3,
|
||||||
|
sigEncJWT: signature,
|
||||||
|
jwt: accessTokenEncoded,
|
||||||
|
})
|
||||||
|
|
||||||
|
const accessUrl = await ProviderProvider.getProvider().getAccessUrl(accessToken, payload)
|
||||||
|
|
||||||
|
Logger.log("consume url: ", accessUrl)
|
||||||
|
|
||||||
|
return accessUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
30
src/provider/Provider.ts
Normal file
30
src/provider/Provider.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import fetch from "node-fetch"
|
||||||
|
import Logger from "../utils/Logger"
|
||||||
|
|
||||||
|
export default class Provider {
|
||||||
|
public static async getAccessUrl(accessToken: any, payload: any): Promise<string> {
|
||||||
|
|
||||||
|
const accessUrl = await fetch(`${accessToken.service_endpoint}/${accessToken.resource_id}`, {
|
||||||
|
method: "POST",
|
||||||
|
body: payload,
|
||||||
|
headers: {
|
||||||
|
"Content-type": "application/json",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((response: any) => {
|
||||||
|
if (response.ok) {
|
||||||
|
return response.text()
|
||||||
|
}
|
||||||
|
Logger.log("Failed: ", response.status, response.statusText)
|
||||||
|
})
|
||||||
|
.then((consumptionUrl: string) => {
|
||||||
|
Logger.log("Success accessing consume endpoint: ", consumptionUrl)
|
||||||
|
return consumptionUrl
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
Logger.error("Error fetching the data asset consumption url: ", error)
|
||||||
|
})
|
||||||
|
|
||||||
|
return accessUrl
|
||||||
|
}
|
||||||
|
}
|
14
src/provider/ProviderProvider.ts
Normal file
14
src/provider/ProviderProvider.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
export default class ProviderProvider {
|
||||||
|
|
||||||
|
public static setProvider(provider) {
|
||||||
|
|
||||||
|
ProviderProvider.provider = provider
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getProvider() {
|
||||||
|
|
||||||
|
return ProviderProvider.provider
|
||||||
|
}
|
||||||
|
|
||||||
|
private static provider
|
||||||
|
}
|
10
src/squid.ts
10
src/squid.ts
@ -1,7 +1,11 @@
|
|||||||
import Ocean from "./ocean";
|
import Asset from "./ocean/Asset"
|
||||||
import Logger from "./utils/logger";
|
import Ocean from "./ocean/Ocean"
|
||||||
|
import Order from "./ocean/Order"
|
||||||
|
import Logger from "./utils/Logger"
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Ocean,
|
Ocean,
|
||||||
|
Order,
|
||||||
|
Asset,
|
||||||
Logger,
|
Logger,
|
||||||
};
|
}
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
export default class Logger {
|
export default class Logger {
|
||||||
public static log(...args: any[]) {
|
public static log(...args: any[]) {
|
||||||
Logger.dispatch("log", ...args);
|
Logger.dispatch("log", ...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static debug(...args: any[]) {
|
public static debug(...args: any[]) {
|
||||||
Logger.dispatch("debug", ...args);
|
Logger.dispatch("debug", ...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static warn(...args: any[]) {
|
public static warn(...args: any[]) {
|
||||||
Logger.dispatch("warn", ...args);
|
Logger.dispatch("warn", ...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static error(...args: any[]) {
|
public static error(...args: any[]) {
|
||||||
Logger.dispatch("error", ...args);
|
Logger.dispatch("error", ...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
private static dispatch(verb: string, ...args: any[]) {
|
private static dispatch(verb: string, ...args: any[]) {
|
||||||
/* eslint-disable-next-line no-console */
|
console[verb](...args)
|
||||||
console[verb](...args);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,59 +0,0 @@
|
|||||||
import Web3 = require("web3");
|
|
||||||
import Config from "./config";
|
|
||||||
|
|
||||||
export default class Web3Helper {
|
|
||||||
public web3: Web3;
|
|
||||||
|
|
||||||
public constructor(config: Config) {
|
|
||||||
const web3Provider = config.web3Provider || new Web3.providers.HttpProvider(config.nodeUri);
|
|
||||||
this.web3 = new Web3(web3Provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getAccounts(): Promise<any[]> {
|
|
||||||
return new Promise<any[]>((resolve, reject) => {
|
|
||||||
this.web3.eth.getAccounts((err: any, accounts: string[]) => {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
resolve(accounts);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getNetworkName(): Promise<string> {
|
|
||||||
return new Promise<string>((resolve, reject) => {
|
|
||||||
let network: string = "unknown";
|
|
||||||
// @ts-ignore old version of web3, lets get to 1.0
|
|
||||||
this.web3.version.getNetwork((err, networkId) => {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
switch (networkId) {
|
|
||||||
case "1":
|
|
||||||
network = "Main";
|
|
||||||
break;
|
|
||||||
case "2":
|
|
||||||
network = "Morden";
|
|
||||||
break;
|
|
||||||
case "3":
|
|
||||||
network = "Ropsten";
|
|
||||||
break;
|
|
||||||
case "4":
|
|
||||||
network = "Rinkeby";
|
|
||||||
break;
|
|
||||||
case "42":
|
|
||||||
network = "Kovan";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
network = "development";
|
|
||||||
}
|
|
||||||
resolve(network);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// web3 wrappers
|
|
||||||
public sign(accountAddress: string, message: string) {
|
|
||||||
return this.web3.eth.sign(accountAddress, message);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
export default class Config {
|
|
||||||
|
|
||||||
public defaultGas: number = 300000;
|
|
||||||
public providerUri: string;
|
|
||||||
public nodeUri: string;
|
|
||||||
public web3Provider: any;
|
|
||||||
}
|
|
25
test/Squid.test.ts
Normal file
25
test/Squid.test.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import * as assert from "assert"
|
||||||
|
import * as squid from "../src/squid"
|
||||||
|
|
||||||
|
describe("Squid", () => {
|
||||||
|
|
||||||
|
describe("interface", () => {
|
||||||
|
|
||||||
|
it("should expose Ocean", async () => {
|
||||||
|
assert(squid.Ocean)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should expose Logger", async () => {
|
||||||
|
assert(squid.Logger)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should expose Asset", async () => {
|
||||||
|
assert(squid.Asset)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should expose Order", async () => {
|
||||||
|
assert(squid.Order)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
5
test/config.ts
Normal file
5
test/config.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import Config from "../src/models/Config"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
nodeUri: "http://localhost:8545",
|
||||||
|
} as Config
|
68
test/keeper/ContractBase.test.ts
Normal file
68
test/keeper/ContractBase.test.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import ConfigProvider from "../../src/ConfigProvider"
|
||||||
|
import ContractHandler from "../../src/keeper/ContractHandler"
|
||||||
|
import config from "../config"
|
||||||
|
import ContractBaseMock from "../mocks/ContractBase.Mock"
|
||||||
|
|
||||||
|
const wrappedContract = new ContractBaseMock("OceanToken")
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
ConfigProvider.configure(config)
|
||||||
|
await ContractHandler.deployContracts()
|
||||||
|
wrappedContract.initMock()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("ContractWrapperBase", () => {
|
||||||
|
|
||||||
|
describe("#call()", () => {
|
||||||
|
|
||||||
|
it("should fail to call on an unknown contract function", (done) => {
|
||||||
|
|
||||||
|
wrappedContract.callMock("balanceOfxxx", [])
|
||||||
|
.catch(() => {
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should fail to call on an contract function with wrong set of parameters", (done) => {
|
||||||
|
|
||||||
|
wrappedContract.callMock("balanceOf", [])
|
||||||
|
.catch(() => {
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should fail to call on an unknown contract function", (done) => {
|
||||||
|
|
||||||
|
wrappedContract.sendMock("balanceOfxxx", "0x00", ["0x00"])
|
||||||
|
.catch(() => {
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should fail to call on an contract function with wrong set of parameters", (done) => {
|
||||||
|
|
||||||
|
wrappedContract.sendMock("approve", "0x000", [])
|
||||||
|
.catch(() => {
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#getEventData()", () => {
|
||||||
|
|
||||||
|
it("should fail on unknown event", (done) => {
|
||||||
|
|
||||||
|
wrappedContract.getEventData("crazyevent", {})
|
||||||
|
.catch(() => {
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
28
test/keeper/ContractHandler.test.ts
Normal file
28
test/keeper/ContractHandler.test.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import {assert} from "chai"
|
||||||
|
import ConfigProvider from "../../src/ConfigProvider"
|
||||||
|
import ContractHandler from "../../src/keeper/ContractHandler"
|
||||||
|
import config from "../config"
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
ConfigProvider.configure(config)
|
||||||
|
await ContractHandler.deployContracts()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("ContractHandler", () => {
|
||||||
|
|
||||||
|
describe("#get()", () => {
|
||||||
|
|
||||||
|
it("should load and get OceanToken correctly", async () => {
|
||||||
|
assert(await ContractHandler.get("OceanToken"))
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should fail to load an unknown contract", (done) => {
|
||||||
|
|
||||||
|
ContractHandler.get("OceanXXX")
|
||||||
|
.catch(() => {
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
40
test/keeper/Keeper.test.ts
Normal file
40
test/keeper/Keeper.test.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import {assert} from "chai"
|
||||||
|
import ConfigProvider from "../../src/ConfigProvider"
|
||||||
|
import ContractHandler from "../../src/keeper/ContractHandler"
|
||||||
|
import Keeper from "../../src/keeper/Keeper"
|
||||||
|
import config from "../config"
|
||||||
|
|
||||||
|
let keeper: Keeper
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
ConfigProvider.configure(config)
|
||||||
|
await ContractHandler.deployContracts()
|
||||||
|
keeper = await Keeper.getInstance()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Keeper", () => {
|
||||||
|
|
||||||
|
describe("public interface", () => {
|
||||||
|
|
||||||
|
it("should have market", () => {
|
||||||
|
assert(keeper.market !== null)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should have auth", () => {
|
||||||
|
assert(keeper.auth !== null)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should have token", () => {
|
||||||
|
assert(keeper.token !== null)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#getNetworkName()", () => {
|
||||||
|
|
||||||
|
it("should get development as default", async () => {
|
||||||
|
const networkName: string = await keeper.getNetworkName()
|
||||||
|
assert(networkName === "development")
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
5
test/mocha.opts
Normal file
5
test/mocha.opts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
--require ts-node/register
|
||||||
|
--require source-map-support/register
|
||||||
|
--full-trace
|
||||||
|
--bail
|
||||||
|
test/**/*.test.ts
|
15
test/mocks/ContractBase.Mock.ts
Normal file
15
test/mocks/ContractBase.Mock.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import ContractBase from "../../src/keeper/ContractBase"
|
||||||
|
|
||||||
|
export default class ContractBaseMock extends ContractBase {
|
||||||
|
public async initMock() {
|
||||||
|
this.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
public async callMock(name: string, args: any[], from?: string) {
|
||||||
|
return this.call(name, args, from)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async sendMock(name: string, from: string, args: any[]) {
|
||||||
|
return this.sendTransaction(name, from, args)
|
||||||
|
}
|
||||||
|
}
|
4
test/mocks/OceanBase.Mock.ts
Normal file
4
test/mocks/OceanBase.Mock.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import OceanBase from "../../src/ocean/OceanBase"
|
||||||
|
|
||||||
|
export default class OceanBaseMock extends OceanBase {
|
||||||
|
}
|
8
test/mocks/Provider.Mock.ts
Normal file
8
test/mocks/Provider.Mock.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import Provider from "../../src/provider/Provider"
|
||||||
|
|
||||||
|
export default class ProviderMock extends Provider {
|
||||||
|
|
||||||
|
public static async getAccessUrl(accessToken: any, payload: any): Promise<string> {
|
||||||
|
return "http://test/test"
|
||||||
|
}
|
||||||
|
}
|
78
test/ocean/Account.test.ts
Normal file
78
test/ocean/Account.test.ts
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import {assert} from "chai"
|
||||||
|
import ConfigProvider from "../../src/ConfigProvider"
|
||||||
|
import ContractHandler from "../../src/keeper/ContractHandler"
|
||||||
|
import Web3Provider from "../../src/keeper/Web3Provider"
|
||||||
|
import Account from "../../src/ocean/Account"
|
||||||
|
import Ocean from "../../src/ocean/Ocean"
|
||||||
|
import config from "../config"
|
||||||
|
|
||||||
|
let ocean: Ocean
|
||||||
|
let accounts: Account[]
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
ConfigProvider.configure(config)
|
||||||
|
await ContractHandler.deployContracts()
|
||||||
|
ocean = await Ocean.getInstance(config)
|
||||||
|
|
||||||
|
accounts = await ocean.getAccounts()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Account", () => {
|
||||||
|
|
||||||
|
describe("#getOceanBalance()", () => {
|
||||||
|
|
||||||
|
it("should get initial ocean balance", async () => {
|
||||||
|
|
||||||
|
const balance = await accounts[8].getOceanBalance()
|
||||||
|
|
||||||
|
assert(0 === balance, `Expected 0 got ${balance}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should get the correct balance", async () => {
|
||||||
|
|
||||||
|
const amount: number = 100
|
||||||
|
const account: Account = accounts[0]
|
||||||
|
await account.requestTokens(amount)
|
||||||
|
const balance = await account.getOceanBalance()
|
||||||
|
|
||||||
|
assert(amount === balance)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#getEthBalance()", () => {
|
||||||
|
|
||||||
|
it("should get initial ether balance", async () => {
|
||||||
|
|
||||||
|
const account: Account = accounts[9]
|
||||||
|
const balance = await account.getEtherBalance()
|
||||||
|
const web3 = Web3Provider.getWeb3()
|
||||||
|
|
||||||
|
assert(Number(web3.utils.toWei("100", "ether")) === balance)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#getBalance()", () => {
|
||||||
|
|
||||||
|
it("should get initial balance", async () => {
|
||||||
|
|
||||||
|
const account: Account = accounts[9]
|
||||||
|
const balance = await account.getBalance()
|
||||||
|
const web3 = Web3Provider.getWeb3()
|
||||||
|
|
||||||
|
assert(Number(web3.utils.toWei("100", "ether")) === balance.eth)
|
||||||
|
assert(0 === balance.ocn)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#requestTokens()", () => {
|
||||||
|
|
||||||
|
it("should return the amount of tokens granted", async () => {
|
||||||
|
|
||||||
|
const tokens = 500
|
||||||
|
const account: Account = accounts[0]
|
||||||
|
const tokensGranted: number = await account.requestTokens(tokens)
|
||||||
|
|
||||||
|
assert(tokensGranted === tokens)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
47
test/ocean/Asset.test.ts
Normal file
47
test/ocean/Asset.test.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import {assert} from "chai"
|
||||||
|
import ConfigProvider from "../../src/ConfigProvider"
|
||||||
|
import ContractHandler from "../../src/keeper/ContractHandler"
|
||||||
|
import Account from "../../src/ocean/Account"
|
||||||
|
import Asset from "../../src/ocean/Asset"
|
||||||
|
import Ocean from "../../src/ocean/Ocean"
|
||||||
|
import Order from "../../src/ocean/Order"
|
||||||
|
import ProviderProvider from "../../src/provider/ProviderProvider"
|
||||||
|
import config from "../config"
|
||||||
|
import ProviderMock from "../mocks/Provider.Mock"
|
||||||
|
|
||||||
|
const testName = "Test Asset 2"
|
||||||
|
const testDescription = "This asset is pure owange"
|
||||||
|
const testPrice = 100
|
||||||
|
const timeout = 100000
|
||||||
|
|
||||||
|
let ocean: Ocean
|
||||||
|
let testAsset: Asset
|
||||||
|
let accounts: Account[]
|
||||||
|
let testPublisher: Account
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
ConfigProvider.configure(config)
|
||||||
|
ProviderProvider.setProvider(ProviderMock)
|
||||||
|
|
||||||
|
await ContractHandler.deployContracts()
|
||||||
|
ocean = await Ocean.getInstance(config)
|
||||||
|
accounts = await ocean.getAccounts()
|
||||||
|
testPublisher = accounts[0]
|
||||||
|
testAsset = new Asset(testName, testDescription, testPrice, testPublisher)
|
||||||
|
|
||||||
|
await ocean.register(testAsset)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Asset", () => {
|
||||||
|
|
||||||
|
describe("#purchase()", () => {
|
||||||
|
|
||||||
|
it("should purchase an asset", async () => {
|
||||||
|
|
||||||
|
// todo
|
||||||
|
const consumerAccount = accounts[5]
|
||||||
|
const order: Order = await testAsset.purchase(consumerAccount, timeout)
|
||||||
|
assert(order)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
87
test/ocean/Ocean.test.ts
Normal file
87
test/ocean/Ocean.test.ts
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import {assert} from "chai"
|
||||||
|
import ConfigProvider from "../../src/ConfigProvider"
|
||||||
|
import ContractHandler from "../../src/keeper/ContractHandler"
|
||||||
|
import Account from "../../src/ocean/Account"
|
||||||
|
import Asset from "../../src/ocean/Asset"
|
||||||
|
import Ocean from "../../src/ocean/Ocean"
|
||||||
|
import Order from "../../src/ocean/Order"
|
||||||
|
import config from "../config"
|
||||||
|
|
||||||
|
let ocean: Ocean
|
||||||
|
let accounts: Account[]
|
||||||
|
let testAsset: Asset
|
||||||
|
let testPublisher: Account
|
||||||
|
|
||||||
|
const name = "Test Asset 3"
|
||||||
|
const description = "This asset is pure owange"
|
||||||
|
const price = 100
|
||||||
|
const timeout = 100000000
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
ConfigProvider.configure(config)
|
||||||
|
await ContractHandler.deployContracts()
|
||||||
|
ocean = await Ocean.getInstance(config)
|
||||||
|
accounts = await ocean.getAccounts()
|
||||||
|
|
||||||
|
testPublisher = accounts[0]
|
||||||
|
testAsset = new Asset(name, description, price, testPublisher)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Ocean", () => {
|
||||||
|
|
||||||
|
describe("#getInstance()", () => {
|
||||||
|
|
||||||
|
it("should list accounts", async () => {
|
||||||
|
|
||||||
|
const ocn = Ocean.getInstance(config)
|
||||||
|
|
||||||
|
assert(ocn)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#getAccounts()", () => {
|
||||||
|
|
||||||
|
it("should list accounts", async () => {
|
||||||
|
|
||||||
|
const accs: Account[] = await ocean.getAccounts()
|
||||||
|
|
||||||
|
assert(10 === accs.length)
|
||||||
|
assert(0 === (await accs[5].getBalance()).ocn)
|
||||||
|
assert("string" === typeof accs[0].getId())
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#register()", () => {
|
||||||
|
|
||||||
|
it("should register an asset", async () => {
|
||||||
|
|
||||||
|
const assetId: string = await ocean.register(testAsset)
|
||||||
|
|
||||||
|
assert(assetId.length === 66)
|
||||||
|
assert(assetId.startsWith("0x"))
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#getOrdersByConsumer()", () => {
|
||||||
|
|
||||||
|
it("should list orders", async () => {
|
||||||
|
|
||||||
|
const testConsumer = accounts[1]
|
||||||
|
const asset: Asset = new Asset("getOrdersByConsumer test", description, price, testPublisher)
|
||||||
|
|
||||||
|
await ocean.register(asset)
|
||||||
|
|
||||||
|
const order: Order = await asset.purchase(testConsumer, timeout)
|
||||||
|
const orders = await ocean.getOrdersByAccount(testConsumer)
|
||||||
|
|
||||||
|
assert(orders.length === 1)
|
||||||
|
assert(orders[0].getId() === order.getId())
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
30
test/ocean/OceanBase.test.ts
Normal file
30
test/ocean/OceanBase.test.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import {assert} from "chai"
|
||||||
|
import OceanBaseMock from "../mocks/OceanBase.Mock"
|
||||||
|
|
||||||
|
describe("OceanBase", () => {
|
||||||
|
|
||||||
|
describe("#getId()", () => {
|
||||||
|
|
||||||
|
it("should get the id", async () => {
|
||||||
|
|
||||||
|
const id = "test"
|
||||||
|
const oceanBase = new OceanBaseMock(id)
|
||||||
|
|
||||||
|
assert(oceanBase.getId() === id)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#setId()", () => {
|
||||||
|
|
||||||
|
it("should get the id", async () => {
|
||||||
|
|
||||||
|
const id = "test"
|
||||||
|
const oceanBase = new OceanBaseMock()
|
||||||
|
oceanBase.setId(id)
|
||||||
|
|
||||||
|
assert(oceanBase.getId() === id)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
101
test/ocean/Order.test.ts
Normal file
101
test/ocean/Order.test.ts
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import {assert} from "chai"
|
||||||
|
import ConfigProvider from "../../src/ConfigProvider"
|
||||||
|
import ContractHandler from "../../src/keeper/ContractHandler"
|
||||||
|
import AccessStatus from "../../src/models/AccessStatus"
|
||||||
|
import Account from "../../src/ocean/Account"
|
||||||
|
import Asset from "../../src/ocean/Asset"
|
||||||
|
import Ocean from "../../src/ocean/Ocean"
|
||||||
|
import Order from "../../src/ocean/Order"
|
||||||
|
import config from "../config"
|
||||||
|
|
||||||
|
const testName = "Order Test Asset"
|
||||||
|
const testDescription = "This asset is pure owange"
|
||||||
|
const testPrice = 100
|
||||||
|
const timeout = 1000000
|
||||||
|
const accessToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzM4NCJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1Mzk3ODcxMDEsImV4cCI6NDcyNjk5NjcwNCwiYXVkIjoiIiwic3ViIjoiIiwic2VydmljZV9lbmRwb2ludCI6Imh0dHA6Ly9hZGFzZCIsInJlc291cmNlX2lkIjoiMTIzNDUifQ.2H3TRC3CAToVE9divSckwHi_HNvgOHKrtJPo8128qrKBHTk7YYb0UNfVCuYqwhGR"
|
||||||
|
|
||||||
|
let ocean: Ocean
|
||||||
|
let testAsset: Asset
|
||||||
|
let accounts: Account[]
|
||||||
|
let testPublisher: Account
|
||||||
|
let testConsumer: Account
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
ConfigProvider.configure(config)
|
||||||
|
await ContractHandler.deployContracts()
|
||||||
|
ocean = await Ocean.getInstance(config)
|
||||||
|
accounts = await ocean.getAccounts()
|
||||||
|
testPublisher = accounts[0]
|
||||||
|
testConsumer = accounts[1]
|
||||||
|
// register an asset to play around with
|
||||||
|
testAsset = new Asset(testName, testDescription, testPrice, testPublisher)
|
||||||
|
await ocean.register(testAsset)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Order", () => {
|
||||||
|
|
||||||
|
describe("#pay()", async () => {
|
||||||
|
|
||||||
|
it("should pay for an order", async () => {
|
||||||
|
|
||||||
|
const order: Order = await testAsset.purchase(testConsumer, timeout)
|
||||||
|
assert(order)
|
||||||
|
|
||||||
|
await order.commit(accessToken)
|
||||||
|
await testConsumer.requestTokens(testAsset.price)
|
||||||
|
const paymentId: string = await order.pay(testConsumer)
|
||||||
|
assert(paymentId)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#commit()", async () => {
|
||||||
|
|
||||||
|
it("should commit the order", async () => {
|
||||||
|
|
||||||
|
const order: Order = await testAsset.purchase(testConsumer, timeout)
|
||||||
|
assert(order)
|
||||||
|
|
||||||
|
await order.commit(accessToken)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#getStatus()", async () => {
|
||||||
|
|
||||||
|
it("should get status Requested on new order", async () => {
|
||||||
|
|
||||||
|
const order: Order = await testAsset.purchase(testConsumer, timeout)
|
||||||
|
assert(order)
|
||||||
|
|
||||||
|
const status: AccessStatus = await order.getStatus()
|
||||||
|
assert(status === AccessStatus.Requested)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should get status Delivered on commited order", async () => {
|
||||||
|
|
||||||
|
const order: Order = await testAsset.purchase(testConsumer, timeout)
|
||||||
|
assert(order)
|
||||||
|
|
||||||
|
await order.commit(accessToken)
|
||||||
|
|
||||||
|
const status: AccessStatus = await order.getStatus()
|
||||||
|
assert(status === AccessStatus.Delivered)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#consume()", () => {
|
||||||
|
|
||||||
|
it("should consume an asset", async () => {
|
||||||
|
const consumerAccount = accounts[5]
|
||||||
|
await consumerAccount.requestTokens(testAsset.price)
|
||||||
|
// place order - consumer
|
||||||
|
const order: Order = await testAsset.purchase(consumerAccount, timeout)
|
||||||
|
// commit order - provider
|
||||||
|
await order.commit(accessToken)
|
||||||
|
// pay order - consumer
|
||||||
|
await order.pay(consumerAccount)
|
||||||
|
const url = await order.consume(consumerAccount)
|
||||||
|
assert(url)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
9
test/tsconfig.json
Normal file
9
test/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": [
|
||||||
|
"es6",
|
||||||
|
"es7"
|
||||||
|
],
|
||||||
|
"noUnusedLocals": true
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,12 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"resolveJsonModule": true,
|
||||||
"lib": [
|
"lib": [
|
||||||
"es6",
|
"es6",
|
||||||
"es7"
|
"es7"
|
||||||
],
|
],
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
"removeComments": true,
|
"removeComments": true,
|
||||||
@ -18,6 +20,6 @@
|
|||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules",
|
"node_modules",
|
||||||
"**/*.spec.ts"
|
"**/*.test.ts"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,11 @@
|
|||||||
],
|
],
|
||||||
"jsRules": {},
|
"jsRules": {},
|
||||||
"rules": {
|
"rules": {
|
||||||
"object-literal-sort-keys": false
|
"object-literal-sort-keys": false,
|
||||||
|
"semicolon": [
|
||||||
|
true,
|
||||||
|
"never"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"rulesDirectory": []
|
"rulesDirectory": []
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user