From 10f3fb80856634b8d0d1e985751af7db28c1999f Mon Sep 17 00:00:00 2001 From: Brendan McGill Date: Thu, 24 Jun 2021 15:19:10 -0700 Subject: [PATCH 1/3] update emissions factor --- src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 0a21501..a825199 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,9 +9,9 @@ export type { CalculatorOptions, AddressEmissionsResult }; /** * Based on 2021 methodology, see github and carbon.fyi for details. - * Last updated: Mar. 7, 2021 + * Last updated: June 24, 2021 */ -const KG_CO2_PER_GAS = 0.0002873993139; +const KG_CO2_PER_GAS = 0.0001809589427; /** * Calculate emissions of an address. Emissions are allocated for SENT (outgoing) transactions only. From 56b5165347b8b9fd16370dc27b40e7c9e6e37795 Mon Sep 17 00:00:00 2001 From: Brendan McGill Date: Thu, 24 Jun 2021 15:19:31 -0700 Subject: [PATCH 2/3] filter duplicate txn hashes and add tests. --- src/test-fixtures/getRandomHash.ts | 3 ++ .../filterValidOutgoingTransactions.test.ts | 33 +++++++++++++++++++ src/utils/filterValidOutgoingTransactions.ts | 16 ++++++--- src/utils/filterValidTransactions.test.ts | 31 +++++++++++++++++ src/utils/filterValidTransactions.ts | 11 ++++--- src/utils/getAddressTransactions.ts | 1 + 6 files changed, 86 insertions(+), 9 deletions(-) create mode 100644 src/test-fixtures/getRandomHash.ts diff --git a/src/test-fixtures/getRandomHash.ts b/src/test-fixtures/getRandomHash.ts new file mode 100644 index 0000000..979bfea --- /dev/null +++ b/src/test-fixtures/getRandomHash.ts @@ -0,0 +1,3 @@ +export const getRandomHash = () => { + return `0x${Math.random()}`; +}; diff --git a/src/utils/filterValidOutgoingTransactions.test.ts b/src/utils/filterValidOutgoingTransactions.test.ts index 0fef7a4..e24e8ef 100644 --- a/src/utils/filterValidOutgoingTransactions.test.ts +++ b/src/utils/filterValidOutgoingTransactions.test.ts @@ -1,5 +1,6 @@ import { TransactionData } from "../types"; import { filterValidOutgoingTransactions } from "./filterValidOutgoingTransactions"; +import { getRandomHash } from "../test-fixtures/getRandomHash"; const SENDER = "0x5abfec25f74cd88437631a7731906932776356f9"; const RECIEVER = "0x3fb1cd2cd96c6d5c0b5eb3322d807b34482481d4"; @@ -14,10 +15,12 @@ describe("filterValidOutgoingTransactions", () => { to: RECIEVER, from: SENDER, isError: "1", + hash: getRandomHash(), }, { to: RECIEVER, from: SENDER, + hash: getRandomHash(), }, ] as TransactionData[]; expect(filterValidOutgoingTransactions(txns, SENDER)).toHaveLength(1); @@ -28,10 +31,12 @@ describe("filterValidOutgoingTransactions", () => { to: RECIEVER, from: SENDER, isError: "0", + hash: getRandomHash(), }, { to: RECIEVER, from: SENDER, + hash: getRandomHash(), }, ] as TransactionData[]; expect(filterValidOutgoingTransactions(txns, SENDER)).toHaveLength(2); @@ -41,13 +46,41 @@ describe("filterValidOutgoingTransactions", () => { { to: SENDER, from: RECIEVER, + hash: getRandomHash(), }, { to: RECIEVER, from: SENDER, + hash: getRandomHash(), }, ] as TransactionData[]; expect(filterValidOutgoingTransactions(txns, SENDER)).toHaveLength(1); expect(true).toBe(true); }); + test("Remove duplicates", () => { + const txns = [ + { + to: RECIEVER, + from: SENDER, + isError: "0", + hash: "unique-hash", + }, + { + to: RECIEVER, + from: SENDER, + hash: "duplicate-hash", + }, + { + to: RECIEVER, + from: SENDER, + hash: "duplicate-hash", + }, + { + to: RECIEVER, + from: SENDER, + hash: "duplicate-hash", + }, + ] as TransactionData[]; + expect(filterValidOutgoingTransactions(txns, SENDER)).toHaveLength(2); + }); }); diff --git a/src/utils/filterValidOutgoingTransactions.ts b/src/utils/filterValidOutgoingTransactions.ts index 9b425bb..79612d3 100644 --- a/src/utils/filterValidOutgoingTransactions.ts +++ b/src/utils/filterValidOutgoingTransactions.ts @@ -8,9 +8,15 @@ export const filterValidOutgoingTransactions = ( transactions: TransactionData[], address: string ): TransactionData[] => { - return transactions.filter((txn: TransactionData) => { - const isOutgoing = txn.from.toLowerCase() === address.toLowerCase(); - const succeeded = txn.isError !== "1"; - return isOutgoing && succeeded; - }); + return transactions.reduce((prev, txn) => { + // since response is sorted, we only need to compare hash of preceeding valid txn + if ( + txn.from.toLowerCase() === address.toLowerCase() && + txn.isError !== "1" && + prev[prev.length - 1]?.hash !== txn.hash + ) { + return prev.concat([txn]); + } + return prev; + }, []); }; diff --git a/src/utils/filterValidTransactions.test.ts b/src/utils/filterValidTransactions.test.ts index 77a2db8..824d34c 100644 --- a/src/utils/filterValidTransactions.test.ts +++ b/src/utils/filterValidTransactions.test.ts @@ -1,5 +1,6 @@ import { TransactionData } from "../types"; import { filterValidTransactions } from "./filterValidTransactions"; +import { getRandomHash } from "../test-fixtures/getRandomHash"; const SENDER = "0x5abfec25f74cd88437631a7731906932776356f9"; const RECIEVER = "0x3fb1cd2cd96c6d5c0b5eb3322d807b34482481d4"; @@ -14,10 +15,12 @@ describe("filterValidTransactions", () => { to: RECIEVER, from: SENDER, isError: "1", + hash: getRandomHash(), }, { to: RECIEVER, from: SENDER, + hash: getRandomHash(), }, ] as TransactionData[]; expect(filterValidTransactions(txns)).toHaveLength(1); @@ -28,10 +31,38 @@ describe("filterValidTransactions", () => { to: RECIEVER, from: SENDER, isError: "0", + hash: getRandomHash(), }, { to: RECIEVER, from: SENDER, + hash: getRandomHash(), + }, + ] as TransactionData[]; + expect(filterValidTransactions(txns)).toHaveLength(2); + }); + test("Remove duplicates", () => { + const txns = [ + { + to: RECIEVER, + from: SENDER, + isError: "0", + hash: "unique-hash", + }, + { + to: RECIEVER, + from: SENDER, + hash: "duplicate-hash", + }, + { + to: RECIEVER, + from: SENDER, + hash: "duplicate-hash", + }, + { + to: RECIEVER, + from: SENDER, + hash: "duplicate-hash", }, ] as TransactionData[]; expect(filterValidTransactions(txns)).toHaveLength(2); diff --git a/src/utils/filterValidTransactions.ts b/src/utils/filterValidTransactions.ts index 55e9ed6..19f8024 100644 --- a/src/utils/filterValidTransactions.ts +++ b/src/utils/filterValidTransactions.ts @@ -7,8 +7,11 @@ import { TransactionData } from "../types"; export const filterValidTransactions = ( transactions: TransactionData[] ): TransactionData[] => { - return transactions.filter((txn: TransactionData) => { - const succeeded = txn.isError !== "1"; - return succeeded; - }); + return transactions.reduce((prev, txn) => { + // since response is sorted, we only need to compare hash of preceeding valid txn + if (txn.isError !== "1" && prev[prev.length - 1]?.hash !== txn.hash) { + return prev.concat([txn]); + } + return prev; + }, []); }; diff --git a/src/utils/getAddressTransactions.ts b/src/utils/getAddressTransactions.ts index cd2d7ea..58035eb 100644 --- a/src/utils/getAddressTransactions.ts +++ b/src/utils/getAddressTransactions.ts @@ -19,6 +19,7 @@ export const getAddressTransactions = async ( return { done: false, // we can't rely on the txn count for completeness (it might be slightly less than 10k after filtering) transactions: transactions.filter( + // filter out the lowest block number because we don't know if we captured all of that blocks txns. (txn) => txn.blockNumber > lowestBlockNumber ), }; From aae99c9228c626c1365aa678fc6d7fbd31029bb5 Mon Sep 17 00:00:00 2001 From: Brendan McGill Date: Thu, 24 Jun 2021 15:19:38 -0700 Subject: [PATCH 3/3] 2.1.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index bb731d2..c7f592d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "ethereum-emissions-calculator", - "version": "2.0.0", + "version": "2.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 440b720..7cabd86 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ethereum-emissions-calculator", - "version": "2.0.0", + "version": "2.1.0", "description": "TypeScript utils to calculate the CO2 emissions of an Ethereum wallet. Powered by the Etherscan.io API.", "main": "./lib/index.js", "directories": {