From 11a7fdd7a6479e4fcd57a37ece1595465f275b9b Mon Sep 17 00:00:00 2001 From: Max Berman Date: Thu, 2 Jan 2020 15:13:06 +0100 Subject: [PATCH] initial prototype --- .prettierrc | 7 +- .../src/components/molecules/AssetTeaser.tsx | 2 +- .../components/molecules/NetworkSwitcher.tsx | 25 +- client/src/config.ts | 101 ++-- client/src/context/BurnerWalletProvider.ts | 74 +-- client/src/context/UserProvider.tsx | 493 ++++++++++-------- client/src/context/index.tsx | 56 +- 7 files changed, 407 insertions(+), 351 deletions(-) diff --git a/.prettierrc b/.prettierrc index 96fdb0b..49955e2 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,6 +1,5 @@ { - "semi": false, - "singleQuote": true, - "trailingComma": "none", - "tabWidth": 2 + "semi": false, + "singleQuote": true, + "trailingComma": "none" } diff --git a/client/src/components/molecules/AssetTeaser.tsx b/client/src/components/molecules/AssetTeaser.tsx index 07a6e56..ea64f71 100644 --- a/client/src/components/molecules/AssetTeaser.tsx +++ b/client/src/components/molecules/AssetTeaser.tsx @@ -46,7 +46,7 @@ const AssetTeaser = ({ /> )}

{main.name}

- +

test

