Skip to content

Commit

Permalink
Merge pull request #74 from ubiquity-os/development
Browse files Browse the repository at this point in the history
Merge development into main
  • Loading branch information
gentlementlegen authored Oct 7, 2024
2 parents d2d0822 + 004a17c commit d1abbd7
Show file tree
Hide file tree
Showing 12 changed files with 7,050 additions and 10,302 deletions.
2 changes: 1 addition & 1 deletion .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
nodeLinker: node-modules
nodeLinker: node-modules
52 changes: 22 additions & 30 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"knip-ci": "knip --no-exit-code --reporter json",
"prepare": "husky install",
"test": "jest",
"build": "rollup -c"
"build": "tsup"
},
"keywords": [
"typescript",
Expand All @@ -28,14 +28,14 @@
"dependencies": {
"@actions/core": "^1.10.1",
"@actions/github": "^6.0.0",
"@octokit/rest": "^20.0.2",
"@octokit/webhooks": "^13.1.0",
"@octokit/rest": "^20.1.0",
"@octokit/webhooks": "^13.3.0",
"@sinclair/typebox": "^0.32.5",
"@supabase/supabase-js": "2.42.0",
"@ubiquity-dao/rpc-handler": "1.3.0",
"@uniswap/permit2-sdk": "^1.2.0",
"dotenv": "^16.4.4",
"ethers": "6.11.1",
"ethers": "^5.7.2",
"libsodium-wrappers": "^0.7.13"
},
"devDependencies": {
Expand All @@ -46,11 +46,6 @@
"@cspell/dict-typescript": "^3.1.2",
"@jest/globals": "29.7.0",
"@jest/types": "29.6.3",
"@rollup/plugin-commonjs": "25.0.7",
"@rollup/plugin-json": "6.1.0",
"@rollup/plugin-node-resolve": "15.2.3",
"@rollup/plugin-terser": "0.4.4",
"@rollup/plugin-yaml": "4.1.2",
"@types/libsodium-wrappers": "^0.7.8",
"@types/node": "^20.11.19",
"@typescript-eslint/eslint-plugin": "^7.0.1",
Expand All @@ -66,12 +61,9 @@
"lint-staged": "^15.2.2",
"npm-run-all": "^4.1.5",
"prettier": "^3.2.5",
"rollup": "4.14.3",
"rollup-plugin-delete": "2.0.0",
"rollup-plugin-dts-bundle-generator": "1.4.0",
"rollup-plugin-typescript2": "0.36.0",
"ts-jest": "29.1.2",
"ts-node": "10.9.2",
"tsup": "8.3.0",
"tsx": "^4.7.1",
"typescript": "^5.3.3"
},
Expand All @@ -97,29 +89,29 @@
"README.md",
"package.json"
],
"module": "dist/core/index.esm.js",
"main": "dist/core/index.js",
"typings": "dist/types/index.d.ts",
"module": "./dist/index.mjs",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
"./core": {
"require": "./dist/core/index.js",
"import": "./dist/core/index.js",
"types": "./dist/core/index.d.ts"
".": {
"types": "./dist/index.d.ts",
"require": "./dist/index.js",
"import": "./dist/index.mjs"
},
"./utils": {
"types": "./dist/utils/index.d.mts",
"require": "./dist/utils/index.js",
"import": "./dist/utils/index.mjs"
},
"./types": {
"types": "./dist/types/index.d.mts",
"require": "./dist/types/index.js",
"import": "./dist/types/index.js",
"types": "./dist/types/index.d.ts"
"import": "./dist/types/index.mjs"
},
"./handlers": {
"require": "./dist/handlers/index.js",
"import": "./dist/handlers/index.js",
"types": "./dist/handlers/index.d.ts"
},
"./utils": {
"require": "./dist/utils/index.js",
"import": "./dist/utils/index.js",
"types": "./dist/utils/index.d.ts"
"types": "./dist/handlers/index.d.ts",
"require": "./dist/handlers/index.mjs",
"import": "./dist/handlers/index.js"
}
}
}
46 changes: 0 additions & 46 deletions rollup.config.mjs

This file was deleted.

2 changes: 2 additions & 0 deletions src/adapters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ export function createAdapters(supabaseClient: SupabaseClient<Database>, context
},
};
}

