Skip to content

Commit

Permalink
Merge pull request #151 from barebind/feature/fetch-fastest-rpc
Browse files Browse the repository at this point in the history
Feature: Fetch Fastest RPC For Rewards dApp
  • Loading branch information
0x4007 authored Feb 15, 2024
2 parents 68c9706 + 04a6391 commit a23be4c
Show file tree
Hide file tree
Showing 13 changed files with 230 additions and 84 deletions.
36 changes: 20 additions & 16 deletions scripts/typescript/generate-permit2-url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,27 +37,31 @@ async function generate() {
process.env.CHAIN_ID ? Number(process.env.CHAIN_ID) : 1,
);
const signature = await myWallet._signTypedData(domain, types, values);
const txData = {
permit: {
permitted: {
token: permitTransferFromData.permitted.token,
amount: permitTransferFromData.permitted.amount.toString(),
const txData = [
{
type: "erc20-permit",
permit: {
permitted: {
token: permitTransferFromData.permitted.token,
amount: permitTransferFromData.permitted.amount.toString(),
},
nonce: permitTransferFromData.nonce.toString(),
deadline: permitTransferFromData.deadline.toString(),
},
nonce: permitTransferFromData.nonce.toString(),
deadline: permitTransferFromData.deadline.toString(),
},
transferDetails: {
to: permitTransferFromData.spender,
requestedAmount: permitTransferFromData.permitted.amount.toString(),
transferDetails: {
to: permitTransferFromData.spender,
requestedAmount: permitTransferFromData.permitted.amount.toString(),
},
owner: myWallet.address,
signature: signature,
networkId: Number(process.env.CHAIN_ID),
},
owner: myWallet.address,
signature: signature,
};
];

const base64encodedTxData = Buffer.from(JSON.stringify(txData)).toString("base64");
log.ok("Testing URL:");
console.log(`${process.env.FRONTEND_URL}?claim=${base64encodedTxData}&network=${process.env.CHAIN_ID}`);
console.log(`${process.env.FRONTEND_URL}?claim=${base64encodedTxData}`);
log.ok("Public URL:");
console.log(`https://pay.ubq.fi?claim=${base64encodedTxData}&network=${process.env.CHAIN_ID}`);
console.log(`https://pay.ubq.fi?claim=${base64encodedTxData}`);
console.log();
}
5 changes: 3 additions & 2 deletions static/scripts/audit-report/audit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
getCurrency,
getGitHubUrlPartsArray,
getRandomAPIKey,
getRandomRpcUrl,
getOptimalRPC,
isValidUrl,
parseRepoUrl,
populateTable,
Expand Down Expand Up @@ -568,7 +568,8 @@ const rpcFetcher = async () => {
if (data) {
const { hash, chain } = data;

const txInfo = await getTxInfo(hash, getRandomRpcUrl(chain), chain);
const providerUrl = await getOptimalRPC(chain);
const txInfo = await getTxInfo(hash, providerUrl, chain);

if (txInfo.input.startsWith(permitTransferFromSelector)) {
const decodedFunctionData = permit2Interface.decodeFunctionData(permitFunctionName, txInfo.input);
Expand Down
61 changes: 61 additions & 0 deletions static/scripts/audit-report/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ethers } from "ethers";
import { API_KEYS, Chain, ChainScan, RPC_URLS } from "./constants";
import { BountyHunter, GitHubUrlParts } from "./types";
import axios from "axios";

export interface RateLimitOptions {
method: string;
Expand All @@ -9,6 +10,27 @@ export interface RateLimitOptions {

const resultTableTbodyElem = document.querySelector("#resultTable tbody") as HTMLTableCellElement;

type DataType = {
jsonrpc: string;
id: number;
result: {
number: string;
timestamp: string;
hash: string;
};
};

const RPC_BODY = JSON.stringify({
jsonrpc: "2.0",
method: "eth_getBlockByNumber",
params: ["latest", false],
id: 1,
});

const RPC_HEADER = {
"Content-Type": "application/json",
};

export const shortenTransactionHash = (hash: string | undefined, length = 8): string => {
if (!hash) return "";
const prefixLength = Math.floor(length / 2);
Expand Down Expand Up @@ -71,6 +93,45 @@ export const getRandomRpcUrl = (chain: Chain): string => {
return urls[randomIndex];
};

const verifyBlock = (data: DataType) => {
try {
const { jsonrpc, id, result } = data;
const { number, timestamp, hash } = result;
return jsonrpc === "2.0" && id === 1 && parseInt(number, 16) > 0 && parseInt(timestamp, 16) > 0 && hash.match(/[0-9|a-f|A-F|x]/gm)?.join("").length === 66;
} catch (error) {
return false;
}
};

export const getOptimalRPC = async (chain: Chain): Promise<string> => {
const promises = RPC_URLS[chain].map(async (baseURL: string) => {
try {
const startTime = performance.now();
const API = axios.create({
baseURL,
headers: RPC_HEADER,
});

const { data } = await API.post("", RPC_BODY);
const endTime = performance.now();
const latency = endTime - startTime;
if (await verifyBlock(data)) {
return Promise.resolve({
latency,
baseURL,
});
} else {
return Promise.reject();
}
} catch (error) {
return Promise.reject();
}
});

const { baseURL: optimalRPC } = await Promise.any(promises);
return optimalRPC;
};

export const parseRepoUrl = (issueUrl: string): [string, string] => {
const match = issueUrl.match(/github\.com\/([^/]+)\/([^/]+)\/issues\/\d+/i);
if (match) {
Expand Down
18 changes: 8 additions & 10 deletions static/scripts/onboarding/onboarding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -360,13 +360,12 @@ let signer: JsonRpcSigner | undefined = undefined;

const nextStep = async () => {
const configChainId = Number(chainIdSelect.value);
const configChainIdHex = `0x${configChainId.toString(16)}`;

const tokenNameSpan = document.getElementById("allowance + span");
if (tokenNameSpan) {
if (configChainIdHex === NetworkIds.Mainnet) {
if (configChainId === NetworkIds.Mainnet) {
tokenNameSpan.innerHTML = "DAI";
} else if (configChainIdHex === NetworkIds.Gnosis) {
} else if (configChainId === NetworkIds.Gnosis) {
tokenNameSpan.innerHTML = "WXDAI";
}
}
Expand Down Expand Up @@ -397,18 +396,18 @@ const nextStep = async () => {
const currentChainId = await signer.getChainId();

if (configChainId !== currentChainId) {
singleToggle("error", `Error: Please connect to ${getNetworkName(configChainIdHex)}.`);
singleToggle("error", `Error: Please connect to ${getNetworkName(configChainId)}.`);
if (await switchNetwork(provider, configChainId)) {
singleToggle("success", ``);
}
}

// watch for chain changes
window.ethereum.on("chainChanged", async (currentChainId: string) => {
if (configChainIdHex === currentChainId) {
if (configChainId === parseInt(currentChainId, 16)) {
singleToggle("success", ``);
} else {
singleToggle("error", `Error: Please connect to ${getNetworkName(configChainIdHex)}.`);
singleToggle("error", `Error: Please connect to ${getNetworkName(configChainId)}.`);
switchNetwork(provider, configChainId);
}
});
Expand Down Expand Up @@ -510,10 +509,9 @@ const step2Handler = async () => {

const walletChainId = await signer.getChainId();
const configChainId = Number(chainIdSelect.value);
const configChainIdHex = `0x${configChainId.toString(16)}`;

window.ethereum.on("chainChanged", async (currentChainId: string) => {
if (configChainIdHex === currentChainId) {
if (configChainId === parseInt(currentChainId, 16)) {
singleToggle("success", ``);
} else {
singleToggle("error", `Error: Please connect to ${chainIdSelect.value}.`);
Expand All @@ -530,9 +528,9 @@ const step2Handler = async () => {

// load token contract
let token = "";
if (configChainIdHex === NetworkIds.Mainnet) {
if (configChainId === NetworkIds.Mainnet) {
token = Tokens.DAI;
} else if (configChainIdHex === NetworkIds.Gnosis) {
} else if (configChainId === NetworkIds.Gnosis) {
token = Tokens.WXDAI;
}
const erc20 = new ethers.Contract(token, erc20Abi, signer);
Expand Down
24 changes: 15 additions & 9 deletions static/scripts/rewards/constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export enum NetworkIds {
Mainnet = "0x1",
Goerli = "0x5",
Gnosis = "0x64",
Mainnet = 1,
Goerli = 5,
Gnosis = 100,
}

export enum Tokens {
Expand All @@ -15,24 +15,30 @@ export const networkNames = {
[NetworkIds.Gnosis]: "Gnosis Chain",
};

export function getNetworkName(networkId?: string) {
export const networkCurrencies: Record<number, object> = {
[NetworkIds.Mainnet]: { symbol: "ETH", decimals: 18 },
[NetworkIds.Goerli]: { symbol: "GoerliETH", decimals: 18 },
[NetworkIds.Gnosis]: { symbol: "XDAI", decimals: 18 },
};

export function getNetworkName(networkId?: number) {
const networkName = networkNames[networkId as keyof typeof networkNames];
if (!networkName) {
console.error(`Unknown network ID: ${networkId}`);
}
return networkName ?? "Unknown Network";
}

export const networkExplorers: Record<string, string> = {
export const networkExplorers: Record<number, string> = {
[NetworkIds.Mainnet]: "https://etherscan.io",
[NetworkIds.Goerli]: "https://goerli.etherscan.io",
[NetworkIds.Gnosis]: "https://gnosisscan.io",
};

export const networkRpcs: Record<string, string> = {
[NetworkIds.Mainnet]: "https://rpc-pay.ubq.fi/v1/mainnet",
[NetworkIds.Goerli]: "https://rpc-pay.ubq.fi/v1/goerli",
[NetworkIds.Gnosis]: "https://rpc.gnosischain.com",
export const networkRpcs: Record<number, string[]> = {
[NetworkIds.Mainnet]: ["https://rpc-pay.ubq.fi/v1/mainnet"],
[NetworkIds.Goerli]: ["https://rpc-pay.ubq.fi/v1/goerli"],
[NetworkIds.Gnosis]: ["https://rpc.gnosischain.com"],
};

export const permit2Address = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
Expand Down
25 changes: 0 additions & 25 deletions static/scripts/rewards/get-contract.ts

This file was deleted.

73 changes: 73 additions & 0 deletions static/scripts/rewards/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import axios from "axios";
import { NetworkIds, networkRpcs } from "./constants";
import { Contract, ethers } from "ethers";
import { erc20Abi } from "./abis";
import { Type as T, StaticDecode } from "@sinclair/typebox";
import { Value } from "@sinclair/typebox/value";

type DataType = {
jsonrpc: string;
id: number;
result: {
number: string;
timestamp: string;
hash: string;
};
};

const verifyBlock = (data: DataType) => {
try {
const { jsonrpc, id, result } = data;
const { number, timestamp, hash } = result;
return jsonrpc === "2.0" && id === 1 && parseInt(number, 16) > 0 && parseInt(timestamp, 16) > 0 && hash.match(/[0-9|a-f|A-F|x]/gm)?.join("").length === 66;
} catch (error) {
return false;
}
};

const RPC_BODY = JSON.stringify({
jsonrpc: "2.0",
method: "eth_getBlockByNumber",
params: ["latest", false],
id: 1,
});

const RPC_HEADER = {
"Content-Type": "application/json",
};

export const getErc20Contract = async (contractAddress: string, networkId: number): Promise<Contract> => {
const providerUrl = await getOptimalRPC(networkId);
const provider = new ethers.providers.JsonRpcProvider(providerUrl);
const contractInstance = new ethers.Contract(contractAddress, erc20Abi, provider);
return contractInstance;
};

export const getOptimalRPC = async (networkId: number): Promise<string> => {
const promises = networkRpcs[networkId].map(async (baseURL: string) => {
try {
const startTime = performance.now();
const API = axios.create({
baseURL,
headers: RPC_HEADER,
});

const { data } = await API.post("", RPC_BODY);
const endTime = performance.now();
const latency = endTime - startTime;
if (await verifyBlock(data)) {
return Promise.resolve({
latency,
baseURL,
});
} else {
return Promise.reject();
}
} catch (error) {
return Promise.reject();
}
});

const { baseURL: optimalRPC } = await Promise.any(promises);
return optimalRPC;
};
7 changes: 4 additions & 3 deletions static/scripts/rewards/render-transaction/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { networkRpcs, networkExplorers } from "../constants";
import { getOptimalRPC } from "../helpers";
import { ClaimTx } from "./tx-type";

class AppState {
Expand All @@ -13,11 +14,11 @@ class AppState {
return this.currentIndex < this.claimTxs.length ? this.claimTxs[this.currentIndex] : null;
}

get currentNetworkRpc(): string {
async currentNetworkRpc(): Promise<string> {
if (!this.currentTx) {
return "0x1";
return getOptimalRPC(1);
}
return networkRpcs[this.currentTx.networkId] || "0x1";
return getOptimalRPC(this.currentTx.networkId);
}

get currentExplorerUrl(): string {
Expand Down
Loading

0 comments on commit a23be4c

Please sign in to comment.