checkTokenRules
Returns two Rule arrays, tokenPassing for rules the user meets the criteria for, and tokenFailing for rules the user doesn't meet the criteria for.
/* src/utils/checkTokenRules */
import { Rule, Block3dConfig } from "../types/block3d";
import { createPublicClient, http, extractChain, Chain } from "viem";
import * as chains from "viem/chains";
import { type Config } from "wagmi";
import { getRpcUrls } from "./getRpcUrls";
import { abi } from "../constants/abi";
/**
* @param address is the currently connected user address
* @param block3dConfig is the block3d config object
* @param wagmiConfig is the wagmi config object
* @return `passing` is a Rule[] containing all token rule checks the user passed
* @return `failing` is a Rule[] containing all token rule checks the user failed
*/
export async function checkTokenRules(
address: string,
block3dConfig: Block3dConfig,
wagmiConfig: Config,
) {
let passing: Rule[] = [];
let failing: Rule[] = [];
const allChains: Chain[] = Object.values(chains);
const validChainIds = allChains.map((chain) => chain.id);
let tokenRules: Rule[] = block3dConfig.rules.filter(
(rule) => rule.type === "token",
);
/* Remove invalid rules from the array */
tokenRules = tokenRules
.map((rule) => {
/* Filter out invalid `Contract`s from the rule's `contracts` array */
const validContracts = rule.contracts?.filter((contract) => {
if (!validChainIds.includes(contract.chainId)) {
return false;
}
const minimumBal = parseFloat(
contract?.minimumBal ?? rule?.minimumBal ?? "0",
);
return (
contract?.address !== undefined &&
contract.address.length === 42 &&
minimumBal >= 1
);
});
/* Return the rule with only valid `Contract` objects */
return {
...rule,
contracts: validContracts,
};
})
.filter((rule) => rule.contracts && rule.contracts.length > 0);
const rpcUrls = await getRpcUrls(tokenRules, wagmiConfig);
/* Compare user balance to `minimumBal` from config and add to respective array (passing/failing) */
for (const rule of tokenRules) {
const strictRule = rule.strict ?? false;
let loopBroken: boolean = false;
for (const contract of rule.contracts!) {
const minimumBal = contract.minimumBal ?? rule.minimumBal ?? "0";
/* If a rule exists on a chain that doesn't have a transport url provided in the wagmi config, we use the default provided by the client */
let publicClient;
if (rpcUrls[contract.chainId]) {
publicClient = createPublicClient({
transport: http(rpcUrls[contract.chainId]),
});
} else {
/* If there is no transport url, we need to use `extractChain` to create client with default RPC URL for that chain */
const chain = extractChain({
chains: allChains,
id: contract.chainId ?? 1, // If chainId is undefined for some reason we use Ethereum Mainnet
});
publicClient = createPublicClient({
chain: chain,
transport: http(),
});
}
let balance: unknown;
/* If contract address is address(0), we view native currency balance */
if (contract.address === "0x0000000000000000000000000000000000000000") {
balance = await publicClient.getBalance({
address: address as `0x${string}`,
});
} else {
balance = await publicClient.readContract({
address: contract.address as `0x${string}`,
abi: abi,
functionName: "balanceOf",
args: [address],
});
}
const minimumBalNumber = parseFloat(minimumBal);
const balanceNumber = parseFloat(balance as unknown as string);
/* If minimum balance is more than user's actual balance, and ruleset is strict, we push the rule to failing array */
if (minimumBalNumber > balanceNumber && strictRule === true) {
failing.push(rule);
loopBroken = true;
break;
}
/* If minimum balance is less than user's actual balance, and ruleset is not strict, we push the rule to passing array */
if (minimumBalNumber <= balanceNumber && strictRule === false) {
passing.push(rule);
loopBroken = true;
break;
}
}
/* If the loop wasn't broken and reaches this code, either
* 1. rule type is strict and user had enough tokens
* 2. rule type isn't strict and user didn't have enough tokens
*/
if (strictRule === false && loopBroken === false) {
failing.push(rule);
} else if (strictRule === true && loopBroken === false) {
passing.push(rule);
}
loopBroken = false;
}
return { tokenPassing: passing, tokenFailing: failing };
}
Last updated