mirror of
https://github.com/oceanprotocol/react.git
synced 2024-12-24 10:06:20 +01:00
Merge pull request #66 from oceanprotocol/feature/cache-connect
refactor web3 connection
This commit is contained in:
commit
612fafc26b
57
README.md
57
README.md
@ -15,16 +15,16 @@
|
||||
|
||||
![iu](https://user-images.githubusercontent.com/90316/80356686-1650c080-887a-11ea-854e-bdc2bbdb0c20.jpeg)
|
||||
|
||||
**WE ARE IN HARDWARE MODE. This project is in a conceptual phase and most stuff does not work yet.**
|
||||
**WE ARE IN HARDWARE MODE. This project is in a conceptual phase and most stuff changes weekly. More importantly, it only works against bleeding edge v3 components of Ocean Protocol which are not completely public yet.**
|
||||
|
||||
---
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
- [🏗 Installation](#-installation)
|
||||
- [🏄 Usage](#-usage)
|
||||
- [1. Providers](#1-providers)
|
||||
- [2. Hooks](#2-hooks)
|
||||
- [🏄 Quick Start](#-quick-start)
|
||||
- [1. Add Provider](#1-add-provider)
|
||||
- [2. Use Hooks](#2-use-hooks)
|
||||
- [🦑 Development](#-development)
|
||||
- [✨ Code Style](#-code-style)
|
||||
- [👩🔬 Testing](#-testing)
|
||||
@ -43,62 +43,25 @@
|
||||
npm install @oceanprotocol/react
|
||||
```
|
||||
|
||||
## 🏄 Usage
|
||||
## 🏄 Quick Start
|
||||
|
||||
First, wrap your whole app with the [`Web3Provider`](src/providers/Web3Provider) and the [`OceanProvider`](src/providers/OceanProvider).
|
||||
### 1. Add Provider
|
||||
|
||||
### 1. Providers
|
||||
First, wrap your whole app with the [`<OceanProvider />`](src/providers/OceanProvider).
|
||||
|
||||
```tsx
|
||||
import React, { ReactNode } from 'react'
|
||||
import { Web3Provider, OceanProvider, Config } from '@oceanprotocol/react'
|
||||
|
||||
const config: Config = {
|
||||
nodeUri: '',
|
||||
aquariusUri: '',
|
||||
...
|
||||
}
|
||||
|
||||
export default function MyApp({
|
||||
children
|
||||
}: {
|
||||
children: ReactNode
|
||||
}): ReactNode {
|
||||
return (
|
||||
<Web3Provider>
|
||||
<OceanProvider config={config}>
|
||||
<h1>My App</h1>
|
||||
{children}
|
||||
</OceanProvider>
|
||||
)}
|
||||
</Web3Provider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
The `OceanProvider` requires a Web3 instance to be passed as prop so you can replace the basic [`Web3Provider`](src/providers/Web3Provider) with whatever component/library/provider returning a Web3 instance.
|
||||
|
||||
### 2. Hooks
|
||||
### 2. Use Hooks
|
||||
|
||||
Then within your component use the included hooks to interact with Ocean's functionality. Each hook can be used independently:
|
||||
|
||||
```tsx
|
||||
import React from 'react'
|
||||
import {
|
||||
useWeb3,
|
||||
useOcean,
|
||||
useMetadata,
|
||||
useConsume
|
||||
} from '@oceanprotocol/react'
|
||||
import { useOcean, useMetadata, useConsume } from '@oceanprotocol/react'
|
||||
|
||||
const did = 'did:op:0x000000000'
|
||||
|
||||
export default function MyComponent() {
|
||||
// Get web3 from built-in Web3Provider context
|
||||
const { web3 } = useWeb3()
|
||||
|
||||
// Get Ocean instance from built-in OceanProvider context
|
||||
const { ocean, account } = useOcean()
|
||||
const { ocean, web3, account } = useOcean()
|
||||
|
||||
// Get metadata for this asset
|
||||
const { title, metadata } = useMetadata(did)
|
||||
|
23
example/.gitignore
vendored
23
example/.gitignore
vendored
@ -1,23 +0,0 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
@ -0,0 +1,8 @@
|
||||
# Example
|
||||
|
||||
Simple example app based on Create React App.
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm start
|
||||
```
|
6279
example/package-lock.json
generated
6279
example/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -5,22 +5,19 @@
|
||||
"dependencies": {
|
||||
"-": "0.0.1",
|
||||
"@oceanprotocol/react": "file:../",
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.5.0",
|
||||
"@testing-library/user-event": "^7.2.1",
|
||||
"@toruslabs/torus-embed": "^1.7.3",
|
||||
"@toruslabs/torus-embed": "^1.8.2",
|
||||
"@types/jest": "^24.9.1",
|
||||
"@types/node": "^12.12.47",
|
||||
"@types/react": "^16.9.41",
|
||||
"@types/react-dom": "^16.9.8",
|
||||
"@types/shortid": "0.0.29",
|
||||
"@walletconnect/web3-provider": "^1.0.14",
|
||||
"@types/shortid": "^0.0.29",
|
||||
"@walletconnect/web3-provider": "^1.1.0",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-scripts": "3.4.1",
|
||||
"shortid": "^2.2.15",
|
||||
"typescript": "^3.7.5",
|
||||
"web3-eth-contract": "^1.2.9"
|
||||
"typescript": "^3.9.7",
|
||||
"web3-eth-contract": "^1.2.11"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
@ -31,16 +28,10 @@
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
"browserslist": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not ie <= 11",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -10,11 +10,6 @@
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
|
@ -1,25 +0,0 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
@ -7,7 +7,9 @@ import { Config } from '@oceanprotocol/lib'
|
||||
import { AllDdos } from './AllDdos'
|
||||
import { ConsumeDdo } from './ConsumeDdo'
|
||||
|
||||
function App() {
|
||||
import WalletConnectProvider from '@walletconnect/web3-provider'
|
||||
import Torus from '@toruslabs/torus-embed'
|
||||
|
||||
// factory Address needs to be updated each time you deploy the contract on local network
|
||||
const config = {
|
||||
metadataStoreUri: 'http://aquarius:5000',
|
||||
@ -22,14 +24,41 @@ function App() {
|
||||
nodeUri: `https://rinkeby.infura.io/a983b53583044593956054de049922fd`,
|
||||
factoryAddress: '0xB9d406D24B310A7D821D0b782a36909e8c925471'
|
||||
} as Config
|
||||
|
||||
const providerOptions = {
|
||||
walletconnect: {
|
||||
package: WalletConnectProvider,
|
||||
options: {
|
||||
infuraId: ''
|
||||
}
|
||||
},
|
||||
torus: {
|
||||
package: Torus,
|
||||
options: {
|
||||
networkParams: {
|
||||
host: config.nodeUri // optional
|
||||
// chainId: 1337, // optional
|
||||
// networkId: 1337 // optional
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const web3ModalOpts = {
|
||||
cacheProvider: true,
|
||||
providerOptions
|
||||
}
|
||||
|
||||
function App() {
|
||||
const init = async () => {}
|
||||
|
||||
useEffect(() => {
|
||||
init()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="app">
|
||||
<OceanProvider config={config}>
|
||||
<OceanProvider config={config} web3ModalOpts={web3ModalOpts}>
|
||||
<div className="container">
|
||||
<div>
|
||||
<Wallet />
|
||||
|
@ -29,10 +29,8 @@ export function Publish() {
|
||||
}
|
||||
}
|
||||
|
||||
const marketAddress = '0x4D156A2ef69ffdDC55838176C6712C90f60a2285'
|
||||
|
||||
const publishAsset = async () => {
|
||||
const ddo = await publish(asset as Metadata, '4', marketAddress, [
|
||||
const ddo = await publish(asset as Metadata, '4', [
|
||||
{ serviceType: 'access', cost: '1' },
|
||||
{ serviceType: 'compute', cost: '1' }
|
||||
])
|
||||
|
@ -3,29 +3,19 @@ import { useOcean } from '@oceanprotocol/react'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export function Wallet() {
|
||||
const { web3, ocean, connect, logout, accountId } = useOcean()
|
||||
const { ocean, connect, logout, accountId } = useOcean()
|
||||
|
||||
const conn = async () => {
|
||||
const { default: WalletConnectProvider } = await import(
|
||||
'@walletconnect/web3-provider'
|
||||
)
|
||||
const providerOptions = {
|
||||
/* See Provider Options Section */
|
||||
walletconnect: {
|
||||
package: WalletConnectProvider, // required
|
||||
options: {
|
||||
infuraId: 'INFURA_ID' // required
|
||||
}
|
||||
}
|
||||
await connect()
|
||||
}
|
||||
|
||||
await connect({ cacheProvider: true, providerOptions })
|
||||
}
|
||||
const init = async () => {
|
||||
if (ocean === undefined || accountId === undefined) return
|
||||
|
||||
const assets = await ocean.assets.ownerAssets(accountId)
|
||||
console.log(assets)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
init()
|
||||
}, [ocean, accountId])
|
||||
@ -34,6 +24,7 @@ export function Wallet() {
|
||||
await logout()
|
||||
await conn()
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>wallet</div>
|
||||
|
@ -27,7 +27,6 @@
|
||||
"dependencies": {
|
||||
"@oceanprotocol/lib": "^0.1.10",
|
||||
"axios": "^0.19.2",
|
||||
"react": "^16.13.1",
|
||||
"web3": "^1.2.11",
|
||||
"web3modal": "^1.9.0"
|
||||
},
|
||||
|
@ -10,6 +10,7 @@ import ProviderStatus from './ProviderStatus'
|
||||
import { Ocean, Logger, Account, Config } from '@oceanprotocol/lib'
|
||||
import Web3Modal, { ICoreOptions } from 'web3modal'
|
||||
import { getDefaultProviders } from './getDefaultProviders'
|
||||
import { getAccountId, getBalance } from '../../utils'
|
||||
|
||||
interface Balance {
|
||||
eth: string | undefined
|
||||
@ -35,9 +36,11 @@ const OceanContext = createContext(null)
|
||||
|
||||
function OceanProvider({
|
||||
config,
|
||||
web3ModalOpts,
|
||||
children
|
||||
}: {
|
||||
config: Config
|
||||
web3ModalOpts?: Partial<ICoreOptions>
|
||||
children: any
|
||||
}): ReactElement {
|
||||
const [web3, setWeb3] = useState<Web3 | undefined>()
|
||||
@ -54,13 +57,20 @@ function OceanProvider({
|
||||
const [status, setStatus] = useState<ProviderStatus>(
|
||||
ProviderStatus.NOT_AVAILABLE
|
||||
)
|
||||
const [web3ModalOpts, setWeb3ModalOpts] = useState<Partial<ICoreOptions>>()
|
||||
|
||||
function init() {
|
||||
async function init() {
|
||||
Logger.log('Ocean Provider init')
|
||||
window &&
|
||||
window.ethereum &&
|
||||
(window.ethereum.autoRefreshOnNetworkChange = false)
|
||||
|
||||
Logger.log('Web3Modal init.')
|
||||
if (web3ModalOpts === undefined) {
|
||||
web3ModalOpts = await getDefaultProviders()
|
||||
}
|
||||
const web3ModalInstance = new Web3Modal(web3ModalOpts)
|
||||
setWeb3Modal(web3ModalInstance)
|
||||
Logger.log('Web3Modal instance created.', web3ModalInstance)
|
||||
}
|
||||
|
||||
// On mount setup Web3Modal instance
|
||||
@ -68,18 +78,17 @@ function OceanProvider({
|
||||
init()
|
||||
}, [])
|
||||
|
||||
async function connect(opts?: Partial<ICoreOptions>) {
|
||||
Logger.log('Connecting ....')
|
||||
// Connect automatically to cached provider if present
|
||||
useEffect(() => {
|
||||
if (!web3Modal) return
|
||||
web3Modal.cachedProvider && connect()
|
||||
}, [web3Modal])
|
||||
|
||||
if (opts === undefined) {
|
||||
opts = await getDefaultProviders()
|
||||
}
|
||||
async function connect() {
|
||||
try {
|
||||
Logger.log('Connecting ...')
|
||||
|
||||
const instance = web3Modal || new Web3Modal(opts)
|
||||
setWeb3Modal(instance)
|
||||
Logger.log('Web3Modal instance created.', instance)
|
||||
|
||||
const provider = web3Provider || (await instance.connect())
|
||||
const provider = await web3Modal.connect()
|
||||
setWeb3Provider(provider)
|
||||
|
||||
const web3 = new Web3(provider)
|
||||
@ -108,62 +117,51 @@ function OceanProvider({
|
||||
const balance = await getBalance(account)
|
||||
setBalance(balance)
|
||||
Logger.log('balance', JSON.stringify(balance))
|
||||
} catch (error) {
|
||||
Logger.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
// ToDo check how is the proper way to logout
|
||||
// TODO: check how is the proper way to logout
|
||||
web3Modal.clearCachedProvider()
|
||||
}
|
||||
|
||||
async function getAccountId(web3: Web3) {
|
||||
const accounts = await web3.eth.getAccounts()
|
||||
return accounts[0]
|
||||
}
|
||||
|
||||
async function getBalance(account: Account) {
|
||||
const eth = await account.getEtherBalance()
|
||||
const ocean = await account.getOceanBalance()
|
||||
|
||||
return { eth, ocean }
|
||||
}
|
||||
|
||||
//
|
||||
// Listen for provider, account & network changes
|
||||
// and react to it
|
||||
//
|
||||
const handleConnect = async (provider: any) => {
|
||||
Logger.debug("Handling 'connect' event with payload", provider)
|
||||
}
|
||||
|
||||
// const handleConnect = async (provider: any) => {
|
||||
// Logger.debug("Handling 'connect' event with payload", provider)
|
||||
// }
|
||||
|
||||
const handleAccountsChanged = async (accounts: string[]) => {
|
||||
console.debug("Handling 'accountsChanged' event with payload", accounts)
|
||||
// if (status === ProviderStatus.CONNECTED) {
|
||||
connect(web3ModalOpts)
|
||||
// }
|
||||
Logger.debug("Handling 'accountsChanged' event with payload", accounts)
|
||||
connect()
|
||||
}
|
||||
|
||||
// ToDo need to handle this, it's not implemented, need to update chainId and reinitialize ocean lib
|
||||
const handleNetworkChanged = async (networkId: string | number) => {
|
||||
console.debug(
|
||||
"Handling 'networkChanged' event with payload",
|
||||
Logger.debug(
|
||||
"Handling 'chainChanged' event with payload",
|
||||
networkId,
|
||||
status
|
||||
)
|
||||
// if (status === ProviderStatus.CONNECTED) {
|
||||
connect(web3ModalOpts)
|
||||
// }
|
||||
connect()
|
||||
}
|
||||
|
||||
// TODO: Refetch balance periodically, or figure out some event to subscribe to
|
||||
|
||||
useEffect(() => {
|
||||
web3Modal && web3Modal.on('connect', handleConnect)
|
||||
// web3Modal && web3Modal.on('connect', handleConnect)
|
||||
|
||||
if (web3Provider !== undefined && web3Provider !== null) {
|
||||
web3Provider.on('accountsChanged', handleAccountsChanged)
|
||||
web3Provider.on('networkChanged', handleNetworkChanged)
|
||||
web3Provider.on('chainChanged', handleNetworkChanged)
|
||||
|
||||
return () => {
|
||||
web3Provider.removeListener('accountsChanged', handleAccountsChanged)
|
||||
web3Provider.removeListener('networkChanged', handleNetworkChanged)
|
||||
web3Provider.removeListener('chainChanged', handleNetworkChanged)
|
||||
}
|
||||
}
|
||||
}, [web3Modal, web3Provider])
|
||||
|
@ -2,11 +2,11 @@
|
||||
|
||||
The `OceanProvider` maintains a connection to the Ocean Protocol network in multiple steps:
|
||||
|
||||
1. On mount, connect to Aquarius instance right away so any asset metadata can be retrieved before, and independent of any Web3 connections.
|
||||
2. Once Web3 becomes available, a connection to all Ocean Protocol network components is established.
|
||||
3. Once Ocean becomes available, spits out some info about it.
|
||||
1. On mount, setup [Web3Modal](https://github.com/Web3Modal/).
|
||||
2. Once connection with Web3Modal is started, Web3 becomes available.
|
||||
3. Once Web3 becomes available, connection to Ocean Protocol components are initiated, all available under the `ocean` object.
|
||||
|
||||
Also provides a `useOcean` helper hook to access its context values from any component.
|
||||
With the included `useOcean` helper hook you can access all context values from any component.
|
||||
|
||||
## Usage
|
||||
|
||||
@ -18,36 +18,14 @@ import { OceanProvider, Config } from '@oceanprotocol/react'
|
||||
|
||||
const config: Config = {
|
||||
nodeUri: '',
|
||||
aquariusUri: '',
|
||||
metadataStoreUri: '',
|
||||
...
|
||||
}
|
||||
|
||||
export default function MyApp({
|
||||
children
|
||||
}: {
|
||||
children: ReactNode
|
||||
}): ReactNode {
|
||||
const web3 = await getWeb3()
|
||||
|
||||
return (
|
||||
<OceanProvider config={config} web3={web3}>
|
||||
<h1>My App</h1>
|
||||
{children}
|
||||
</OceanProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
The `OceanProvider` requires a Web3 instance to be passed as prop so you can either handle this with your own `getWeb3()`, or use the basic [`Web3Provider`](../Web3Provider):
|
||||
|
||||
```tsx
|
||||
import React, { ReactNode } from 'react'
|
||||
import { Web3Provider, OceanProvider, Config } from '@oceanprotocol/react'
|
||||
|
||||
const config: Config = {
|
||||
nodeUri: '',
|
||||
aquariusUri: '',
|
||||
...
|
||||
const web3ModalOpts = {
|
||||
network: 'mainnet', // optional
|
||||
cacheProvider: true, // optional
|
||||
providerOptions // required
|
||||
}
|
||||
|
||||
export default function MyApp({
|
||||
@ -56,30 +34,28 @@ export default function MyApp({
|
||||
children: ReactNode
|
||||
}): ReactNode {
|
||||
return (
|
||||
<Web3Provider>
|
||||
{({ web3 }) => (
|
||||
<OceanProvider config={config} web3={web3}>
|
||||
<OceanProvider config={config} web3ModalOpts={web3ModalOpts}>
|
||||
<h1>My App</h1>
|
||||
{children}
|
||||
</OceanProvider>
|
||||
)}
|
||||
</Web3Provider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
The `OceanProvider` uses [Web3Modal](https://github.com/Web3Modal/) to make its initial wallet connection. If you do not pass `web3ModalOpts` as a prop, only the default injected provider will be available. Adding more providers requires you to add them as dependencies to your project and pass them as `providerOptions`. See all the available [Provider Options](https://github.com/Web3Modal/web3modal#provider-options).
|
||||
|
||||
You can then access the provider context values with the `useOcean` hook:
|
||||
|
||||
```tsx
|
||||
import { useOcean } from '@oceanprotocol/react'
|
||||
|
||||
function MyComponent() {
|
||||
const { ocean, account } = useOcean()
|
||||
const { ocean, accountId } = useOcean()
|
||||
|
||||
return (
|
||||
<ul>
|
||||
<li>Ocean available: {`${Boolean(ocean)}`}</li>
|
||||
<li>Account: {account}</li>
|
||||
<li>Account: {accountId}</li>
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
@ -25,3 +25,5 @@ export const publishFeedback: { [key in number]: string } = {
|
||||
3: '3/4 Publishing asset ...',
|
||||
4: '4/4 Asset published succesfully'
|
||||
}
|
||||
|
||||
export * from './web3'
|
||||
|
15
src/utils/web3.ts
Normal file
15
src/utils/web3.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import Web3 from 'web3'
|
||||
import { Account } from '@oceanprotocol/lib'
|
||||
import { Balance } from '../providers'
|
||||
|
||||
export async function getAccountId(web3: Web3): Promise<string> {
|
||||
const accounts = await web3.eth.getAccounts()
|
||||
return accounts[0]
|
||||
}
|
||||
|
||||
export async function getBalance(account: Account): Promise<Balance> {
|
||||
const eth = await account.getEtherBalance()
|
||||
const ocean = await account.getOceanBalance()
|
||||
|
||||
return { eth, ocean }
|
||||
}
|
Loading…
Reference in New Issue
Block a user