lync-wallet-sdk
TypeScript icon, indicating that this package has built-in type declarations

2.2.6 • Public • Published

Metamask Wallet Connect SDK

LYNC MetaMask wallet integration SDK for integrating MetaMask wallets inside any dapp in a few lines of code.

Prerequisites

Before you begin integrating Fuel marketplace SDK, make sure you have the following prerequisites:

  • Node.js (version 20 or above) installed on your system.
  • NPM (version 10 or above) or Yarn (latest version) installed on your system.

Installing the Package

You can install the package using either NPM or Yarn. Follow these steps:

  1. Open your preferred terminal.
  2. Navigate to your project's directory.
  3. Run the following command:
# Using NPM
npm install --save lync-wallet-sdk@latest
# Using Yarn
yarn add lync-wallet-sdk

Congratulations! You have successfully installed lync-wallet-sdk. If you encounter any issues or have any questions, feel free to reach out to our support team for assistance.

Using the SDK

Wrapping your application with the LYNCMetaMaskProvider

Your entire application should be wrapped inside the LYNCMetaMaskProvider to utilize the hooks and components provided by the SDK.

Example: Here's an example of how to wrap your application with LYNCMetaMaskProvider:

// main.tsx

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';

// Importing `index.css` is important if you wish to use the components
// provided by the SDK. This file contains necessary styling for the
// components provided by the SDK
import 'lync-wallet-sdk/build/index.css';

import './index.css';
import App from './app';
import LYNCMetaMaskProvider from 'lync-wallet-sdk';

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <LYNCMetaMaskProvider autoConnect>
      <App />
    </LYNCMetaMaskProvider>
  </StrictMode>
);

The autoConnect prop passed to LYNCMetaMaskProvider can be used to automatically connect your dapp with metamask wallet on the very first load of your dapp. The SDK will automatically try to connect with metamask wallet if autoConnect is true.

Using the hooks provided by the SDK to connect with metamask wallet

lync-wallet-sdk provides a simple, hook-based approach for handling wallet connections. The SDK provides different hooks to connect and manage user wallet in your dapp. With the SDK, you can:

  • Connect user's metamask wallet to your dapp.
  • Access user accounts (addresses).
  • Handle wallet connections (connect/disconnect).
  • Handle adding and switching networks.
  • Listen for account and network changes in real time.
  • Use providers and signers to interact with user's wallet

Handle wallet connections

This SDK provides different hooks to connect user's metamask wallet to your dapp and handle wallet connections. For example:

import React from 'react';
import { collapseAddress, MetaMaskFunctionErrorCodes, useAccount, useConnect, useDisconnect } from 'lync-wallet-sdk';

const MetaMaskConnectExample: React.FC = () => {
  const { account } = useAccount();
  const { isConnecting, connect } = useConnect();
  const { disconnect } = useDisconnect();

  const connectToMetaMask = async () => {
    const response = await connect();
    if (response.success) return;

    if (response.errorData.code === MetaMaskFunctionErrorCodes.MetaMaskProviderNotFound) {
      window.open('https://metamask.io/download/', '_blank');
      return;
    }

    console.error('Error connecting to metamask: ', response.errorData);
  };

  const disconnectMetaMask = async () => {
    const response = await disconnect();
    if (!response.success) console.error(response.errorData);
  };

  return (
    <div>
      {!account && (
        <button disabled={isConnecting} onClick={connectToMetaMask}>
          Connect Metamask
        </button>
      )}
      {account && <span>{collapseAddress(account)}</span>}
      {account && <button onClick={disconnectMetaMask}>Disconnect</button>}
    </div>
  );
};

Additionally, the SDK provides a MetamaskConnect component which wraps the connection logic and returns a button the triggers wallet connection on click. You can also import and use the MetamaskConnect component to connect user's metamask wallet to your dapp. For example:

import React from 'react';
import { MetamaskConnect, MetaMaskFunctionErrorCodes, useAccount, useDisconnect } from 'lync-wallet-sdk';

