mirror of
https://github.com/cerc-io/watcher-ts
synced 2024-11-20 12:56:20 +00:00
nikugogoi
3cee10607e
* Remove using graph-node from mobymask-watcher * Parse batched transactions for MobyMask events * Update to use same ethers package version * Add CLI for processing block out of order * Fix job-runner for already processed blocks out of order
175 lines
4.8 KiB
TypeScript
175 lines
4.8 KiB
TypeScript
//
|
|
// Copyright 2021 Vulcanize, Inc.
|
|
//
|
|
|
|
/* eslint-disable no-unused-expressions */
|
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
import { ContractInterface } from 'ethers';
|
|
import '@nomiclabs/hardhat-ethers';
|
|
import { artifacts, ethers } from 'hardhat';
|
|
import { CompilerOutput, CompilerOutputBytecode } from 'hardhat/types';
|
|
import { expect } from 'chai';
|
|
import isArray from 'lodash/isArray';
|
|
|
|
import { StorageLayout, GetStorageAt } from '../src';
|
|
|
|
// storageLayout doesnt exist in type CompilerOutput doesnt.
|
|
// Extending CompilerOutput type to include storageLayout property.
|
|
interface StorageCompilerOutput extends CompilerOutput {
|
|
contracts: {
|
|
[sourceName: string]: {
|
|
[contractName: string]: {
|
|
abi: ContractInterface;
|
|
evm: {
|
|
bytecode: CompilerOutputBytecode;
|
|
deployedBytecode: CompilerOutputBytecode;
|
|
methodIdentifiers: {
|
|
[methodSignature: string]: string;
|
|
};
|
|
};
|
|
storageLayout?: StorageLayout;
|
|
}
|
|
};
|
|
};
|
|
}
|
|
|
|
interface ProofData {
|
|
blockHash: string;
|
|
account: {
|
|
address: string;
|
|
storage: {
|
|
ipldBlock: string;
|
|
cid: string;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get storage layout of specified contract.
|
|
* @param contractName
|
|
*/
|
|
export const getStorageLayout = async (contractName: string): Promise<StorageLayout> => {
|
|
const artifact = await artifacts.readArtifact(contractName);
|
|
const buildInfo = await artifacts.getBuildInfo(`${artifact.sourceName}:${artifact.contractName}`);
|
|
|
|
if (!buildInfo) {
|
|
throw new Error('storageLayout not present in compiler output.');
|
|
}
|
|
|
|
const output: StorageCompilerOutput = buildInfo.output;
|
|
const { storageLayout } = output.contracts[artifact.sourceName][artifact.contractName];
|
|
|
|
if (!storageLayout) {
|
|
throw new Error('Contract hasn\'t been compiled.');
|
|
}
|
|
|
|
return storageLayout;
|
|
};
|
|
|
|
/**
|
|
* Get storage value in hardhat environment using ethers.
|
|
* @param address
|
|
* @param position
|
|
*/
|
|
export const getStorageAt: GetStorageAt = async ({ blockHash, contract, slot }) => {
|
|
// TODO: Fix use of blockHash as hex string in getStorageAt.
|
|
// Using blockHash in getStorageAt throws error.
|
|
// https://github.com/ethers-io/ethers.js/pull/1550#issuecomment-841746994
|
|
// Using latest tag for temporary fix in test scenario.
|
|
blockHash = 'latest';
|
|
const value = await ethers.provider.getStorageAt(contract, slot, blockHash);
|
|
|
|
return {
|
|
value,
|
|
proof: {
|
|
// Returning null value as proof, since ethers library getStorageAt method doesnt return proof.
|
|
// This function is used in tests to mock the getStorageAt method of ipld-eth-client which returns proof along with value.
|
|
data: JSON.stringify(null)
|
|
}
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Generate array of dummy addresses of specified length.
|
|
* @param length
|
|
*/
|
|
export const generateDummyAddresses = (length: number): Array<string> => {
|
|
return Array.from({ length }, () => {
|
|
return ethers.utils.getAddress(ethers.utils.hexlify(ethers.utils.randomBytes(20)));
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Get latest blockHash.
|
|
*/
|
|
export const getBlockHash = async (): Promise<string> => {
|
|
const blockNumber = await ethers.provider.getBlockNumber();
|
|
const { hash } = await ethers.provider.getBlock(blockNumber);
|
|
return hash;
|
|
};
|
|
|
|
/**
|
|
* Assert proof data returned from ipld graphql server.
|
|
* @param blockHash
|
|
* @param address
|
|
* @param proofData
|
|
*/
|
|
export const assertProofData = (blockHash: string, address: string, proofData: ProofData): void => {
|
|
const {
|
|
blockHash: proofBlockHash,
|
|
account: {
|
|
address: proofAddress,
|
|
storage
|
|
}
|
|
} = proofData;
|
|
|
|
expect(proofBlockHash).to.equal(blockHash);
|
|
expect(proofAddress).to.equal(address);
|
|
expect(storage.cid).to.not.be.empty;
|
|
expect(storage.ipldBlock).to.not.be.empty;
|
|
};
|
|
|
|
/**
|
|
* Assert array of proof data.
|
|
* @param blockHash
|
|
* @param address
|
|
* @param proofArray
|
|
*/
|
|
export const assertProofArray = (blockHash: string, address: string, proofArray: Array<any>): void => {
|
|
proofArray.forEach(proofData => {
|
|
if (isArray(proofData)) {
|
|
assertProofArray(blockHash, address, proofData);
|
|
return;
|
|
}
|
|
|
|
if (['blockHash', 'account'].every(key => key in proofData)) {
|
|
assertProofData(blockHash, address, proofData);
|
|
return;
|
|
}
|
|
|
|
assertProofStruct(blockHash, address, proofData);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Assert array of proof data from structure type.
|
|
* @param blockHash
|
|
* @param address
|
|
* @param proofStruct
|
|
*/
|
|
export const assertProofStruct = (blockHash: string, address: string, proofStruct: {[key: string]: any}): void => {
|
|
Object.values(proofStruct).forEach(proofData => {
|
|
if (isArray(proofData)) {
|
|
assertProofArray(blockHash, address, proofData);
|
|
return;
|
|
}
|
|
|
|
if (['blockHash', 'account'].every(key => key in proofData)) {
|
|
assertProofData(blockHash, address, proofData);
|
|
return;
|
|
}
|
|
|
|
assertProofStruct(blockHash, address, proofData);
|
|
});
|
|
};
|