Skip to content

Commit

Permalink
Merge pull request #78 from moshmage/feat/decode-log
Browse files Browse the repository at this point in the history
apply decodeLog logic on transaction receipt
  • Loading branch information
bepro-bot authored Mar 4, 2023
2 parents 5d283a7 + 0030014 commit 97fa20a
Show file tree
Hide file tree
Showing 8 changed files with 768 additions and 540 deletions.
1,193 changes: 670 additions & 523 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,29 @@
"bignumber.js": "^9.0.2",
"date-fns": "^2.27.0",
"ipfs-http-client": "^50.1.2",
"web3": "^1.6.1",
"web3-eth": "^1.6.1",
"web3-utils": "^1.6.1"
"web3": "^1.8.2",
"web3-eth": "^1.8.2",
"web3-utils": "^1.8.2"
},
"devDependencies": {
"@openzeppelin/contracts": "^3.4.2",
"@taikai/dappkit-launchpad": "^0.0.19",
"@truffle/workflow-compile": "4.0.21",
"@types/chai": "^4.3.0",
"@types/chai-like": "^1.1.1",
"@types/chai-things": "^0.0.35",
"@types/mocha": "^9.0.0",
"@types/yargs": "^17.0.7",
"@typescript-eslint/eslint-plugin": "^5.11.0",
"@typescript-eslint/parser": "^5.11.0",
"@uniswap/v3-core": "^1.0.0",
"@uniswap/v3-periphery": "^1.1.1",
"chai": "^4.3.4",
"chai-like": "^1.1.1",
"chai-things": "^0.2.0",
"dotenv": "^8.2.0",
"eslint": "^8.8.0",
"find": "^0.3.0",
"ganache": "^7.0.0-beta.2",
"ganache-time-traveler": "^1.0.5",
"mocha": "^9.1.3",
Expand Down
4 changes: 2 additions & 2 deletions src/base/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export class Model<Methods = any> {
}: Partial<Web3ConnectionOptions> = {}): Promise<TransactionReceipt> {
const from = (await this.web3.eth.givenProvider.request({method: 'eth_requestAccounts'}))[0];

return new Promise(async (resolve, reject) => {
return new Promise<TransactionReceipt>(async (resolve, reject) => {
try {
const options = await this.contract.txOptions(method, value, from);
const sendMethod = () => method.send({from, value, ...options}, noop);
Expand All @@ -136,7 +136,7 @@ export class Model<Methods = any> {
console.error(e);
reject(e);
}
});
}).then(receipt => this.contract.parseReceiptLogs(receipt));
}
/* eslint-enable no-async-promise-executor */

Expand Down
44 changes: 38 additions & 6 deletions src/base/web3-contract.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {AbiItem} from 'web3-utils';
import {AbiItem, sha3} from 'web3-utils';
import {Contract, ContractSendMethod, DeployOptions} from 'web3-eth-contract';
import Web3 from 'web3';
import {Account, TransactionConfig} from 'web3-core';
import {TransactionReceipt} from '@interfaces/web3-core';
import {Log, TransactionReceipt} from '@interfaces/web3-core';
import {Errors} from '@interfaces/error-enum';
import {transactionHandler} from '@utils/transaction-handler';
import {Web3ConnectionOptions} from "@interfaces/web3-connection-options";
Expand Down Expand Up @@ -40,6 +40,7 @@ export interface Web3ContractOptions {

export class Web3Contract<Methods = any, Events = any> {
readonly self!: Contract;
readonly abi: AbiItem[];

/**
* Transaction options that will be used on each transaction.
Expand All @@ -52,11 +53,12 @@ export class Web3Contract<Methods = any, Events = any> {
readonly options: Web3ContractOptions = {auto: true}

constructor(readonly web3: Web3,
readonly abi: AbiItem[],
abi: AbiItem[],
readonly address?: string,
options: Web3ContractOptions = {auto: true}) {
this.self = new web3.eth.Contract(abi, address);
this.options = options;
this.abi = abi;
}

get methods(): Methods { return this.self.methods; }
Expand Down Expand Up @@ -88,6 +90,36 @@ export class Web3Contract<Methods = any, Events = any> {
}
/* eslint-enable complexity */

/* eslint-disable complexity */
/**
* Parses the logs of a transaction receipt using its abi events
*/
parseReceiptLogs<T = any>(receipt: TransactionReceipt): TransactionReceipt<T> {
if (receipt.logs?.length) {
const _events =
this.abi.filter(({type}) => type === "event")
.map(({inputs, ...rest}) =>
({inputs, ...rest, topic: sha3(`${rest.name}(${inputs?.map(i=> i.type).join(',')})`)}));

const hasAddressAndEqualLog = ({address}: Log) =>
!this.address ? true : this.address.toLowerCase() === address.toLowerCase()

for (const [i, log] of receipt.logs.entries()) {
for (const _event of _events) {
if (_event.topic === log.topics[0] && hasAddressAndEqualLog(log)) {
const args =
this.web3.eth.abi
.decodeLog(_event.inputs || [], log.data, _event.anonymous ? log.topics : log.topics.slice(1))
receipt.logs[i] = {...log, event: _event?.name, args} as unknown as Log & {event: string, args: T};
}
}
}
}

return receipt as TransactionReceipt<T>;
}
/* eslint-enable complexity */

/**
* Deploys the new AbiItem and returns its transaction receipt
*/
Expand Down Expand Up @@ -125,7 +157,7 @@ export class Web3Contract<Methods = any, Events = any> {
}
}

return new Promise<TransactionReceipt>(deployer);
return new Promise<TransactionReceipt>(deployer).then(receipt => this.parseReceiptLogs(receipt));
}

/**
Expand All @@ -139,7 +171,7 @@ export class Web3Contract<Methods = any, Events = any> {
customTransactionHandler: cb
}: Partial<Web3ConnectionOptions> = {}): Promise<TransactionReceipt> {
/* eslint-disable no-async-promise-executor */
return new Promise(async (resolve, reject) => {
return new Promise<TransactionReceipt>(async (resolve, reject) => {
try {

const from = account.address;
Expand All @@ -156,7 +188,7 @@ export class Web3Contract<Methods = any, Events = any> {
console.error(e);
reject(e);
}
})
}).then((receipt) => this.parseReceiptLogs(receipt))
/* eslint-enable no-async-promise-executor */
}
}
3 changes: 2 additions & 1 deletion src/interfaces/web3-connection-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,6 @@ export interface Web3ConnectionOptions {

customTransactionHandler?: (event: PromiEvent<TransactionReceipt | Contract>,
resolve: (data: any) => void,
reject: (e: unknown) => void, debug?: boolean) => void;
reject: (e: unknown) => void,
debug?: boolean) => void;
}
9 changes: 6 additions & 3 deletions src/interfaces/web3-core.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export interface Log {
export interface Log<T = any> {
blockNumber: number;
blockHash: string;
transactionIndex: number;
Expand All @@ -12,9 +12,12 @@ export interface Log {

transactionHash: string;
logIndex: number;

event?: string;
args?: T;
}

export interface TransactionReceipt {
export interface TransactionReceipt<T = any> {
to: string;
from: string;
contractAddress: string,
Expand All @@ -24,7 +27,7 @@ export interface TransactionReceipt {
logsBloom: string,
blockHash: string,
transactionHash: string,
logs: Array<Log>,
logs: Array<Log<T>>,
blockNumber: number,
confirmations: number,
cumulativeGasUsed: number,
Expand Down
21 changes: 21 additions & 0 deletions test/utils/decode-receipt-log.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {defaultWeb3Connection, erc20Deployer, expectEvent} from "./index";
import {Web3Connection} from "../../src";
import {nativeZeroAddress} from "../../src/utils/constants";

describe(`parseReceiptLogs`, () => {

const deploy_amount = `10000`;


let connection: Web3Connection;

before(async () => {
connection = await defaultWeb3Connection(true, true);
});

it(`Deploys ERC20 and expect transfer log event`, async () => {
await expectEvent(
erc20Deployer(`name`, `symbol`, deploy_amount, connection),
`Transfer`, {from: nativeZeroAddress, to: await connection.getAddress(), value: deploy_amount});
})
})
23 changes: 21 additions & 2 deletions test/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import {Web3ConnectionOptions, Web3Connection, ERC20, toSmartContractDecimals} from '../../src';
import {ERC20, toSmartContractDecimals, Web3Connection, Web3ConnectionOptions} from '../../src';
import {readFileSync} from 'fs';
import {resolve} from 'path';
import {expect} from 'chai';
import {expect, use} from 'chai';
import Web3 from 'web3';
import {HttpProvider, WebsocketProvider} from 'web3-core';
import {TransactionReceipt} from 'web3-eth';
import {Log} from "../../src/interfaces/web3-core";
import like from 'chai-like';
import things from 'chai-things';

use(like);
use(things);


/**
* Returns the private key from the output provided by ganache
Expand Down Expand Up @@ -130,9 +137,21 @@ export async function revertChain(web3: Web3) {
*/
export async function hasTxBlockNumber(promise: Promise<any>, message = `Should have blockNumber`) {
const tx = await promise.catch(e => {
console.log(e);
expect(e?.data?.reason || e?.data?.message || e?.message || `Should not have been rejected`, message).to.be.empty;
});

expect(tx, message).property('blockNumber').to.exist;

return tx;
}

export async function expectEvent<T = any>(promise: Promise<T>, eventName: string, params: any) {
const tx = await hasTxBlockNumber(promise);
expect(tx.logs, `Should have logs array`).to.exist;

expect(tx.logs).to.be.an('array').that.contains.something.like({event: eventName});
expect(tx.logs.map((l: Log) => l.args)).to.be.an('array').that.contains.something.like(params);
}

export function calculateAPR(apr = 1, start = 0,
Expand Down

0 comments on commit 97fa20a

Please sign in to comment.