Skip to content

Commit

Permalink
fix(iam): updated offset counts & added successful txn checks
Browse files Browse the repository at this point in the history
  • Loading branch information
aminah-io committed Aug 25, 2022
1 parent 18d8ef2 commit 6c8c32c
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 33 deletions.
25 changes: 12 additions & 13 deletions iam/__tests__/ethTransactions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ const toUnixTime = () => {
return Math.floor(new Date().getTime() / 1000);
};

const ETH_GAS_OFFSET_COUNT = 1000;
const FIRST_ETH_TXN_OFFSET_COUNT = 1;
const ETH_GTE_ONE_TXN_OFFSET_COUNT = 10;
const ETH_GAS_OFFSET_COUNT = 500;
const FIRST_ETH_GTE_TXN_OFFSET_COUNT = 100;

const validEtherscanResponse = {
data: {
Expand Down Expand Up @@ -278,7 +277,7 @@ describe("Attempt verification for gte 30 days since first ETH transaction stamp
} as unknown as RequestPayload);

expect(axios.get).toHaveBeenCalledTimes(1);
expect(mockedAxios.get).toBeCalledWith(`https://api.etherscan.io/api?module=account&action=txlist&address=${MOCK_ADDRESS_LOWER}&page=1&offset=${FIRST_ETH_TXN_OFFSET_COUNT}&sort=asc&apikey=${ETHERSCAN_API_KEY}`);
expect(mockedAxios.get).toBeCalledWith(`https://api.etherscan.io/api?module=account&action=txlist&address=${MOCK_ADDRESS_LOWER}&page=1&offset=${FIRST_ETH_GTE_TXN_OFFSET_COUNT}&sort=asc&apikey=${ETHERSCAN_API_KEY}`);
expect(verifiedPayload).toEqual({
valid: true,
record: {
Expand All @@ -301,7 +300,7 @@ describe("Attempt verification for gte 30 days since first ETH transaction stamp
} as unknown as RequestPayload);

expect(axios.get).toHaveBeenCalledTimes(1);
expect(mockedAxios.get).toBeCalledWith(`https://api.etherscan.io/api?module=account&action=txlist&address=${MOCK_ADDRESS_LOWER}&page=1&offset=${FIRST_ETH_TXN_OFFSET_COUNT}&sort=asc&apikey=${ETHERSCAN_API_KEY}`);
expect(mockedAxios.get).toBeCalledWith(`https://api.etherscan.io/api?module=account&action=txlist&address=${MOCK_ADDRESS_LOWER}&page=1&offset=${FIRST_ETH_GTE_TXN_OFFSET_COUNT}&sort=asc&apikey=${ETHERSCAN_API_KEY}`);

expect(verifiedPayload).toMatchObject({ valid: false });
});
Expand All @@ -319,7 +318,7 @@ describe("Attempt verification for gte 30 days since first ETH transaction stamp
} as unknown as RequestPayload);

expect(axios.get).toHaveBeenCalledTimes(1);
expect(mockedAxios.get).toBeCalledWith(`https://api.etherscan.io/api?module=account&action=txlist&address=${MOCK_ADDRESS_LOWER}&page=1&offset=${FIRST_ETH_TXN_OFFSET_COUNT}&sort=asc&apikey=${ETHERSCAN_API_KEY}`);
expect(mockedAxios.get).toBeCalledWith(`https://api.etherscan.io/api?module=account&action=txlist&address=${MOCK_ADDRESS_LOWER}&page=1&offset=${FIRST_ETH_GTE_TXN_OFFSET_COUNT}&sort=asc&apikey=${ETHERSCAN_API_KEY}`);

expect(verifiedPayload).toMatchObject({ valid: false });
});
Expand All @@ -337,7 +336,7 @@ describe("Attempt verification for gte 30 days since first ETH transaction stamp
} as unknown as RequestPayload);

expect(axios.get).toHaveBeenCalledTimes(1);
expect(mockedAxios.get).toBeCalledWith(`https://api.etherscan.io/api?module=account&action=txlist&address=&page=1&offset=${FIRST_ETH_TXN_OFFSET_COUNT}&sort=asc&apikey=${ETHERSCAN_API_KEY}`);
expect(mockedAxios.get).toBeCalledWith(`https://api.etherscan.io/api?module=account&action=txlist&address=&page=1&offset=${FIRST_ETH_GTE_TXN_OFFSET_COUNT}&sort=asc&apikey=${ETHERSCAN_API_KEY}`);

expect(verifiedPayload).toMatchObject({ valid: false });
});
Expand All @@ -355,7 +354,7 @@ describe("Attempt verification for gte 30 days since first ETH transaction stamp
} as unknown as RequestPayload);

