npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@derivation-tech/web3-core

v1.0.42

Published

Common core utilities to interact with the web3 world

Downloads

1,364

Readme

Web3-Core

Introduction

Web3-Core offers essential functionalities to facilitate user interactions with EVM-compatible networks. It locally configures assets and chain information, encapsulating transactions throughout their entire lifecycle. The output is presented in a user-friendly format, encompassing parsed function calls, identified addresses, transaction data, emitted events, errors, and more. Interaction with the web3 world begins here!

Installation

yarn add @derivation-tech/web3-core

Getting started with a ChainContext instance

Initialization

Simplify initialization using the network name or ID in the following TypeScript code:

  // Initialization by chain ID
  const ctx = ChainContext.getInstance(5);
  // Initialization by chain name
  const ctx = ChainContext.getInstance('goerli');

Web3-Core streamlines the initialization of JsonRpc/WebSocket provider instances by retrieving endpoint information from environment variables: {CHAIN_NAME_UPPERCASE}_RPC and {CHAIN_NAME_UPPERCASE}_WSS, respectively. For instance, if you are working on Goerli network, ensure you configure GOERLI_RPC and, optionally, GOERLI_WSS in your environment variables (you can also specify them in a .env file).

GOERLI_RPC=https://goerli.infura.io/v3/77e667fxxx95476299975383d80a7ce3
# optional
GOERLI_WSS=wss://goerli.infura.io/ws/v3/77e667fxxx5476299975383d80a7ce3

if the rpc endpoint need authentication, you can also set {CHAIN_NAME_UPPERCASE}_RPC_AUTH in your environment variables, for example, GOERLI_RPC_AUTH=xxx:yyy

GOERLI_RPC_AUTH=userName:password
# optional
GOERLI_WSS_AUTH=userName:password

ChainContext is a singleton

The ChainContext is designed following the singleton pattern, enabling you to access it seamlessly throughout your project by invoking ChainContext.getInstance(). Subsequent calls with the same network parameter will consistently yield the same instance.

const ctx1 = ChainContext.getInstance('goerli');
const ctx2 = ChainContext.getInstance('goerli');
const ctx3 = ChainContext.getInstance('goerli');

console.log(ctx1 === ctx2); // true
console.log(ctx2 === ctx3); // true

Prepare Local Network

For local network, you should call prepareLocalNetwork before getting instance.

await prepareLocalNetwork();
const ctx = ChainContext.getInstance('local');

This is because wrappedNativeToken, tokenAssistant and multicall3 and some popular ERC20 tokens are essential contracts for ChainContext to work. These essential contracts will be deployed automatically during the prepareLocalNetwork process.

Again, once you have called await prepareLocalNetwork(), you can do ChainContext.getInstance('local') anywhere, which returns the same instance.

Customize Call Options

Also, Web3-Core provides a powerful way to customize your transaction request, you can specify the following options to meet your needs.

export interface CallOption {
    // tx.wait or not
    waitReceipt?: boolean;
    // wait timeout in seconds
    waitTimeout?: number;
    // gas limit scaler, default: 1.5
    gasLimitScaler?: number;
    // estimate gas or not
    estimateGas?: boolean;
    // gas price scaler, default: 1.2
    // for legacy tx, gasPrice = gasPrice * gasPriceScaler
    // for EIP-1559 tx, maxFeePerGas = maxFeePerGas * gasPriceScaler
    gasPriceScaler?: number;
    // gas estimator is used for estimating gas price
    gasEstimator?: EthGasEstimator;
}

The default call option is:

export const DEFAULT_CALL_OPTION: CallOption = {
    waitReceipt: true,
    waitTimeout: 3 * 60,
    estimateGas: true,
    gasLimitScaler: 1.5,
    gasPriceScaler: 1.2,
};

By the way, If you want to customize your own gas estimator, simply implement the EthGasEstimator interface and pass the instance through callOption.

Obtaining Signers

Web3-Core provides three straightforward methods for obtaining signers: one involves configured private keys, another entails using a mnemonic, and the last option is to acquire a signer from a Ledger wallet.

  • Get Signers by Private Keys

To obtain signers using private keys, set {CUSTOM_IDENTIFIER}_PRIVATE_KEY in your environment variables (excluding the '0x' prefix) and then call ctx.getSigner({CUSTOM_IDENTIFIER}). For example:

ALICE_PRIVATE_KEY=abababababab...
const ctx = ChainContext.getInstance('goerli');
const signer = await ctx.getSigner('alice'); // not case-sensitive. 'ALICE' do the same thing.
  • Get Signers by Mnemonic

To obtain signers using a mnemonic, set {CUSTOM_IDENTIFIER}_MNEMONIC in your environment variables. For example,

BOB_MNEMONIC=how are you fine thank you and you
const ctx = ChainContext.getInstance('goerli');
// get first 3 wallets derived by BOB_MNEMONIC with DEFAULT HD_PATH
const signer0 = await ctx.getSigner('BOB:0');
const signer1 = await ctx.getSigner('BOB:1');
const signer2 = await ctx.getSigner('BOB:2');
// get the forth wallet derived from BOB_MNEMONIC with HD_PATH: `m/44'/60'/0'/0/`
const signer3 = await ctx.getSigner(`bob:m/44'/60'/0'/0/3`);

