Skip to content

Commit

Permalink
feat: permit second pass working
Browse files Browse the repository at this point in the history
  • Loading branch information
Keyrxng committed Feb 23, 2024
1 parent 2d96f10 commit 8693e52
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 140 deletions.
62 changes: 62 additions & 0 deletions scripts/typescript/generate-erc20-permit-url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { MaxUint256, PermitTransferFrom, SignatureTransfer } from "@uniswap/permit2-sdk";
import { randomBytes } from "crypto";
import * as dotenv from "dotenv";
import { BigNumber, ethers } from "ethers";
import { log } from "./utils";
dotenv.config();

const PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3"; // same on all chains

export async function generateERC20Permit() {
const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_PROVIDER_URL);
const myWallet = new ethers.Wallet(process.env.UBIQUIBOT_PRIVATE_KEY || "", provider);

const permitTransferFromData: PermitTransferFrom = {
permitted: {
// token we are permitting to be transferred
token: process.env.PAYMENT_TOKEN_ADDRESS || "",
// amount we are permitting to be transferred
amount: ethers.utils.parseUnits(process.env.AMOUNT_IN_ETH || "", 18),
},
// who can transfer the tokens
spender: process.env.BENEFICIARY_ADDRESS || "",
nonce: BigNumber.from(`0x${randomBytes(32).toString("hex")}`),
// signature deadline
deadline: MaxUint256,
};

const { domain, types, values } = SignatureTransfer.getPermitData(
permitTransferFromData,
PERMIT2_ADDRESS,
process.env.CHAIN_ID ? Number(process.env.CHAIN_ID) : 1
);
const signature = await myWallet._signTypedData(domain, types, values);
const txData = [
{
type: "erc20-permit",
permit: {
permitted: {
token: permitTransferFromData.permitted.token,
amount: permitTransferFromData.permitted.amount.toString(),
},
nonce: permitTransferFromData.nonce.toString(),
deadline: permitTransferFromData.deadline.toString(),
},
transferDetails: {
to: permitTransferFromData.spender,
requestedAmount: permitTransferFromData.permitted.amount.toString(),
},
owner: myWallet.address,
signature: signature,
networkId: Number(process.env.CHAIN_ID),
},
];

const base64encodedTxData = Buffer.from(JSON.stringify(txData)).toString("base64");

// log.ok("ERC20 Public URL:");
// console.log(`https://pay.ubq.fi?claim=${base64encodedTxData}`);

log.ok("ERC20 Testing URL:");
console.log(`${process.env.FRONTEND_URL}?claim=${base64encodedTxData}`);
}
130 changes: 130 additions & 0 deletions scripts/typescript/generate-erc721-permit-url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { MaxUint256, PermitTransferFrom } from "@uniswap/permit2-sdk";
import * as dotenv from "dotenv";
import { ethers } from "ethers";
import { log } from "./utils";
import { solidityKeccak256 } from "ethers/lib/utils";
dotenv.config();

const NFT_REWARDS_ANVIL_DEPLOYMENT = "0x38A70c040CA5F5439ad52d0e821063b0EC0B52b6"; // Address when using Anvil acc 1 on forked Gnosis
const ANVIL_ACC_1_PRIVATE_KEY = "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d";
// const NFT_ADDRESS = "0xAa1bfC0e51969415d64d6dE74f27CDa0587e645b"; // Real Gnosis address