expect(axios.get).toHaveBeenCalledTimes(1);
expect(mockedAxios.get).toBeCalledWith(`https://api.etherscan.io/api?module=account&action=txlist&address=${BAD_MOCK_ADDRESS_LOWER}&page=1&offset=${FIRST_ETH_TXN_OFFSET_COUNT}&sort=asc&apikey=${ETHERSCAN_API_KEY}`);
expect(mockedAxios.get).toBeCalledWith(`https://api.etherscan.io/api?module=account&action=txlist&address=${BAD_MOCK_ADDRESS_LOWER}&page=1&offset=${FIRST_ETH_GTE_TXN_OFFSET_COUNT}&sort=asc&apikey=${ETHERSCAN_API_KEY}`);

expect(verifiedPayload).toMatchObject({ valid: false });
});
Expand All @@ -379,7 +378,7 @@ describe("Attempt verification for at least one ETH transaction on the mainnet s
} as unknown as RequestPayload);

expect(axios.get).toHaveBeenCalledTimes(1);
expect(mockedAxios.get).toBeCalledWith(`https://api.etherscan.io/api?module=account&action=txlist&address=${MOCK_ADDRESS_LOWER}&page=1&offset=${ETH_GTE_ONE_TXN_OFFSET_COUNT}&sort=asc&apikey=${ETHERSCAN_API_KEY}`);
expect(mockedAxios.get).toBeCalledWith(`https://api.etherscan.io/api?module=account&action=txlist&address=${MOCK_ADDRESS_LOWER}&page=1&offset=${FIRST_ETH_GTE_TXN_OFFSET_COUNT}&sort=asc&apikey=${ETHERSCAN_API_KEY}`);

expect(verifiedPayload).toEqual({
valid: true,
Expand All @@ -403,7 +402,7 @@ describe("Attempt verification for at least one ETH transaction on the mainnet s
} as unknown as RequestPayload);

expect(axios.get).toHaveBeenCalledTimes(1);
expect(mockedAxios.get).toBeCalledWith(`https://api.etherscan.io/api?module=account&action=txlist&address=${MOCK_ADDRESS_LOWER}&page=1&offset=${ETH_GTE_ONE_TXN_OFFSET_COUNT}&sort=asc&apikey=${ETHERSCAN_API_KEY}`);
expect(mockedAxios.get).toBeCalledWith(`https://api.etherscan.io/api?module=account&action=txlist&address=${MOCK_ADDRESS_LOWER}&page=1&offset=${FIRST_ETH_GTE_TXN_OFFSET_COUNT}&sort=asc&apikey=${ETHERSCAN_API_KEY}`);

expect(verifiedPayload).toMatchObject({ valid: false });
});
Expand All @@ -421,7 +420,7 @@ describe("Attempt verification for at least one ETH transaction on the mainnet s
} as unknown as RequestPayload);

expect(axios.get).toHaveBeenCalledTimes(1);
expect(mockedAxios.get).toBeCalledWith(`https://api.etherscan.io/api?module=account&action=txlist&address=${MOCK_ADDRESS_LOWER}&page=1&offset=${ETH_GTE_ONE_TXN_OFFSET_COUNT}&sort=asc&apikey=${ETHERSCAN_API_KEY}`);
expect(mockedAxios.get).toBeCalledWith(`https://api.etherscan.io/api?module=account&action=txlist&address=${MOCK_ADDRESS_LOWER}&page=1&offset=${FIRST_ETH_GTE_TXN_OFFSET_COUNT}&sort=asc&apikey=${ETHERSCAN_API_KEY}`);

expect(verifiedPayload).toMatchObject({ valid: false });
});
Expand All @@ -439,7 +438,7 @@ describe("Attempt verification for at least one ETH transaction on the mainnet s
} as unknown as RequestPayload);

expect(axios.get).toHaveBeenCalledTimes(1);
expect(mockedAxios.get).toBeCalledWith(`https://api.etherscan.io/api?module=account&action=txlist&address=&page=1&offset=${ETH_GTE_ONE_TXN_OFFSET_COUNT}&sort=asc&apikey=${ETHERSCAN_API_KEY}`);
expect(mockedAxios.get).toBeCalledWith(`https://api.etherscan.io/api?module=account&action=txlist&address=&page=1&offset=${FIRST_ETH_GTE_TXN_OFFSET_COUNT}&sort=asc&apikey=${ETHERSCAN_API_KEY}`);

expect(verifiedPayload).toMatchObject({ valid: false });
});
Expand All @@ -457,7 +456,7 @@ describe("Attempt verification for at least one ETH transaction on the mainnet s
} as unknown as RequestPayload);

expect(axios.get).toHaveBeenCalledTimes(1);
expect(mockedAxios.get).toBeCalledWith(`https://api.etherscan.io/api?module=account&action=txlist&address=${BAD_MOCK_ADDRESS_LOWER}&page=1&offset=${ETH_GTE_ONE_TXN_OFFSET_COUNT}&sort=asc&apikey=${ETHERSCAN_API_KEY}`);
expect(mockedAxios.get).toBeCalledWith(`https://api.etherscan.io/api?module=account&action=txlist&address=${BAD_MOCK_ADDRESS_LOWER}&page=1&offset=${FIRST_ETH_GTE_TXN_OFFSET_COUNT}&sort=asc&apikey=${ETHERSCAN_API_KEY}`);

