Skip to content

Commit 8693e52

Browse files
committed
feat: permit second pass working
1 parent 2d96f10 commit 8693e52

File tree

6 files changed

+218
-140
lines changed

6 files changed

+218
-140
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { MaxUint256, PermitTransferFrom, SignatureTransfer } from "@uniswap/permit2-sdk";
2+
import { randomBytes } from "crypto";
3+
import * as dotenv from "dotenv";
4+
import { BigNumber, ethers } from "ethers";
5+
import { log } from "./utils";
6+
dotenv.config();
7+
8+
const PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3"; // same on all chains
9+
10+
export async function generateERC20Permit() {
11+
const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_PROVIDER_URL);
12+
const myWallet = new ethers.Wallet(process.env.UBIQUIBOT_PRIVATE_KEY || "", provider);
13+
14+
const permitTransferFromData: PermitTransferFrom = {
15+
permitted: {
16+
// token we are permitting to be transferred
17+
token: process.env.PAYMENT_TOKEN_ADDRESS || "",
18+
// amount we are permitting to be transferred
19+
amount: ethers.utils.parseUnits(process.env.AMOUNT_IN_ETH || "", 18),
20+
},
21+
// who can transfer the tokens
22+
spender: process.env.BENEFICIARY_ADDRESS || "",
23+
nonce: BigNumber.from(`0x${randomBytes(32).toString("hex")}`),
24+
// signature deadline
25+
deadline: MaxUint256,
26+
};
27+
28+
const { domain, types, values } = SignatureTransfer.getPermitData(
29+
permitTransferFromData,
30+
PERMIT2_ADDRESS,
31+
process.env.CHAIN_ID ? Number(process.env.CHAIN_ID) : 1
32+
);
33+
const signature = await myWallet._signTypedData(domain, types, values);
34+
const txData = [
35+
{
36+
type: "erc20-permit",
37+
permit: {
38+
permitted: {
39+
token: permitTransferFromData.permitted.token,
40+
amount: permitTransferFromData.permitted.amount.toString(),
41+
},
42+
nonce: permitTransferFromData.nonce.toString(),
43+
deadline: permitTransferFromData.deadline.toString(),
44+
},
45+
transferDetails: {
46+
to: permitTransferFromData.spender,
47+
requestedAmount: permitTransferFromData.permitted.amount.toString(),
48+
},
49+
owner: myWallet.address,
50+
signature: signature,
51+
networkId: Number(process.env.CHAIN_ID),
52+
},
53+
];
54+
55+
const base64encodedTxData = Buffer.from(JSON.stringify(txData)).toString("base64");
56+
57+
// log.ok("ERC20 Public URL:");
58+
// console.log(`https://pay.ubq.fi?claim=${base64encodedTxData}`);
59+
60+
log.ok("ERC20 Testing URL:");
61+
console.log(`${process.env.FRONTEND_URL}?claim=${base64encodedTxData}`);
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { MaxUint256, PermitTransferFrom } from "@uniswap/permit2-sdk";
2+
import * as dotenv from "dotenv";
3+
import { ethers } from "ethers";
4+
import { log } from "./utils";
5+
import { solidityKeccak256 } from "ethers/lib/utils";
6+
dotenv.config();
7+
8+
const NFT_REWARDS_ANVIL_DEPLOYMENT = "0x38A70c040CA5F5439ad52d0e821063b0EC0B52b6"; // Address when using Anvil acc 1 on forked Gnosis
9+
const ANVIL_ACC_1_PRIVATE_KEY = "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d";
10+
// const NFT_ADDRESS = "0xAa1bfC0e51969415d64d6dE74f27CDa0587e645b"; // Real Gnosis address
11+
12+
/**
13+
* hardcoded digest signature for the below txData721.request object
14+
* anvil --chain-id 31337 --fork-url https//...
15+
* nft-rewards repo: forge script ./script/Deploy001_NftReward.s.sol --rpc-url http://127.0.0.1:8545 --broadcast
16+
*
17+
* string[] memory values = new string[](5);
18+
* values[0] = "ubiquity";
19+
* values[1] = "pay.ubq.fi";
20+
* values[2] = "1";
21+
* values[3] = "testing";
22+
* values[4] = "issue";
23+
*
24+
* bytes32[] memory keys = new bytes32[](5);
25+
* keys[0] = 0x1c474488c03c83ad98714cfe3a60c752036f92ab8378227adfcb13585f115c5c;
26+
* keys[1] = 0x0480f34997cc2d3abc2dafd652b25652ba845458fc3e3692f28acbd4920a37ea;
27+
* keys[2] = 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;
28+
* keys[3] = 0x5f16f4c7f149ac4f9510d9cf8cf384038ad348b3bcdc01915f95de12df9d1b02;
29+
* keys[4] = 0xa33d0fabbfddf3db8e1458550edc0ab7e061990c85809d25341eab0885973d7d;
30+
*
31+
* MintRequest memory _mintRequest = MintRequest({
32+
* beneficiary: ANVIL_ACC_1, // (0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266),
33+
* deadline: type(uint256).max,
34+
* keys: keys,
35+
* nonce: 31337,
36+
* values: values
37+
* });
38+
*
39+
* `Signed not by minter` error thrown trying to generate it on the fly
40+
* SignatureTransfer.getPermitData() expects the permitTransferFrom details, not the request object
41+
* so myWallet._signTypedData(domain, types, values) signs the permitTransferFrom details
42+
*
43+
* whereas the nftRewards contract recovers against the signed request digest
44+
* which is typedDataHashed within the contract, so we are only signing a hash
45+
* which from a claim portal standpoint shouldn't need to change the signature
46+
* admittedly, I'm not sure how to generate it on the fly so hardcoded for now
47+
*/
48+
49+
export async function generateERC721Permit() {
50+
const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_PROVIDER_URL);
51+
const myWallet = new ethers.Wallet(ANVIL_ACC_1_PRIVATE_KEY, provider);
52+
53+
const GITHUB_CONTRIBUTION_TYPE = "issue";
54+
const GITHUB_ISSUE_ID = "1";
55+
const GITHUB_ORGANIZATION_NAME = "ubiquity";
56+
const GITHUB_REPOSITORY_NAME = "pay.ubq.fi";
57+
const GITHUB_USERNAME = "testing";
58+
const ANVIL_ACC_1 = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";
59+
60+
const erc721TransferFromData: PermitTransferFrom = {
61+
permitted: {
62+
token: NFT_REWARDS_ANVIL_DEPLOYMENT, // anvil instance via nft-rewards repo
63+
amount: 1,
64+
},
65+
spender: ANVIL_ACC_1, // anvil acc 1
66+
nonce: 31337,
67+
deadline: MaxUint256,
68+
};
69+
70+
const valueBytes = [
71+
solidityKeccak256(["string"], [GITHUB_ORGANIZATION_NAME]),
72+
solidityKeccak256(["string"], [GITHUB_REPOSITORY_NAME]),
73+
solidityKeccak256(["string"], [GITHUB_ISSUE_ID]),
74+
solidityKeccak256(["string"], [GITHUB_USERNAME]),
75+
solidityKeccak256(["string"], [GITHUB_CONTRIBUTION_TYPE])
76+
]
77+
78+
const sig = "0x900f4c98c1829255903fbf57f46d7b2f9ad4e79f0abbd58b9921136319e7001d173e26d3560fe5dc674782d78f670f9c18d2b7356ced073df7410678f0981c411c";
79+
80+
const txData721 = [
81+
{
82+
type: "erc721-permit",
83+
permit: {
84+
permitted: {
85+
token: erc721TransferFromData.permitted.token,
86+
amount: erc721TransferFromData.permitted.amount.toString(),
87+
},
88+
nonce: erc721TransferFromData.nonce.toString(),
89+
deadline: erc721TransferFromData.deadline.toString(),
90+
},
91+
transferDetails: {
92+
to: erc721TransferFromData.spender,
93+
requestedAmount: erc721TransferFromData.permitted.amount.toString(),
94+
},
95+
owner: myWallet.address,
96+
signature: sig,
97+
networkId: Number(process.env.CHAIN_ID), // fork gnosis with --chain-id 31337
98+
nftMetadata: {
99+
GITHUB_ORGANIZATION_NAME,
100+
GITHUB_REPOSITORY_NAME,
101+
GITHUB_ISSUE_ID,
102+
GITHUB_USERNAME,
103+
GITHUB_CONTRIBUTION_TYPE
104+
},
105+
request: {
106+
beneficiary: ANVIL_ACC_1,
107+
deadline: erc721TransferFromData.deadline.toString(),
108+
keys: valueBytes,
109+
nonce: erc721TransferFromData.nonce.toString(),
110+
values: [
111+
GITHUB_ORGANIZATION_NAME,
112+
GITHUB_REPOSITORY_NAME,
113+
GITHUB_ISSUE_ID,
114+
GITHUB_USERNAME,
115+
GITHUB_CONTRIBUTION_TYPE
116+
]
117+
},
118+
},
119+
];
120+
121+
const base64encodedTxData721 = Buffer.from(JSON.stringify(txData721)).toString("base64");
122+
123+
// log.ok("ERC721 Public URL:");
124+
// console.log(`https://pay.ubq.fi?claim=${base64encodedTxData721}`);
125+
126+
// console.log("\n")
127+
128+
log.ok("ERC721 Local URL:");
129+
console.log(`${process.env.FRONTEND_URL}?claim=${base64encodedTxData721}`);
130+
}
+18-134
Original file line numberDiff line numberDiff line change
@@ -1,134 +1,18 @@
1-
import { MaxUint256, PermitTransferFrom, SignatureTransfer } from "@uniswap/permit2-sdk";
2-
import { randomBytes } from "crypto";
3-
import * as dotenv from "dotenv";
4-
import { BigNumber, ethers } from "ethers";
5-
import { log, verifyEnvironmentVariables } from "./utils";
6-
dotenv.config();
7-
8-
const PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3"; // same on all chains
9-
10-
generate().catch((error) => {
11-
console.error(error);
12-
verifyEnvironmentVariables();
13-
process.exitCode = 1;
14-
});
15-
16-
async function generate() {
17-
const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_PROVIDER_URL);
18-
const myWallet = new ethers.Wallet(process.env.UBIQUIBOT_PRIVATE_KEY || "", provider);
19-
20-
const erc721TransferFromData: PermitTransferFrom = {
21-
permitted: {
22-
token: process.env.NFT_TOKEN_ADDRESS || "0x5FbDB2315678afecb367f032d93F642f64180aa3", // anvil no salt first acc NFT deployment
23-
amount: 1, // this could be the tokenId if the permit is identified as an NFT via permitType
24-
},
25-
spender: process.env.BENEFICIARY_ADDRESS || "",
26-
nonce: BigNumber.from(`0x${randomBytes(32).toString("hex")}`),
27-
deadline: MaxUint256,
28-
};
29-
30-
const { domain: domain721, types: types721, values: values721 } = SignatureTransfer.getPermitData(
31-
erc721TransferFromData,
32-
PERMIT2_ADDRESS,
33-
process.env.CHAIN_ID ? Number(process.env.CHAIN_ID) : 1
34-
);
35-
36-
const signature721 = await myWallet._signTypedData(domain721, types721, values721);
37-
38-
const GITHUB_CONTRIBUTION_TYPE = process.env.GITHUB_CONTRIBUTION_TYPE || "issue";
39-
const GITHUB_ISSUE_ID = process.env.GITHUB_ISSUE_ID || "1";
40-
const GITHUB_ORGANIZATION_NAME = process.env.GITHUB_ORGANIZATION_NAME || "ubiquity";
41-
const GITHUB_REPOSITORY_NAME = process.env.GITHUB_REPOSITORY_NAME || "pay.ubq.fi";
42-
const GITHUB_USERNAME = process.env.GITHUB_USERNAME || "keyrxng";
43-
44-
const txData721 = [
45-
{
46-
type: "erc721-permit",
47-
permit: {
48-
permitted: {
49-
token: erc721TransferFromData.permitted.token,
50-
amount: erc721TransferFromData.permitted.amount.toString(),
51-
},
52-
nonce: erc721TransferFromData.nonce.toString(),
53-
deadline: erc721TransferFromData.deadline.toString(),
54-
},
55-
transferDetails: {
56-
to: erc721TransferFromData.spender,
57-
requestedAmount: erc721TransferFromData.permitted.amount.toString(),
58-
},
59-
owner: myWallet.address,
60-
signature: signature721,
61-
networkId: Number(process.env.CHAIN_ID),
62-
nftMetadata: {
63-
GITHUB_ORGANIZATION_NAME,
64-
GITHUB_REPOSITORY_NAME,
65-
GITHUB_ISSUE_ID,
66-
GITHUB_USERNAME,
67-
GITHUB_CONTRIBUTION_TYPE
68-
},
69-
request: {
70-
beneficiary: process.env.BENEFICIARY_ADDRESS ?? "",
71-
deadline: erc721TransferFromData.deadline.toString(),
72-
keys: ["GITHUB_ORGANIZATION_NAME", "GITHUB_REPOSITORY_NAME", "GITHUB_ISSUE_ID", "GITHUB_USERNAME", "GITHUB_CONTRIBUTION_TYPE"],
73-
nonce: erc721TransferFromData.nonce.toString(),
74-
values: [GITHUB_ORGANIZATION_NAME, GITHUB_REPOSITORY_NAME, GITHUB_ISSUE_ID, GITHUB_USERNAME, GITHUB_CONTRIBUTION_TYPE],
75-
},
76-
},
77-
];
78-
79-
const base64encodedTxData721 = Buffer.from(JSON.stringify(txData721)).toString("base64");
80-
log.ok("Testing URL:");
81-
console.log(`${process.env.FRONTEND_URL}?claim=${base64encodedTxData721}`);
82-
log.ok("Public URL:");
83-
console.log(`https://pay.ubq.fi?claim=${base64encodedTxData721}`);
84-
console.log();
85-
86-
const permitTransferFromData: PermitTransferFrom = {
87-
permitted: {
88-
// token we are permitting to be transferred
89-
token: process.env.PAYMENT_TOKEN_ADDRESS || "",
90-
// amount we are permitting to be transferred
91-
amount: ethers.utils.parseUnits(process.env.AMOUNT_IN_ETH || "", 18),
92-
},
93-
// who can transfer the tokens
94-
spender: process.env.BENEFICIARY_ADDRESS || "",
95-
nonce: BigNumber.from(`0x${randomBytes(32).toString("hex")}`),
96-
// signature deadline
97-
deadline: MaxUint256,
98-
};
99-
100-
const { domain, types, values } = SignatureTransfer.getPermitData(
101-
permitTransferFromData,
102-
PERMIT2_ADDRESS,
103-
process.env.CHAIN_ID ? Number(process.env.CHAIN_ID) : 1
104-
);
105-
const signature = await myWallet._signTypedData(domain, types, values);
106-
107-
const txData = [
108-
{
109-
type: "erc20-permit",
110-
permit: {
111-
permitted: {
112-
token: permitTransferFromData.permitted.token,
113-
amount: permitTransferFromData.permitted.amount.toString(),
114-
},
115-
nonce: permitTransferFromData.nonce.toString(),
116-
deadline: permitTransferFromData.deadline.toString(),
117-
},
118-
transferDetails: {
119-
to: permitTransferFromData.spender,
120-
requestedAmount: permitTransferFromData.permitted.amount.toString(),
121-
},
122-
owner: myWallet.address,
123-
signature: signature,
124-
networkId: Number(process.env.CHAIN_ID),
125-
},
126-
];
127-
128-
const base64encodedTxData = Buffer.from(JSON.stringify(txData)).toString("base64");
129-
log.ok("Testing URL:");
130-
console.log(`${process.env.FRONTEND_URL}?claim=${base64encodedTxData}`);
131-
log.ok("Public URL:");
132-
console.log(`https://pay.ubq.fi?claim=${base64encodedTxData}`);
133-
console.log();
134-
}
1+
import {generateERC20Permit} from "./generate-erc20-permit-url";
2+
import {generateERC721Permit} from "./generate-erc721-permit-url";
3+
import { verifyEnvironmentVariables } from "./utils";
4+
5+
(async () => {
6+
7+
generateERC721Permit().catch((error) => {
8+
console.error(error);
9+
verifyEnvironmentVariables();
10+
process.exitCode = 1;
11+
});
12+
13+
generateERC20Permit().catch((error) => {
14+
console.error(error);
15+
verifyEnvironmentVariables();
16+
process.exitCode = 1;
17+
});
18+
})().catch(console.error);