{!minimal && (
diff --git a/client/src/components/molecules/NetworkSwitcher.tsx b/client/src/components/molecules/NetworkSwitcher.tsx index 6938e08..8e44d92 100644 --- a/client/src/components/molecules/NetworkSwitcher.tsx +++ b/client/src/components/molecules/NetworkSwitcher.tsx @@ -1,15 +1,30 @@ -import React, { useState } from 'react' +import React, { useState, useContext } from 'react' import { CONNECTIONS } from '../../config' +import { User } from '../../context' /* NETWORK SWITCHER */ const urlParams = new URLSearchParams(window.location.search) const networkFromParam = urlParams.get('network') || 'pacific' -const idx = Object.keys(CONNECTIONS).indexOf(networkFromParam) -const commonsNetwork = Object.values(CONNECTIONS)[idx] // TypeScript won't let me access CONNECTIONS[networkFromParam] directly -console.log(commonsNetwork) +// console.log(Object.keys(CONNECTIONS)) export function NetworkSwitcher() { - return null + const userContext = useContext(User) + const switchNetwork = (networkName: string): any => { + const idx = Object.keys(CONNECTIONS).indexOf(networkName) + userContext.switchNetwork(networkName, Object.values(CONNECTIONS)[idx]) // TypeScript won't let me access CONNECTIONS[networkName] directly + } + return ( +
+
    + {Object.keys(CONNECTIONS).map((networkName, i) => ( +
  • switchNetwork(networkName)}> + {networkName} +
  • + ))} +
+ {userContext.network} +
+ ) } diff --git a/client/src/config.ts b/client/src/config.ts index 53be7e1..b68241d 100644 --- a/client/src/config.ts +++ b/client/src/config.ts @@ -2,26 +2,7 @@ // commons-server connection // export const serviceUri = - process.env.REACT_APP_SERVICE_URI || 'http://localhost:4000' - -// -// OCEAN REMOTE CONNECTIONS -// -export const nodeUri = - process.env.REACT_APP_NODE_URI || 'https://pacific.oceanprotocol.com' -export const aquariusUri = - process.env.REACT_APP_AQUARIUS_URI || - 'https://aquarius.commons.oceanprotocol.com' -export const brizoUri = - process.env.REACT_APP_BRIZO_URI || 'https://brizo.commons.oceanprotocol.com' -export const brizoAddress = - process.env.REACT_APP_BRIZO_ADDRESS || - '0x008c25ed3594e094db4592f4115d5fa74c4f41ea' -export const secretStoreUri = - process.env.REACT_APP_SECRET_STORE_URI || - 'https://secret-store.oceanprotocol.com' -export const faucetUri = - process.env.REACT_APP_FAUCET_URI || 'https://faucet.oceanprotocol.com' + process.env.REACT_APP_SERVICE_URI || 'http://localhost:4000' // // APP CONFIG @@ -30,40 +11,62 @@ export const verbose = true export const analyticsId = 'UA-60614729-11' export const showChannels = - process.env.REACT_APP_SHOW_CHANNELS === 'true' || false + process.env.REACT_APP_SHOW_CHANNELS === 'true' || false export const allowPricing = - process.env.REACT_APP_ALLOW_PRICING === 'true' || false + process.env.REACT_APP_ALLOW_PRICING === 'true' || false export const showRequestTokens = - process.env.REACT_APP_SHOW_REQUEST_TOKENS_BUTTON === 'true' || false + process.env.REACT_APP_SHOW_REQUEST_TOKENS_BUTTON === 'true' || false // https://ipfs.github.io/public-gateway-checker/ export const ipfsGatewayUri = - process.env.REACT_APP_IPFS_GATEWAY_URI || 'https://gateway.ipfs.io' + process.env.REACT_APP_IPFS_GATEWAY_URI || 'https://gateway.ipfs.io' export const ipfsNodeUri = - process.env.REACT_APP_IPFS_NODE_URI || 'https://ipfs.infura.io:5001' + process.env.REACT_APP_IPFS_NODE_URI || 'https://ipfs.infura.io:5001' + +// +// OCEAN REMOTE CONNECTIONS +// +export const nodeUri = + process.env.REACT_APP_NODE_URI || 'https://pacific.oceanprotocol.com' +export const aquariusUri = + process.env.REACT_APP_AQUARIUS_URI || + 'https://aquarius.commons.oceanprotocol.com' +export const brizoUri = + process.env.REACT_APP_BRIZO_URI || 'https://brizo.commons.oceanprotocol.com' +export const brizoAddress = + process.env.REACT_APP_BRIZO_ADDRESS || + '0x008c25ed3594e094db4592f4115d5fa74c4f41ea' +export const secretStoreUri = + process.env.REACT_APP_SECRET_STORE_URI || + 'https://secret-store.oceanprotocol.com' +export const faucetUri = + process.env.REACT_APP_FAUCET_URI || 'https://faucet.oceanprotocol.com' export const CONNECTIONS = { - pacific: { - nodeUri: 'https://pacific.oceanprotocol.com', - aquariusUri: 'https://aquarius.commons.oceanprotocol.com', - brizoUri: 'https://brizo.commons.oceanprotocol.com', - brizoAddress: '0x008c25ed3594e094db4592f4115d5fa74c4f41ea', - secretStoreUri: 'https://secret-store.oceanprotocol.com', - faucetUri: 'https://faucet.oceanprotocol.com' - }, - nile: { - nodeUri: 'https://nile.dev-ocean.com', - aquariusUri: 'https://aquarius.nile.dev-ocean.com', - brizoUri: 'https://brizo.nile.dev-ocean.com', - brizoAddress: '0x4aaab179035dc57b35e2ce066919048686f82972', - secretStoreUri: 'https://secret-store.nile.dev-ocean.com', - faucetUri: 'https://faucet.nile.dev-ocean.com' - }, - duero: { - nodeUri: 'https://duero.dev-ocean.com', - aquariusUri: 'https://aquarius.duero.dev-ocean.com', - brizoUri: 'https://brizo.duero.dev-ocean.com', - brizoAddress: '0x9d4ed58293f71122ad6a733c1603927a150735d0', - secretStoreUri: 'https://secret-store.duero.dev-ocean.com', - faucetUri: 'https://faucet.duero.dev-ocean.com' - } + Pacific: { + nodeUri: 'https://pacific.oceanprotocol.com', + aquariusUri: 'https://aquarius.commons.oceanprotocol.com', + brizoUri: 'https://brizo.commons.oceanprotocol.com', + brizoAddress: '0x008c25ed3594e094db4592f4115d5fa74c4f41ea', + secretStoreUri: 'https://secret-store.oceanprotocol.com', + //faucetUri: 'https://faucet.oceanprotocol.com', + verbose: true + }, + Nile: { + nodeUri: 'https://nile.dev-ocean.com', + aquariusUri: 'https://aquarius.nile.dev-ocean.com', + brizoUri: 'https://brizo.nile.dev-ocean.com', + brizoAddress: '0x4aaab179035dc57b35e2ce066919048686f82972', + secretStoreUri: 'https://secret-store.nile.dev-ocean.com', + //faucetUri: 'https://faucet.nile.dev-ocean.com', + verbose: true + }, + Duero: { + nodeUri: 'https://duero.dev-ocean.com', + aquariusUri: 'https://aquarius.duero.dev-ocean.com', + brizoUri: 'https://brizo.duero.dev-ocean.com', + brizoAddress: '0x9d4ed58293f71122ad6a733c1603927a150735d0', + secretStoreUri: 'https://secret-store.duero.dev-ocean.com', + //faucetUri: 'https://faucet.duero.dev-ocean.com', + verbose: true + } } diff --git a/client/src/context/BurnerWalletProvider.ts b/client/src/context/BurnerWalletProvider.ts index 395842b..dc37d80 100644 --- a/client/src/context/BurnerWalletProvider.ts +++ b/client/src/context/BurnerWalletProvider.ts @@ -7,47 +7,47 @@ import { requestFromFaucet } from '../ocean' const bip39 = require('bip39') export class BurnerWalletProvider { - private web3: Web3 + private web3: Web3 - public constructor() { - // Default - this.web3 = null as any - } - - public async isLogged() { - if (localStorage.getItem('seedphrase') !== null) { - return true - } - return false - } - - public async startLogin() { - let mnemonic - const isLogged = await this.isLogged() - - if (isLogged) { - mnemonic = localStorage.getItem('seedphrase') - } else { - mnemonic = bip39.generateMnemonic() - localStorage.setItem('seedphrase', mnemonic) + public constructor() { + // Default + this.web3 = null as any } - localStorage.setItem('logType', 'BurnerWallet') - const provider = new HDWalletProvider(mnemonic, nodeUri, 0, 1) - this.web3 = new Web3(provider as any) - const accounts = await this.web3.eth.getAccounts() - const balance = await this.web3.eth.getBalance(accounts[0]) + public async isLogged() { + if (localStorage.getItem('seedphrase') !== null) { + return true + } + return false + } - // fill with Ether if account balance is empty - balance === '0' && (await requestFromFaucet(provider.getAddress(0))) - } + public async startLogin() { + let mnemonic + const isLogged = await this.isLogged() - public async logout() { - // localStorage.removeItem('seedphrase') - localStorage.removeItem('logType') - } + if (isLogged) { + mnemonic = localStorage.getItem('seedphrase') + } else { + mnemonic = bip39.generateMnemonic() + localStorage.setItem('seedphrase', mnemonic) + } - public getProvider() { - return this.web3 - } + localStorage.setItem('logType', 'BurnerWallet') + const provider = new HDWalletProvider(mnemonic, nodeUri, 0, 1) + this.web3 = new Web3(provider as any) + const accounts = await this.web3.eth.getAccounts() + const balance = await this.web3.eth.getBalance(accounts[0]) + + // fill with Ether if account balance is empty + balance === '0' && (await requestFromFaucet(provider.getAddress(0))) + } + + public async logout() { + // localStorage.removeItem('seedphrase') + localStorage.removeItem('logType') + } + + public getProvider() { + return this.web3 + } } diff --git a/client/src/context/UserProvider.tsx b/client/src/context/UserProvider.tsx index 3eb2350..87cb881 100644 --- a/client/src/context/UserProvider.tsx +++ b/client/src/context/UserProvider.tsx @@ -6,263 +6,298 @@ import { provideOcean, requestFromFaucet, FaucetResponse } from '../ocean' import MarketProvider from './MarketProvider' import { MetamaskProvider } from './MetamaskProvider' import { BurnerWalletProvider } from './BurnerWalletProvider' - import { NetworkSwitcher } from '../components/molecules/NetworkSwitcher' import { - aquariusUri, - brizoUri, - brizoAddress, - nodeUri, - secretStoreUri, - verbose + aquariusUri, + brizoUri, + brizoAddress, + nodeUri, + secretStoreUri, + verbose, + CONNECTIONS } from '../config' +const oceanConfig = { + nodeUri, + aquariusUri, + brizoUri, + brizoAddress, + secretStoreUri, + verbose +} + const POLL_ACCOUNTS = 1000 // every 1s const POLL_NETWORK = POLL_ACCOUNTS * 60 // every 1 min const DEFAULT_WEB3 = new Web3(new Web3.providers.HttpProvider(nodeUri)) // default web3 interface UserProviderState { - isLogged: boolean - isBurner: boolean - isWeb3Capable: boolean - isLoading: boolean - account: string - balance: { - eth: number - ocn: number - } - network: string - web3: Web3 - ocean: Ocean - requestFromFaucet(account: string): Promise - loginMetamask(): Promise - loginBurnerWallet(): Promise - logoutBurnerWallet(): Promise - loadOcean: (config: Config) => Promise - message: string -} - -const oceanConfig = { - nodeUri, - aquariusUri, - brizoUri, - brizoAddress, - secretStoreUri, - verbose + isLogged: boolean + isBurner: boolean + isWeb3Capable: boolean + isLoading: boolean + account: string + balance: { + eth: number + ocn: number + } + network: string + web3: Web3 + ocean: Ocean + switchNetwork(network: string, config: Config): any + requestFromFaucet(account: string): Promise + loginMetamask(): Promise + loginBurnerWallet(): Promise + logoutBurnerWallet(): Promise + loadOcean: (config: Config) => Promise + message: string } export default class UserProvider extends PureComponent<{}, UserProviderState> { - private loginMetamask = async () => { - const metamaskProvider = new MetamaskProvider() - await metamaskProvider.startLogin() - const web3 = metamaskProvider.getProvider() - this.setState( - { - isLogged: true, - isBurner: false, - web3 - }, - () => { - this.loadOcean({ - web3Provider: this.state.web3, - ...oceanConfig - }) - } - ) - } - - private loginBurnerWallet = async () => { - const burnerwalletProvider = new BurnerWalletProvider() - await burnerwalletProvider.startLogin() - const web3 = burnerwalletProvider.getProvider() - this.setState( - { - isLogged: true, - isBurner: true, - web3 - }, - () => { - this.loadOcean({ - web3Provider: this.state.web3, - ...oceanConfig - }) - } - ) - } - - private logoutBurnerWallet = async () => { - const burnerwalletProvider = new BurnerWalletProvider() - await burnerwalletProvider.logout() - } - - public state = { - isLogged: false, - isBurner: false, - isWeb3Capable: Boolean(window.web3 || window.ethereum), - isLoading: true, - balance: { - eth: 0, - ocn: 0 - }, - network: '', - web3: DEFAULT_WEB3, - account: '', - ocean: {} as any, - requestFromFaucet: () => requestFromFaucet(''), - loginMetamask: () => this.loginMetamask(), - loginBurnerWallet: () => this.loginBurnerWallet(), - logoutBurnerWallet: () => this.logoutBurnerWallet(), - loadOcean: (config: Config) => this.loadOcean(config), - message: 'Connecting to Ocean...' - } - - private accountsInterval: any = null - - private networkInterval: any = null - - public async componentDidMount() { - await this.bootstrap() - } - - private initAccountsPoll() { - if (!this.accountsInterval) { - this.accountsInterval = setInterval(this.fetchAccounts, POLL_ACCOUNTS) - } - } - - private initNetworkPoll() { - if (!this.networkInterval) { - this.networkInterval = setInterval(this.fetchNetwork, POLL_NETWORK) - } - } - - private loadDefaultWeb3 = async () => { - this.setState( - { + public state = { isLogged: false, isBurner: false, - web3: DEFAULT_WEB3 - }, - () => { - this.loadOcean({ - web3Provider: this.state.web3, - ...oceanConfig - }) - } - ) - } + isWeb3Capable: Boolean(window.web3 || window.ethereum), + isLoading: true, + balance: { + eth: 0, + ocn: 0 + }, + network: '', + web3: DEFAULT_WEB3, + account: '', + ocean: {} as any, + requestFromFaucet: () => requestFromFaucet(''), + loginMetamask: () => this.loginMetamask(), + switchNetwork: (network: string, config: Config) => + this.switchNetwork(network, config), + loginBurnerWallet: () => this.loginBurnerWallet(), + logoutBurnerWallet: () => this.logoutBurnerWallet(), + loadOcean: (config: Config) => this.loadOcean(config), + message: 'Connecting to Ocean...' + } - private loadOcean = async (config: Config) => { - const { ocean } = await provideOcean({ - web3Provider: this.state.web3, - ...config - }) - this.setState({ ocean, isLoading: false }, () => { - this.initNetworkPoll() - this.initAccountsPoll() - this.fetchNetwork() - this.fetchAccounts() - }) - } - - private bootstrap = async () => { - const logType = localStorage.getItem('logType') - const metamaskProvider = new MetamaskProvider() - - switch (logType) { - case 'Metamask': - if ( - (await metamaskProvider.isAvailable()) && - (await metamaskProvider.isLogged()) - ) { - const web3 = metamaskProvider.getProvider() - this.setState( + private switchNetwork = (network: string, config: Config) => { + console.log({ network, config, oceanConfig }) + const nodeUrl: any = config.nodeUri + this.setState( { - isLogged: true, - web3 + network, + web3: new Web3(new Web3.providers.HttpProvider(nodeUrl)) + }, + () => + this.loadOcean({ + web3Provider: this.state.web3, + ...config + }) + ) + } + + private loginMetamask = async () => { + console.log('loginMetamask') + const metamaskProvider = new MetamaskProvider() + await metamaskProvider.startLogin() + const web3 = metamaskProvider.getProvider() + this.setState( + { + isLogged: true, + isBurner: false, + web3 }, () => { - this.loadOcean({ - web3Provider: this.state.web3, - ...oceanConfig - }) + this.loadOcean({ + web3Provider: this.state.web3, + ...oceanConfig + }) } - ) - } else { - this.loadDefaultWeb3() + ) + } + + private loginBurnerWallet = async () => { + console.log('loginMetamask') + const burnerwalletProvider = new BurnerWalletProvider() + await burnerwalletProvider.startLogin() + const web3 = burnerwalletProvider.getProvider() + this.setState( + { + isLogged: true, + isBurner: true, + web3 + }, + () => { + this.loadOcean({ + web3Provider: this.state.web3, + ...oceanConfig + }) + } + ) + } + + private logoutBurnerWallet = async () => { + const burnerwalletProvider = new BurnerWalletProvider() + await burnerwalletProvider.logout() + } + + private accountsInterval: any = null + + private networkInterval: any = null + + public async componentDidMount() { + await this.bootstrap() + } + + private initAccountsPoll() { + if (!this.accountsInterval) { + this.accountsInterval = setInterval( + this.fetchAccounts, + POLL_ACCOUNTS + ) } - break - case 'BurnerWallet': - this.loginBurnerWallet() - break - default: - this.loginBurnerWallet() - break } - } - private fetchAccounts = async () => { - const { ocean, isLogged } = this.state - - if (isLogged) { - let accounts - - // Modern dapp browsers - if (window.ethereum && !isLogged) { - // simply set to empty, and have user click a button somewhere - // to initiate account unlocking - accounts = [] - - // alternatively, automatically prompt for account unlocking - // await this.unlockAccounts() - } - - accounts = await ocean.accounts.list() - - if (accounts.length > 0) { - const account = await accounts[0].getId() - - if (account !== this.state.account) { - this.setState({ - account, - isLogged: true, - requestFromFaucet: () => requestFromFaucet(account) - }) - - await this.fetchBalance(accounts[0]) + private initNetworkPoll() { + if (!this.networkInterval) { + this.networkInterval = setInterval(this.fetchNetwork, POLL_NETWORK) } - } else { - !isLogged && this.setState({ isLogged: false, account: '' }) - } } - } - private fetchBalance = async (account: Account) => { - const balance = await account.getBalance() - const { eth, ocn } = balance - if (eth !== this.state.balance.eth || ocn !== this.state.balance.ocn) { - this.setState({ balance: { eth, ocn } }) + private loadDefaultWeb3 = async () => { + console.log('loadDefaultWeb3') + this.setState( + { + isLogged: false, + isBurner: false, + web3: DEFAULT_WEB3 + }, + () => { + this.loadOcean({ + web3Provider: this.state.web3, + ...oceanConfig + }) + } + ) } - } - private fetchNetwork = async () => { - const { ocean } = this.state - let network = 'Unknown' - if (ocean.keeper) { - network = await ocean.keeper.getNetworkName() + private loadOcean = async (config: Config) => { + console.log('load ocean', config) + const { ocean } = await provideOcean({ + web3Provider: this.state.web3, + ...config + }) + this.setState({ ocean, isLoading: false }, () => { + this.initNetworkPoll() + this.initAccountsPoll() + this.fetchNetwork() + this.fetchAccounts() + }) } - network !== this.state.network && this.setState({ network }) - } - public render() { - return ( - - - - {this.props.children} - - - ) - } + private bootstrap = async () => { + const logType = localStorage.getItem('logType') + const metamaskProvider = new MetamaskProvider() + + console.log('BOOTSTRAP', { logType }) + + switch (logType) { + case 'Metamask': + if ( + (await metamaskProvider.isAvailable()) && + (await metamaskProvider.isLogged()) + ) { + const web3 = metamaskProvider.getProvider() + this.setState( + { + isLogged: true, + web3 + }, + () => { + this.loadOcean({ + web3Provider: this.state.web3, + ...oceanConfig + }) + } + ) + } else { + this.loadDefaultWeb3() + } + break + case 'BurnerWallet': + this.loginBurnerWallet() + break + default: + this.loginBurnerWallet() + break + } + } + + private fetchAccounts = async () => { + const { ocean, isLogged } = this.state + + if (isLogged) { + let accounts + + // Modern dapp browsers + if (window.ethereum && !isLogged) { + // simply set to empty, and have user click a button somewhere + // to initiate account unlocking + accounts = [] + + // alternatively, automatically prompt for account unlocking + // await this.unlockAccounts() + } + + accounts = await ocean.accounts.list() + + if (accounts.length > 0) { + const account = await accounts[0].getId() + + if (account !== this.state.account) { + this.setState({ + account, + isLogged: true, + requestFromFaucet: () => requestFromFaucet(account) + }) + + await this.fetchBalance(accounts[0]) + } + } else { + !isLogged && + this.setState({ + isLogged: false, + account: '' + }) + } + } + } + + private fetchBalance = async (account: Account) => { + const balance = await account.getBalance() + const { eth, ocn } = balance + if (eth !== this.state.balance.eth || ocn !== this.state.balance.ocn) { + this.setState({ balance: { eth, ocn } }) + } + } + + private fetchNetwork = async () => { + const { ocean } = this.state + console.log('1 this.state.network', this.state.network) + let network = 'Unknown' + if (ocean.keeper) { + network = await ocean.keeper.getNetworkName() + } + + network !== this.state.network && this.setState({ network }) + console.log('2 this.state.network', this.state.network) + } + + public render() { + return ( + + + + {this.props.children} + + + ) + } } diff --git a/client/src/context/index.tsx b/client/src/context/index.tsx index ace840d..7df44b4 100644 --- a/client/src/context/index.tsx +++ b/client/src/context/index.tsx @@ -1,33 +1,37 @@ import React from 'react' +import { Config } from '@oceanprotocol/squid' export const User = React.createContext({ - isLogged: false, - isBurner: false, - isWeb3Capable: false, - isLoading: false, - account: '', - web3: {}, - ocean: {}, - balance: { - eth: 0, - ocn: 0 - }, - network: '', - requestFromFaucet: () => { - /* empty */ - }, - loginMetamask: () => { - /* empty */ - }, - loginBurnerWallet: () => { - /* empty */ - }, - message: '' + isLogged: false, + isBurner: false, + isWeb3Capable: false, + isLoading: false, + account: '', + web3: {}, + ocean: {}, + balance: { + eth: 0, + ocn: 0 + }, + network: '', + switchNetwork: (network: string, config: Config) => { + /* empty */ + }, + requestFromFaucet: () => { + /* empty */ + }, + loginMetamask: () => { + /* empty */ + }, + loginBurnerWallet: () => { + /* empty */ + }, + message: '' }) export const Market = React.createContext({ - totalAssets: 0, - categories: [''], - network: '', - networkMatch: false + totalAssets: 0, + categories: [''], + network: '', + networkMatch: false })