export * from "./supabase/types/database";
169 changes: 86 additions & 83 deletions src/handlers/generate-erc20-permit.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PERMIT2_ADDRESS, PermitTransferFrom, SignatureTransfer } from "@uniswap/permit2-sdk";
import { ethers, keccak256, MaxInt256, parseUnits, toUtf8Bytes } from "ethers";
import { PERMIT2_ADDRESS, PermitTransferFrom, SignatureTransfer, MaxUint256 } from "@uniswap/permit2-sdk";
import { ethers, utils } from "ethers";
import { Context, Logger } from "../types/context";
import { PermitReward, TokenType } from "../types";
import { decrypt, parseDecryptedPrivateKey } from "../utils";
Expand All @@ -22,38 +22,38 @@ export async function generateErc20PermitSignature(
amount: number,
tokenAddress: string
): Promise<PermitReward> {
let _logger: Logger;
let logger: Logger;
const _username = username;
let _walletAddress: string | null | undefined;
let _issueNodeId: string;
let _evmNetworkId: number;
let _evmPrivateEncrypted: string;
let _userId: number;
let walletAddress: string | null | undefined;
let issueNodeId: string;
let evmNetworkId: number;
let evmPrivateEncrypted: string;
let userId: number;

if ("issueNodeId" in contextOrPayload) {
_logger = contextOrPayload.logger as Logger;
_walletAddress = contextOrPayload.walletAddress;
_evmNetworkId = contextOrPayload.evmNetworkId;
_evmPrivateEncrypted = contextOrPayload.evmPrivateEncrypted;
_issueNodeId = contextOrPayload.issueNodeId;
_userId = contextOrPayload.userId;
logger = contextOrPayload.logger as Logger;
walletAddress = contextOrPayload.walletAddress;
evmNetworkId = contextOrPayload.evmNetworkId;
evmPrivateEncrypted = contextOrPayload.evmPrivateEncrypted;
issueNodeId = contextOrPayload.issueNodeId;
userId = contextOrPayload.userId;
} else {
const config = contextOrPayload.config;
_logger = contextOrPayload.logger;
const { evmNetworkId, evmPrivateEncrypted } = config;
logger = contextOrPayload.logger;
const { evmNetworkId: configEvmNetworkId, evmPrivateEncrypted: configEvmPrivateEncrypted } = config;
const { data: userData } = await contextOrPayload.octokit.users.getByUsername({ username: _username });
if (!userData) {
throw new Error(`GitHub user was not found for id ${_username}`);
}
_userId = userData.id;
userId = userData.id;
const { wallet } = contextOrPayload.adapters.supabase;
_walletAddress = await wallet.getWalletByUserId(_userId);
_evmNetworkId = evmNetworkId;
_evmPrivateEncrypted = evmPrivateEncrypted;
walletAddress = await wallet.getWalletByUserId(userId);
evmNetworkId = configEvmNetworkId;
evmPrivateEncrypted = configEvmPrivateEncrypted;
if ("issue" in contextOrPayload.payload) {
_issueNodeId = contextOrPayload.payload.issue.node_id;
issueNodeId = contextOrPayload.payload.issue.node_id;
} else if ("pull_request" in contextOrPayload.payload) {
_issueNodeId = contextOrPayload.payload.pull_request.node_id;
issueNodeId = contextOrPayload.payload.pull_request.node_id;
} else {
throw new Error("Issue Id is missing");
}
Expand All @@ -62,94 +62,97 @@ export async function generateErc20PermitSignature(
if (!_username) {
throw new Error("User was not found");
}
if (!_walletAddress) {
if (!walletAddress) {
const errorMessage = "ERC20 Permit generation error: Wallet not found";
_logger.error(errorMessage);
logger.error(errorMessage);
throw new Error(errorMessage);
}

const provider = await getFastestProvider(_evmNetworkId);
const provider = await getFastestProvider(evmNetworkId);
if (!provider) {
_logger.error("Provider is not defined");
logger.error("Provider is not defined");
throw new Error("Provider is not defined");
}

let privateKey = null;
const privateKey = await getPrivateKey(evmPrivateEncrypted, logger);
const adminWallet = await getAdminWallet(privateKey, provider, logger);
const tokenDecimals = await getTokenDecimals(tokenAddress, provider, logger);

const permitTransferFromData: PermitTransferFrom = {
permitted: {
token: tokenAddress,
amount: utils.parseUnits(amount.toString(), tokenDecimals),
},
spender: walletAddress,
nonce: BigInt(utils.keccak256(utils.toUtf8Bytes(`${userId}-${issueNodeId}`))),
deadline: MaxUint256,
};

const { domain, types, values } = SignatureTransfer.getPermitData(permitTransferFromData, PERMIT2_ADDRESS, evmNetworkId);

const domainData = {
name: domain.name,
version: domain.version || "1", // default to 1 if it's undefined
chainId: domain.chainId,
verifyingContract: domain.verifyingContract,
};

try {
const privateKeyDecrypted = await decrypt(_evmPrivateEncrypted, String(process.env.X25519_PRIVATE_KEY));
const signature = await adminWallet._signTypedData(domainData, types, values);

const erc20Permit: PermitReward = {
tokenType: TokenType.ERC20,
tokenAddress: permitTransferFromData.permitted.token,
beneficiary: permitTransferFromData.spender,
nonce: permitTransferFromData.nonce.toString(),
deadline: permitTransferFromData.deadline.toString(),
amount: permitTransferFromData.permitted.amount.toString(),
owner: adminWallet.address,
signature: signature,
networkId: evmNetworkId,
};

logger.info("Generated ERC20 permit2 signature", erc20Permit);

return erc20Permit;
} catch (error) {
logger.error(`Failed to sign typed data: ${error}`);
throw error;
}
}

async function getPrivateKey(evmPrivateEncrypted: string, logger: Logger) {
try {
const privateKeyDecrypted = await decrypt(evmPrivateEncrypted, String(process.env.X25519_PRIVATE_KEY));
const privateKeyParsed = parseDecryptedPrivateKey(privateKeyDecrypted);
privateKey = privateKeyParsed.privateKey;
const privateKey = privateKeyParsed.privateKey;
if (!privateKey) throw new Error("Private key is not defined");
return privateKey;
} catch (error) {
const errorMessage = `Failed to decrypt a private key: ${error}`;
_logger.error(errorMessage);
logger.error(errorMessage);
throw new Error(errorMessage);
}
}

let adminWallet;
let tokenDecimals;

async function getAdminWallet(privateKey: string, provider: ethers.providers.Provider, logger: Logger) {
try {
adminWallet = new ethers.Wallet(privateKey, provider);
return new ethers.Wallet(privateKey, provider);
} catch (error) {
const errorMessage = `Failed to instantiate wallet: ${error}`;
_logger.debug(errorMessage);
logger.debug(errorMessage);
throw new Error(errorMessage);
}
}

async function getTokenDecimals(tokenAddress: string, provider: ethers.providers.Provider, logger: Logger) {
try {
const erc20Abi = ["function decimals() public view returns (uint8)"];
const tokenContract = new ethers.Contract(tokenAddress, erc20Abi, provider);
tokenDecimals = await tokenContract.decimals();
return await tokenContract.decimals();
} catch (error) {
const errorMessage = `Failed to get token decimals for token: ${tokenAddress}`;
_logger.debug(errorMessage);
logger.debug(errorMessage);
throw new Error(errorMessage);
}

const permitTransferFromData: PermitTransferFrom = {
permitted: {
token: tokenAddress,
amount: parseUnits(amount.toString(), tokenDecimals),
},
spender: _walletAddress,
nonce: BigInt(keccak256(toUtf8Bytes(`${_userId}-${_issueNodeId}`))),
deadline: MaxInt256,
};

const { domain, types, values } = SignatureTransfer.getPermitData(permitTransferFromData, PERMIT2_ADDRESS, _evmNetworkId);

const signature = await adminWallet
.signTypedData(
{
name: domain.name,
version: domain.version,
chainId: domain.chainId ? domain.chainId.toString() : undefined,
verifyingContract: domain.verifyingContract,
salt: domain.salt?.toString(),
},
types,
values
)
.catch((error) => {
const errorMessage = `Failed to sign typed data ${error}`;
_logger.error(errorMessage);
throw new Error(errorMessage);
});

const erc20Permit: PermitReward = {
tokenType: TokenType.ERC20,
tokenAddress: permitTransferFromData.permitted.token,
beneficiary: permitTransferFromData.spender,
nonce: permitTransferFromData.nonce.toString(),
deadline: permitTransferFromData.deadline.toString(),
amount: permitTransferFromData.permitted.amount.toString(),
owner: adminWallet.address,
signature: signature,
networkId: _evmNetworkId,
};

_logger.info("Generated ERC20 permit2 signature", erc20Permit);

return erc20Permit;
}
Loading

0 comments on commit d1abbd7

Please sign in to comment.