Skip to content

Commit

Permalink
trustwallet#255 - implement ERC721Parser.parseTransactionOperations()…
Browse files Browse the repository at this point in the history
… and it's test
  • Loading branch information
johnnynanjiang committed Jun 9, 2018
1 parent f97cef6 commit b2ac419
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 35 deletions.
67 changes: 49 additions & 18 deletions src/common/erc721/ERC721Parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ export class ERC721Parser {
}
}

public extractDecodedLogsFromTransactions(transactions): Promise<any[]> {
const promises = transactions.map((transaction) => {
return new Promise((resolve) => {
resolve(
this.extractDecodedLogsFromTransaction(transaction)
);
})
})

return Promise.all(promises).then((decodedLogs) => {
return [].concat.apply([], decodedLogs);
});
}

public extractDecodedLogsFromTransaction(transaction): any[] {
const results = [];

Expand All @@ -47,7 +61,7 @@ export class ERC721Parser {

const contractAddresses: string[] = [];

transactions.map((transaction: any) => {
transactions.map((transaction) => {
const decodedLogs = this.extractDecodedLogsFromTransaction(transaction);

decodedLogs.forEach((decodedLog: any) => {
Expand Down Expand Up @@ -124,7 +138,14 @@ export class ERC721Parser {
}
}

public updateDatabase(erc721Contract): Promise<any> {
public updateDatabase(erc721Contracts: any[]): Promise<any[]> {
return Promise.all(erc721Contracts.map((contract) => {
return this.updateContractRecord(contract);
})
)
}

public updateContractRecord(erc721Contract: any): Promise<any> {
erc721Contract.verified = this.isContractVerified(erc721Contract.address);
erc721Contract.enabled = true;

Expand All @@ -139,21 +160,31 @@ export class ERC721Parser {
});
}

public parseTransactionOperations(transactions: ISavedTransaction[], contracts: IContract[], decodedLogs: IDecodedLog[]) {
if (!transactions || !contracts || !decodedLogs) return Promise.resolve([]);
public async parseTransactionOperations(transactions: ISavedTransaction[], contracts: IContract[]) {
const rawTxOps = await this.parseRawTransactionOperations(transactions, contracts);
const rawTxOpsFlat = [].concat.apply([], rawTxOps); // flatten [[1],[2]] to [1, 2]
const rawTxOpsWithoutUndefined = rawTxOpsFlat.filter(function(e) { return e }); // remove undefined elements
return rawTxOpsWithoutUndefined;
}

if (decodedLogs.length == 0) return Promise.resolve([]);
public parseRawTransactionOperations(transactions: ISavedTransaction[], contracts: IContract[]) {
if (!transactions || !contracts) return Promise.resolve();

return Bluebird.map(transactions, (transaction) => {
return Bluebird.mapSeries(decodedLogs, (decodedLog: IDecodedLog, index: number) => {
const contract = contracts.find((contract: IContract) => contract.address === decodedLog.address.toLowerCase());
if (contract) {
const transfer = this.parseEventLog(decodedLog);
return this.findOrCreateTransactionOperation(transaction._id, index, transfer.from, transfer.to, transfer.value, contract._id);
}
})
const decodedLogs = this.extractDecodedLogsFromTransaction(transaction);

if (decodedLogs.length > 0) {
return Bluebird.mapSeries(decodedLogs, (decodedLog: IDecodedLog, index: number) => {
const contract = contracts.find((contract: IContract) => contract.address === decodedLog.address.toLowerCase());
if (contract) {
const transfer = this.parseEventLog(decodedLog);
return this.createOperationObject(transaction._id, index, transfer.from, transfer.to, transfer.value, decodedLog.name, contract._id);
}
})

}
}).catch((err: Error) => {
winston.error(`Could not parse transaction operations, error: ${err}`);
winston.error(`Could not parse transaction operations with error: ${err}`);
});
}

Expand Down Expand Up @@ -188,14 +219,14 @@ export class ERC721Parser {
return `${transactionId}-${index}`.toLowerCase();
}

private createOperationObject(transactionId: string, index: number, from: string, to: string, value: string, erc20ContractId?: any): ITransactionOperation {
private createOperationObject(transactionId: string, index: number, from: string, to: string, value: string, type: string, contractID?: any): ITransactionOperation {
return {
transactionId: this.getIndexedOperation(transactionId, index),
type: "token_transfer",
type: type,
from: from.toLocaleLowerCase(),
to,
value,
contract: erc20ContractId
to: to,
value: value,
contract: contractID,
};
}

Expand Down
2 changes: 1 addition & 1 deletion test/Common/ERC721/BlockTransactionParser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ describe("Test BlockTransactionParser", () => {
expect(receipt.from).to.equal("0x0681d8db095565fe8a346fa0277bffde9c0edbbf");
expect(receipt.to).to.equal("0xd850942ef8811f2a866692a623011bde52a462c1");
expect(receipt.gasUsed).to.equal(54835);
expect(receipt.status).to.equal(true);
expect(receipt.status).to.equal("0x1");
expect(receipt.transactionHash).to.equal("0xa22465a41c60485f29eb4f8f57a04836ab56cd43faafe2439c6de8938f10e974");
expect(receipt.transactionIndex).to.equal(0);

Expand Down
50 changes: 34 additions & 16 deletions test/Common/ERC721/ERC721Parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { BlockTransactionParser } from "../../../src/common/erc721/BlockTransact
import { ERC721Parser } from "../../../src/common/erc721/ERC721Parser";
import { Database } from "../../../src/models/Database";

const mongoose = require("mongoose");
const config = require("config");
const chai = require("chai")
chai.use(require("chai-as-promised"))
Expand Down Expand Up @@ -32,17 +33,6 @@ describe("Test ERC721Parser", () => {

expect(emptyDecodedLogs.length).to.equal(0);

const decodedLogs = erc721Parser.extractDecodedLogsFromTransaction(
transactions.filter((tx) => {
return tx._id === "0x1020494743d0a85187c9cc92f1e8f649552df9502464f72e219e7363d7446a17"
})[0]
);

expect(decodedLogs.length).to.equal(1);
expect(decodedLogs[0].name).to.equal("Approval");
expect(decodedLogs[0].address).to.equal("0x80A7E048F37A50500351C204Cb407766fA3baE7f");
expect(decodedLogs[0].events.length).to.equal(3);

const contractAddresses = await erc721Parser.extractContractAddresses(transactions);

expect(transactions.length).to.equal(178);
Expand All @@ -57,11 +47,39 @@ describe("Test ERC721Parser", () => {

expect(contracts.length).to.equal(1);
expect(contracts[0].address).to.equal("0x87d598064c736dd0c712d329afcfaa0ccc1921a1");
expect(contracts[0].address).to.equal("0x87d598064c736dd0c712d329afcfaa0ccc1921a1");
expect(contracts[0].address).to.equal("0x87d598064c736dd0c712d329afcfaa0ccc1921a1");
expect(contracts[0].address).to.equal("0x87d598064c736dd0c712d329afcfaa0ccc1921a1");
expect(contracts[0].name).to.equal("CryptoFighters");
expect(contracts[0].symbol).to.equal("CF");
expect(contracts[0].totalSupply).is.a("string");
expect(contracts[0].implementsERC721).is.true;

const savedContracts = await erc721Parser.updateDatabase(contracts);

const decodedLogs = await erc721Parser.extractDecodedLogsFromTransactions(transactions);

expect(decodedLogs.length).to.equal(94);

const decodedLogsFromATransaction = decodedLogs.filter((log) => {
return log.address === "0x80A7E048F37A50500351C204Cb407766fA3baE7f"
})[0];

expect(decodedLogsFromATransaction.events.length).to.equal(3);
expect(decodedLogsFromATransaction.name).to.equal("Approval");
expect(decodedLogsFromATransaction.address).to.equal("0x80A7E048F37A50500351C204Cb407766fA3baE7f");

const transactionOperations = await erc721Parser.parseTransactionOperations(transactions, savedContracts);

expect(transactionOperations.length).to.equal(1);

const approvalTxOps = transactionOperations.filter((txOps) => {
return txOps.type === "Approval"
})[0];

const results = await erc721Parser.parseTransactionOperations(transactions, contracts, decodedLogs);
expect(approvalTxOps.type).to.equal("Approval");
expect(approvalTxOps.from).to.equal("0xdcf005aa5550f76cd32c925c06a570bc36b0ac6f");
expect(approvalTxOps.to).to.equal("0xb2c3531f77ee0a7ec7094a0bc87ef4a269e0bcfc");
expect(approvalTxOps.value).to.equal("0");
expect(approvalTxOps.transactionId).to.equal("0x39f5aa0e8782662503910daefa905876cd7b798dab3c15dc0f361ea98ab55cdb-0");
expect(mongoose.Types.ObjectId.isValid(approvalTxOps.contract)).is.true;
})

it("Should get ERC721 contract", async () => {
Expand All @@ -80,7 +98,7 @@ describe("Test ERC721Parser", () => {
expect(erc721Contract_CF).to.have.property("totalSupply").a("string");
expect(erc721Contract_CF).to.have.property("implementsERC721").eql(true);

const result = await erc721Parser.updateDatabase(erc721Contract_CF);
const result = await erc721Parser.updateContractRecord(erc721Contract_CF);
// NOTE: check the database, delete the record then run the test, it should appear again.
})
})

0 comments on commit b2ac419

Please sign in to comment.