@broxus/tvm-connect
Version:
Nekoton-compatible wallets connector.
460 lines (393 loc) • 15.9 kB
Markdown
# TVM Connect
Create a connection to Nekoton-based wallets and dApps
## Usage
### Installation
To apply this component, install it with **npm** using following command:
```shell
npm install @broxus/tvm-connect
```
or using **yarn**:
```shell
yarn add @broxus/tvm-connect
```
### Requirements
In general it works great with our [UIkit](https://www.npmjs.com/package/@broxus/react-uikit) package.
If you use this package you don't need to worry about the details.
#### Minimum requirements
If you are only going to use stores, you should install a few required packages
```shell
npm i everscale-inpage-provider mobx
```
or using **yarn**:
```shell
yarn add everscale-inpage-provider mobx
```
#### Full requirements
If you would like to use stores and included react components, you need to install a few more packages
```shell
npm i everscale-inpage-providermobx mobx-react-lite react-intl
```
or using **yarn**:
```shell
yarn add everscale-inpage-provider mobx-react-lite react-intl
```
### TvmWalletService
The Wallet Service is a key part of this module. It accepts a number of settings and
parameters when created and has a convenient interface for working with a connected wallet.
It works with wallets whose providers and standalone RPC connections are
based on `everscale-inpage-provider` and `everscale-standalone-client`.
```typescript
import { TvmChains } from '@broxus/js-core'
import { EverWallet, SparXWallet, TvmWalletProviderConfig, TvmWalletService, VenomWallet } from '@broxus/tvm-connect'
const sparxWallet: TvmWalletProviderConfig = {
connector: new SparXWallet(), // autoInit: false to prevent auto initialization. Optional
id: 'SparXWallet',
info: {
description: 'Your universal tool for TVM',
icon: '/assets/icons/SparXWallet.svg',
links: {
android: 'https://play.google.com/store/apps/details?id=com.broxus.sparx.app',
homepage: 'https://sparxwallet.com/',
ios: 'https://apps.apple.com/us/app/sparx-tvm-wallet/id6670219321',
},
name: 'SparX Wallet',
},
}
const everWallet: TvmWalletProviderConfig = {
connector: new EverWallet({ autoInit: false }), // autoInit: false to prevent auto initialization. Optional
id: 'EverWallet',
info: {
description: 'Premier wallet for the Everscale',
icon: '/assets/icons/EverWallet.svg',
links: {
android: 'https://play.google.com/store/apps/details?id=com.broxus.crystal.app',
chromeExtension: 'https://chrome.google.com/webstore/detail/ever-wallet/cgeeodpfagjceefieflmdfphplkenlfk',
firefoxExtension: 'https://addons.mozilla.org/en-GB/firefox/addon/ever-wallet/',
homepage: 'https://everwallet.net/',
ios: 'https://apps.apple.com/us/app/ever-wallet-everscale/id1581310780',
},
name: 'Ever Wallet',
},
}
const venomWallet: TvmWalletProviderConfig = {
connector: new VenomWallet(),
id: 'VenomWallet',
info: {
description: 'Safe, reliable, and 100% yours',
icon: '/assets/icons/VenomWallet.svg',
links: {
android: 'https://play.google.com/store/apps/details?id=com.venom.wallet',
chromeExtension: 'https://chrome.google.com/webstore/detail/venom-wallet/ojggmchlghnjlapmfbnjholfjkiidbch',
homepage: 'https://venomwallet.com/',
ios: 'https://apps.apple.com/app/venom-blockchain-wallet/id1622970889',
},
name: 'Venom Wallet',
},
}
const walletService = new TvmWalletService({
autoInit: false,
defaultNetworkId: TvmChains.EverscaleMainnet, // 42
networks: [
{
chainId: TvmChains.EverscaleMainnet.toString(),
currency: {
decimals: 9,
icon: '/assets/icons/EVER.svg',
name: 'Native currency',
symbol: 'EVER',
wrappedCurrencyAddress: new AddressLiteral(
'0:a49cd4e158a9a15555e624759e2e4e766d22600b7800d891e46f9291f044a93d'), // WEVER
},
explorer: {
accountsSubPath: 'accounts',
baseUrl: 'https://everscan.io',
title: 'EVER Scan',
transactionsSubPath: 'transactions',
},
id: `tvm-${TvmChains.EverscaleMainnet.toString()}`, // <type>-<chainId>
name: 'Everscale',
rpcUrl: 'https://jrpc.everwallet.net',
shortName: 'Everscale',
type: 'tvm',
},
],
providerId: 'EverWallet', // TvmWalletProviderConfig['id']
providers: [sparxWallet, everWallet, venomWallet],
})
await everWallet.connector.init()
const provider = await walletService.init()
await walletService.connect()
```
> This is library/framework agnostic component. So, you can use it anywhere.
## Using with React
First at all, you should wrap entire your app with `TvmWalletServiceProvider` to share `TvmWalletService`
through all your app components.
```typescript jsx
import { TvmConnector, TvmWalletServiceProvider } from '@broxus/tvm-connect'
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import { IntlProvider } from 'react-intl'
function App(): JSX.Element {
return (
<IntlProvider>
<TvmWalletServiceProvider>
...
<TvmConnector />
...
</TvmWalletServiceProvider>
</IntlProvider>
)
}
ReactDOM.render(<App />, document.body)
```
## Custom service
You may use `TvmWalletService` to create your own service with provider and connection.
```typescript jsx
import { TvmWalletService } from '@broxus/tvm-connect'
let service: TvmWalletService
export function useTvmConnect(): TvmWalletService {
if (service === undefined) {
service = new TvmWalletService({
defaultNetworkId: number, // (optional) use TvmChains enum to provide a network id
networks: TvmNetworkConfig[], // (optional) supported networks configurations
providerId: string, // (optional) TvmWalletProviderConfig['id'] provide option to define a default connector
providers: TvmWalletProviderConfig[], // (optional) providers config
})
}
return service
}
// Pass custom wallet to service provider
function App(): JSX.Element {
const wallet = useTvmConnect()
return (
<IntlProvider>
<TvmWalletServiceProvider wallet={wallet}>
...
<TvmConnector />
...
</TvmWalletServiceProvider>
</IntlProvider>
)
}
```
### Network configuration
Below you can see a models of the network configuration, native currency and explorer config.
```typescript
interface NativeCurrency<T = any> {
balance?: string
decimals: number
icon?: string
name?: string
symbol: string
wrappedCurrencyAddress?: T
}
interface TvmNetworkConfig {
badge?: string
chainId: string
currency: NativeCurrency<Address>
disabled?: boolean
explorer: NetworkExplorerConfig
icon?: string
id: string
name: string
rpcUrl: string
shortName: string
tokensListUri?: string
tokenType?: string
type: 'tvm'
}
interface NetworkExplorerConfig {
accountsSubPath?: string | null
baseUrl: string
title: string
tokensSubPath?: string | null
transactionsSubPath?: string | null
}
```
### Provider configuration
To use more providers (wallets) and their connections, you can configure these providers with
the `providers` option that can be passed when instantiating the `TvmWalletService`.
```typescript
interface TvmWalletProviderConfig {
connector: NekotonConnector
info: {
description?: string
icon?: string
links?: TvmProviderPlatformLinks & { homepage?: string, universalLink?: string }
name: string
}
id: string
isRecent?: boolean
minVersion?: string
}
export type TvmProviderAvailablePlatforms = 'ios' | 'android' | 'chromeExtension' | 'firefoxExtension'
export type TvmProviderPlatformLinks = Partial<Record<TvmProviderAvailablePlatforms, string>>
```
## Helpful utils
You can use `isEverWalletBrowser` or `isVenomWalletBrowser` to check environment.
- `isSparXWalletBrowser` - checks if your dApp is opened in mobile SparX Wallet WebView
- `isEverWalletBrowser` - checks if your dApp is opened in mobile Ever Wallet WebView
- `isVenomWalletBrowser` - checks if your dApp is opened in mobile Venom Wallet WebView
This will help you determine which connectors to use for mobile applications and for all other cases
```typescript
import { TvmChains } from '@broxus/js-core'
import { TvmWalletService, useRecentConnectionMeta } from '@broxus/tvm-connect'
import { AddressLiteral } from 'everscale-inpage-provider'
const providers: TvmWalletProviderConfig[] = []
const networks: TvmNetworkConfig[] = [
{
chainId: TvmChains.EverscaleMainnet.toString(),
currency: {
decimals: 9,
icon: '/assets/icons/EVER.svg',
name: 'EVER',
symbol: 'EVER',
wrappedCurrencyAddress: new AddressLiteral('0:a49cd4e158a9a15555e624759e2e4e766d22600b7800d891e46f9291f044a93d'),
},
explorer: {
accountsSubPath: 'accounts',
baseUrl: 'https://everscan.io',
title: 'EVER Scan',
transactionsSubPath: 'transactions',
},
icon: '/assets/icons/EVER.svg',
id: `tvm-${TvmChains.EverscaleMainnet}`,
name: 'Everscale',
rpcUrl: 'https://jrpc.everwallet.net',
shortName: 'Everscale',
type: 'tvm',
},
{
chainId: TvmChains.VenomMainnet.toString(),
currency: {
decimals: 9,
icon: '/assets/icons/VENOM.svg',
name: 'VENOM',
symbol: 'VENOM',
wrappedCurrencyAddress: new AddressLiteral('0:77d36848bb159fa485628bc38dc37eadb74befa514395e09910f601b841f749e'),
},
explorer: {
accountsSubPath: 'accounts',
baseUrl: 'https://venomscan.com',
title: 'VenomScan',
transactionsSubPath: 'transactions',
},
icon: '/assets/icons/VENOM.svg',
id: `tvm-${TvmChains.VenomMainnet}`,
name: 'Venom Mainnet',
rpcUrl: 'https://jrpc.venom.foundation',
shortName: 'Venom',
type: 'tvm',
},
]
try {
const ua = getUserAgent()
const isSparXWallet = isSparXWalletBrowser(ua)
const isEverWallet = isEverWalletBrowser(ua)
const isVenomWallet = isVenomWalletBrowser(ua)
if (isSparXWallet) {
providers.push(sparxWallet)
defaultProviderId = sparxWallet.id
predefinedNetworkId = TvmChains.EverscaleMainnet
}
else if (isEverWallet) {
providers.push(everWallet)
defaultProviderId = everWallet.id
predefinedNetworkId = TvmChains.EverscaleMainnet
}
else if (isVenomWallet) {
providers.push(venomWallet)
defaultProviderId = venomWallet.id
predefinedNetworkId = TvmChains.VenomMainnet
}
else {
providers.push({
...sparxWallet,
minVersion: '0.4.0',
}, {
...everWallet,
minVersion: '0.4.0',
}, {
...venomWallet,
minVersion: '0.3.173',
})
}
}
catch (e) {}
let service: TvmWalletService
export function useTvmWallet(): TvmWalletService {
const [recentMeta] = useRecentConnectionMeta()
if (service === undefined) {
const networkId = recentMeta?.chainId ? Number(recentMeta.chainId) : TvmChains.EverscaleMainnet
service = new TvmWalletService({
defaultNetworkId: predefinedNetworkId ?? networkId,
networks,
providerId: recentMeta?.disconnected ? defaultProviderId : recentMeta?.providerId ?? defaultProviderId,
providers,
})
}
return service
}
```
## Styling
If you are using our [UIkit](https://www.npmjs.com/package/@broxus/react-uikit) package it will it automatically adapts to your interface colors.
Otherwise, you can import standalone CSS
```typescript jsx
import '@broxus/tvm-connect/uikit.min.css' // include all required styles from UIkit
import '@broxus/tvm-connect/style.min.css' // include all required styles from TVM Connect
```
Below you can find all supported CSS variables and their defaults
```css
/* Connector */
--tvm-connect-dropdown-trigger-horizontal-padding: var(--global-small-gutter, 8px);
--tvm-connect-dropdown-trigger-vertical-padding: 0;
--tvm-connect-dropdown-background: var(--dropdown-background, #fff);
--tvm-connect-dropdown-border-radius: var(--dropdown-border-radius, 5px);
--tvm-connect-dropdown-box-shadow: 0 8px 32px 0 rgb(63 74 111 / 12%), 0 1px 4px 0 rgb(63 74 111 / 8%);
--tvm-connect-dropdown-color: var(--dropdown-color, #333);
--tvm-connect-dropdown-link-color: var(--dropdown-color, #0af);
/* Providers list buttons */
--tvm-connect-provider-button-border-width: 2px;
--tvm-connect-provider-button-border-style: solid;
--tvm-connect-provider-button-border: transparent;
--tvm-connect-provider-button-hover-border: var(--global-primary-border, transparent);
/* Modal */
--tvm-connect-modal-content-background: var(--modal-content-background, #fff);
--tvm-connect-modal-content-border-radius: 12px;
--tvm-connect-modal-content-box-shadow: 0 8px 32px 0 rgb(63 74 111 / 12%), 0 1px 4px 0 rgb(63 74 111 / 8%);
--tvm-connect-modal-content-color: var(--base-body-color, #383838);
--tvm-connect-modal-content-padding-horizontal: 18px;
--tvm-connect-modal-content-padding-vertical: var(--tvm-connect-modal-content-padding-horizontal);
--tvm-connect-modal-header-padding-horizontal: 0;
--tvm-connect-modal-header-padding-vertical: var(--tvm-connect-modal-content-padding-vertical);
--tvm-connect-modal-title-color: var(--base-heading-color, #383838);
--tvm-connect-modal-title-font-size: var(--modal-title-font-size, 18px);
--tvm-connect-modal-title-font-weight: 500;
--tvm-connect-modal-title-line-height: var(--modal-title-line-height, 22px);
--tvm-connect-modal-body-padding-horizontal: 0;
--tvm-connect-modal-body-padding-vertical: var(--tvm-connect-modal-content-padding-vertical);
--tvm-connect-modal-footer-padding-horizontal: 0;
--tvm-connect-modal-footer-padding-vertical: var(--tvm-connect-modal-content-padding-vertical);
/* Drawer */
--tvm-connect-drawer-content-background: var(--drawer-content-background, #fff);
--tvm-connect-drawer-content-border-radius: 16px;
--tvm-connect-drawer-content-box-shadow: 0 8px 32px 0 rgb(63 74 111 / 12%), 0 1px 4px 0 rgb(63 74 111 / 8%);
--tvm-connect-drawer-content-color: var(--base-body-color, #383838);
--tvm-connect-drawer-content-padding-horizontal: 24px;
--tvm-connect-drawer-content-padding-vertical: var(--tvm-connect-drawer-content-padding-horizontal);
--tvm-connect-drawer-header-padding-horizontal: 0;
--tvm-connect-drawer-header-padding-vertical: var(--tvm-connect-drawer-content-padding-vertical);
--tvm-connect-drawer-title-color: var(--base-heading-color, #383838);
--tvm-connect-drawer-title-font-size: var(--drawer-title-font-size, 24px);
--tvm-connect-drawer-title-font-weight: 500;
--tvm-connect-drawer-title-line-height: var(--drawer-title-line-height, 28px);
--tvm-connect-drawer-body-padding-horizontal: 0;
--tvm-connect-drawer-body-padding-vertical: var(--tvm-connect-drawer-content-padding-vertical);
--tvm-connect-drawer-footer-padding-horizontal: 0;
--tvm-connect-drawer-footer-padding-vertical: var(--tvm-connect-drawer-content-padding-vertical);
/* Connection approve popup stage */
--tvm-connect-connection-request-button-border-width: 2px;
--tvm-connect-connection-request-button-border-style: solid;
--tvm-connect-connection-request-button-border: transparent;
--tvm-connect-connection-request-button-hover-border: var(--global-border, transparent);
```