type MetaMaskConnectionError = {
  error?: E;
  code: MetaMaskFunctionErrorCodes;
  message: string;
};

const MetaMaskConnectExample: React.FC = () => {
  const { account } = useAccount();
  const { disconnect } = useDisconnect();

  const onConnectionSuccess = () => {
    console.info('MetaMask connected successfully.');
  };

  const onConnectionError = (error: MetaMaskConnectionError) => {
    if (errorData.code === MetaMaskFunctionErrorCodes.MetaMaskProviderNotFound) {
      console.error('Please install MetaMask: https://metamask.io/download/');
      window.open('https://metamask.io/download/', '_blank');
    } else {
      console.error(error);
    }
  };

  return (
    <div>
      <MetamaskConnect onSuccess={onConnectionSuccess} onError={onConnectionError} />
      {account && <button onClick={disconnect}>Disconnect</button>}
    </div>
  );
};

NOTE: When you are using MetamaskConnect component in your dapp, you must import 'lync-wallet-sdk/build/index.css' to get necessary styling for the component (see here). If you want to change the default styles implemented by tye SDK for connect button, you can add your custom styles by targeting .LYNCMetaMaskConnectSDK__metamask_connect_btn class, and .LYNCMetaMaskConnectSDK__metamask_connect_btn:hover and .LYNCMetaMaskConnectSDK__metamask_connect_btn:disabled states.

Manage networks

You can manage networks in your dapp with the hooks provided by the SDK. You can:

  • Detect the current network and monitor network changes.
  • Switch between networks programmatically.
  • Add new networks to MetaMask.
  • Handle common network-related errors.

The SDK provides intuitive hooks for several network-related operations. The following are examples of using these hooks.

Detect the current network:

// chains.ts

import { SupportedChains } from 'lync-wallet-sdk';

type MetaMaskChain = {
  id: string;
  label: string;
};

const Testnet_Chains = [
  { id: SupportedChains.EthereumSepoliaTestnet, label: 'Sepolia Testnet' },
  { id: SupportedChains.BaseSepoliaTestnet, label: 'Base Sepolia Testnet' },
  { id: SupportedChains.PolygonAmoyTestnet, label: 'Polygon Amoy Testnet' },
  { id: SupportedChains.MetisSepoliaTestnet, label: 'Metis Sepolia Testnet' },
  { id: SupportedChains.BNBTestnet, label: 'BNB Smart Chain Testnet' },
] as const satisfies Array<MetaMaskChain>;

const Mainnet_Chains = [
  { id: SupportedChains.EthereumMainnet, label: 'Ethereum Mainnet' },
  { id: SupportedChains.BaseMainnet, label: 'Base Mainnet' },
  { id: SupportedChains.PolygonMainnet, label: 'Polygon Mainnet' },
  { id: SupportedChains.MetisAndromedaMainnet, label: 'Metis Andromeda Mainnet' },
  { id: SupportedChains.BNBMainnet, label: 'BNB Smart Chain Mainnet' },
] as const satisfies Array<MetaMaskChain>;
import React, { useMemo } from 'react';
import { useAccount, useNetwork } from 'lync-wallet-sdk';
import { Mainnet_Chains, Testnet_Chains } from './chains';

const ConnectedNetwork: React.FC = () => {
  const { account } = useAccount();
  const { chainId } = useNetwork();

  const connectedChain = useMemo(
    () => [...Testnet_Chains, ...Mainnet_Chains].find((chain) => chain.id === chainId),
    [chainId]
  );

  if (!account) return null;

  return (
    <div>
      <p>Connected Network: {connectedChain?.label ?? 'Unsupported Network'}</p>
    </div>
  );
};

Switch networks:

import React, { useMemo } from 'react';
import { SupportedChains, useAccount, useNetwork } from 'lync-wallet-sdk';
import { Mainnet_Chains, Testnet_Chains } from './chains';