static/scripts/rewards/constants.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,4 @@ export const networkRpcs: Record<number, string[]> = {
4848
};
4949

5050
export const permit2Address = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
51-
export const nftAddress = "0xAa1bfC0e51969415d64d6dE74f27CDa0587e645b";
51+
export const nftAddress = "0xAa1bfC0e51969415d64d6dE74f27CDa0587e645b";

static/scripts/rewards/render-transaction/insert-table-data.ts

+4
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ function renderTokenFields(tokenAddress: string, explorerUrl: string) {
9494
function renderToFields(receiverAddress: string, explorerUrl: string) {
9595
const toFull = document.querySelector("#rewardRecipient .full") as Element;
9696
const toShort = document.querySelector("#rewardRecipient .short") as Element;
97+
98+
// after a single claim toFull returns null as creates a toaster error
99+
if(!toFull || !toShort) return console.error("Could not find toFull or toShort");
100+
97101
toFull.innerHTML = `<div>${receiverAddress}</div>`;
98102
toShort.innerHTML = `<div>${shortenAddress(receiverAddress)}</div>`;
99103

static/scripts/rewards/web3/erc721-permit.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { nftRewardAbi } from "../abis/nft-reward-abi";
44
import { app } from "../app-state";
55
import { renderTransaction } from "../render-transaction/render-transaction";
66
import { Erc721Permit } from "../render-transaction/tx-type";
7-
import { claimButton, errorToast, showLoader, toaster } from "../toaster";
7+
import { claimButton, showLoader, toaster } from "../toaster";
88
import { connectWallet } from "./connect-wallet";
99
export function claimErc721PermitHandler(permit: Erc721Permit) {
1010
return async function claimButtonHandler() {
@@ -46,10 +46,8 @@ export function claimErc721PermitHandler(permit: Erc721Permit) {
4646
toaster.create("error", `Error rendering transaction: ${error.message}`);
4747
});
4848
} catch (error: unknown) {
49-
if (error instanceof MetaMaskError) {
50-
console.error(error);
51-
errorToast(error, error.message ?? error);
52-
}
49+
console.error(error);
50+
toaster.create("error", `Error claiming NFT: ${typeof error === "string" ? error : error.message ? error.message : "Unknown error"}`);
5351
}
5452
};
5553
}

0 commit comments

Comments
 (0)