Example application for connecting blockchain wallet
This example will help you get started with the Rarible SDK. You will know how to:
- Connect and configure connection providers and wallet connectors in the Rarible SDK.
- Connect the available wallets via the web application.
Example Application is written using React. Also, you can try our prepared Example APP with advanced functionality.
Create React App
In the beginning, you will need to have Node.js and Yarn on your machine to create a new single-page application in React. Also, we will be use Typescript template.
To create a project, run:
yarn create react-app my-app --template typescript
Go to the project folder:
cd my-app
Install dependencies
Our Example App will use some dependencies. To install them, run:
yarn add @rarible/connector @rarible/connector-helper @rarible/connector-beacon @rarible/connector-fcl \
@rarible/connector-fortmatic @rarible/connector-iframe @rarible/connector-mew \
@rarible/connector-phantom @rarible/connector-portis @rarible/connector-solflare \
@rarible/connector-torus @rarible/connector-walletconnect @rarible/connector-immutablex-link \
@rarible/connector-walletlink @rarible/sdk @rarible/types
Connectors setup
Create the src/connector
folder and connectors-setup.ts
file there. It lists all the wallets that our application can connect to.
??? example "connectors-setup.ts"
```typescript
import { NetworkType as TezosNetwork } from "@airgap/beacon-sdk"
import { RaribleSdkEnvironment } from "@rarible/sdk/build/config/domain"
import {
ConnectionProvider,
Connector,
IConnectorStateProvider,
InjectedWeb3ConnectionProvider,
} from "@rarible/connector"
import { FclConnectionProvider } from "@rarible/connector-fcl"
import { MEWConnectionProvider } from "@rarible/connector-mew"
import { BeaconConnectionProvider } from "@rarible/connector-beacon"
import { TorusConnectionProvider } from "@rarible/connector-torus"
import { WalletLinkConnectionProvider } from "@rarible/connector-walletlink"
import { WalletConnectConnectionProvider } from "@rarible/connector-walletconnect"
import { PhantomConnectionProvider } from "@rarible/connector-phantom"
import { SolflareConnectionProvider } from "@rarible/connector-solflare"
import type { IWalletAndAddress } from "@rarible/connector-helper"
import { mapEthereumWallet, mapFlowWallet, mapSolanaWallet, mapTezosWallet, mapImmutableXWallet } from "@rarible/connector-helper"
// import { FortmaticConnectionProvider } from "@rarible/connector-fortmatic"
// import { PortisConnectionProvider } from "@rarible/connector-portis"
const ethereumRpcMap: Record<number, string> = {
1: "https://node-mainnet.rarible.com",
3: "https://node-ropsten.rarible.com",
4: "https://node-rinkeby.rarible.com",
17: "https://node-e2e.rarible.com",
137: "https://polygon-rpc.com",
80001: "https://rpc-mumbai.matic.today"
}
const ethereumNetworkMap: Record<number, string> = {
1: "mainnet",
3: "ropsten",
4: "rinkeby",
17: "e2e",
137: "polygon",
80001: "mumbai"
}
function environmentToEthereumChainId(environment: RaribleSdkEnvironment) {
switch (environment) {
case "prod":
return 1
case "dev":
return 3
case "e2e":
return 17
case "staging":
default:
return 4
}
}
function environmentToFlowNetwork(environment: RaribleSdkEnvironment) {
switch (environment) {
case "prod":
return {
network: "mainnet",
accessNode: "https://access.onflow.org",
walletDiscovery: "https://flow-wallet.blocto.app/authn",
}
case "dev":
case "e2e":
case "staging":
default:
return {
network: "testnet",
accessNode: "https://access-testnet.onflow.org",
walletDiscovery: "https://flow-wallet-testnet.blocto.app/authn",
}
}
}
function environmentToTezosNetwork(environment: RaribleSdkEnvironment) {
switch (environment) {
case "prod":
return {
accessNode: "https://tezos-node.rarible.org",
network: TezosNetwork.MAINNET
}
case "dev":
case "e2e":
case "staging":
default:
return {
accessNode: "https://test-tezos-node.rarible.org",
network: TezosNetwork.ITHACANET
}
}
}
function environmentToImmutableXEnv(environment: RaribleSdkEnvironment) {
switch (environment) {
case "prod":
return "prod"
default:
return "dev"
}
}
const state: IConnectorStateProvider = {
async getValue(): Promise<string | undefined> {
const value = localStorage.getItem("saved_provider")
return value ? value : undefined
},
async setValue(value: string | undefined): Promise<void> {
localStorage.setItem("saved_provider", value || "")
},
}
export function getConnector(environment: RaribleSdkEnvironment): Connector<string, IWalletAndAddress> {
const ethChainId = environmentToEthereumChainId(environment)
const ethNetworkName = ethereumNetworkMap[ethChainId]
const isEthNetwork = ["mainnet", "ropsten", "rinkeby"].includes(ethNetworkName)
const flowNetwork = environmentToFlowNetwork(environment)
const tezosNetwork = environmentToTezosNetwork(environment)
const injected = mapEthereumWallet(new InjectedWeb3ConnectionProvider())
const mew = mapEthereumWallet(new MEWConnectionProvider({
networkId: ethChainId,
rpcUrl: ethereumRpcMap[ethChainId]
}))
const beacon: ConnectionProvider<"beacon", IWalletAndAddress> = mapTezosWallet(new BeaconConnectionProvider({
appName: "Rarible Test",
accessNode: tezosNetwork.accessNode,
network: tezosNetwork.network
}))
const fcl = mapFlowWallet(new FclConnectionProvider({
accessNode: flowNetwork.accessNode,
walletDiscovery: flowNetwork.walletDiscovery,
network: flowNetwork.network,
applicationTitle: "Rari Test",
applicationIcon: "https://rarible.com/favicon.png?2d8af2455958e7f0c812"
}))
let torus = undefined
if (isEthNetwork) {
torus = mapEthereumWallet(new TorusConnectionProvider({
network: {
host: ethNetworkName
}
}))
}
const walletLink = mapEthereumWallet(new WalletLinkConnectionProvider({
networkId: ethChainId,
estimationUrl: ethereumRpcMap[ethChainId],
url: ethereumRpcMap[ethChainId]
}, {
appName: "Rarible",
appLogoUrl: "https://rarible.com/static/logo-500.static.png",
darkMode: false,
}))
const walletConnect = mapEthereumWallet(new WalletConnectConnectionProvider({
rpc: ethereumRpcMap,
chainId: ethChainId,
}))
const phantomConnect = mapSolanaWallet(new PhantomConnectionProvider())
const solflareConnect = mapSolanaWallet(new SolflareConnectionProvider({
network: environment === "prod" ? "mainnet-beta" : "devnet"
}))
const imxConnect = mapImmutableXWallet(new ImmutableXLinkConnectionProvider({
env: environmentToImmutableXEnv(environment),
}))
// Providers required secrets
// const fortmatic = mapEthereumWallet(new FortmaticConnectionProvider({ apiKey: "ENTER", ethNetwork: { chainId: 4, rpcUrl: "https://node-rinkeby.rarible.com" } }))
// const portis = mapEthereumWallet(new PortisConnectionProvider({ appId: "ENTER", network: "rinkeby" }))
const connector = Connector
.create(injected, state)
.add(walletLink)
.add(mew)
.add(beacon)
.add(fcl)
.add(walletConnect)
.add(phantomConnect)
.add(solflareConnect)
.add(imxConnect)
// .add(portis)
// .add(fortmatic)
if (torus) {
return connector.add(torus)
}
return connector
}
```
Connection provider
Create the sdk-connection-provider.tsx
file in src/connector
folder. Connection provider needed for handling blockchain wallet connection and pass connection and SDK objects to React context for using them in any app components.
??? example "sdk-connection-provider.tsx"
```typescript
import React, { useEffect, useState } from "react"
import { createRaribleSdk } from "@rarible/sdk"
import type { ConnectionState } from "@rarible/connector"
import { getStateDisconnected, IConnector } from "@rarible/connector"
import { IRaribleSdk } from "@rarible/sdk/build/domain"
import type { IWalletAndAddress } from "@rarible/connector-helper"
import { RaribleSdkEnvironment } from "@rarible/sdk/build/config/domain"
import { getConnector } from "./connectors-setup"
export interface IConnectorContext {
connector?: IConnector<string, IWalletAndAddress>
state: ConnectionState<IWalletAndAddress>
sdk?: IRaribleSdk
walletAddress?: string
}
export const ConnectorContext = React.createContext<IConnectorContext>({
connector: undefined,
state: getStateDisconnected(),
sdk: undefined,
walletAddress: undefined
})
const environment: RaribleSdkEnvironment = "development"
export function SdkConnectionProvider({children}: {children: React.ReactNode}) {
const [context, setContext] = useState<IConnectorContext>()
const [sdk, setSdk] = useState<IRaribleSdk>()
const connector = getConnector(environment)
useEffect(() => {
const subscription = connector.connection.subscribe(s => {
const sdkInstance = s.status === "connected" ? createRaribleSdk(s.connection.wallet, environment) : undefined
setSdk(sdkInstance)
const computedContext: IConnectorContext = {
connector,
state: s,
sdk,
walletAddress: s.status === "connected" ? s.connection.blockchain + ":" + s.connection.address : undefined,
}
setContext(computedContext)
})
return () => subscription.unsubscribe()
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return <ConnectorContext.Provider value={context!}>
{children}
</ConnectorContext.Provider>
}
```
Usage in APP
Create the src/pages
folder and main.tsx
file there. This will be the main page of our application.
??? example "main.tsx"
```typescript
import React, { useContext, useEffect, useState } from "react"
import { ConnectorContext } from "../connector/sdk-connection-provider"
import { ProviderOption } from "@rarible/connector"
import type { IWalletAndAddress } from "@rarible/connector-helper"
import { toOrderId } from "@rarible/types"
export function MainPage() {
const [options, setOptions] = useState<ProviderOption<string, IWalletAndAddress>[]>([])
const connection = useContext(ConnectorContext)
useEffect(() => {
connection?.connector?.getOptions().then(o => {
setOptions(o)
})
}, [connection])
const connect = async (option: ProviderOption<string, IWalletAndAddress>) => {
await connection.connector?.connect(option)
}
const disconnect = async () => {
if (connection?.state.status === "connected" && connection?.state?.disconnect) {
await connection?.state?.disconnect()
}
}
const someSdkAction = async () => {
const prepare = await connection?.sdk?.order.buy({orderId: toOrderId("orderid")})
await prepare?.submit({amount: 1})
}
return <div>
Connection status: {connection?.state?.status}
<div>
<button
disabled={connection?.state?.status !== "connected"}
onClick={disconnect}
>
Disconnect
</button>
</div>
{options.map((option, i) => {
return <div key={option.option}>
<button
onClick={() => connect(option)}
disabled={connection?.state?.status !== "disconnected"}
>
Connect {option.option}
</button>
</div>
})}
</div>
}
```
Now replace the contents of the App.tsx
file with the following:
??? example "App.tsx"
```typescript
import './App.css';
import {SdkConnectionProvider} from "./connector/sdk-connection-provider";
import { MainPage } from "./pages/main"
function App() {
return (
<SdkConnectionProvider>
<MainPage/>
</SdkConnectionProvider>
);
}
export default App;
```
Check the project structure. It will be looks like this:
To start application, run in project folder:
yarn start
If everything is configured correctly, the app will open at the http://localhost:3000/
address:
Now you can connect to any of the available wallets. For example, this is how the connection to MetaMask looks like.
That's it, now you have a working sample application on React! Try also our prepared Example APP with advanced functionality.
Polyfill modules in Webpack 5
If you are using webpack version 5 you may encounter errors with polyfill node core modules. Use the following steps to fix these errors:
-
Install
react-app-rewired
and missing dependenciesyarn add react-app-rewired crypto-browserify stream-browserify assert stream-http https-browserify \ os-browserify url buffer process path-browserify
-
Override the create-react-app webpack config file
??? example "config-overrides.js"
```javascript /* config-overrides.js */ const webpack = require('webpack'); module.exports = function override(config, env) { config.resolve.fallback = { url: require.resolve('url'), assert: require.resolve('assert'), crypto: require.resolve('crypto-browserify'), http: require.resolve('stream-http'), https: require.resolve('https-browserify'), os: require.resolve('os-browserify/browser'), buffer: require.resolve('buffer'), stream: require.resolve('stream-browserify'), path: require.resolve("path-browserify") }; config.plugins.push( new webpack.ProvidePlugin({ process: 'process/browser', Buffer: ['buffer', 'Buffer'], }), ); return config; } ```
-
Override
package.json
Replace
react-scripts
withreact-app-rewired
scripts for the three following scripts fields to update the webpack configuration inpackage.json
file. It will be looks like this:"scripts": { "start": "react-app-rewired start", "build": "react-app-rewired build", "test": "react-app-rewired test", "eject": "react-scripts eject" },
-
To start application, run in project folder:
yarn start
See more information about fixing polyfill node core modules here.
Updated 12 months ago