Skip to content

Commit

Permalink
trustwallet#255 - add models: ERC721Transaction, ERC721TransactionOpe…
Browse files Browse the repository at this point in the history
…ration, ERC721Token
  • Loading branch information
johnnynanjiang committed Jun 10, 2018
1 parent 3934a41 commit 4279e84
Show file tree
Hide file tree
Showing 8 changed files with 219 additions and 19 deletions.
8 changes: 4 additions & 4 deletions src/common/erc721/BlockTransactionParser.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as Bluebird from "bluebird";
import * as winston from "winston";

import { Transaction } from "../../models/TransactionModel";
import { ERC721Transaction } from "../../models/Erc721TransactionModel";
import { IBlock, IExtractedTransaction, ITransaction } from "../CommonInterfaces";
import { Config } from "../Config";

Expand All @@ -16,7 +16,7 @@ export class BlockTransactionParser {

public extractTransactions(block): any[] {
return this.getRawTransactions(block).map((tx: ITransaction) => {
return new Transaction(this.extractTransaction(block, tx));
return new ERC721Transaction(this.extractTransaction(block, tx));
});
}

Expand Down Expand Up @@ -90,8 +90,8 @@ export class BlockTransactionParser {
return Promise.resolve(results);
}

public updateTransactionsDatabase(transactions: any) {
const bulkTransactions = Transaction.collection.initializeUnorderedBulkOp();
public updateTransactionsInDatabase(transactions: any) {
const bulkTransactions = ERC721Transaction.collection.initializeUnorderedBulkOp();

transactions.forEach((transaction: IExtractedTransaction) =>
bulkTransactions.find({_id: transaction._id}).upsert().replaceOne(transaction)
Expand Down
28 changes: 25 additions & 3 deletions src/common/erc721/ERC721Parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { nameABI, ownerOfABI, standardERC721ABI } from "../abi/ABI";
import { ERC721Contract } from "../../models/Erc721ContractModel";
import { contracts } from "../tokens/contracts";
import { IContract, IDecodedLog, ISavedTransaction, ITransactionOperation } from "../CommonInterfaces";
import { ERC721TransactionOperation } from "../../models/TransactionOperationModel";
import { Transaction } from "../../models/TransactionModel";
import { ERC721TransactionOperation } from "../../models/Erc721TransactionOperationModel";
import { ERC721Transaction } from "../../models/Erc721TransactionModel";

export class ERC721Parser {
private abiDecoder = require("abi-decoder");
Expand Down Expand Up @@ -196,12 +196,34 @@ export class ERC721Parser {
}
}

public getSavedTransactionsInDatabase(blockNumber: number): Promise<any[]> {
return ERC721Transaction.find({blockNumber: {$eq: blockNumber}})
.populate({
path: "operations",
populate: {
path: "contract",
model: "ERC721Contract"
}
});
}

public createOperations(transactions: any[]): any[] {
const operations: any = [];
transactions.forEach(transaction => {
transaction.operations.forEach((operation: any) => {
operations.push({address: operation.to, contract: operation.contract._id})
operations.push({address: operation.from, contract: operation.contract._id})
})
})
return operations
}

// ###### private methods ######

private updateTransactionOperationInDatabase(transactionOperation): Promise<ITransactionOperation[]> {
return ERC721TransactionOperation.findOneAndUpdate({transactionId: transactionOperation.transactionId}, transactionOperation, {upsert: true, new: true})
.then((operation: any) => {
return Transaction.findOneAndUpdate({_id: transactionOperation.originalTransactionId}, {$push: {operations: operation._id, addresses: {$each: [operation.to]}}})
return ERC721Transaction.findOneAndUpdate({_id: transactionOperation.originalTransactionId}, {$addToSet: {operations: operation._id, addresses: {$each: [operation.to]}}})
.catch((error: Error) => {
winston.error(`Could not update operation and address to transactionID ${transactionOperation.transactionId} with error: ${error}`);
})
Expand Down
22 changes: 22 additions & 0 deletions src/models/Erc721TokenModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const mongoose = require("mongoose");
const mongoosePaginate = require("mongoose-paginate");
const Schema = mongoose.Schema;

const erc721tokenSchema = new Schema({
_id: {
type: String,
required: true,
},
tokens: [{
type: mongoose.Schema.Types.ObjectId,
ref: "ERC721Contract",
required: true,
index: true
}]
}, {
versionKey: false,
});

erc721tokenSchema.plugin(mongoosePaginate);

export const ERC721Token = mongoose.model("ERC721Token", erc721tokenSchema );
92 changes: 92 additions & 0 deletions src/models/Erc721TransactionModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
const mongoose = require("mongoose");
const mongoosePaginate = require("mongoose-paginate");
const Schema = mongoose.Schema;


/**
* Model for a single transaction.
*
* @type {"mongoose".Schema}
*/
const transactionSchema = new Schema({
_id: {
type: String,
required: true
},
blockNumber: {
type: Number,
required: true,
index: true
},
timeStamp: {
type: String,
required: true,
index: true
},
nonce: {
type: Number,
required: true
},
from: {
type: String,
required: true
},
to: {
type: String,
required: true
},
addresses: [{
type: String,
index: true
}],
value: {
type: String,
required: true
},
gas: {
type: String,
required: true
},
gasPrice: {
type: String,
required: true
},
input: {
type: String,
required: true
},
gasUsed: {
type: String,
required: true
},
error: {
type: String
},
operations: [{
type: mongoose.Schema.Types.ObjectId,
ref: "ERC721TransactionOperation"
}],
contract: {
type: String,
default: null
}

}, {
versionKey: false,
toObject: {
virtuals: true
},
toJSON: {
virtuals: true
}
});

transactionSchema.virtual("success").get(function() {
if (this.hasOwnProperty("error")) {
return this.error === "";
}
});

transactionSchema.plugin(mongoosePaginate);

export const ERC721Transaction = mongoose.model("Erc721Transaction", transactionSchema );
42 changes: 42 additions & 0 deletions src/models/Erc721TransactionOperationModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const mongoose = require("mongoose");
const Schema = mongoose.Schema;

/**
* Model for a transaction action
* specifying the purpose of a
* transaction.
*
* @type {"mongoose".Schema}
*/
const transactionOperationSchema = new Schema({
transactionId: {
type: String,
required: true,
index: true
},
type: {
type: String,
required: true
},
from: {
type: String,
required: true
},
to: {
type: String,
required: true
},
value: {
type: String,
required: true
},
contract: {
type: mongoose.Schema.Types.ObjectId,
ref: "ERC721Contract",
required: true
}
}, {
versionKey: false
});

export const ERC721TransactionOperation = mongoose.model("ERC721TransactionOperation", transactionOperationSchema );
4 changes: 1 addition & 3 deletions src/models/TransactionOperationModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,4 @@ const transactionOperationSchema = new Schema({
versionKey: false
});


export const TransactionOperation = mongoose.model("TransactionOperation", transactionOperationSchema );
export const ERC721TransactionOperation = mongoose.model("ERC721TransactionOperation", transactionOperationSchema );
export const TransactionOperation = mongoose.model("TransactionOperation", transactionOperationSchema );
19 changes: 14 additions & 5 deletions test/Common/ERC721/BlockTransactionParser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ describe("Test BlockTransactionParser", () => {
it("Should parse transactions from a block", async () => {
const blockParser = new BlockParser();
const blockTransactionParser = new BlockTransactionParser();

const block = await blockParser.getBlockByNumber(5665445);

const transactions = blockTransactionParser.extractTransactions(block);
Expand Down Expand Up @@ -88,11 +89,19 @@ describe("Test BlockTransactionParser", () => {
expect(mergedTransaction.receipt.logs[0].topics.length).to.equal(3);
expect(mergedTransaction.receipt.logs[0].topics[0]).to.equal("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef");

const results = await blockTransactionParser.updateTransactionsDatabase(mergedTransactions);
const savedTransactions = await blockTransactionParser.updateTransactionsInDatabase(mergedTransactions);

expect(savedTransactions.length).to.equal(178);

const savedTransaction = savedTransactions.filter(
(stx) => { return stx._id === "0x39f5aa0e8782662503910daefa905876cd7b798dab3c15dc0f361ea98ab55cdb"; }
)[0];

expect(results.length).to.equal(178);
// NOTE: check the database to see
// if transaction _id = 0xb2c6a21504db37e36c5daae3663c704bbba7f1c4b0d16441fc347756e6bbfc9b
// is there, delete it then run the test, it should appear again.
/* NOTE:
this saved transaction has an ERC721 approval operation,
but it won't get saved until transaction operation parsing is done later,
by ERC721Parser.updateTransactionOperationsInDatabase().
*/
expect(savedTransaction.operations.length).to.equal(0);
})
})
23 changes: 19 additions & 4 deletions test/Common/ERC721/ERC721Parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,23 @@ describe("Test ERC721Parser", () => {
expect(approvalTxOps.value).to.equal("0");
expect(mongoose.Types.ObjectId.isValid(approvalTxOps.contract)).is.true;

const results = await erc721Parser.updateTransactionOperationsInDatabase(transactionOperations);
// NOTE: please note this method returns saved transactions instead of saved transaction operations.
const savedTransactions = await erc721Parser.updateTransactionOperationsInDatabase(transactionOperations);

expect(savedTransactions.length).to.equal(1);
expect(savedTransactions[0]._id).to.equal("0x39f5aa0e8782662503910daefa905876cd7b798dab3c15dc0f361ea98ab55cdb");

/*
NOTE:
after calling ERC721Parser.updateTransactionOperationsInDatabase(),
the transaction's operations get populated.
*/
const transactionsInDB = await erc721Parser.getSavedTransactionsInDatabase(5665445);
const transactionInDB = transactionsInDB.filter((tx) => {
return tx._id === "0x39f5aa0e8782662503910daefa905876cd7b798dab3c15dc0f361ea98ab55cdb";
})[0];

expect(results.length).to.equal(1);
expect(transactionInDB.operations.length).to.equal(1);
})

it("Should get ERC721 contract", async () => {
Expand All @@ -103,7 +117,8 @@ 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.updateERC721ContractInDatabase(erc721Contract_CF);
// NOTE: check the database, delete the record then run the test, it should appear again.
const savedERC721Contract = await erc721Parser.updateERC721ContractInDatabase(erc721Contract_CF);

expect(savedERC721Contract.name).to.equal("CryptoFighters");
})
})

0 comments on commit 4279e84

Please sign in to comment.