Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/permit fees #43

Merged
merged 13 commits into from
Jul 15, 2024
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
GITHUB_TOKEN="<token>"
OPENAI_API_KEY="<api_key>"
# treasury fee applied to the final permits, ex: 100 = 100%, 0.1 = 0.1%
PERMIT_FEE_RATE=0
# github account associated with EVM treasury address allowed to claim permit fees, ex: "ubiquibot-treasury"
PERMIT_TREASURY_GITHUB_USERNAME=""
# comma separated list of token addresses which should not incur any fees, ex: "0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d, 0x4ECaBa5870353805a9F068101A40E0f32ed605C6"
PERMIT_ERC20_TOKENS_NO_FEE_WHITELIST=""
3 changes: 3 additions & 0 deletions .github/workflows/compute.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ jobs:
SUPABASE_KEY: ${{ secrets.SUPABASE_KEY }}
NFT_MINTER_PRIVATE_KEY: ${{ secrets.NFT_MINTER_PRIVATE_KEY }}
NFT_CONTRACT_ADDRESS: ${{ secrets.NFT_CONTRACT_ADDRESS }}
PERMIT_FEE_RATE: ${{ secrets.PERMIT_FEE_RATE }}
PERMIT_TREASURY_GITHUB_USERNAME: ${{ secrets.PERMIT_TREASURY_GITHUB_USERNAME }}
PERMIT_ERC20_TOKENS_NO_FEE_WHITELIST: ${{ secrets.PERMIT_ERC20_TOKENS_NO_FEE_WHITELIST }}

steps:
- name: Checkout code
Expand Down
79 changes: 79 additions & 0 deletions src/parser/permit-generation-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
SupportedEvents,
TokenType,
} from "@ubiquibot/permit-generation/core";
import Decimal from "decimal.js";
import configuration from "../configuration/config-reader";
import {
PermitGenerationConfiguration,
Expand Down Expand Up @@ -69,6 +70,9 @@ export class PermitGenerationModule implements Module {
};
const adapters = {} as ReturnType<typeof createAdapters>;

// apply fees
result = await this._applyFees(result, payload.erc20RewardToken);

for (const [key, value] of Object.entries(result)) {
try {
const config: Context["config"] = {
Expand Down Expand Up @@ -113,6 +117,81 @@ export class PermitGenerationModule implements Module {
return result;
}

/**
* Applies fees to the final result.
* How it works:
* 1. Fee (read from ENV variable) is subtracted from all of the final result items (user.total, user.task.reward, user.comments[].reward)
* 2. Total fee is calculated
* 3. A new item is added to the final result object, example:
* ```
* {
* ...other items
* "ubiquibot-treasury": {
* total: 10.00,
* userId: 1
* }
* }
* ```
* This method is meant to be called before the final permit generation.
* @param result Result object
* @param erc20RewardToken ERC20 address of the reward token
* @returns Result object
*/
async _applyFees(result: Result, erc20RewardToken: string): Promise<Result> {
// validate fee related env variables
if (!process.env.PERMIT_FEE_RATE) {
console.log("PERMIT_FEE_RATE is not set, skipping permit fee generation");
return result;
}
if (!process.env.PERMIT_TREASURY_GITHUB_USERNAME) {
console.log("PERMIT_TREASURY_GITHUB_USERNAME is not set, skipping permit fee generation");
rndquu marked this conversation as resolved.
Show resolved Hide resolved
return result;
}
if (process.env.PERMIT_ERC20_TOKENS_NO_FEE_WHITELIST) {
const erc20TokensNoFee = process.env.PERMIT_ERC20_TOKENS_NO_FEE_WHITELIST.split(",");
if (erc20TokensNoFee.includes(erc20RewardToken)) {
console.log(`Token address ${erc20RewardToken} is whitelisted to be fee free, skipping permit fee generation`);
return result;
}
}

// Get treasury github user id
const octokit = getOctokitInstance();
const { data: treasuryGithubData } = await octokit.users.getByUsername({ username: process.env.PERMIT_TREASURY_GITHUB_USERNAME });
if (!treasuryGithubData) {
console.log(`GitHub user was not found for username ${process.env.PERMIT_TREASURY_GITHUB_USERNAME}, skipping permit fee generation`);
return result;
}

// Subtract fees from the final result:
// - user.total
// - user.task.reward
// - user.comments[].reward
const feeRateDecimal = new Decimal(100).minus(process.env.PERMIT_FEE_RATE).div(100);
const permitFeeAmountDecimal = new Decimal(0);
for (const [_, rewardResult] of Object.entries(result)) {
// accumulate total permit fee amount
const totalAfterFee = +(new Decimal(rewardResult.total).mul(feeRateDecimal).toFixed(2));
permitFeeAmountDecimal.add(new Decimal(rewardResult.total).minus(totalAfterFee));
// subtract fees
rewardResult.total = +totalAfterFee.toFixed(2);
gentlementlegen marked this conversation as resolved.
Show resolved Hide resolved
if (rewardResult.task) rewardResult.task.reward = +(new Decimal(rewardResult.task.reward).mul(feeRateDecimal).toFixed(2));
if (rewardResult.comments) {
for (let comment of rewardResult.comments) {
if (comment.score) comment.score.reward = +(new Decimal(comment.score.reward).mul(feeRateDecimal).toFixed(2));
}
}
}

// Add a new result item for treasury
result[process.env.PERMIT_TREASURY_GITHUB_USERNAME] = {
total: +permitFeeAmountDecimal.toFixed(2),
rndquu marked this conversation as resolved.
Show resolved Hide resolved
userId: treasuryGithubData.id,
};

return result;
}

async _getOrCreateIssueLocation(issue: { issueId: number; issueUrl: string }) {
let locationId: number | null = null;

Expand Down
3 changes: 3 additions & 0 deletions src/types/env-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ const envConfigSchema = Type.Object({
OPENAI_API_KEY: Type.String(),
NFT_MINTER_PRIVATE_KEY: Type.String({ default: "" }),
NFT_CONTRACT_ADDRESS: Type.String({ default: "" }),
PERMIT_FEE_RATE: Type.Number({ default: 0 }),
PERMIT_TREASURY_GITHUB_USERNAME: Type.String(),
PERMIT_ERC20_TOKENS_NO_FEE_WHITELIST: Type.String(),
});

export type EnvConfigType = Static<typeof envConfigSchema>;
Expand Down
3 changes: 3 additions & 0 deletions src/types/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ declare global {
SUPABASE_URL: string;
NFT_CONTRACT_ADDRESS: string;
NFT_MINTER_PRIVATE_KEY: string;
PERMIT_FEE_RATE: number,
PERMIT_TREASURY_GITHUB_USERNAME: string,
PERMIT_ERC20_TOKENS_NO_FEE_WHITELIST: string,
}
}
}
Expand Down
Loading