expect(verifiedPayload).toMatchObject({ valid: false });
});
Expand Down
41 changes: 21 additions & 20 deletions iam/src/providers/ethTransactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export class EthGasProvider implements Provider {
// Verify that the address that is passed in has accumulated >= 0.5 ETH on gas fees
async verify(payload: RequestPayload): Promise<VerifiedPayload> {
const address = payload.address.toLocaleLowerCase();
const offsetCount = 1000;
const offsetCount = 500;
let valid = false,
ethData: EtherscanRequestResponse["data"],
verifiedPayload = {
Expand Down Expand Up @@ -103,7 +103,7 @@ export class FirstEthTxnProvider implements Provider {
// first ETH transaction on the mainnet
async verify(payload: RequestPayload): Promise<VerifiedPayload> {
const address = payload.address.toLocaleLowerCase();
const offsetCount = 1;
const offsetCount = 100;
let valid = false,
ethData: EtherscanRequestResponse["data"],
verifiedPayload = {
Expand All @@ -112,7 +112,7 @@ export class FirstEthTxnProvider implements Provider {

try {
ethData = await requestEthData(address, offsetCount);
verifiedPayload = checkFirstTxn(ethData);
verifiedPayload = checkFirstTxn(ethData, address);

valid = address && verifiedPayload.hasGTE30DaysSinceFirstTxn ? true : false;
} catch (e) {
Expand Down Expand Up @@ -148,7 +148,7 @@ export class EthGTEOneTxnProvider implements Provider {
// ETH transaction on the mainnet
async verify(payload: RequestPayload): Promise<VerifiedPayload> {
const address = payload.address.toLocaleLowerCase();
const offsetCount = 10;
const offsetCount = 100;
let valid = false,
ethData: EtherscanRequestResponse["data"],
verifiedPayload = {
Expand All @@ -157,7 +157,7 @@ export class EthGTEOneTxnProvider implements Provider {

try {
ethData = await requestEthData(address, offsetCount);
verifiedPayload = checkForTxns(ethData);
verifiedPayload = checkForTxns(ethData, address);

valid = address && verifiedPayload.hasGTEOneEthTxn ? true : false;
} catch (e) {
Expand Down Expand Up @@ -218,36 +218,37 @@ const checkGasFees = (ethData: EtherscanRequestResponse["data"]): EthGasCheck =>
};
};

const checkFirstTxn = (ethData: EtherscanRequestResponse["data"]): EthFirstTxnCheck => {
const checkFirstTxn = (ethData: EtherscanRequestResponse["data"], address: string): EthFirstTxnCheck => {
// set variables for timestamp to days calculations
let hasGTE30DaysSinceFirstTxn = false;
let firstResult;
const results = ethData.result;

// Return the first successful transaction that was made >= 30 days ago by the wallet holder
if (ethData.result.length > 0) {
firstResult = ethData.result[0];
}
const successfulFirstTxn = results.findIndex((result) => {
const txnInMilliseconds = parseInt(result.timeStamp) * 1000;
const todayInMilliseconds = new Date().getTime();
const timeDifference = todayInMilliseconds - txnInMilliseconds;
const daysDifference = timeDifference / (1000 * 3600 * 24);

// Sort direction should be asc so the 1st txn can be checked -- if the first txn is
// >= 30 days ago, return true
const txnInMilliseconds = parseInt(firstResult.timeStamp) * 1000;
const todayInMilliseconds = new Date().getTime();
const timeDifference = todayInMilliseconds - txnInMilliseconds;
const daysDifference = timeDifference / (1000 * 3600 * 24);
return daysDifference >= 30 && result.isError === "0" && result.from.toLowerCase() === address;
});

if (daysDifference >= 30) {
hasGTE30DaysSinceFirstTxn = true;
successfulFirstTxn === -1 ? (hasGTE30DaysSinceFirstTxn = false) : (hasGTE30DaysSinceFirstTxn = true);
}

return {
hasGTE30DaysSinceFirstTxn,
};
};

const checkForTxns = (ethData: EtherscanRequestResponse["data"]): EthGTEOneTxnCheck => {
const checkForTxns = (ethData: EtherscanRequestResponse["data"], address: string): EthGTEOneTxnCheck => {
const results = ethData.result;
let hasGTEOneEthTxn = false;
// Iterate through the result array until the first instance where a successful txn was made
// Iterate through the results array until the first instance where a
// successful txn was made by the wallet holder
if (results.length > 0) {
const txnsCheck = results.findIndex((result) => result.isError === "0");
const txnsCheck = results.findIndex((result) => result.isError === "0" && result.from.toLowerCase() === address);
hasGTEOneEthTxn = txnsCheck === -1 ? false : true;
}

Expand Down

0 comments on commit 6c8c32c

Please sign in to comment.