const NetworkSwitcher: React.FC = () => {
  const { account } = useAccount();
  const { chainId, isSwitchingNetwork, switchNetwork } = useNetwork();

  const connectedChain = useMemo(
    () => [...Testnet_Chains, ...Mainnet_Chains].find((chain) => chain.id === chainId),
    [chainId]
  );

  const switchMetaMaskNetwork = async (chainIdToConnect: SupportedChains) => {
    const response = await switchNetwork(chainIdToConnect);
    if (!response.success) {
      console.error(response.errorData.message);
    }
  };

  if (!account) return null;

  return (
    <div>
      <p>Connected Network: {connectedChain?.label ?? 'Unsupported Network'}</p>
      {Mainnet_Chains.map((chain) => (
        <button key={chain.id} disabled={isSwitchingNetwork} onClick={() => switchMetaMaskNetwork(chain.id)}>
          {chain.label}
        </button>
      ))}
      {Testnet_Chains.map((chain) => (
        <button key={chain.id} disabled={isSwitchingNetwork} onClick={() => switchMetaMaskNetwork(chain.id)}>
          {chain.label}
        </button>
      ))}
    </div>
  );
};

Apart from chains supported by lync-wallet-sdk, user can also switch and connect to any ethereum chains. User can provide valid chain configuration according to metamask documentation (see valid configuration type here) as second parameter of function switchNetwork, in order to add and switch to the other chain. For example:

import React, { useMemo } from 'react';
import { useAccount, useNetwork } from 'lync-wallet-sdk';
import type { MetamaskAddChainConfigurations } from 'lync-wallet-sdk';
import { Mainnet_Chains, Testnet_Chains } from './chains';

const avalancheFujiTestnetConfig = {
  chainId: '0xa869',
  chainName: 'Avalanche Fuji Testnet',
  blockExplorerUrls: ['https://testnet.snowscan.xyz'],
  rpcUrls: ['https://api.avax-test.network/ext/bc/C/rpc'],
  nativeCurrency: {
    name: 'AVAX',
    symbol: 'AVAX',
    decimals: 18,
  },
} as const satisfies MetamaskAddChainConfigurations;

const NetworkSwitcher: React.FC = () => {
  const { account } = useAccount();
  const { chainId, isSwitchingNetwork, switchNetwork } = useNetwork();

  const connectedChain = useMemo(
    () => [...Testnet_Chains, ...Mainnet_Chains].find((chain) => chain.id === chainId),
    [chainId]
  );

  const switchToAvalancheFuji = async () => {
    const response = await switchNetwork(avalancheFujiTestnetConfig.chainId, avalancheFujiTestnetConfig);
    if (!response.success) {
      console.error(response.errorData.message);
    }
  };

  if (!account) return null;

  return (
    <div>
      <p>
        Connected Network:
        {chainId !== '0xa869' && (connectedChain?.label ?? 'Unsupported Network')}
        {chainId === '0xa869' && 'Avalanche Fuji Testnet'}
      </p>
      <button disabled={isSwitchingNetwork} onClick={switchToAvalancheFuji}>
        Avalanche Fuji Testnet
      </button>
    </div>
  );
};

Handle network changes:

import React, { useEffect } from 'react';
import { useNetwork } from 'lync-wallet-sdk';

const NetworkWatcher: React.FC = () => {
  const { chainId } = useNetwork();

  useEffect(() => {
    console.info('Chain Id changed: ', chainId);
  }, [chainId]);

  return null;
};

Get the native currency balance

The SDK provides a dedicated hook for fetching the native currency balance on the current connected network of a user's connected account. The hook automatically handles updating the balance when the network is switched or the connected account is changed, making it easier for developers to get an updated balance according to the change in network or account.

import React, { useMemo } from 'react';
import { useAccount, useBalance, useNetwork } from 'lync-wallet-sdk';

export const AccountBalance: React.FC = () => {
  const { account } = useAccount();
  const { balance, isFetching } = useBalance();
  const { chainId } = useNetwork();

  const formattedBalance = useMemo(() => {
    if (Number(balance) <= 0) return '0';
    if (Number(Number(balance).toFixed(6)) <= 0) return '< 0.000001';
    return Number(balance).toFixed(6);
  }, [balance]);

  if (!account) return null;

  return (
    <div>
      <p>Connected account: {account}</p>
      <p>Connected network: {chainId}</p>
      {isFetching && <p>Fetching account balance...</p>}
      {!isFetching && <p>Balance: {formattedBalance}</p>}
    </div>
  );
};

