From 2d96f1088daff7c27b8ea233ee7e2a8ef40babaf Mon Sep 17 00:00:00 2001
From: Keyrxng <106303466+Keyrxng@users.noreply.github.com>
Date: Fri, 23 Feb 2024 04:53:22 +0000
Subject: [PATCH] chore: erc permits first pass
---
scripts/typescript/generate-permit2-url.ts | 67 +++++++++++++++++++
.../render-transaction/insert-table-data.ts | 8 +--
.../read-claim-data-from-url.ts | 4 +-
.../render-transaction/render-transaction.ts | 4 +-
.../rewards/render-transaction/tx-type.ts | 55 ++++++++-------
static/scripts/rewards/web3/erc20-permit.ts | 6 +-
static/scripts/rewards/web3/erc721-permit.ts | 9 ++-
7 files changed, 109 insertions(+), 44 deletions(-)
diff --git a/scripts/typescript/generate-permit2-url.ts b/scripts/typescript/generate-permit2-url.ts
index 7abf435d..5642921b 100644
--- a/scripts/typescript/generate-permit2-url.ts
+++ b/scripts/typescript/generate-permit2-url.ts
@@ -17,6 +17,72 @@ async function generate() {
const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_PROVIDER_URL);
const myWallet = new ethers.Wallet(process.env.UBIQUIBOT_PRIVATE_KEY || "", provider);
+ const erc721TransferFromData: PermitTransferFrom = {
+ permitted: {
+ token: process.env.NFT_TOKEN_ADDRESS || "0x5FbDB2315678afecb367f032d93F642f64180aa3", // anvil no salt first acc NFT deployment
+ amount: 1, // this could be the tokenId if the permit is identified as an NFT via permitType
+ },
+ spender: process.env.BENEFICIARY_ADDRESS || "",
+ nonce: BigNumber.from(`0x${randomBytes(32).toString("hex")}`),
+ deadline: MaxUint256,
+ };
+
+ const { domain: domain721, types: types721, values: values721 } = SignatureTransfer.getPermitData(
+ erc721TransferFromData,
+ PERMIT2_ADDRESS,
+ process.env.CHAIN_ID ? Number(process.env.CHAIN_ID) : 1
+ );
+
+ const signature721 = await myWallet._signTypedData(domain721, types721, values721);
+
+ const GITHUB_CONTRIBUTION_TYPE = process.env.GITHUB_CONTRIBUTION_TYPE || "issue";
+ const GITHUB_ISSUE_ID = process.env.GITHUB_ISSUE_ID || "1";
+ const GITHUB_ORGANIZATION_NAME = process.env.GITHUB_ORGANIZATION_NAME || "ubiquity";
+ const GITHUB_REPOSITORY_NAME = process.env.GITHUB_REPOSITORY_NAME || "pay.ubq.fi";
+ const GITHUB_USERNAME = process.env.GITHUB_USERNAME || "keyrxng";
+
+ const txData721 = [
+ {
+ type: "erc721-permit",
+ permit: {
+ permitted: {
+ token: erc721TransferFromData.permitted.token,
+ amount: erc721TransferFromData.permitted.amount.toString(),
+ },
+ nonce: erc721TransferFromData.nonce.toString(),
+ deadline: erc721TransferFromData.deadline.toString(),
+ },
+ transferDetails: {
+ to: erc721TransferFromData.spender,
+ requestedAmount: erc721TransferFromData.permitted.amount.toString(),
+ },
+ owner: myWallet.address,
+ signature: signature721,
+ networkId: Number(process.env.CHAIN_ID),
+ nftMetadata: {
+ GITHUB_ORGANIZATION_NAME,
+ GITHUB_REPOSITORY_NAME,
+ GITHUB_ISSUE_ID,
+ GITHUB_USERNAME,
+ GITHUB_CONTRIBUTION_TYPE
+ },
+ request: {
+ beneficiary: process.env.BENEFICIARY_ADDRESS ?? "",
+ deadline: erc721TransferFromData.deadline.toString(),
+ keys: ["GITHUB_ORGANIZATION_NAME", "GITHUB_REPOSITORY_NAME", "GITHUB_ISSUE_ID", "GITHUB_USERNAME", "GITHUB_CONTRIBUTION_TYPE"],
+ nonce: erc721TransferFromData.nonce.toString(),
+ values: [GITHUB_ORGANIZATION_NAME, GITHUB_REPOSITORY_NAME, GITHUB_ISSUE_ID, GITHUB_USERNAME, GITHUB_CONTRIBUTION_TYPE],
+ },
+ },
+ ];
+
+ const base64encodedTxData721 = Buffer.from(JSON.stringify(txData721)).toString("base64");
+ log.ok("Testing URL:");
+ console.log(`${process.env.FRONTEND_URL}?claim=${base64encodedTxData721}`);
+ log.ok("Public URL:");
+ console.log(`https://pay.ubq.fi?claim=${base64encodedTxData721}`);
+ console.log();
+
const permitTransferFromData: PermitTransferFrom = {
permitted: {
// token we are permitting to be transferred
@@ -37,6 +103,7 @@ async function generate() {
process.env.CHAIN_ID ? Number(process.env.CHAIN_ID) : 1
);
const signature = await myWallet._signTypedData(domain, types, values);
+
const txData = [
{
type: "erc20-permit",
diff --git a/static/scripts/rewards/render-transaction/insert-table-data.ts b/static/scripts/rewards/render-transaction/insert-table-data.ts
index 60ff1c01..8ed783a0 100644
--- a/static/scripts/rewards/render-transaction/insert-table-data.ts
+++ b/static/scripts/rewards/render-transaction/insert-table-data.ts
@@ -33,17 +33,17 @@ export function insertErc20PermitTableData(
export function insertErc721PermitTableData(permit: Erc721Permit, table: Element): Element {
const requestedAmountElement = document.getElementById("rewardAmount") as Element;
- renderToFields(permit.request.beneficiary, app.currentExplorerUrl);
- renderTokenFields(permit.nftAddress, app.currentExplorerUrl);
+ renderToFields(permit.transferDetails.to, app.currentExplorerUrl);
+ renderTokenFields(permit.permit.permitted.token, app.currentExplorerUrl);
const { GITHUB_REPOSITORY_NAME, GITHUB_CONTRIBUTION_TYPE, GITHUB_ISSUE_ID, GITHUB_ORGANIZATION_NAME, GITHUB_USERNAME } = permit.nftMetadata;
renderDetailsFields([
{
name: "NFT address",
- value: `${permit.nftAddress}`,
+ value: `${permit.permit.permitted.token}`,
},
{
name: "Expiry",
- value: permit.request.deadline.lte(Number.MAX_SAFE_INTEGER.toString()) ? new Date(permit.request.deadline.toNumber()).toLocaleString() : undefined,
+ value: permit.permit.deadline.lte(Number.MAX_SAFE_INTEGER.toString()) ? new Date(permit.permit.deadline.toNumber()).toLocaleString() : undefined,
},
{
name: "GitHub Organization",
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 0763b8fc..58d9b189 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
@@ -24,12 +24,12 @@ export async function readClaimDataFromUrl(app: AppState) {
app.claims = decodeClaimData(base64encodedTxData);
app.provider = await useFastestRpc(app);
const networkId = app.permit?.networkId || app.networkId;
- app.signer = await connectWallet(networkId).catch(console.error);
+ app.signer = await connectWallet().catch(console.error);
displayRewardDetails();
displayRewardPagination();
renderTransaction(true)
- .then(() => verifyCurrentNetwork(networkId))
+ .then(() => verifyCurrentNetwork(networkId as number))
.catch(console.error);
}
diff --git a/static/scripts/rewards/render-transaction/render-transaction.ts b/static/scripts/rewards/render-transaction/render-transaction.ts
index 768aa04c..bc531637 100644
--- a/static/scripts/rewards/render-transaction/render-transaction.ts
+++ b/static/scripts/rewards/render-transaction/render-transaction.ts
@@ -62,7 +62,7 @@ export async function renderTransaction(nextTx?: boolean): Promise {
table.setAttribute(`data-claim`, "ok");
renderNftSymbol({
- tokenAddress: app.permit.nftAddress,
+ tokenAddress: app.permit.permit.permitted.token,
explorerUrl: networkExplorers[app.permit.networkId],
table,
requestedAmountElement,
@@ -70,7 +70,7 @@ export async function renderTransaction(nextTx?: boolean): Promise {
}).catch(console.error);
const toElement = document.getElementById(`rewardRecipient`) as Element;
- renderEnsName({ element: toElement, address: app.permit.request.beneficiary }).catch(console.error);
+ renderEnsName({ element: toElement, address: app.permit.transferDetails.to }).catch(console.error);
claimButton.element.addEventListener("click", claimErc721PermitHandler(app.permit));
}
diff --git a/static/scripts/rewards/render-transaction/tx-type.ts b/static/scripts/rewards/render-transaction/tx-type.ts
index 8891dcaf..dcaa0cf3 100644
--- a/static/scripts/rewards/render-transaction/tx-type.ts
+++ b/static/scripts/rewards/render-transaction/tx-type.ts
@@ -23,18 +23,18 @@ const erc20PermitT = T.Object({
type: T.Literal("erc20-permit"),
permit: T.Object({
permitted: T.Object({
- token: T.RegExp(/^0x[a-fA-F0-9]{40}$/),
- amount: T.Union([T.RegExp(/^\d+$/), T.Number()]),
+ token: addressT,
+ amount: bigNumberT,
}),
- nonce: T.Union([T.RegExp(/^\d+$/), T.Number()]),
- deadline: T.Union([T.RegExp(/^\d+$/), T.Number()]),
+ nonce: bigNumberT,
+ deadline: bigNumberT,
}),
transferDetails: T.Object({
- to: T.RegExp(/^0x[a-fA-F0-9]{40}$/),
- requestedAmount: T.Union([T.RegExp(/^\d+$/), T.Number()]),
+ to: addressT,
+ requestedAmount: bigNumberT,
}),
- owner: T.RegExp(/^0x[a-fA-F0-9]{40}$/),
- signature: T.RegExp(/^0x[a-fA-F0-9]+$/),
+ owner: addressT,
+ signature: signatureT,
networkId: T.Number(),
});
@@ -42,13 +42,22 @@ export type Erc20Permit = StaticDecode;
const erc721Permit = T.Object({
type: T.Literal("erc721-permit"),
- request: T.Object({
- beneficiary: addressT,
- deadline: bigNumberT,
- keys: T.Array(T.String()),
+ permit: T.Object({
+ permitted: T.Object({
+ token: addressT,
+ // explicitly state tokenId or keep as "amount" but pass in the tokenId anyway? I'm passing amount in test case
+ amount: bigNumberT,
+ }),
nonce: bigNumberT,
- values: T.Array(T.String()),
+ deadline: bigNumberT,
+ }),
+ transferDetails: T.Object({
+ to: addressT,
+ requestedAmount: bigNumberT,
}),
+ owner: addressT,
+ signature: signatureT,
+ networkId: networkIdT,
nftMetadata: T.Object({
GITHUB_ORGANIZATION_NAME: T.String(),
GITHUB_REPOSITORY_NAME: T.String(),
@@ -56,22 +65,12 @@ const erc721Permit = T.Object({
GITHUB_USERNAME: T.String(),
GITHUB_CONTRIBUTION_TYPE: T.String(),
}),
- nftAddress: addressT,
- networkId: networkIdT,
- signature: signatureT,
- // @whilefoo: they should have matching key names.
- owner: addressT,
- permit: T.Object({
- permitted: T.Object({
- token: addressT,
- amount: bigNumberT,
- }),
- nonce: bigNumberT,
+ request: T.Object({
+ beneficiary: addressT,
deadline: bigNumberT,
- }),
- transferDetails: T.Object({
- to: T.RegExp(/^0x[a-fA-F0-9]{40}$/),
- requestedAmount: T.Union([T.RegExp(/^\d+$/), T.Number()]),
+ keys: T.Array(T.String()),
+ nonce: bigNumberT,
+ values: T.Array(T.String()),
}),
});
diff --git a/static/scripts/rewards/web3/erc20-permit.ts b/static/scripts/rewards/web3/erc20-permit.ts
index f6121862..edef4fe3 100644
--- a/static/scripts/rewards/web3/erc20-permit.ts
+++ b/static/scripts/rewards/web3/erc20-permit.ts
@@ -153,9 +153,9 @@ export async function checkPermitClaimable(app: AppState): Promise {
return false;
}
- const permit = app.permit.permit;
+ const permit = app.permit;
- if (permit.deadline.lt(Math.floor(Date.now() / 1000))) {
+ if (permit.permit.deadline.lt(Math.floor(Date.now() / 1000))) {
toaster.create("error", `This reward has expired.`);
return false;
}
@@ -169,7 +169,7 @@ export async function checkPermitClaimable(app: AppState): Promise {
}
const { balance, allowance } = treasury;
- const permitted = BigNumber.from(permit.permitted.amount);
+ const permitted = BigNumber.from(permit.permit.permitted.amount);
const isSolvent = balance.gte(permitted);
const isAllowed = allowance.gte(permitted);
diff --git a/static/scripts/rewards/web3/erc721-permit.ts b/static/scripts/rewards/web3/erc721-permit.ts
index ffd04ff9..e6893a0c 100644
--- a/static/scripts/rewards/web3/erc721-permit.ts
+++ b/static/scripts/rewards/web3/erc721-permit.ts
@@ -6,7 +6,6 @@ import { renderTransaction } from "../render-transaction/render-transaction";
import { Erc721Permit } from "../render-transaction/tx-type";
import { claimButton, errorToast, showLoader, toaster } from "../toaster";
import { connectWallet } from "./connect-wallet";
-
export function claimErc721PermitHandler(permit: Erc721Permit) {
return async function claimButtonHandler() {
const signer = await connectWallet();
@@ -19,7 +18,7 @@ export function claimErc721PermitHandler(permit: Erc721Permit) {
return;
}
- if (permit.request.deadline.lt(Math.floor(Date.now() / 1000))) {
+ if (permit.permit.deadline.lt(Math.floor(Date.now() / 1000))) {
toaster.create("error", `This NFT has expired.`);
return;
}
@@ -32,7 +31,7 @@ export function claimErc721PermitHandler(permit: Erc721Permit) {
showLoader();
try {
- const nftContract = new ethers.Contract(permit.nftAddress, nftRewardAbi, signer);
+ const nftContract = new ethers.Contract(permit.permit.permitted.token, nftRewardAbi, signer);
const tx: TransactionResponse = await nftContract.safeMint(permit.request, permit.signature);
toaster.create("info", `Transaction sent. Waiting for confirmation...`);
@@ -47,7 +46,7 @@ export function claimErc721PermitHandler(permit: Erc721Permit) {
toaster.create("error", `Error rendering transaction: ${error.message}`);
});
} catch (error: unknown) {
- if (error instanceof Error) {
+ if (error instanceof MetaMaskError) {
console.error(error);
errorToast(error, error.message ?? error);
}
@@ -56,6 +55,6 @@ export function claimErc721PermitHandler(permit: Erc721Permit) {
}
export async function isNonceRedeemed(nftMint: Erc721Permit, provider: JsonRpcProvider): Promise {
- const nftContract = new ethers.Contract(nftMint.nftAddress, nftRewardAbi, provider);
+ const nftContract = new ethers.Contract(nftMint.permit.permitted.token, nftRewardAbi, provider);
return nftContract.nonceRedeemed(nftMint.request.nonce);
}