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

chaingrep

v0.1.0

Published

Chaingrep's JS library for parsing and simulating Ethereum transaction data

Downloads

5

Readme

Chaingrep JS library

NPM Version NPM Monthly Downloads NPM License

Chaingrep is making blockchain transactions and data human-readable through our API. The Chaingrep JS library exposes this API in a JS-native library interface. More detailed documentation on the API and the library can be found in the Chaingrep Docs.

Usage

To start using the Chaingrep JS library, you need to request an API key. Once you've obtained your API key, you can install chaingrep using Yarn or NPM and start using it inside your JavaScript or TypeScript applications.

yarn add chaingrep
npm install chaingrep
const { Chaingrep } = require('chaingrep');

const chaingrep = new Chaingrep('YOUR_API_KEY_HERE');

Types are shown in the definitions and examples below, for their full definition, see the actual type definitions in the code.

Get a transaction

chaingrep.transaction(transactionHash: string).get(): Promise<RawTransactionResult>
chaingrep.transaction(transactionHash: string).parse(): Promise<ParsedTransactionResult>
interface RawTransactionResult {
  transactionHash: string;
  blockNumber: number;
  from: string;
  to?: string;
  deployedContract?: string;
  value: BigNumber;
  data: string;
  eventLogs: EventLog[];
  status: 'SUCCESS' | 'FAILED';
  timestamp: number;
  gasUsed: number;
  gasPrice: BigNumber;
}

interface EventLog {
  address: string;
  data: string;
  topics: string[];
}
interface ParsedTransactionResult {
  transactionHash: string;
  transactionType: TransactionType;
  transactionMetadata: ParsedTransactionAction; // See type definitions for details
  standardEvents: DecodedEventData[];
  protocol: {
    contractAddress: string;
    protocol: string;
  }
  value: Erc20TokenAmount;
  sender: string;
  action: string;
  transactionCost: {
    gasPrice: {
      price: number;
      unit: 'gwei';
    };
    gasUsed: number;
    paid: Erc20TokenAmount;
  }
  time: {
    formatted: string;
    timeago: string;
    unixTimestamp: number;
  }
  status: string;
  chain: {
    chainId: 1;
    name: string;
  }
}

type TransactionType =
  'ETH_TRANSFER'
  | 'ERC20_TRANSFER'
  | 'ERC20_APPROVAL'
  | 'ERC721_TRANSFER'
  | 'ERC721_APPROVAL'
  | 'ERC721_APPROVAL_FOR_ALL'
  | 'DEX_SWAP'
  | 'DEX_PROVIDE_LIQUIDITY'
  | 'DEX_REMOVE_LIQUIDITY'
  | 'NFT_SALE'
  | 'WRAP_ETHER'
  | 'UNWRAP_ETHER'
  | 'ZKSYNC_DEPOSIT'
  | 'ENS_NAME_REGISTRATION';

interface DecodedEventData {
  type: string;
  address: string;
  data: any;
}

interface Erc20TokenAmount {
  raw: BigNumber;
  formatted: string;
}

A transaction can be retrieved and/or parsed by using the transaction functionality in the Chaingrep library.

