-
-
-
+
diff --git a/static/scripts/rewards/app-state.ts b/static/scripts/rewards/app-state.ts
index 1c992b68..54c0c354 100644
--- a/static/scripts/rewards/app-state.ts
+++ b/static/scripts/rewards/app-state.ts
@@ -17,6 +17,10 @@ export class AppState {
this._signer = value;
}
+ get networkId(): number | null {
+ return this.reward?.networkId || null;
+ }
+
get provider(): JsonRpcProvider {
return this._provider;
}
@@ -29,11 +33,29 @@ export class AppState {
return this._currentIndex;
}
- getCurrentExplorerUrl(claim: Permit): string {
- if (!claim) {
+ get reward(): Permit {
+ return this.rewardIndex < this.claims.length ? this.claims[this.rewardIndex] : this.claims[0];
+ }
+
+ get permitNetworkId() {
+ return this.reward?.networkId;
+ }
+
+ get currentExplorerUrl(): string {
+ if (!this.reward) {
return "https://blockscan.com";
}
- return networkExplorers[claim.networkId] || "https://blockscan.com";
+ return networkExplorers[this.reward.networkId] || "https://blockscan.com";
+ }
+
+ nextPermit(): Permit | null {
+ this._currentIndex = Math.min(this.claims.length - 1, this.rewardIndex + 1);
+ return this.reward;
+ }
+
+ previousPermit(): Permit | null {
+ this._currentIndex = Math.max(0, this._currentIndex - 1);
+ return this.reward;
}
}
diff --git a/static/scripts/rewards/init.ts b/static/scripts/rewards/init.ts
index b2d599db..75c6a308 100644
--- a/static/scripts/rewards/init.ts
+++ b/static/scripts/rewards/init.ts
@@ -7,7 +7,7 @@ displayCommitHash();
grid(document.getElementById("grid") as HTMLElement, gridLoadedCallback); // @DEV: display grid background
readClaimDataFromUrl(app).catch(console.error); // @DEV: read claim data from URL
-const footer = document.querySelector("footer") as Element;
+const footer = document.querySelector(".footer") as Element;
footer.classList.add("ready");
// cSpell:ignore llback
diff --git a/static/scripts/rewards/render-transaction/claim-rewards-pagination.ts b/static/scripts/rewards/render-transaction/claim-rewards-pagination.ts
new file mode 100644
index 00000000..37735ba5
--- /dev/null
+++ b/static/scripts/rewards/render-transaction/claim-rewards-pagination.ts
@@ -0,0 +1,21 @@
+import { app } from "../app-state";
+import { getMakeClaimButton } from "../toaster";
+import { table } from "./read-claim-data-from-url";
+import { renderTransaction } from "./render-transaction";
+import { removeAllEventListeners } from "./utils";
+
+const nextTxButton = document.getElementById("nextTx");
+const prevTxButton = document.getElementById("prevTx");
+
+export function claimRewardsPagination(rewardsCount: HTMLElement) {
+ rewardsCount.innerHTML = `${app.rewardIndex + 1}/${app.claims.length} reward`;
+ if (nextTxButton) nextTxButton.addEventListener("click", () => transactionHandler("next"));
+ if (prevTxButton) prevTxButton.addEventListener("click", () => transactionHandler("previous"));
+}
+
+function transactionHandler(direction: "next" | "previous") {
+ removeAllEventListeners(getMakeClaimButton()) as HTMLButtonElement;
+ direction === "next" ? app.nextPermit() : app.previousPermit();
+ table.setAttribute(`data-make-claim`, "error");
+ renderTransaction().catch(console.error);
+}
diff --git a/static/scripts/rewards/render-transaction/insert-table-data.ts b/static/scripts/rewards/render-transaction/insert-table-data.ts
index 7b96d3a1..59cf3c22 100644
--- a/static/scripts/rewards/render-transaction/insert-table-data.ts
+++ b/static/scripts/rewards/render-transaction/insert-table-data.ts
@@ -1,114 +1,100 @@
-import { ERC20PermitReward, ERC721PermitReward } from "@ubiquibot/permit-generation/types";
+import { ERC20Permit, ERC721Permit } from "@ubiquibot/permit-generation/types";
import { BigNumber, ethers } from "ethers";
-import { app } from "../app-state";
-import { ButtonController } from "../button-controller";
-import { buttonControllers } from "../toaster";
+import { app, AppState } from "../app-state";
function shortenAddress(address: string): string {
return `${address.slice(0, 10)}...${address.slice(-8)}`;
}
export function insertErc20PermitTableData(
- reward: ERC20PermitReward,
+ app: AppState,
table: Element,
treasury: { balance: BigNumber; allowance: BigNumber; decimals: number; symbol: string }
): Element {
- renderToFields(reward.beneficiary, reward.currentExplorerUrl, table);
- renderTokenFields(reward.tokenAddress, reward.currentExplorerUrl, table);
- renderDetailsFields(
- [
- {
- name: "From",
- value: `
${reward.owner}`,
- },
- {
- name: "Expiry",
- value: (() => {
- const deadline = BigNumber.isBigNumber(reward.deadline) ? reward.deadline : BigNumber.from(reward.deadline);
- return deadline.lte(Number.MAX_SAFE_INTEGER.toString()) ? new Date(deadline.toNumber()).toLocaleString() : undefined;
- })(),
- },
- { name: "Balance", value: treasury.balance.gte(0) ? `${ethers.utils.formatUnits(treasury.balance, treasury.decimals)} ${treasury.symbol}` : "N/A" },
- { name: "Allowance", value: treasury.allowance.gte(0) ? `${ethers.utils.formatUnits(treasury.allowance, treasury.decimals)} ${treasury.symbol}` : "N/A" },
- ],
- table
- );
-
- // We need to update the controls after inserting the detail rows
- const controls = table.querySelector(".controls") as HTMLDivElement;
- buttonControllers[table.id] = new ButtonController(controls);
+ const reward = app.reward as ERC20Permit;
+ const requestedAmountElement = document.getElementById("rewardAmount") as Element;
+ renderToFields(reward.beneficiary, app.currentExplorerUrl);
+ renderTokenFields(reward.tokenAddress, app.currentExplorerUrl);
+ renderDetailsFields([
+ { name: "From", value: `
${reward.owner}` },
+ {
+ name: "Expiry",
+ value: (() => {
+ const deadline = BigNumber.isBigNumber(reward.deadline) ? reward.deadline : BigNumber.from(reward.deadline);
+ return deadline.lte(Number.MAX_SAFE_INTEGER.toString()) ? new Date(deadline.toNumber()).toLocaleString() : undefined;
+ })(),
+ },
+ { name: "Balance", value: treasury.balance.gte(0) ? `${ethers.utils.formatUnits(treasury.balance, treasury.decimals)} ${treasury.symbol}` : "N/A" },
+ { name: "Allowance", value: treasury.allowance.gte(0) ? `${ethers.utils.formatUnits(treasury.allowance, treasury.decimals)} ${treasury.symbol}` : "N/A" },
+ ]);
table.setAttribute(`data-make-claim-rendered`, "true");
-
- return table.querySelector(".reward-amount") as Element;
+ return requestedAmountElement;
}
-export function insertErc721PermitTableData(reward: ERC721PermitReward, table: Element): Element {
- const requestedAmountElement = table.querySelector(".reward-amount") as Element;
- renderToFields(reward.beneficiary, app.getCurrentExplorerUrl(reward), table);
- renderTokenFields(reward.tokenAddress, app.getCurrentExplorerUrl(reward), table);
+export function insertErc721PermitTableData(reward: ERC721Permit, table: Element): Element {
+ const requestedAmountElement = document.getElementById("rewardAmount") as Element;
+ renderToFields(reward.beneficiary, app.currentExplorerUrl);
+ renderTokenFields(reward.tokenAddress, app.currentExplorerUrl);
const { GITHUB_REPOSITORY_NAME, GITHUB_CONTRIBUTION_TYPE, GITHUB_ISSUE_ID, GITHUB_ORGANIZATION_NAME, GITHUB_USERNAME } = reward.erc721Request?.metadata || {};
- renderDetailsFields(
- [
- {
- name: "NFT address",
- value: `
${reward.tokenAddress}`,
- },
- {
- name: "Expiry",
- value: BigNumber.from(reward.deadline).lte(Number.MAX_SAFE_INTEGER.toString()) ? new Date(Number(reward.deadline)).toLocaleString() : undefined,
- },
- {
- name: "GitHub Organization",
- value: `
${GITHUB_ORGANIZATION_NAME}`,
- },
- {
- name: "GitHub Repository",
- value: `
${GITHUB_REPOSITORY_NAME}`,
- },
- {
- name: "GitHub Issue",
- value: `
${GITHUB_ISSUE_ID}`,
- },
- {
- name: "GitHub Username",
- value: `
${GITHUB_USERNAME}`,
- },
- { name: "Contribution Type", value: GITHUB_CONTRIBUTION_TYPE?.split(",").join(", ") },
- ],
- table
- );
+ renderDetailsFields([
+ {
+ name: "NFT address",
+ value: `
${reward.tokenAddress}`,
+ },
+ {
+ name: "Expiry",
+ value: BigNumber.from(reward.deadline).lte(Number.MAX_SAFE_INTEGER.toString()) ? new Date(Number(reward.deadline)).toLocaleString() : undefined,
+ },
+ {
+ name: "GitHub Organization",
+ value: `
${GITHUB_ORGANIZATION_NAME}`,
+ },
+ {
+ name: "GitHub Repository",
+ value: `
${GITHUB_REPOSITORY_NAME}`,
+ },
+ {
+ name: "GitHub Issue",
+ value: `
${GITHUB_ISSUE_ID}`,
+ },
+ {
+ name: "GitHub Username",
+ value: `
${GITHUB_USERNAME}`,
+ },
+ { name: "Contribution Type", value: GITHUB_CONTRIBUTION_TYPE?.split(",").join(", ") },
+ ]);
table.setAttribute(`data-make-claim-rendered`, "true");
return requestedAmountElement;
}
-function renderDetailsFields(additionalDetails: { name: string; value: string | undefined }[], table: Element) {
- const additionalDetailsEl = table.querySelector("tbody") as Element;
+function renderDetailsFields(additionalDetails: { name: string; value: string | undefined }[]) {
+ const additionalDetailsDiv = document.getElementById("additionalDetailsTable") as Element;
let additionalDetailsHtml = "";
for (const { name, value } of additionalDetails) {
if (!value) continue;
- additionalDetailsHtml += `
+ additionalDetailsHtml += `
${name} |
${value} |
`;
}
- additionalDetailsEl.innerHTML = additionalDetailsHtml + additionalDetailsEl.innerHTML;
+ additionalDetailsDiv.innerHTML = additionalDetailsHtml;
}
-function renderTokenFields(tokenAddress: string, explorerUrl: string, table: Element) {
- const tokenFull = table.querySelector(".token .full") as Element;
- const tokenShort = table.querySelector(".token .short") as Element;
+function renderTokenFields(tokenAddress: string, explorerUrl: string) {
+ const tokenFull = document.querySelector("#Token .full") as Element;
+ const tokenShort = document.querySelector("#Token .short") as Element;
tokenFull.innerHTML = `
${tokenAddress}
`;
tokenShort.innerHTML = `
${shortenAddress(tokenAddress)}
`;
- const tokenBoth = table.querySelector(`.reward-token`) as Element;
+ const tokenBoth = document.getElementById(`rewardToken`) as Element;
tokenBoth.innerHTML = `
${tokenBoth.innerHTML}`;
}
-function renderToFields(receiverAddress: string, explorerUrl: string, table: Element) {
- const toFull = table.querySelector(".reward-recipient .full") as Element;
- const toShort = table.querySelector(".reward-recipient .short") as Element;
+function renderToFields(receiverAddress: string, explorerUrl: string) {
+ const toFull = document.querySelector("#rewardRecipient .full") as Element;
+ const toShort = document.querySelector("#rewardRecipient .short") as Element;
// if the for address is an ENS name neither will be found
if (!toFull || !toShort) return;
@@ -116,6 +102,6 @@ function renderToFields(receiverAddress: string, explorerUrl: string, table: Ele
toFull.innerHTML = `
${receiverAddress}
`;
toShort.innerHTML = `
${shortenAddress(receiverAddress)}
`;
- const toBoth = table.querySelector(`.reward-recipient`) as Element;
+ const toBoth = document.getElementById(`rewardRecipient`) as Element;
toBoth.innerHTML = `
${toBoth.innerHTML}`;
}
diff --git a/static/scripts/rewards/render-transaction/read-claim-data-from-url.ts b/static/scripts/rewards/render-transaction/read-claim-data-from-url.ts
index f3f527f0..225475c7 100644
--- a/static/scripts/rewards/render-transaction/read-claim-data-from-url.ts
+++ b/static/scripts/rewards/render-transaction/read-claim-data-from-url.ts
@@ -2,13 +2,14 @@ import { createClient } from "@supabase/supabase-js";
import { decodePermits } from "@ubiquibot/permit-generation/handlers";
import { Permit } from "@ubiquibot/permit-generation/types";
import { app, AppState } from "../app-state";
-import { buttonControllers, toaster } from "../toaster";
-import { checkRenderInvalidatePermitAdminControl, checkRenderMakeClaimControl, createInvalidatorActions } from "../web3/erc20-permit";
+import { toaster } from "../toaster";
+import { connectWallet } from "../web3/connect-wallet";
+import { checkRenderInvalidatePermitAdminControl, checkRenderMakeClaimControl } from "../web3/erc20-permit";
+import { verifyCurrentNetwork } from "../web3/verify-current-network";
+import { claimRewardsPagination } from "./claim-rewards-pagination";
+import { renderTransaction } from "./render-transaction";
import { setClaimMessage } from "./set-claim-message";
import { useRpcHandler } from "../web3/use-rpc-handler";
-import { renderTransaction } from "./render-transaction";
-import { ButtonController } from "../button-controller";
-import { connectWallet } from "../web3/connect-wallet";
declare const SUPABASE_URL: string;
declare const SUPABASE_ANON_KEY: string;
@@ -22,7 +23,6 @@ const base64encodedTxData = urlParams.get("claim");
export async function readClaimDataFromUrl(app: AppState) {
if (!base64encodedTxData) {
// No claim data found
- // A single table shows the error message
setClaimMessage({ type: "Notice", message: `No claim data found.` });
table.setAttribute(`data-make-claim`, "error");
return;
@@ -32,7 +32,7 @@ export async function readClaimDataFromUrl(app: AppState) {
app.claimTxs = await getClaimedTxs(app);
try {
- app.provider = await useRpcHandler(app.claims[0]);
+ app.provider = await useRpcHandler(app);
} catch (e) {
if (e instanceof Error) {
toaster.create("error", e.message);
@@ -42,7 +42,7 @@ export async function readClaimDataFromUrl(app: AppState) {
}
try {
- app.signer = await connectWallet(app.claims[0].networkId);
+ app.signer = await connectWallet();
} catch (error) {
/* empty */
}
@@ -60,7 +60,15 @@ export async function readClaimDataFromUrl(app: AppState) {
*/
}
- await displayRewardsWithDetails();
+ displayRewardDetails();
+ displayRewardPagination();
+
+ await renderTransaction();
+ if (app.networkId !== null) {
+ await verifyCurrentNetwork(app.networkId);
+ } else {
+ throw new Error("Network ID is null");
+ }
}
async function getClaimedTxs(app: AppState): Promise
> {
@@ -89,44 +97,23 @@ function decodeClaimData(base64encodedTxData: string): Permit[] {
}
}
-function displayRewardDetails(table: Element) {
+function displayRewardPagination() {
+ const rewardsCount = document.getElementById("rewardsCount");
+ if (rewardsCount) {
+ if (!app.claims || app.claims.length <= 1) {
+ // already hidden
+ } else {
+ claimRewardsPagination(rewardsCount);
+ }
+ }
+}
+
+function displayRewardDetails() {
let isDetailsVisible = false;
table.setAttribute(`data-details-visible`, isDetailsVisible.toString());
- const additionalDetails = table.querySelector(`.additional-details`) as HTMLElement;
+ const additionalDetails = document.getElementById(`additionalDetails`) as HTMLElement;
additionalDetails.addEventListener("click", () => {
isDetailsVisible = !isDetailsVisible;
table.setAttribute(`data-details-visible`, isDetailsVisible.toString());
});
}
-
-/**
- * @summary Create a separate table element for each claim
- */
-async function displayRewardsWithDetails() {
- const tableEl = document.getElementsByTagName("table")[0];
- if (!tableEl) return;
- tableEl.id = app.claims[0].nonce;
- const controls = tableEl.querySelector(".controls") as HTMLDivElement;
- const buttonController = new ButtonController(controls);
- buttonControllers[app.claims[0].nonce] = buttonController;
-
- await Promise.all(
- app.claims.slice(1).map(async (claim) => {
- // Create a new copy of the table
- const newTable = tableEl.cloneNode(true) as Element;
- newTable.id = claim.nonce;
- tableEl.parentElement?.appendChild(newTable);
-
- const controls = newTable.querySelector(".controls") as HTMLDivElement;
- const buttonController = new ButtonController(controls);
- buttonControllers[claim.nonce] = buttonController;
- await renderTransaction(claim, newTable);
- displayRewardDetails(newTable);
- })
- );
-
- // The first claim's table is populated last
- await renderTransaction(app.claims[0], tableEl);
- displayRewardDetails(tableEl);
- createInvalidatorActions();
-}
diff --git a/static/scripts/rewards/render-transaction/render-ens-name.ts b/static/scripts/rewards/render-transaction/render-ens-name.ts
index 97440420..810df96d 100644
--- a/static/scripts/rewards/render-transaction/render-ens-name.ts
+++ b/static/scripts/rewards/render-transaction/render-ens-name.ts
@@ -1,4 +1,3 @@
-import { Permit } from "@ubiquibot/permit-generation/types";
import { app } from "../app-state";
import { ensLookup } from "../cirip/ens-lookup";
@@ -18,7 +17,7 @@ type EnsParams =
networkId: number;
};
-export async function renderEnsName(claim: Permit, { element, address, tokenAddress, tokenView, networkId }: EnsParams): Promise {
+export async function renderEnsName({ element, address, tokenAddress, tokenView, networkId }: EnsParams): Promise {
let href: string = "";
try {
const resolved = await ensLookup(address, networkId);
@@ -33,9 +32,9 @@ export async function renderEnsName(claim: Permit, { element, address, tokenAddr
}
if (ensName) {
if (tokenView) {
- href = `${app.getCurrentExplorerUrl(claim)}/token/${tokenAddress}?a=${address}`;
+ href = `${app.currentExplorerUrl}/token/${tokenAddress}?a=${address}`;
} else {
- href = `${app.getCurrentExplorerUrl(claim)}/address/${address}"`;
+ href = `${app.currentExplorerUrl}/address/${address}"`;
}
element.innerHTML = `${ensName}`;
}
diff --git a/static/scripts/rewards/render-transaction/render-token-symbol.ts b/static/scripts/rewards/render-transaction/render-token-symbol.ts
index 9c14a966..f4ab43c7 100644
--- a/static/scripts/rewards/render-transaction/render-token-symbol.ts
+++ b/static/scripts/rewards/render-transaction/render-token-symbol.ts
@@ -1,9 +1,6 @@
import { BigNumberish, ethers, utils } from "ethers";
-import { PermitReward } from "@ubiquibot/permit-generation/types";
import { erc20Abi } from "../abis/erc20-abi";
import { app } from "../app-state";
-import { useRpcHandler } from "../web3/use-rpc-handler";
-
export async function renderTokenSymbol({
table,
requestedAmountElement,
@@ -11,7 +8,6 @@ export async function renderTokenSymbol({
ownerAddress,
amount,
explorerUrl,
- claim,
}: {
table: Element;
requestedAmountElement: Element;
@@ -19,13 +15,7 @@ export async function renderTokenSymbol({
ownerAddress: string;
amount: BigNumberish;
explorerUrl: string;
- claim: PermitReward;
}): Promise {
- // If the reward is on a different chain we need a new provider
- if (app.provider.network.chainId !== claim.networkId) {
- console.log("Different network. Switching");
- app.provider = await useRpcHandler(claim);
- }
const contract = new ethers.Contract(tokenAddress, erc20Abi, app.provider);
let symbol, decimals;
@@ -67,18 +57,12 @@ export async function renderNftSymbol({
requestedAmountElement,
tokenAddress,
explorerUrl,
- claim,
}: {
table: Element;
requestedAmountElement: Element;
tokenAddress: string;
explorerUrl: string;
- claim: PermitReward;
}): Promise {
- if (app.provider.network.chainId !== claim.networkId) {
- console.log("Different network. Switching");
- app.provider = await useRpcHandler(claim);
- }
const contract = new ethers.Contract(tokenAddress, erc20Abi, app.provider);
let symbol: string;
diff --git a/static/scripts/rewards/render-transaction/render-transaction.ts b/static/scripts/rewards/render-transaction/render-transaction.ts
index 32f54d4b..d19583d2 100644
--- a/static/scripts/rewards/render-transaction/render-transaction.ts
+++ b/static/scripts/rewards/render-transaction/render-transaction.ts
@@ -1,80 +1,86 @@
-import { ERC20PermitReward, PermitReward, TokenType } from "@ubiquibot/permit-generation/types";
+import { ERC20Permit, Permit, TokenType } from "@ubiquibot/permit-generation/types";
import { networkExplorers } from "@ubiquity-dao/rpc-handler";
import { app } from "../app-state";
-import { buttonControllers, getMakeClaimButton, getViewClaimButton } from "../toaster";
+import { buttonController, getMakeClaimButton, viewClaimButton } from "../toaster";
import { checkRenderInvalidatePermitAdminControl, claimErc20PermitHandlerWrapper, fetchTreasury } from "../web3/erc20-permit";
import { claimErc721PermitHandler } from "../web3/erc721-permit";
+import { verifyCurrentNetwork } from "../web3/verify-current-network";
import { insertErc20PermitTableData, insertErc721PermitTableData } from "./insert-table-data";
import { renderEnsName } from "./render-ens-name";
import { renderNftSymbol, renderTokenSymbol } from "./render-token-symbol";
-export async function renderTransaction(claim: PermitReward, table: Element): Promise {
- if (!claim) {
- buttonControllers[claim.nonce].hideAll();
+const carousel = document.getElementById("carousel") as Element;
+const table = document.querySelector(`table`) as HTMLTableElement;
+type Success = boolean;
+
+export async function renderTransaction(): Promise {
+ if (app.claims && app.claims.length > 1) {
+ carousel.className = "ready";
+ const rewardsCount = document.getElementById("rewardsCount") as Element;
+ rewardsCount.innerHTML = `${app.rewardIndex + 1}/${app.claims.length} reward`;
+ }
+
+ if (!app.reward) {
+ buttonController.hideAll();
console.log("No reward found");
return false;
}
- if (!table) {
- throw new Error("Missing transaction table");
- }
+ verifyCurrentNetwork(app.reward.networkId).catch(console.error);
- if (isErc20Permit(claim)) {
- const treasury = await fetchTreasury(claim);
+ if (isErc20Permit(app.reward)) {
+ const treasury = await fetchTreasury(app.reward);
table.setAttribute(`data-additional-data-size`, "small");
// insert tx data into table
- const requestedAmountElement = insertErc20PermitTableData(claim, table, treasury);
+ const requestedAmountElement = insertErc20PermitTableData(app, table, treasury);
- await renderTokenSymbol({
- tokenAddress: claim.tokenAddress,
- ownerAddress: claim.owner,
- amount: claim.amount,
- explorerUrl: networkExplorers[claim.networkId],
+ renderTokenSymbol({
+ tokenAddress: app.reward.tokenAddress,
+ ownerAddress: app.reward.owner,
+ amount: app.reward.amount,
+ explorerUrl: networkExplorers[app.reward.networkId],
table,
requestedAmountElement,
- claim,
- });
+ }).catch(console.error);
- const toElement = table.querySelector(`.reward-recipient`) as Element;
- renderEnsName(claim, { element: toElement, address: claim.beneficiary, networkId: claim.networkId as number }).catch(console.error);
+ const toElement = document.getElementById(`rewardRecipient`) as Element;
+ renderEnsName({ element: toElement, address: app.reward.beneficiary, networkId: app.networkId }).catch(console.error);
if (app.provider) {
checkRenderInvalidatePermitAdminControl(app).catch(console.error);
}
- if (app.claimTxs[claim.nonce.toString()] !== undefined) {
- buttonControllers[claim.nonce].showViewClaim();
- const viewClaimButton = getViewClaimButton(table);
- viewClaimButton.addEventListener("click", () => window.open(`${app.getCurrentExplorerUrl(claim)}/tx/${app.claimTxs[claim.nonce.toString()]}`));
+ if (app.claimTxs[app.reward.nonce.toString()] !== undefined) {
+ buttonController.showViewClaim();
+ viewClaimButton.addEventListener("click", () => window.open(`${app.currentExplorerUrl}/tx/${app.claimTxs[app.reward.nonce.toString()]}`));
} else if (window.ethereum) {
// requires wallet connection to claim
- buttonControllers[claim.nonce].showMakeClaim();
- getMakeClaimButton(table).addEventListener("click", claimErc20PermitHandlerWrapper(table, claim));
+ buttonController.showMakeClaim();
+ getMakeClaimButton().addEventListener("click", claimErc20PermitHandlerWrapper(app));
}
table.setAttribute(`data-make-claim`, "ok");
} else {
- const requestedAmountElement = insertErc721PermitTableData(claim, table);
+ const requestedAmountElement = insertErc721PermitTableData(app.reward, table);
table.setAttribute(`data-make-claim`, "ok");
table.setAttribute(`data-additional-data-size`, "large");
renderNftSymbol({
- tokenAddress: claim.tokenAddress,
- explorerUrl: networkExplorers[claim.networkId],
+ tokenAddress: app.reward.tokenAddress,
+ explorerUrl: networkExplorers[app.reward.networkId],
table,
requestedAmountElement,
- claim,
}).catch(console.error);
- const toElement = document.getElementById(`reward-recipient`) as Element;
- renderEnsName(claim, { element: toElement, address: claim.beneficiary, networkId: claim.networkId as number }).catch(console.error);
+ const toElement = document.getElementById(`rewardRecipient`) as Element;
+ renderEnsName({ element: toElement, address: app.reward.beneficiary, networkId: app.networkId }).catch(console.error);
- getMakeClaimButton(table).addEventListener("click", claimErc721PermitHandler(table, claim));
+ getMakeClaimButton().addEventListener("click", claimErc721PermitHandler(app.reward));
}
return true;
}
-function isErc20Permit(permit: PermitReward): permit is ERC20PermitReward {
+function isErc20Permit(permit: Permit): permit is ERC20Permit {
return permit.tokenType === TokenType.ERC20;
}
diff --git a/static/scripts/rewards/render-transaction/utils.ts b/static/scripts/rewards/render-transaction/utils.ts
new file mode 100644
index 00000000..80281a64
--- /dev/null
+++ b/static/scripts/rewards/render-transaction/utils.ts
@@ -0,0 +1,5 @@
+export function removeAllEventListeners(element: Element): Element {
+ const clone = element.cloneNode(true) as Element;
+ element.replaceWith(clone);
+ return clone;
+}
diff --git a/static/scripts/rewards/toaster.ts b/static/scripts/rewards/toaster.ts
index 39cb813a..84089e42 100644
--- a/static/scripts/rewards/toaster.ts
+++ b/static/scripts/rewards/toaster.ts
@@ -11,17 +11,16 @@ export const toaster = {
},
};
-export function getMakeClaimButton(table: Element) {
- return table.querySelector(".make-claim") as HTMLButtonElement;
-}
-export function getViewClaimButton(table: Element) {
- return table.querySelector(".view-claim") as HTMLButtonElement;
+const controls = document.getElementById("controls") as HTMLDivElement;
+export function getMakeClaimButton() {
+ return document.getElementById("make-claim") as HTMLButtonElement;
}
+export const viewClaimButton = document.getElementById("view-claim") as HTMLButtonElement;
const notifications = document.querySelector(".notifications") as HTMLUListElement;
-export const buttonControllers: { [key: string]: ButtonController } = {};
+export const buttonController = new ButtonController(controls);
function createToast(meaning: keyof typeof toaster.icons, text: string, timeout: number = 5000) {
- if (meaning != "info" && buttonControllers.length) Object.keys(buttonControllers).forEach((key) => buttonControllers[key].hideLoader());
+ if (meaning != "info") buttonController.hideLoader();
const toastDetails = {
timer: timeout,
} as {
diff --git a/static/scripts/rewards/web3/connect-wallet.ts b/static/scripts/rewards/web3/connect-wallet.ts
index 0fcc0d36..77b43623 100644
--- a/static/scripts/rewards/web3/connect-wallet.ts
+++ b/static/scripts/rewards/web3/connect-wallet.ts
@@ -1,6 +1,6 @@
import { JsonRpcSigner } from "@ethersproject/providers";
import { ethers } from "ethers";
-import { buttonControllers, toaster } from "../toaster";
+import { buttonController, toaster } from "../toaster";
import { app } from "../app-state";
import { useHandler } from "../web3/use-rpc-handler";
@@ -24,7 +24,7 @@ function mobileCheck() {
return checkMobile(navigator.userAgent || navigator.vendor || (window as unknown as { opera: string }).opera);
}
-export async function connectWallet(networkId: number): Promise {
+export async function connectWallet(): Promise {
try {
const wallet = new ethers.providers.Web3Provider(window.ethereum);
@@ -40,7 +40,7 @@ export async function connectWallet(networkId: number): Promise buttonControllers[key].hideAll());
+ buttonController.hideAll();
console.error("Wallet not connected");
return null;
}
@@ -54,7 +54,7 @@ export async function connectWallet(networkId: number): Promise buttonControllers[key].hideAll());
+ buttonController.hideAll();
}
} else {
toaster.create("error", error.message);
diff --git a/static/scripts/rewards/web3/erc20-permit.ts b/static/scripts/rewards/web3/erc20-permit.ts
index abdc1f4e..9e1fe826 100644
--- a/static/scripts/rewards/web3/erc20-permit.ts
+++ b/static/scripts/rewards/web3/erc20-permit.ts
@@ -1,23 +1,17 @@
import { JsonRpcSigner, TransactionResponse } from "@ethersproject/providers";
-import { PermitReward, ERC20PermitReward } from "@ubiquibot/permit-generation/types";
-import { permit2Address } from "@ubiquity-dao/rpc-handler";
+import { Permit } from "@ubiquibot/permit-generation/types";
import { BigNumber, BigNumberish, Contract, ethers } from "ethers";
import { erc20Abi, permit2Abi } from "../abis";
import { app, AppState } from "../app-state";
+import { permit2Address } from "@ubiquity-dao/rpc-handler";
import { supabase } from "../render-transaction/read-claim-data-from-url";
-import { MetaMaskError, buttonControllers, errorToast, getMakeClaimButton, toaster, getViewClaimButton } from "../toaster";
+import { MetaMaskError, buttonController, errorToast, getMakeClaimButton, toaster, viewClaimButton } from "../toaster";
import { connectWallet } from "./connect-wallet";
-import { verifyCurrentNetwork } from "./verify-current-network";
-import { useRpcHandler } from "./use-rpc-handler";
-export async function fetchTreasury(permit: PermitReward): Promise<{ balance: BigNumber; allowance: BigNumber; decimals: number; symbol: string }> {
+export async function fetchTreasury(permit: Permit): Promise<{ balance: BigNumber; allowance: BigNumber; decimals: number; symbol: string }> {
let balance: BigNumber, allowance: BigNumber, decimals: number, symbol: string;
try {
- if (app.provider.network.chainId !== permit.networkId) {
- console.log("Different network. Switching");
- app.provider = await useRpcHandler(permit);
- }
const tokenAddress = permit.tokenAddress;
const tokenContract = new ethers.Contract(tokenAddress, erc20Abi, app.provider);
@@ -49,9 +43,9 @@ export async function fetchTreasury(permit: PermitReward): Promise<{ balance: Bi
}
}
-async function checkPermitClaimability(reward: PermitReward): Promise {
+async function checkPermitClaimability(app: AppState): Promise {
try {
- return await checkPermitClaimable(reward);
+ return await checkPermitClaimable(app);
} catch (error: unknown) {
if (error instanceof Error) {
const e = error as unknown as MetaMaskError;
@@ -59,11 +53,12 @@ async function checkPermitClaimability(reward: PermitReward): Promise {
errorToast(e, e.reason);
}
}
- buttonControllers[reward.nonce].hideMakeClaim();
+ buttonController.hideMakeClaim();
return false;
}
-async function transferFromPermit(permit2Contract: Contract, reward: ERC20PermitReward) {
+async function transferFromPermit(permit2Contract: Contract, app: AppState) {
+ const reward = app.reward;
const signer = app.signer;
if (!signer) return null;
@@ -90,8 +85,8 @@ async function transferFromPermit(permit2Contract: Contract, reward: ERC20Permit
if (e.code == "ACTION_REJECTED") {
// Handle the user rejection case
toaster.create("info", `Transaction was not sent because it was rejected by the user.`);
- buttonControllers[reward.nonce].hideLoader();
- buttonControllers[reward.nonce].showMakeClaim();
+ buttonController.hideLoader();
+ buttonController.showMakeClaim();
} else {
// Handle other errors
console.error("Error in permitTransferFrom:", e);
@@ -102,18 +97,18 @@ async function transferFromPermit(permit2Contract: Contract, reward: ERC20Permit
}
}
-async function waitForTransaction(tx: TransactionResponse, table: Element) {
+async function waitForTransaction(tx: TransactionResponse) {
try {
const receipt = await tx.wait();
- const viewClaimButton = getViewClaimButton(table);
viewClaimButton.onclick = () => {
window.open(`https://blockscan.com/tx/${receipt.transactionHash}`, "_blank");
};
toaster.create("success", `Claim Complete.`);
- buttonControllers[table.id].showViewClaim();
- buttonControllers[table.id].hideLoader();
- buttonControllers[table.id].hideMakeClaim();
+ buttonController.showViewClaim();
+ buttonController.hideLoader();
+ buttonController.hideMakeClaim();
+ console.log(receipt.transactionHash);
return receipt;
} catch (error: unknown) {
@@ -125,55 +120,43 @@ async function waitForTransaction(tx: TransactionResponse, table: Element) {
}
}
-export function claimErc20PermitHandlerWrapper(table: Element, permit: PermitReward) {
+export function claimErc20PermitHandlerWrapper(app: AppState) {
return async function claimErc20PermitHandler() {
- if (app.provider.network.chainId !== permit.networkId) {
- console.log("Different network. Switching");
- app.provider = await useRpcHandler(permit);
- }
- const signer = await connectWallet(permit.networkId); // we are re-testing the in-wallet rpc at this point
- verifyCurrentNetwork(permit.networkId).catch(console.error);
+ const signer = await connectWallet(); // we are re-testing the in-wallet rpc at this point
if (!signer) {
- // If the signer is unavailable, we will disable button for each reward
- Object.keys(buttonControllers).forEach((key) => buttonControllers[key].hideAll());
+ buttonController.hideAll();
toaster.create("error", `Please connect your wallet to claim this reward.`);
return;
}
app.signer = signer; // update this here to be sure it's set if it wasn't before
- buttonControllers[table.id].hideMakeClaim();
- buttonControllers[table.id].showLoader();
+ buttonController.hideMakeClaim();
+ buttonController.showLoader();
- const isPermitClaimable = await checkPermitClaimability(permit);
- if (!isPermitClaimable) {
- buttonControllers[table.id].hideLoader();
- return;
- }
+ const isPermitClaimable = await checkPermitClaimability(app);
+ if (!isPermitClaimable) return;
const permit2Contract = new ethers.Contract(permit2Address, permit2Abi, signer);
- if (!permit2Contract) {
- buttonControllers[table.id].hideLoader();
- return;
- }
+ if (!permit2Contract) return;
- const tx = await transferFromPermit(permit2Contract, permit);
+ const tx = await transferFromPermit(permit2Contract, app);
if (!tx) return;
- const receipt = await waitForTransaction(tx, table);
+ const receipt = await waitForTransaction(tx);
if (!receipt) return;
- const isHashUpdated = await updatePermitTxHash(permit, receipt.transactionHash);
+ const isHashUpdated = await updatePermitTxHash(app, receipt.transactionHash);
if (!isHashUpdated) return;
- getMakeClaimButton(table).removeEventListener("click", claimErc20PermitHandler);
+ getMakeClaimButton().removeEventListener("click", claimErc20PermitHandler);
};
}
-async function checkPermitClaimable(reward: PermitReward): Promise {
+async function checkPermitClaimable(app: AppState): Promise {
let isClaimed: boolean;
try {
- isClaimed = await isNonceClaimed(reward);
+ isClaimed = await isNonceClaimed(app);
} catch (error: unknown) {
console.error("Error in isNonceClaimed: ", error);
return false;
@@ -181,10 +164,12 @@ async function checkPermitClaimable(reward: PermitReward): Promise {
if (isClaimed) {
toaster.create("error", `Your reward for this task has already been claimed.`);
- buttonControllers[reward.nonce].showViewClaim();
+ buttonController.showViewClaim();
return false;
}
+ const reward = app.reward;
+
if (BigNumber.from(reward.deadline).lt(Math.floor(Date.now() / 1000))) {
toaster.create("error", `This reward has expired.`);
return false;
@@ -192,17 +177,18 @@ async function checkPermitClaimable(reward: PermitReward): Promise {
const { balance, allowance } = await fetchTreasury(reward);
const permitted = BigNumber.from(reward.amount);
+
const isSolvent = balance.gte(permitted);
const isAllowed = allowance.gte(permitted);
if (!isSolvent) {
toaster.create("error", `Not enough funds on funding wallet to collect this reward. Please let the financier know.`);
- buttonControllers[reward.nonce].hideMakeClaim();
+ buttonController.hideMakeClaim();
return false;
}
if (!isAllowed) {
toaster.create("error", `Not enough allowance on the funding wallet to collect this reward. Please let the financier know.`);
- buttonControllers[reward.nonce].hideMakeClaim();
+ buttonController.hideMakeClaim();
return false;
}
@@ -218,7 +204,7 @@ async function checkPermitClaimable(reward: PermitReward): Promise {
const beneficiary = reward.beneficiary.toLowerCase();
if (beneficiary !== user) {
toaster.create("warning", `This reward is not for you.`);
- buttonControllers[reward.nonce].hideMakeClaim();
+ buttonController.hideMakeClaim();
return false;
}
@@ -230,20 +216,18 @@ export async function checkRenderMakeClaimControl(app: AppState) {
const address = await app.signer?.getAddress();
const user = address?.toLowerCase();
- app.claims.forEach((claim) => {
- if (claim) {
- const beneficiary = claim.beneficiary.toLowerCase();
- if (beneficiary !== user) {
- buttonControllers[claim.nonce].hideMakeClaim();
- return;
- }
+ if (app.reward) {
+ const beneficiary = app.reward.beneficiary.toLowerCase();
+ if (beneficiary !== user) {
+ buttonController.hideMakeClaim();
+ return;
}
- });
+ }
} catch (error) {
console.error("Error getting address from signer");
console.error(error);
}
- Object.keys(buttonControllers).forEach((key) => buttonControllers[key].showMakeClaim());
+ buttonController.showMakeClaim();
}
export async function checkRenderInvalidatePermitAdminControl(app: AppState) {
@@ -251,65 +235,54 @@ export async function checkRenderInvalidatePermitAdminControl(app: AppState) {
const address = await app.signer?.getAddress();
const user = address?.toLowerCase();
- app.claims.forEach((claim) => {
- if (claim) {
- const owner = claim.owner.toLowerCase();
- if (owner !== user) {
- buttonControllers[claim.nonce].hideInvalidator();
- return;
- }
- buttonControllers[claim.nonce].showInvalidator();
+ if (app.reward) {
+ const owner = app.reward.owner.toLowerCase();
+ if (owner !== user) {
+ buttonController.hideInvalidator();
+ return;
}
- });
+ }
} catch (error) {
console.error("Error getting address from signer");
console.error(error);
}
+ buttonController.showInvalidator();
}
-export function createInvalidatorActions() {
- for (let i = 0; i < app.claims.length; i++) {
- const claim = app.claims[i];
- const table = document.getElementById(claim.nonce) as HTMLElement;
- const invalidateButton = table.querySelector(".invalidator");
- if (!invalidateButton) {
- console.log("Error initializing invalidator");
- break;
+const invalidateButton = document.getElementById("invalidator") as HTMLDivElement;
+
+invalidateButton.addEventListener("click", async function invalidateButtonClickHandler() {
+ try {
+ const isClaimed = await isNonceClaimed(app);
+ if (isClaimed) {
+ toaster.create("error", `This reward has already been claimed or invalidated.`);
+ buttonController.hideInvalidator();
+ return;
+ }
+
+ if (!app.signer) return;
+ await invalidateNonce(app.signer, app.reward.nonce);
+ } catch (error: unknown) {
+ if (error instanceof Error) {
+ const e = error as unknown as MetaMaskError;
+ console.error(e);
+ errorToast(e, e.reason);
+ return;
}
- invalidateButton.addEventListener("click", async function invalidateButtonClickHandler() {
- try {
- const isClaimed = await isNonceClaimed(claim);
- if (isClaimed) {
- toaster.create("error", `This reward has already been claimed or invalidated.`);
- buttonControllers[claim.nonce].hideInvalidator();
- return;
- }
-
- if (!app.signer) return;
- await invalidateNonce(app.signer, claim.nonce);
- } catch (error: unknown) {
- if (error instanceof Error) {
- const e = error as unknown as MetaMaskError;
- console.error(e);
- errorToast(e, e.reason);
- return;
- }
- }
- toaster.create("info", "Nonce invalidation transaction sent");
- buttonControllers[claim.nonce].hideInvalidator();
- });
}
-}
+ toaster.create("info", "Nonce invalidation transaction sent");
+ buttonController.hideInvalidator();
+});
//mimics https://github.com/Uniswap/permit2/blob/a7cd186948b44f9096a35035226d7d70b9e24eaf/src/SignatureTransfer.sol#L150
-async function isNonceClaimed(reward: PermitReward): Promise {
+async function isNonceClaimed(app: AppState): Promise {
const provider = app.provider;
const permit2Contract = new ethers.Contract(permit2Address, permit2Abi, provider);
- const { wordPos, bitPos } = nonceBitmap(BigNumber.from(reward.nonce));
+ const { wordPos, bitPos } = nonceBitmap(BigNumber.from(app.reward.nonce));
- const bitmap = await permit2Contract.nonceBitmap(reward.owner, wordPos).catch((error: MetaMaskError) => {
+ const bitmap = await permit2Contract.nonceBitmap(app.reward.owner, wordPos).catch((error: MetaMaskError) => {
console.error("Error in nonceBitmap method: ", error);
throw error;
});
@@ -339,12 +312,12 @@ function nonceBitmap(nonce: BigNumberish): { wordPos: BigNumber; bitPos: number
return { wordPos, bitPos };
}
-async function updatePermitTxHash(reward: ERC20PermitReward, hash: string): Promise {
+async function updatePermitTxHash(app: AppState, hash: string): Promise {
const { error } = await supabase
.from("permits")
.update({ transaction: hash })
// using only nonce in the condition as it's defined unique on db
- .eq("nonce", reward.nonce.toString());
+ .eq("nonce", app.reward.nonce.toString());
if (error !== null) {
console.error(error);
diff --git a/static/scripts/rewards/web3/erc721-permit.ts b/static/scripts/rewards/web3/erc721-permit.ts
index 4a159bbf..d36658c6 100644
--- a/static/scripts/rewards/web3/erc721-permit.ts
+++ b/static/scripts/rewards/web3/erc721-permit.ts
@@ -3,19 +3,12 @@ import { ERC721Permit } from "@ubiquibot/permit-generation/types";
import { BigNumber, ethers } from "ethers";
import { nftRewardAbi } from "../abis/nft-reward-abi";
import { app } from "../app-state";
-import { buttonControllers, getMakeClaimButton, toaster } from "../toaster";
+import { buttonController, getMakeClaimButton, toaster } from "../toaster";
import { connectWallet } from "./connect-wallet";
-import { verifyCurrentNetwork } from "./verify-current-network";
-import { useRpcHandler } from "./use-rpc-handler";
-export function claimErc721PermitHandler(table: Element, reward: ERC721Permit) {
+export function claimErc721PermitHandler(reward: ERC721Permit) {
return async function claimHandler() {
- if (app.provider.network.chainId !== reward.networkId) {
- console.log("Different network. Switching");
- app.provider = await useRpcHandler(reward);
- }
- const signer = await connectWallet(reward.networkId);
- verifyCurrentNetwork(reward.networkId).catch(console.error);
+ const signer = await connectWallet();
if (!signer) {
return;
}
@@ -36,7 +29,7 @@ export function claimErc721PermitHandler(table: Element, reward: ERC721Permit) {
return;
}
- buttonControllers[table.id].showLoader();
+ buttonController.showLoader();
try {
const nftContract = new ethers.Contract(reward.tokenAddress, nftRewardAbi, signer);
@@ -52,13 +45,13 @@ export function claimErc721PermitHandler(table: Element, reward: ERC721Permit) {
);
toaster.create("info", `Transaction sent. Waiting for confirmation...`);
const receipt = await tx.wait();
- buttonControllers[table.id].hideLoader();
+ buttonController.hideLoader();
toaster.create("success", `Claim Complete.`);
- buttonControllers[table.id].showViewClaim();
- buttonControllers[table.id].hideMakeClaim();
+ buttonController.showViewClaim();
+ buttonController.hideMakeClaim();
console.log(receipt.transactionHash); // @TODO: post to database
- getMakeClaimButton(table).removeEventListener("click", claimHandler);
+ getMakeClaimButton().removeEventListener("click", claimHandler);
// app.nextPermit();
// renderTransaction().catch((error) => {
diff --git a/static/scripts/rewards/web3/handle-if-on-correct-network.ts b/static/scripts/rewards/web3/handle-if-on-correct-network.ts
index 170ee336..a9d17400 100644
--- a/static/scripts/rewards/web3/handle-if-on-correct-network.ts
+++ b/static/scripts/rewards/web3/handle-if-on-correct-network.ts
@@ -1,10 +1,9 @@
-import { buttonControllers } from "../toaster";
+import { buttonController } from "../toaster";
export function handleIfOnCorrectNetwork(currentNetworkId: number, desiredNetworkId: number) {
- // Show or hide claim action for each permit
if (desiredNetworkId === currentNetworkId) {
- Object.keys(buttonControllers).forEach((key) => buttonControllers[key].showMakeClaim());
+ buttonController.showMakeClaim();
} else {
- Object.keys(buttonControllers).forEach((key) => buttonControllers[key].hideMakeClaim());
+ buttonController.hideMakeClaim();
}
}
diff --git a/static/scripts/rewards/web3/not-on-correct-network.ts b/static/scripts/rewards/web3/not-on-correct-network.ts
index 7f396060..d04a6334 100644
--- a/static/scripts/rewards/web3/not-on-correct-network.ts
+++ b/static/scripts/rewards/web3/not-on-correct-network.ts
@@ -1,6 +1,6 @@
import { ethers } from "ethers";
import { getNetworkName } from "@ubiquity-dao/rpc-handler";
-import { buttonControllers, toaster } from "../toaster";
+import { buttonController, toaster } from "../toaster";
import { switchNetwork } from "./switch-network";
export function notOnCorrectNetwork(currentNetworkId: number, desiredNetworkId: number, web3provider: ethers.providers.Web3Provider) {
@@ -12,8 +12,7 @@ export function notOnCorrectNetwork(currentNetworkId: number, desiredNetworkId:
switchNetwork(web3provider, desiredNetworkId).catch((error) => {
console.error(error);
toaster.create("error", `Please switch to the ${networkName} network to claim this reward.`);
- // Display error for each permit
- Object.keys(buttonControllers).forEach((key) => buttonControllers[key].hideAll());
+ buttonController.hideAll();
});
}
}
diff --git a/static/scripts/rewards/web3/switch-network.ts b/static/scripts/rewards/web3/switch-network.ts
index 7e64c09f..98266c2f 100644
--- a/static/scripts/rewards/web3/switch-network.ts
+++ b/static/scripts/rewards/web3/switch-network.ts
@@ -1,12 +1,11 @@
import { ethers } from "ethers";
import { addNetwork } from "./add-network";
-import { buttonControllers } from "../toaster";
+import { buttonController } from "../toaster";
export async function switchNetwork(provider: ethers.providers.Web3Provider, networkId: number): Promise {
try {
await provider.send("wallet_switchEthereumChain", [{ chainId: "0x" + networkId.toString(16) }]);
- // Display make claim for each permit
- Object.keys(buttonControllers).forEach((key) => buttonControllers[key].showMakeClaim());
+ buttonController.showMakeClaim();
return true;
} catch (error: unknown) {
// Add network if it doesn't exist.
diff --git a/static/scripts/rewards/web3/use-rpc-handler.ts b/static/scripts/rewards/web3/use-rpc-handler.ts
index f9eb4838..97d3c2fe 100644
--- a/static/scripts/rewards/web3/use-rpc-handler.ts
+++ b/static/scripts/rewards/web3/use-rpc-handler.ts
@@ -1,5 +1,5 @@
import { RPCHandler } from "@ubiquity-dao/rpc-handler";
-import { Permit } from "@ubiquibot/permit-generation/types";
+import { AppState } from "../app-state";
export function useHandler(networkId: number) {
const config = {
@@ -16,13 +16,13 @@ export function useHandler(networkId: number) {
return new RPCHandler(config);
}
-export async function useRpcHandler(claim: Permit) {
- const networkId = claim.networkId;
+export async function useRpcHandler(app: AppState) {
+ const networkId = app.networkId;
if (!networkId) {
throw new Error("Network ID not set");
}
- const handler = useHandler(networkId);
+ const handler = await useHandler(networkId);
const provider = await handler.getFastestRpcProvider();
const url = provider.connection.url;
if (!url) {
diff --git a/static/scripts/rewards/web3/verify-current-network.ts b/static/scripts/rewards/web3/verify-current-network.ts
index 50a7620f..8d1e530d 100644
--- a/static/scripts/rewards/web3/verify-current-network.ts
+++ b/static/scripts/rewards/web3/verify-current-network.ts
@@ -1,13 +1,12 @@
import { ethers } from "ethers";
-import { buttonControllers } from "../toaster";
+import { buttonController } from "../toaster";
import { handleIfOnCorrectNetwork } from "./handle-if-on-correct-network";
import { notOnCorrectNetwork } from "./not-on-correct-network";
// verifyCurrentNetwork checks if the user is on the correct network and displays an error if not
export async function verifyCurrentNetwork(desiredNetworkId: number) {
if (!window.ethereum) {
- // Display error for each permit
- Object.keys(buttonControllers).forEach((key) => buttonControllers[key].hideAll());
+ buttonController.hideAll();
return;
}
diff --git a/static/styles/rewards/claim-table.css b/static/styles/rewards/claim-table.css
index 0b91f04e..565441f5 100644
--- a/static/styles/rewards/claim-table.css
+++ b/static/styles/rewards/claim-table.css
@@ -7,30 +7,28 @@
--background-color-light: hsl(225 50% var(--background-color-light-brightness) / 1);
--border-color: hsl(225 25% var(--border-brightness) / 1);
}
+#claim {
+ display: flex;
+}
main > div {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: center;
- align-items: center;
- width: 100%;
- gap: 64px;
}
table {
border-collapse: collapse;
- max-width: 563px;
- width: 100%;
}
table a:hover,
table a:hover > div {
color: #fff;
}
-table .controls {
+table #controls {
display: none;
color: #fff;
width: 100%;
}
-table .controls > button {
+table #controls > button {
flex: 1;
padding: 0 24px;
}
@@ -91,8 +89,7 @@ table td div {
table td div svg ~ div {
width: unset;
}
-table tr:first-of-type > *,
-table .amount > * {
+table tr:first-of-type > * {
padding-top: 24px;
}
table tr:last-child td div > div {
@@ -103,11 +100,11 @@ table tr:last-child td div > div {
table tr:last-of-type > * {
padding-bottom: 24px;
}
-table tr.additional-details-border > * {
+table tr#additional-details-border > * {
padding: 0;
margin: 0;
}
-table[data-make-claim-rendered] .controls {
+table[data-make-claim-rendered] #controls {
display: inline-flex;
}
table[data-make-claim-rendered] button {
@@ -127,7 +124,7 @@ table[data-make-claim-rendered] button:hover {
table[data-make-claim-rendered] button > div {
display: none;
}
-table[data-make-claim-rendered] button:disabled > svg.claim-icon {
+table[data-make-claim-rendered] button:disabled > svg#claim-icon {
display: none;
}
table[data-make-claim-rendered] button:hover > div {
@@ -140,117 +137,113 @@ table[data-make-claim-rendered] button:hover > svg {
.show {
display: block;
}
-table[data-make-claim-rendered] button.hide > svg.claim-loader {
+table[data-make-claim-rendered] button.hide > svg#claim-loader {
display: none;
}
-table[data-make-claim-rendered] button.show > svg.claim-icon {
+table[data-make-claim-rendered] button.show > svg#claim-icon {
display: unset;
}
-table[data-make-claim-rendered] button.show > svg.claim-loader {
+table[data-make-claim-rendered] button.show > svg#claim-loader {
display: unset;
}
-table[data-make-claim-rendered] button.hide > svg.claim-icon {
+table[data-make-claim-rendered] button.hide > svg#claim-icon {
display: unset;
}
-table .controls {
+table #controls {
opacity: 0;
transition: 1s ease-in-out opacity;
pointer-events: none;
}
-table[data-make-claim-rendered="true"][data-contract-loaded="true"][data-make-claim="ok"] .controls {
+table[data-make-claim-rendered="true"][data-contract-loaded="true"][data-make-claim="ok"] #controls {
opacity: 1;
pointer-events: unset;
}
-table[data-make-claim-rendered] button.additional-details {
+table[data-make-claim-rendered] button#additionalDetails {
width: 100%;
color: #fff;
}
-table[data-make-claim-rendered] tr.additional-details-border + tr > * {
+table[data-make-claim-rendered] tr#additional-details-border + tr > * {
padding-top: 24px;
}
-table .additional-detail {
+table #additionalDetailsTable {
opacity: 0;
pointer-events: none;
+ transform: translate(-50%, -90px);
}
-table[data-additional-data-size="large"] .additional-detail {
+table[data-additional-data-size="large"] #additionalDetailsTable {
opacity: 0;
pointer-events: none;
+ transform: translate(-50%, -175px);
}
-table[data-details-visible="true"] .additional-detail,
-table[data-details-visible="false"] .detail {
+table[data-details-visible="true"] #additionalDetailsTable {
opacity: 1;
pointer-events: all;
}
-table[data-details-visible="true"] .detail,
-table[data-details-visible="false"] .additional-detail {
- opacity: 0;
- pointer-events: all;
- visibility: collapse;
-}
-table[data-contract-loaded] .token {
+table[data-contract-loaded] #Token {
display: none;
}
-.reward-amount a {
+#rewardAmount a {
font-size: 24px;
}
-.reward-amount a,
-.reward-recipient div,
-.rewards-count {
+#rewardAmount a,
+#rewardRecipient div,
+#rewardsCount {
color: #fff;
}
-table[data-details-visible="false"] .reward-token .full,
-table[data-details-visible="true"] .reward-token .short {
+table[data-details-visible="false"] #rewardToken .full,
+table[data-details-visible="true"] #rewardToken .short {
display: none;
}
-table[data-details-visible="false"] .reward-token .short,
-table[data-details-visible="true"] .reward-token .full {
+table[data-details-visible="false"] #rewardToken .short,
+table[data-details-visible="true"] #rewardToken .full {
display: initial;
}
-table[data-details-visible="false"] .reward-recipient .full,
-table[data-details-visible="true"] .reward-recipient .short {
+table[data-details-visible="false"] #rewardRecipient .full,
+table[data-details-visible="true"] #rewardRecipient .short {
display: none;
}
-table[data-details-visible="false"] .reward-recipient .short,
-table[data-details-visible="true"] .reward-recipient .full {
+table[data-details-visible="false"] #rewardRecipient .short,
+table[data-details-visible="true"] #rewardRecipient .full {
display: initial;
}
-.to > td,
-.to > th {
+#To > td,
+#To > th {
padding-bottom: 24px;
}
table[data-make-claim="ok"] thead {
- visibility: collapse;
+ opacity: 0;
pointer-events: none;
+ transform: translate(-50%, -50%);
/* filter: blur(4px); */
}
table[data-make-claim="error"] tbody {
opacity: 0;
pointer-events: none;
+ transform: translate(-50%, -50%);
filter: blur(4px);
- display: none;
}
-.reward-recipient a div {
+#rewardRecipient a div {
opacity: 0.66;
}
-.reward-recipient a:hover div {
+#rewardRecipient a:hover div {
opacity: 1;
}
-.reward-recipient div {
+#rewardRecipient div {
color: #fff;
}
-table[data-details-visible="true"] .additional-details svg.opener {
+table[data-details-visible="true"] #additionalDetails svg.opener {
display: none;
}
-table[data-details-visible="false"] .additional-details svg.opener {
+table[data-details-visible="false"] #additionalDetails svg.opener {
display: unset;
}
-table[data-details-visible="true"] .additional-details svg.closer {
+table[data-details-visible="true"] #additionalDetails svg.closer {
display: unset;
}
-table[data-details-visible="false"] .additional-details svg.closer {
+table[data-details-visible="false"] #additionalDetails svg.closer {
display: none;
}
-td.owner > a {
+td#owner > a {
word-break: break-all;
}
table td {
@@ -260,16 +253,16 @@ table td {
table th {
padding-left: 32px;
}
-[data-loader="false"] .claim-loader {
+[data-loader="false"] #claim-loader {
display: none;
}
-[data-make-claim="false"] .make-claim {
+[data-make-claim="false"] #make-claim {
display: none;
}
-[data-view-claim="false"] .view-claim {
+[data-view-claim="false"] #view-claim {
display: none;
}
-[data-invalidator="false"] .invalidator {
+[data-invalidator="false"] #invalidator {
display: none;
}
@@ -279,7 +272,7 @@ table th {
position: absolute;
}
-.claiming-message::after {
+#claiming-message::after {
content: "";
animation: ellipsis 1s infinite;
position: absolute;
@@ -309,17 +302,26 @@ table th {
transform: rotate(360deg);
}
}
-.claim-loader > svg {
+#claim-loader > svg {
animation: rotate 1s linear infinite;
}
tbody,
thead {
background-color: var(--background-color-default);
+ position: absolute;
box-shadow: inset 0 0 96px #00bfff10;
transition: 0.25s all ease-in-out;
+ border: 1px solid var(--border-color);
+ transform: translate(-50%, -50%);
+ width: 100%;
+ max-width: 563px;
overflow: hidden;
/* box-shadow: 0 12px 64px #00000010; */
}
+table * {
+ text-wrap: nowrap;
+}
+
@keyframes thead-fade-in {
0% {
opacity: 0;
@@ -329,12 +331,6 @@ thead {
}
}
-tr {
- transition:
- visibility 0s,
- opacity 0.5s linear;
-}
-
thead {
animation: thead-fade-in 0.5s ease-in-out;
}
diff --git a/static/styles/rewards/light-mode.css b/static/styles/rewards/light-mode.css
index 3860e1fa..5ceb56a8 100644
--- a/static/styles/rewards/light-mode.css
+++ b/static/styles/rewards/light-mode.css
@@ -13,7 +13,7 @@
table a:hover > div {
color: #000;
}
- .reward-amount > a {
+ #rewardAmount > a {
color: #000;
}
svg path {
@@ -41,11 +41,15 @@
div#build > a {
color: #000;
}
- .reward-amount div,
- .reward-recipient div,
- .rewards-count {
+ #rewardAmount div,
+ #rewardRecipient div,
+ #rewardsCount {
color: #000;
}
+ #nextTx,
+ #previousTx {
+ fill: #000;
+ }
html {
background-color: #fff;
}
@@ -65,6 +69,9 @@
animation: none;
}
+ #carousel > div:hover {
+ color: #000;
+ }
tbody,
thead {
background-color: #fff;
diff --git a/static/styles/rewards/pay.css b/static/styles/rewards/pay.css
index 7813f302..b87a55b2 100644
--- a/static/styles/rewards/pay.css
+++ b/static/styles/rewards/pay.css
@@ -36,6 +36,7 @@ main {
max-width: 100vw;
display: flex;
flex-direction: column;
+ justify-content: center;
align-items: center;
}
@@ -101,7 +102,7 @@ a {
/* font-weight: 400; */
}
-footer > div {
+div.footer > div {
/* bottom: 48px; */
padding: 48px 0;
text-align: center;
@@ -212,7 +213,7 @@ footer {
opacity: 1;
}
-footer {
+.footer {
/* display: flex; */
/* align-items: center; */
opacity: 0;
@@ -220,7 +221,7 @@ footer {
text-align: center;
}
-footer.ready {
+.footer.ready {
opacity: 1;
}