chaingrep
v0.1.0
Published
Chaingrep's JS library for parsing and simulating Ethereum transaction data
Downloads
5
Readme
Chaingrep JS library
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.