Skip to content

Commit

Permalink
chore: clean up
Browse files Browse the repository at this point in the history
  • Loading branch information
Keyrxng committed Aug 14, 2024
1 parent 9204524 commit 0509c64
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 109 deletions.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# `@ubiquibot/crypto-faucet-plugin`

#### What is this plugin for?
This plugin is used to subsize first-time contributors to Ubiquity by providing them with some funds for their first reward claim.

This plugin is used to subsidize first-time contributors to Ubiquity by providing them with some funds for their first reward claim.

#### How does it work?

This plugin is a faucet that can be used to distribute funds to users. It can be configured to distribute native gas tokens or any other ERC20 tokens.

#### Features
Expand All @@ -18,9 +20,8 @@ This plugin is a faucet that can be used to distribute funds to users. It can be
- `/register` - Register a new user. Create a storage entry for the user and direct them to safe.ubq.fi to finalize the registration.
- `/faucet` - A slash command to allow distribution of tokens directly to users. This command can only be used by org admins.
- - `/faucet <recipient> <networkId> <amount> <token>` - Distribute tokens to a user on a specific network. (use `native` for `token` for gas distribution)
- - `/faucet <recipient> <networkId> <amount>` - Distribute gas tokens to a user on a specific network. (ERC20 tokens are not distributable with this command)
- - `/faucet <recipient> <networkId>` - Distribute gas tokens to a user on a specific network. (ERC20 tokens are not distributable with this command)

- - `/faucet <recipient> <networkId> <amount>` - Distribute gas tokens to a user on a specific network.
- - `/faucet <recipient> <networkId>` - Distribute gas tokens to a user on a specific network.

#### Configuration