/**
* hardcoded digest signature for the below txData721.request object
* anvil --chain-id 31337 --fork-url https//...
* nft-rewards repo: forge script ./script/Deploy001_NftReward.s.sol --rpc-url http://127.0.0.1:8545 --broadcast
*
* string[] memory values = new string[](5);
* values[0] = "ubiquity";
* values[1] = "pay.ubq.fi";
* values[2] = "1";
* values[3] = "testing";
* values[4] = "issue";
*
* bytes32[] memory keys = new bytes32[](5);
* keys[0] = 0x1c474488c03c83ad98714cfe3a60c752036f92ab8378227adfcb13585f115c5c;
* keys[1] = 0x0480f34997cc2d3abc2dafd652b25652ba845458fc3e3692f28acbd4920a37ea;
* keys[2] = 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;
* keys[3] = 0x5f16f4c7f149ac4f9510d9cf8cf384038ad348b3bcdc01915f95de12df9d1b02;
* keys[4] = 0xa33d0fabbfddf3db8e1458550edc0ab7e061990c85809d25341eab0885973d7d;
*
* MintRequest memory _mintRequest = MintRequest({
* beneficiary: ANVIL_ACC_1, // (0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266),
* deadline: type(uint256).max,
* keys: keys,
* nonce: 31337,
* values: values
* });
*
* `Signed not by minter` error thrown trying to generate it on the fly
* SignatureTransfer.getPermitData() expects the permitTransferFrom details, not the request object
* so myWallet._signTypedData(domain, types, values) signs the permitTransferFrom details
*
* whereas the nftRewards contract recovers against the signed request digest
* which is typedDataHashed within the contract, so we are only signing a hash
* which from a claim portal standpoint shouldn't need to change the signature
* admittedly, I'm not sure how to generate it on the fly so hardcoded for now
*/

export async function generateERC721Permit() {
const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_PROVIDER_URL);
const myWallet = new ethers.Wallet(ANVIL_ACC_1_PRIVATE_KEY, provider);

const GITHUB_CONTRIBUTION_TYPE = "issue";
const GITHUB_ISSUE_ID = "1";
const GITHUB_ORGANIZATION_NAME = "ubiquity";
const GITHUB_REPOSITORY_NAME = "pay.ubq.fi";
const GITHUB_USERNAME = "testing";
const ANVIL_ACC_1 = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";

const erc721TransferFromData: PermitTransferFrom = {
permitted: {
token: NFT_REWARDS_ANVIL_DEPLOYMENT, // anvil instance via nft-rewards repo
amount: 1,
},
spender: ANVIL_ACC_1, // anvil acc 1
nonce: 31337,
deadline: MaxUint256,
};

const valueBytes = [
solidityKeccak256(["string"], [GITHUB_ORGANIZATION_NAME]),
solidityKeccak256(["string"], [GITHUB_REPOSITORY_NAME]),
solidityKeccak256(["string"], [GITHUB_ISSUE_ID]),
solidityKeccak256(["string"], [GITHUB_USERNAME]),
solidityKeccak256(["string"], [GITHUB_CONTRIBUTION_TYPE])
]

const sig = "0x900f4c98c1829255903fbf57f46d7b2f9ad4e79f0abbd58b9921136319e7001d173e26d3560fe5dc674782d78f670f9c18d2b7356ced073df7410678f0981c411c";

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: sig,
networkId: Number(process.env.CHAIN_ID), // fork gnosis with --chain-id 31337
nftMetadata: {
GITHUB_ORGANIZATION_NAME,
GITHUB_REPOSITORY_NAME,
GITHUB_ISSUE_ID,
GITHUB_USERNAME,
GITHUB_CONTRIBUTION_TYPE
},
request: {
beneficiary: ANVIL_ACC_1,
deadline: erc721TransferFromData.deadline.toString(),
keys: valueBytes,
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("ERC721 Public URL:");
// console.log(`https://pay.ubq.fi?claim=${base64encodedTxData721}`);

// console.log("\n")

log.ok("ERC721 Local URL:");
console.log(`${process.env.FRONTEND_URL}?claim=${base64encodedTxData721}`);
}
152 changes: 18 additions & 134 deletions scripts/typescript/generate-permit2-url.ts
Original file line number Diff line number Diff line change
@@ -1,134 +1,18 @@
import { MaxUint256, PermitTransferFrom, SignatureTransfer } from "@uniswap/permit2-sdk";
import { randomBytes } from "crypto";
import * as dotenv from "dotenv";
import { BigNumber, ethers } from "ethers";
import { log, verifyEnvironmentVariables } from "./utils";
dotenv.config();

const PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3"; // same on all chains