Using providers and signers to interact with user's wallet

The SDK also provide useWallet and useEthSigner hooks, which provides access to user's metamask wallet provider and ethereum signer. You can import and use these hooks to get access to wallet provider and signer, and interact with user's metamask wallet for message and transaction signing.

Using wallet provider:

import React, { useState } from 'react';
import { useAccount, useWallet } from 'lync-wallet-sdk';

const SignPersonalMessage: React.FC = () => {
  const { account } = useAccount();
  const { wallet } = useWallet();

  const [signedMessage, setSignedMessage] = useState<string | null>(null);

  const signMessage = async () => {
    if (!wallet || !account) return;

    const signingMessage = `This message is a demo message signed by account: ${account}`;
    const message = `0x${Buffer.from(signingMessage, 'utf8').toString('hex')}`;

    try {
      const signature = (await wallet.request({
        method: 'personal_sign',
        params: [message, account],
      })) as string;

      setSignedMessage(signature);
    } catch (error) {
      console.error(error);
      setSignedMessage(null);
    }
  };

  return (
    <div>
      {signedMessage && <p>{signedMessage}</p>}
      <button onClick={signMessage}>Sign Message</button>
    </div>
  );
};

Using ethereum signer:

The SDK provides useEthSigner hook that returns ethereum signer for the user's connected account. The hook automatically handles updating the signer when the network is switched or the connected account is changed. You can use this hook to sign transactions like coin transfers using user's connected account. For example:

import React, { useMemo, useState } from 'react';
import { ethers } from 'ethers';
import { useAccount, useBalance, useEthSigner, useNetwork } from 'lync-wallet-sdk';

export const CoinTransferTransaction: React.FC = () => {
  const { account } = useAccount();
  const { balance, isFetching, refetch } = useBalance();
  const { chainId } = useNetwork();
  const signer = useEthSigner();

  const [sendingFunds, setSendingFunds] = useState<boolean>(false);

  const formattedBalance = useMemo(() => {
    if (Number(balance) <= 0) return '0';
    if (Number(Number(balance).toFixed(6)) <= 0) return '< 0.000001';
    return Number(balance).toFixed(6);
  }, [balance]);

  const transferFunds = async () => {
    if (!account || !signer) return;

    if (Number(balance) === 0) {
      console.error('Account balance is too low. Please top-up your account to send funds.');
      return;
    }

    if (Number(balance) <= 0.001) {
      console.error('Insufficient funds. Please top-up your account to send funds.');
      return;
    }

    setSendingFunds(true);

    // Wallet address of the recipient (replace with actual address).
    const transferAddress = '0x0000000000000000000000000000000000000000';

    // Amount to be transferred, in wei.
    const transferAmount = ethers.utils.parseEther('0.001');

    const transactionParams = {
      from: account, // The user's connected account.
      to: transferAddress,
      value: transferAmount.toHexString(),
    };

    try {
      const transaction = await signer.sendTransaction(transactionParams);
      const receipt = await transaction.wait();

      console.log('Fund transfer successful: ', receipt.transactionHash);
      await refetch();
    } catch (error: unknown) {
      console.error('Error sending funds: ', { error });
    } finally {
      setSendingFunds(false);
    }
  };

  if (!account) return null;

  return (
    <div>
      <p>Connected account: {account}</p>
      <p>Connected network: {chainId}</p>
      {isFetching && <p>Fetching account balance...</p>}
      {!isFetching && <p>Balance: {formattedBalance}</p>}
      <button disabled={isFetching || sendingFunds} onClick={transferFunds}>
        Send Funds
      </button>
    </div>
  );
};

Types and Enums provided by the SDK

Enums