Expand All @@ -34,7 +35,7 @@ plugins:
howManyTimesUserCanClaim: 1
fundingWalletPrivateKey: 0x...
nativeGasToken: 100 // in wei. If defined, only gas is distributed
networkdIds:
networkIds:
- 100
- 1337
distributionTokens: // optional, omit for gas only distribution
Expand Down
6 changes: 2 additions & 4 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
{
"name": "crypto-faucet",
"description": "A multi-network supporting faucet for gas subsidies, token distributions, and more.",
"ubiquity:listeners": [
"issue_comment.created"
],
"ubiquity:listeners": ["issue_comment.created"],
"commands": {
"register": {
"ubiquity:example": "/register",
Expand All @@ -14,4 +12,4 @@
"description": "Admin command to manually distribute tokens to a user."
}
}
}
}
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
"dependencies": {
"@actions/core": "1.10.1",
"@actions/github": "6.0.0",
"@octokit/request-error": "^6.1.4",
"@octokit/rest": "20.1.1",
"@octokit/webhooks": "13.2.7",
"@sinclair/typebox": "0.32.33",
Expand Down
15 changes: 8 additions & 7 deletions src/handlers/faucet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ export async function faucet(context: Context, args: Args) {
const { config, storage } = context;
const { recipient, networkId, amount, token } = args;

const userWallet = storage.getUserStorage(recipient);
if (!userWallet.wallet) {
const userStorage = storage.getUserStorage(recipient);
if (!userStorage.wallet) {
return await register(context as Context<"issue_comment.created">, args);
}

Expand Down Expand Up @@ -48,13 +48,14 @@ export async function faucet(context: Context, args: Args) {
if (!value || value <= BigInt(0)) {
return throwError("Invalid amount");
}

const wallet = await getWalletSigner(config.fundingWalletPrivateKey, networkId);
const transfer = await handleTransfer(context, wallet, userWallet.wallet, value, isNative, token);
const transfer = await handleTransfer(context, wallet, userStorage.wallet, value, isNative, token);

if (transfer) {
userWallet.claimed++;
userWallet.lastClaim = new Date();
storage.setUserStorage(recipient, userWallet);
if (transfer && transfer.transactionHash) {
userStorage.claimed++;
userStorage.lastClaim = new Date();
storage.setUserStorage(recipient, userStorage);
}

return transfer;
Expand Down
86 changes: 3 additions & 83 deletions src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ import { Octokit } from "@octokit/rest";
import { Env, PluginInputs } from "./types";
import { Context } from "./types";
import { LogLevel, Logs } from "@ubiquity-dao/ubiquibot-logger";
import { register } from "./handlers/register";
import { faucet } from "./handlers/faucet";
import { logAndComment, throwError } from "./utils/logger";
import { Storage } from "./adapters/storage";
import { gasSubsidize } from "./handlers/gas-subsidize";
import { STRINGS } from "../tests/__mocks__/strings";
import { isIssueClosedEvent, isIssueCommentEvent } from "./types/typeguards";
import { handleSlashCommand } from "./utils/slash-commands";

export async function runPlugin(context: Context) {
const { logger, eventName } = context;
Expand All @@ -19,86 +17,8 @@ export async function runPlugin(context: Context) {
} else if (isIssueClosedEvent(context)) {
return await gasSubsidize(context);
}
{
logger.info(`Ignoring event ${eventName}`);
}
}

function isCommentEvent(context: Context): context is Context<"issue_comment.created"> {
return context.eventName === "issue_comment.created";
}

function isIssueCommentEvent(context: Context): context is Context<"issue_comment.created"> {
return isCommentEvent(context) && context.payload.comment.body.startsWith("/");
}

function isIssueClosedEvent(context: Context): context is Context<"issues.closed"> {
return context.eventName === "issues.closed";
}

async function handleSlashCommand(context: Context) {
if (isIssueCommentEvent(context)) {
const {
payload: {
comment: { body },
},
} = context;
const [command, ...args] = body.split(" ");
const params = await parseArgs(
context,
args.filter((arg) => arg !== "")
);
switch (command) {
case "/register":
return register(context, params);
case "/faucet":
if (Object.keys(params).length < 2) {
await logAndComment(context, "error", STRINGS.INVALID_USE_OF_ARGS);
throwError(STRINGS.INVALID_USE_OF_ARGS);
}
return faucet(context, params);
default:
throwError("Unknown command", { command });
}
} else {
throwError("Unknown event type", { eventName: context.eventName });
}
}

export async function parseArgs(context: Context<"issue_comment.created">, args: string[]) {
if (args.length === 4) {
return {
recipient: args[0].toLowerCase(),
networkId: args[1],
amount: BigInt(args[2]),
token: args[3].toLowerCase(),
};
} else if (args.length === 3) {
return {
recipient: args[0].toLowerCase(),
networkId: args[1],
amount: BigInt(args[2]),
token: "native",
};
} else if (args.length === 2) {
return {
recipient: args[0].toLowerCase(),
networkId: args[1],
amount: BigInt(0),
token: "native",
};
} else if (args.length < 2) {
// only used in /register
return {
recipient: context.payload.comment.user?.login ?? context.payload.sender.login,
networkId: "1",
amount: BigInt(0),
token: "native",
};
} else {
await logAndComment(context, "error", STRINGS.INVALID_USE_OF_ARGS);
throwError(STRINGS.INVALID_USE_OF_ARGS);
}
logger.info(`Ignoring event ${eventName}`);
}

export async function plugin(inputs: PluginInputs, env: Env) {
Expand Down
17 changes: 8 additions & 9 deletions src/types/typeguards.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { Context } from "./context";

/**
* Typeguards are most helpful when you have a union type and you want to narrow it down to a specific one.
* In other words, if `SupportedEvents` has multiple types then these restrict the scope
* of `context` to a specific event payload.
*/
function isCommentEvent(context: Context): context is Context<"issue_comment.created"> {
return context.eventName === "issue_comment.created";
}

/**
* Restricts the scope of `context` to the `issue_comment.created` payload.
*/
export function isIssueCommentEvent(context: Context): context is Context<"issue_comment.created"> {
return context.eventName === "issue_comment.created";
return isCommentEvent(context) && context.payload.comment.body.startsWith("/");
}

export function isIssueClosedEvent(context: Context): context is Context<"issues.closed"> {
return context.eventName === "issues.closed";
}
71 changes: 71 additions & 0 deletions src/utils/slash-commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { STRINGS } from "../../tests/__mocks__/strings";
import { faucet } from "../handlers/faucet";
import { isIssueCommentEvent } from "../types/typeguards";
import { logAndComment, throwError } from "./logger";
import { Context } from "../types";
import { register } from "../handlers/register";

export async function handleSlashCommand(context: Context) {
if (isIssueCommentEvent(context)) {
const {
payload: {
comment: { body },
},
} = context;
const [command, ...args] = body.split(" ");
const params = await parseArgs(
context,
args.filter((arg) => arg !== "")
);
switch (command) {
case "/register":
return register(context, params);
case "/faucet":
if (Object.keys(params).length < 2) {
await logAndComment(context, "error", STRINGS.INVALID_USE_OF_ARGS);
throwError(STRINGS.INVALID_USE_OF_ARGS);
}
return faucet(context, params);
default:
throwError("Unknown command", { command });
}
} else {
throwError("Unknown event type", { eventName: context.eventName });
}
}

export async function parseArgs(context: Context<"issue_comment.created">, args: string[]) {
if (args.length === 4) {
return {
recipient: args[0].toLowerCase(),
networkId: args[1],
amount: BigInt(args[2]),
token: args[3].toLowerCase(),
};
} else if (args.length === 3) {
return {
recipient: args[0].toLowerCase(),
networkId: args[1],
amount: BigInt(args[2]),
token: "native",
};
} else if (args.length === 2) {
return {
recipient: args[0].toLowerCase(),
networkId: args[1],
amount: BigInt(0),
token: "native",
};
} else if (args.length < 2) {
// only used in /register
return {
recipient: context.payload.comment.user?.login.toLowerCase() ?? context.payload.sender.login.toLowerCase(),
networkId: "1",
amount: BigInt(0),
token: "native",
};
} else {
await logAndComment(context, "error", STRINGS.INVALID_USE_OF_ARGS);
throwError(STRINGS.INVALID_USE_OF_ARGS);
}
}

0 comments on commit 0509c64

Please sign in to comment.