generate().catch((error) => {
console.error(error);
verifyEnvironmentVariables();
process.exitCode = 1;
});

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
token: process.env.PAYMENT_TOKEN_ADDRESS || "",
// amount we are permitting to be transferred
amount: ethers.utils.parseUnits(process.env.AMOUNT_IN_ETH || "", 18),
},
// who can transfer the tokens
spender: process.env.BENEFICIARY_ADDRESS || "",
nonce: BigNumber.from(`0x${randomBytes(32).toString("hex")}`),
// signature deadline
deadline: MaxUint256,
};

const { domain, types, values } = SignatureTransfer.getPermitData(
permitTransferFromData,
PERMIT2_ADDRESS,
process.env.CHAIN_ID ? Number(process.env.CHAIN_ID) : 1
);
const signature = await myWallet._signTypedData(domain, types, values);

const txData = [
{
type: "erc20-permit",
permit: {
permitted: {
token: permitTransferFromData.permitted.token,
amount: permitTransferFromData.permitted.amount.toString(),
},
nonce: permitTransferFromData.nonce.toString(),
deadline: permitTransferFromData.deadline.toString(),
},
transferDetails: {
to: permitTransferFromData.spender,
requestedAmount: permitTransferFromData.permitted.amount.toString(),
},
owner: myWallet.address,
signature: signature,
networkId: Number(process.env.CHAIN_ID),
},
];

const base64encodedTxData = Buffer.from(JSON.stringify(txData)).toString("base64");
log.ok("Testing URL:");
console.log(`${process.env.FRONTEND_URL}?claim=${base64encodedTxData}`);
log.ok("Public URL:");
console.log(`https://pay.ubq.fi?claim=${base64encodedTxData}`);
console.log();
}
import {generateERC20Permit} from "./generate-erc20-permit-url";
import {generateERC721Permit} from "./generate-erc721-permit-url";
import { verifyEnvironmentVariables } from "./utils";

(async () => {

generateERC721Permit().catch((error) => {
console.error(error);
verifyEnvironmentVariables();
process.exitCode = 1;
});

generateERC20Permit().catch((error) => {
console.error(error);
verifyEnvironmentVariables();
process.exitCode = 1;
});
})().catch(console.error);
2 changes: 1 addition & 1 deletion static/scripts/rewards/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,4 @@ export const networkRpcs: Record<number, string[]> = {
};

export const permit2Address = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
export const nftAddress = "0xAa1bfC0e51969415d64d6dE74f27CDa0587e645b";
export const nftAddress = "0xAa1bfC0e51969415d64d6dE74f27CDa0587e645b";
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ function renderTokenFields(tokenAddress: string, explorerUrl: string) {
function renderToFields(receiverAddress: string, explorerUrl: string) {
const toFull = document.querySelector("#rewardRecipient .full") as Element;
const toShort = document.querySelector("#rewardRecipient .short") as Element;

// after a single claim toFull returns null as creates a toaster error
if(!toFull || !toShort) return console.error("Could not find toFull or toShort");

toFull.innerHTML = `<div>${receiverAddress}</div>`;
toShort.innerHTML = `<div>${shortenAddress(receiverAddress)}</div>`;

Expand Down
8 changes: 3 additions & 5 deletions static/scripts/rewards/web3/erc721-permit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { nftRewardAbi } from "../abis/nft-reward-abi";
import { app } from "../app-state";
import { renderTransaction } from "../render-transaction/render-transaction";
import { Erc721Permit } from "../render-transaction/tx-type";
import { claimButton, errorToast, showLoader, toaster } from "../toaster";
import { claimButton, showLoader, toaster } from "../toaster";
import { connectWallet } from "./connect-wallet";
export function claimErc721PermitHandler(permit: Erc721Permit) {
return async function claimButtonHandler() {
Expand Down Expand Up @@ -46,10 +46,8 @@ export function claimErc721PermitHandler(permit: Erc721Permit) {
toaster.create("error", `Error rendering transaction: ${error.message}`);
});
} catch (error: unknown) {
if (error instanceof MetaMaskError) {
console.error(error);
errorToast(error, error.message ?? error);
}
console.error(error);
toaster.create("error", `Error claiming NFT: ${typeof error === "string" ? error : error.message ? error.message : "Unknown error"}`);
}
};
}
Expand Down

0 comments on commit 8693e52

Please sign in to comment.