Skip to content

Commit

Permalink
1371 backend alchemy efficiency (passportxyz#1380)
Browse files Browse the repository at this point in the history
* fixing nft endpoint

* refactor(iam): remove redundant Alchemy calls and use more efficient NFT method

* typo

* refactor(iam): working primarily with string & BigNumber when checking posessions

---------

Co-authored-by: Gerald Iakobinyi-Pich <[email protected]>
  • Loading branch information
lucianHymer and nutrina authored Jun 19, 2023
1 parent 2189fc5 commit 053c437
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 167 deletions.
96 changes: 70 additions & 26 deletions platforms/src/ETH/Providers/ethErc20Possession.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// ----- Types
import type { Provider, ProviderOptions } from "../../types";
import type { RequestPayload, VerifiedPayload } from "@gitcoin/passport-types";
import type { RequestPayload, VerifiedPayload, ProviderContext } from "@gitcoin/passport-types";

// ----- Ethers library
import { Contract } from "ethers";
import { formatUnits } from "@ethersproject/units";
import { parseUnits } from "@ethersproject/units";
import { BigNumber } from "@ethersproject/bignumber";

// ----- RPC Getter
import { getRPCProvider } from "../../utils/signer";

Expand Down Expand Up @@ -38,36 +40,69 @@ const ERC20_ABI = [
},
];

type EthErc20Context = ProviderContext & {
ethErc20?: {
counts?: {
eth?: BigNumber;
[tokenAddress: string]: BigNumber;
};
};
};

// set the network rpc url based on env
export const RPC_URL = process.env.RPC_URL;

export async function getTokenBalance(
address: string,
tokenContractAddress: string,
decimalNumber: number,
payload: RequestPayload
): Promise<number> {
// define a provider using the rpc url
const staticProvider = getRPCProvider(payload);
// load Token contract
const readContract = new Contract(tokenContractAddress, ERC20_ABI, staticProvider);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
const tokenBalance: string = await readContract?.balanceOf(address);
const balanceFormatted: string = formatUnits(tokenBalance, decimalNumber);
return parseFloat(balanceFormatted);
payload: RequestPayload,
context: EthErc20Context
): Promise<BigNumber> {
if (context.ethErc20?.counts?.[tokenContractAddress] === undefined) {
// define a provider using the rpc url
const staticProvider = getRPCProvider(payload);
// load Token contract
const readContract = new Contract(tokenContractAddress, ERC20_ABI, staticProvider);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
const tokenBalance: string = await readContract?.balanceOf(address);
const bnTokenBalance: BigNumber = parseUnits(tokenBalance, 0);

if (!context.ethErc20) {
context.ethErc20 = {};
}
if (!context.ethErc20.counts) {
context.ethErc20.counts = {};
}
context.ethErc20.counts[tokenContractAddress] = bnTokenBalance;
}
return context.ethErc20.counts[tokenContractAddress];
}

export async function getEthBalance(address: string, payload: RequestPayload): Promise<number> {
// define a provider using the rpc url
const staticProvider = getRPCProvider(payload);
const ethBalance = await staticProvider?.getBalance(address);
// convert a currency unit from wei to ether
const balanceFormatted: string = formatUnits(ethBalance, 18);
return parseFloat(balanceFormatted);
export async function getEthBalance(
address: string,
payload: RequestPayload,
context: EthErc20Context
): Promise<BigNumber> {
if (context.ethErc20?.counts?.eth === undefined) {
// define a provider using the rpc url
const staticProvider = getRPCProvider(payload);
const ethBalance = await staticProvider?.getBalance(address);
// convert a currency unit from wei to ether

if (!context.ethErc20) {
context.ethErc20 = {};
}
if (!context.ethErc20.counts) {
context.ethErc20.counts = {};
}
context.ethErc20.counts.eth = ethBalance;
}
return context.ethErc20.counts.eth;
}

export type ethErc20PossessionProviderOptions = {
threshold: number;
threshold: string;
recordAttribute: string;
contractAddress: string;
decimalNumber: number;
Expand All @@ -81,7 +116,7 @@ export class EthErc20PossessionProvider implements Provider {

// Options can be set here and/or via the constructor
_options: ethErc20PossessionProviderOptions = {
threshold: 1,
threshold: "1",
recordAttribute: "",
contractAddress: "",
decimalNumber: 18,
Expand All @@ -95,24 +130,33 @@ export class EthErc20PossessionProvider implements Provider {
}

// verify that the proof object contains valid === "true"
async verify(payload: RequestPayload): Promise<VerifiedPayload> {
async verify(payload: RequestPayload, context: EthErc20Context): Promise<VerifiedPayload> {
const { address } = payload;
let valid = false;
let amount = 0;
let amount: BigNumber = BigNumber.from("0");

try {
if (this._options.contractAddress.length > 0) {
amount = await getTokenBalance(address, this._options.contractAddress, this._options.decimalNumber, payload);
amount = await getTokenBalance(
address,
this._options.contractAddress,
this._options.decimalNumber,
payload,
context
);
} else {
amount = await getEthBalance(address, payload);
amount = await getEthBalance(address, payload, context);
}
} catch (e) {
return {
valid: false,
error: [this._options.error],
};
} finally {
valid = amount >= this._options.threshold;
const bnThreshold = parseUnits(this._options.threshold, this._options.decimalNumber);
if (BigNumber.isBigNumber(amount)) {
valid = amount.gte(bnThreshold);
}
}
return {
valid,
Expand Down
Loading

0 comments on commit 053c437

Please sign in to comment.