const parsedTransaction = await chaingrep.transaction('0xdef0cf4872c73dd23d284c4b61c39954f5bba0869d5a4b1cfe1e65f35d9b78a3').parse();
{
  "transactionHash": "0xdef0cf4872c73dd23d284c4b61c39954f5bba0869d5a4b1cfe1e65f35d9b78a3",
  "transactionType": "DEX_SWAP",
  "transactionMetadata": {
    "account": "0xe126b3E5d052f1F575828f61fEBA4f4f2603652a",
    "swappedFrom": {
      "asset": {
        "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
        "name": "Ether",
        "symbol": "ETH",
        "decimals": 18,
        "logoURI": "https://assets.coingecko.com/coins/images/279/small/ethereum.png?1595348880"
      },
      "amount": {
        "raw": "1000000000000000000",
        "formatted": "1.0"
      }
    },
    "swappedTo": {
      "asset": {
        "address": "0xf4d2888d29d722226fafa5d9b24f9164c092421e",
        "name": "LooksRare",
        "symbol": "LOOKS",
        "decimals": 18,
        "logoURI": "https://assets.coingecko.com/coins/images/22173/small/circle-black-256.png?1641173160"
      },
      "amount": {
        "raw": "3806742856923420981865",
        "formatted": "3806.742856923420981865"
      }
    }
  },
  "standardEvents": [
    {
      "type": "ERC20_TRANSFER",
      "address": "0xf4d2888d29d722226fafa5d9b24f9164c092421e",
      "data": {
        "from": "0x4b5Ab61593A2401B1075b90c04cBCDD3F87CE011",
        "to": "0xe126b3E5d052f1F575828f61fEBA4f4f2603652a",
        "value": "3806742856923420981865"
      }
    },
    {
      "type": "ERC20_TRANSFER",
      "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
      "data": {
        "from": "0xDef1C0ded9bec7F1a1670819833240f027b25EfF",
        "to": "0x4b5Ab61593A2401B1075b90c04cBCDD3F87CE011",
        "value": "1000000000000000000"
      }
    }
  ],
  "protocol": {
    "contractAddress": "0xdef1c0ded9bec7f1a1670819833240f027b25eff",
    "protocol": "Zeroex"
  },
  "value": {
    "raw": "1000000000000000000",
    "formatted": "1"
  },
  "sender": "0xe126b3e5d052f1f575828f61feba4f4f2603652a",
  "action": "Swapped 1.00000 ETH for 3806.74286 LOOKS on Zeroex",
  "transactionCost": {
    "gasPrice": {
      "price": 12,
      "unit": "gwei"
    },
    "gasUsed": 118955,
    "paid": {
      "raw": "1498267380513635",
      "formatted": "0.001498267380513635"
    }
  },
  "time": {
    "formatted": "05/22/2022, 11:11:26 AM",
    "timeago": "5 days ago",
    "unixTimestamp": 1653217886
  },
  "status": "SUCCESS",
  "chain": {
    "chainId": 1,
    "name": "Ethereum"
  }
}

Get account transactions

chaingrep.account(address: string).getTransactions(start?: number, amount?: number): Promise<RawTransactionResult[]>
chaingrep.account(address: string).parseTransactions(start?: number, amount?: number): Promise<ParsedTransactionResult[]>

Transactions can be retrieved and/or parsed for a specific account. This includes transactions where the account was the sender or the receiver of the transaction, as well as any transactions where the account received tokens. For more fine-grained transaction selections, see the section on transaction querying. Transactions are sorted by timestamp in descending order. It is possible to add a start and amount parameter to paginate these transactions. By default the most recent 10 transactions are returned.

const accountTransactions = await chaingrep.account('0xe126b3e5d052f1f575828f61feba4f4f2603652a').parseTransactions();

Subscribe to an account's transactions with webhooks

chaingrep.account(address: string).subscribe(webhookUrl: string): Promise<string>
chaingrep.account(address: string).unsubscribe(webhookId: string): Promise<string>
chaingrep.user().getAllActiveSubscriptions(): Promise<Subscription[]>

Subscriptions can be created by calling the subscribe function on an account, with a corresponding webhook URL as its parameter. This returns a webhook ID that can be used to unsubscribe from these updates at any point. A list of all your active subscriptions (for all addresses) can be retrieved at any point by calling chaingrep.user().getAllActiveSubscriptions().

After registering your webhook with chaingrep, we will send a POST request to the webhook URL whenever the passed account sends or receives transactions, or when they are involved in any contract events (such as token transfers). The interface can be found below

interface WebhookPayload {
  uid: string;
  transactions: ParsedTransactionResult[];
}

Query for relevant transactions

chaingrep.query(): Query

interface Query {
  filter(filter: QueryFilter): this
  sort(field: 'timestamp' | 'value' | 'gasPrice' | 'gasUsed', order: 'ascending' | 'descending'): this
  timeframe(from: number | Date, to: number | Date): this
  execute(start?: number, amount?: number): Promise<ParsedTransactionResult[]>
  executeRaw(start?: number, amount?: number): Promise<RawTransactionResult[]>
  count(): Promise<number>
}

Transactions can be queried using some more fine-grained filtering. Filters can be defined for the following transaction properties:

interface QueryFilter {
  account?: string;
  contract?: string;
  value?: string;
  successful?: boolean;
  gasUsed?: string;
  gasPrice?: string;
}

Simple comparison operators may be passed, such as { value: '>0', gasUsed: '<=1000000' }. The account property matches any transactions sent from/to the passed address, as well as any transactions where the passed address received any tokens. The contract property matches any transactions sent to the passed address, as well as any transactions where the passed address emitted any events.

Besides filtering on these properties, a query can be limited by its timestamp with .timeframe(from, to) and you can provide a sort using .sort(field, order). By default, the results are sorted by timestamp in a descending order. It is possible to add a start and amount parameter to paginate these transactions. By default the most recent 10 transactions are returned.