Besides, you can config HD_PATH in environment variables as follows: {CUSTOM_IDENTIFIER}_{CHAIN_NAME_UPPERCASE}_HD_PATH, eg: DAVID_GOERLI_HD_PATH=m/44'/60'/0'/1

const ctx = ChainContext.getInstance('goerli');
const signer0 = await ctx.getSigner('DAVID:0');
  • Get Signers from Ledger wallet

Thus,ledger is a key word, it is specifically used for Ledger wallet.

const ctx = ChainContext.getInstance('goerli');
const ledgerSigner0 = await ctx.getSigner('ledger:1');
const ledgerSigner12 = await ctx.getSigner("ledger:m/44'/60'/0'/0/12")

Basic Chain Info

ChainInfo interface provides comprehensive details about the blockchain, including the chain ID, name, aliases, default transaction type, native and wrapped native token information, explorer URL, token assistant URL, multicall3 URL, and a list of ERC20 tokens.

Definition of the ChainInfo interface

export interface ChainInfo {
    chainId: CHAIN_ID;
    chainName: string;
    chainAlias: string[];
    defaultTxType: string;
    nativeToken: TokenInfo;
    wrappedNativeToken: TokenInfo;
    explorer: string;
    tokenAssistant: string;
    multicall3: string;
    erc20: TokenInfo[];
}

Example usage to retrieve chain information for the Goerli network:

const ctx = ChainContext.getInstance('goerli');
const info = ctx.info;

Asset Information

Obtain ERC20 or native token information by symbol or address: If the parameter is a symbol, the search is conducted locally. If it is an address, the local search is performed first. If the information is not found locally, an attempt is made to query on-chain.

const ctx = ChainContext.getInstance('goerli');
// By symbol
await ctx.getTokenInfo('USDC');
// By address
await ctx.getTokenInfo('0xA375A26dbb09F5c57fB54264f393Ad6952d1d2de');

This functionality allows you to seamlessly retrieve information about ERC20 or native tokens using either their symbols or addresses, with a preference for local data when available.

Transactions

  • Execute an ERC20 transfer using sendTx
const ctx = ChainContext.getInstance('goerli');
const erc20Info = await ctx.getTokenInfo('USDC');
const signer = await ctx.getSigner('alice');
const contract = ERC20__factory.connect(erc20Info.address, signer);
const target = '0x0E038F13d9D5732223cF9b4b61Eed264ccd44641';
// Set the address and its name if you want to identify it in the transaction
ctx.registerAddress(target, 'Alice');
// Optional: If not set, the default parser will be used, and function and event arguments will be parsed as origin
ctx.registerContractParser(contract.address, new ERC20Parser(ERC20__factory.createInterface(), erc20Info));

const unsignedTx = await contract.populateTransaction.transfer(
    target,
    ethers.utils.parseUnits('4', erc20Info.decimals)
);
await ctx.sendTx(signer, unsignedTx);

Parsers

  • Register parsers

Parsers play a crucial role in parsing function calls, events, and errors. They are associated with addresses, and Web3-Core locates the relevant parser by searching for the address in the parser map. You can register a parser using the following syntax:

ctx.registerContractParser(erc20Address, new ERC20Parser(ERC20__factory.createInterface(), erc20Info));

Additionally, you can register an address for the Web3-core to idenitfy it:

ctx.registerAddress(target, 'Alice');
  • Customize Parsers

Parsers are optional; if not set, the default parser will be used, and function and event arguments will be parsed as origin. If you wish to customize your parser, you only need to extend the base class: ContractParser and re-implement function parseBaseParam. Now, let me provide you with an example of how an ERC20 parser is implemented.

export class Erc20Parser extends ContractParser {
    tokenInfo: TokenInfo;

    constructor(
        iface: ethers.utils.Interface,
        tokenInfo: TokenInfo,
        addressParser?: (address: string) => Promise<string>,
    ) {
        super(iface, addressParser);
        this.tokenInfo = tokenInfo;
    }

    override async parseBaseParam(
        description: TransactionDescription | LogDescription | ErrorDescription,
        paramType: ethers.utils.ParamType,
        value: BigNumber | number,
    ) {
        switch (paramType.type) {
            case 'uint256':
                // different erc20 handles maxed approval differently:
                // some reduce the approval gradually as you spend, while others simply check and maintain the maximum approval during spending
                // for better human readability, we will simply show MAX if the approval is greater than MAX_UINT256/2
                if (
                    (description.name.toLowerCase() === 'approve' || description.name.toLowerCase() === 'approval') &&
                    BigNumber.from(value).gte(MAX_UINT256.div(2))
                ) {
                    return 'MAX';
                }
                return `${formatUnits(value, this.tokenInfo.decimals)} ${this.tokenInfo.symbol}`;
            default:
                return value.toString();
        }
    }
}

Supported Networks

  • ETHEREUM
  • ARBITRUM
  • OPTIMISM
  • BASE
  • MANTLE
  • LINEA
  • BSC
  • CONFLUX
  • POLYGON
  • POLYGON_ZKEVM
  • SCROLL
  • ZKSYNC_ERA
  • MAPO
  • KLAYTN
  • GOERLI
  • SEPOLIA
  • BERA_ARTIO