chaingrep
TypeScript icon, indicating that this package has built-in type declarations

0.1.0 • Public • Published

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
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
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();
Result
{
  "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
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();
Result
{
  "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 git@github.com: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.

Package Sidebar

Install

npm i chaingrep

Weekly Downloads

1

Version

0.1.0

License

MIT

Unpacked Size

56.5 kB

Total Files

40

Last publish

Collaborators

  • rkalis
  • steen3s