enum SupportedChains {
  EthereumMainnet = '0x1',
  BaseMainnet = '0x2105',
  PolygonMainnet = '0x89',
  PolygonZkEvmMainnet = '0x44d',
  MetisAndromedaMainnet = '0x440',
  GoatAlphaMainnet = '0x929',
  BNBMainnet = '0x38',
  AvalancheMainnet = '0xa86a',
  ArbitrumOneMainnet = '0xa4b1',
  ArbitrumNovaMainnet = '0xa4ba',
  OptimismMainnet = '0xa',
  RoninMainnet = '0x7e4',
  SagaEvmMainnet = '0x1558',
  EthereumSepoliaTestnet = '0xaa36a7',
  BaseSepoliaTestnet = '0x14a34',
  PolygonAmoyTestnet = '0x13882',
  PolygonCardonaTestnet = '0x98a',
  MetisSepoliaTestnet = '0xe9fe',
  GoatTestnet3 = '0xbeb0',
  BNBTestnet = '0x61',
  AvalancheFujiTestnet = '0xa869',
  ArbitrumSepoliaTestnet = '0x66eee',
  OptimismSepoliaTestnet = '0xaa37dc',
  SaigonTestnet = '0x7e5',
}

enum MetaMaskFunctionErrorCodes {
  MetaMaskAddNetworkError = 'MetaMaskAddNetworkError',
  MetaMaskConnectionError = 'MetaMaskConnectionError',
  MetaMaskDisconnectError = 'MetaMaskDisconnectError',
  MetaMaskProviderNotFound = 'MetaMaskProviderNotFound',
  MetaMaskSwitchNetworkError = 'MetaMaskSwitchNetworkError',
}

Types

type ChainNativeCurrency = {
  name: string;
  symbol: string;
  decimals: number;
};

type MetamaskAddChainConfigurations = {
  chainId: `0x${string}`;
  chainName: string;
  blockExplorerUrls: Array<string>;
  rpcUrls: Array<string>;
  nativeCurrency: ChainNativeCurrency;
};

type MetaMaskFunctionErrorData<E = unknown> = {
  error?: E;
  code: MetaMaskFunctionErrorCodes;
  message: string;
};

type EIP1193Provider = {
  isStatus?: boolean;
  host?: string;
  path?: string;
  sendAsync?: (
    request: { method: string; params?: Array<unknown> },
    callback: (error: Error | null, response: unknown) => void
  ) => void;
  send?: (
    request: { method: string; params?: Array<unknown> },
    callback: (error: Error | null, response: unknown) => void
  ) => void;
  request: (request: { method: string; params?: Array<unknown> }) => Promise<unknown>;
  on: (event: string, callback: (arg: unknown) => void) => void;
};

Supported Chains

Chain Name Chain ID
Ethereum Mainnet 0x1
Base Mainnet 0x2105
Polygon Mainnet 0x89
Polygon ZkEvm Mainnet 0x44d
Metis Andromeda Mainnet 0x440
Goat Alpha Mainnet 0x929
BNB Mainnet 0x38
Avalanche Mainnet 0xa86a
Arbitrum One Mainnet 0xa4b1
Arbitrum Nova Mainnet 0xa4ba
Optimism Mainnet 0xa
Ronin Mainnet 0x7e4
Saga Evm Mainnet 0x1558
Ethereum Sepolia Testnet 0xaa36a7
Base Sepolia Testnet 0x14a34
Polygon Amoy Testnet 0x13882
Polygon Cardona Testnet 0x98a
Metis Sepolia Testnet 0xe9fe
Goat Testnet 3 0xbeb0
BNB Testnet 0x61
Avalanche Fuji Testnet 0xa869
Arbitrum Sepolia Testnet 0x66eee
Optimism Sepolia Testnet 0xaa37dc
Saigon Testnet 0x7e5

Package Sidebar

Install

npm i lync-wallet-sdk

Weekly Downloads

23

Version

2.2.6

License

MIT

Unpacked Size

114 kB

Total Files

35

Last publish

Collaborators

  • lyncworld