const queriedTransactions = await chaingrep.query()
  .filter({ account: '0xe126b3e5d052f1f575828f61feba4f4f2603652a', value: '>0' })
  .timeframe(new Date('2022-05-22T00:00:00'), new Date('2022-05-25T00:00:00'))
  .execute();

Simulating transactions

chaingrep.rawTransaction(transactionRequest: RawTransactionRequest).simulate(): Promise<ParsedTransaction>
interface RawTransactionRequest {
  to?: string;
  from?: string;
  nonce?: BigNumberish;
  gasLimit?: BigNumberish;
  gasPrice?: BigNumberish;
  data?: string;
  value?: BigNumberish;
  chainId?: number;
  type?: number;
  maxFeePerGas?: BigNumberish;
  maxPriorityFeePerGas?: BigNumberish;
}

Sometimes, you'd like to see what a transaction will do before submitting it to the network. For this, we developed our simulation API. This simulates what will happen if the passed transaction would be submitted to the network right now. You can manually create a "transaction request", but this functionality is also fully compatible with Ethers.js' Contract objects as can be seen in the example below.

const voidSigner = new VoidSigner('0xe126b3e5d052f1f575828f61feba4f4f2603652a');
const looksTokenContract = new Contract('0xf4d2888d29D722226FafA5d9B24F9164c092421E', erc20TokenAbi, voidSigner);
const transactionRequest = await looksTokenContract.populateTransaction.approve('0x881D40237659C251811CEC9c364ef91dC08D300C', '0');
const simulatedTransaction = await chaingrep.rawTransaction(transactionRequest).simulate();
{
  "transactionHash": "0x74f7808c20c8c4f961af9fc2ed67b5aad492737868b0b732f52e5ab9d40e0ecc",
  "transactionType": "ERC20_APPROVAL",
  "transactionMetadata": {
    "from": "0xe126b3E5d052f1F575828f61fEBA4f4f2603652a",
    "to": "0x881D40237659C251811CEC9c364ef91dC08D300C",
    "asset": {
      "address": "0xf4d2888d29d722226fafa5d9b24f9164c092421e",
      "name": "LooksRare",
      "symbol": "LOOKS",
      "decimals": 18,
      "logoURI": "https://assets.coingecko.com/coins/images/22173/small/circle-black-256.png?1641173160"
    },
    "amount": {
      "raw": "0",
      "formatted": "0.0"
    }
  },
  "standardEvents": [
    {
      "type": "ERC20_APPROVAL",
      "address": "0xf4d2888d29D722226FafA5d9B24F9164c092421E",
      "data": {
        "owner": "0xe126b3E5d052f1F575828f61fEBA4f4f2603652a",
        "spender": "0x881D40237659C251811CEC9c364ef91dC08D300C",
        "value": "0"
      }
    }
  ],
  "protocol": {
    "contractAddress": "0xf4d2888d29d722226fafa5d9b24f9164c092421e",
    "protocol": "UNKNOWN"
  },
  "value": {
    "raw": "0",
    "formatted": "0"
  },
  "sender": "0xe126b3E5d052f1F575828f61fEBA4f4f2603652a",
  "action": "Revoked a contract's allowance to spend LOOKS",
  "transactionCost": {
    "gasPrice": {
      "price": 8,
      "unit": "gwei"
    },
    "gasUsed": 26255,
    "paid": {
      "raw": "213097433502380",
      "formatted": "0.00021309743350238"
    }
  },
  "time": {
    "formatted": "05/29/2022, 09:28:25 AM",
    "timeago": "just now",
    "unixTimestamp": 1653816505
  },
  "status": 1,
  "chain": {
    "chainId": 1,
    "name": "Ethereum"
  }
}

Examples

A "kitchen sink" example is available here that can be executed with ts-node after cloning the chaingrep-js repository.

yarn ts-node examples/kitchen-sink.ts

Development

git clone [email protected]:chaingrep/chaingrep-js.git
cd chaingrep-js
yarn

Tests

The tests are currently written against the production API for simplicity of implementation. This does mean that it cannot be tested independently. To run all tests you'll need to request an API key and add it to a .env file in this repo.

Release

To create a new release you have to be a maintainer of this repo and of the NPM package. The steps to create a new release are the following. We follow semantic versioning, so because we are still on a 0.x.x release, this means that we bump the minor version for breaking changes, and the patch version for non-breaking changes.

npm version [minor | patch]
npm publish
git push

Then, on GitHub, go to the releases tab and draft a new release with tag and title being v{new version} and write down a short changelog. Refer to the additional releases for examples.