Skip to content

Commit

Permalink
Merge pull request #224 from layerx-labs/dev
Browse files Browse the repository at this point in the history
BEPRO 2.28
  • Loading branch information
vhcsilva authored Jul 16, 2024
2 parents edefede + 5cb8bce commit 4dddd4e
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 22 deletions.
65 changes: 54 additions & 11 deletions src/actions/get-bounty-closed-event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,64 @@ import {Push} from "../services/analytics/push";
import {AnalyticEventName} from "../services/analytics/types/events";
import updateSeoCardBounty from "src/modules/handle-seo-card";
import { savePointEvent } from "../modules/points-system/save-point-event";
import calculateDistributedAmounts from "src/modules/calculate-distributed-amounts";
import { Proposal } from "@taikai/dappkit";
import { networksAttributes } from "src/db/models/networks";

export const name = "getBountyClosedEvents";
export const schedule = "*/12 * * * *";
export const description = "Move to 'Closed' status the bounty";
export const author = "clarkjoao";

async function updateUserPayments(proposal, transactionHash, issueId, tokenAmount) {
return Promise.all(
proposal.details.map(async (detail) => {
async function updateUserPayments(proposal: Proposal,
transactionHash: string,
issueId: number,
tokenAmount: string | number,
chainId: number,
network: networksAttributes,
merger: string) {
const chain = await db.chains.findOne({
where: {
chainId: chainId,
}
});

if (!chain?.closeFeePercentage || !network.mergeCreatorFeeShare || !network.proposerFeeShare) {
logger.warn(`Not possible to save payments for ${issueId} on ${chainId} as it's missing closeFeePercentage, mergeCreatorFeeShare or proposerFeeShare`);
return;
}

const distributedAmounts = calculateDistributedAmounts( chain.closeFeePercentage,
network.mergeCreatorFeeShare,
network.proposerFeeShare,
tokenAmount,
proposal.details);
return Promise.all([
db.users_payments.create({
address: merger,
ammount: +distributedAmounts.mergerAmount.value || 0,
issueId,
transactionHash,
}),
db.users_payments.create({
address: proposal.creator,
ammount: +distributedAmounts.proposerAmount.value || 0,
issueId,
transactionHash,
}),
...distributedAmounts.proposals.map(async (distributed) => {
const payment = {
address: detail?.["recipient"],
ammount:
Number((detail?.["percentage"] / 100) * +tokenAmount) || 0,
address: distributed.recipient,
ammount: +distributed.value || 0,
issueId,
transactionHash,
}
return db.users_payments.findOrCreate({
where: payment,
defaults: payment
})
}))
})
]);
}

async function updateCuratorProposal(address: string, networkId: number) {
Expand All @@ -53,7 +90,7 @@ export async function action(block: DecodedLog, query?: EventsQuery): Promise<Ev

const network = await getNetwork(chainId, address);
if (!network) {
logger.warn(NETWORK_NOT_FOUND(name, address))
logger.warn(NETWORK_NOT_FOUND(name, address));
return eventsProcessed;
}

Expand All @@ -66,12 +103,12 @@ export async function action(block: DecodedLog, query?: EventsQuery): Promise<Ev
});

if (!dbBounty) {
logger.warn(DB_BOUNTY_NOT_FOUND(name, bounty.cid, network.id))
logger.warn(DB_BOUNTY_NOT_FOUND(name, bounty.cid, network.id));
return eventsProcessed;
}

if (dbBounty.state === "closed") {
logger.warn(DB_BOUNTY_ALREADY_CLOSED(name, bounty.cid, network.id))
logger.warn(DB_BOUNTY_ALREADY_CLOSED(name, bounty.cid, network.id));
return eventsProcessed;
}

Expand Down Expand Up @@ -108,7 +145,13 @@ export async function action(block: DecodedLog, query?: EventsQuery): Promise<Ev

sendMessageToTelegramChannels(BOUNTY_CLOSED(dbBounty, dbProposal, proposalId));

await updateUserPayments(bounty.proposals[+proposalId], block.transactionHash, dbBounty.id, bounty.tokenAmount);
await updateUserPayments( bounty.proposals[+proposalId],
block.transactionHash,
dbBounty.id,
bounty.tokenAmount,
chainId,
network,
transaction.from);
await updateCuratorProposal(bounty.proposals[+proposalId].creator, network?.id)
updateLeaderboardNfts()
updateLeaderboardBounties("closed");
Expand Down
10 changes: 10 additions & 0 deletions src/actions/get-bounty-funded-updated-event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {BOUNTY_FUNDED} from "../integrations/telegram/messages";
import {Push} from "../services/analytics/push";
import {AnalyticEventName} from "../services/analytics/types/events";
import updateSeoCardBounty from "src/modules/handle-seo-card";
import { handleFundedFundingPoints } from "src/modules/points-system/handle-funded-funding-points";
import { Network_v2 } from "@taikai/dappkit";

export const name = "getBountyFundedEvents";
export const schedule = "*/14 * * * *";
Expand Down Expand Up @@ -56,6 +58,14 @@ export async function action(block: DecodedLog<BountyFunded['returnValues']>, qu

updateSeoCardBounty(dbBounty.id, name);

const _network = new Network_v2(connection, address);
await _network.start();

await handleFundedFundingPoints({
bounty: await _network.getBounty(dbBounty.contractId!),
issue: dbBounty
});

eventsProcessed[network.name!] = {
[dbBounty.id!.toString()]: {bounty: dbBounty, eventBlock: parseLogWithContext(block)}
};
Expand Down
26 changes: 21 additions & 5 deletions src/actions/get-bounty-moved-to-open.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import updateSeoCardBounty from "src/modules/handle-seo-card";
import { getCoinPrice } from "../services/coingecko";
import { savePointEvent } from "../modules/points-system/save-point-event";
import { getOrUpdateLastTokenPrice } from "../modules/tokens";
import { handleFundedFundingPoints } from "src/modules/points-system/handle-funded-funding-points";

export const name = "get-bounty-moved-to-open";
export const schedule = "*/1 * * * *";
Expand Down Expand Up @@ -84,13 +85,28 @@ export async function action(query?: EventsQuery): Promise<EventsProcessed> {
sendMessageToTelegramChannels(BOUNTY_STATE_CHANGED(dbBounty.state!, dbBounty));

updateSeoCardBounty(dbBounty.id, name);

const tokenPrice = await getOrUpdateLastTokenPrice(dbBounty.transactionalTokenId!, currency);

await savePointEvent( "created_task",
dbBounty.user.address!,
{ taskId: dbBounty.id, taskAmount: dbBounty.amount, tokenPrice, currency },
(pointsPerAction, scalingFactor) => pointsPerAction * scalingFactor * +dbBounty.amount! * tokenPrice);
if (!dbBounty.fundingAmount) {

await savePointEvent( "created_task",
dbBounty.user.address!,
{ taskId: dbBounty.id, taskAmount: dbBounty.amount, tokenPrice, currency },
(pointsPerAction, scalingFactor) => pointsPerAction * scalingFactor * +dbBounty.amount! * tokenPrice);
} else {
if (!!dbBounty.rewardAmount && +dbBounty.rewardAmount > 0) {
await savePointEvent( "give_funding_reward",
dbBounty.user.address!,
{ taskId: dbBounty.id, rewardAmount: dbBounty.rewardAmount, tokenPrice, currency },
(pointsPerAction, scalingFactor) => pointsPerAction * scalingFactor * +dbBounty.rewardAmount! * tokenPrice);
}

await handleFundedFundingPoints({
bounty: await _network.getBounty(dbBounty.contractId!),
issue: dbBounty
});
}

eventsProcessed[networkName!] = {
...eventsProcessed[networkName!],
Expand Down
1 change: 1 addition & 0 deletions src/assets/templates/user-profile.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
border: 3px solid #4250e4;
border-radius: 50%;
height: 250px;
width: 250px;
}
header h1, h3 {
Expand Down
10 changes: 5 additions & 5 deletions src/db/models/users_payments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { issues, issuesId } from './issues';
export interface users_paymentsAttributes {
id: number;
address: string;
ammount: number;
ammount?: number;
issueId: number;
transactionHash?: string;
createdAt: Date;
Expand All @@ -14,13 +14,13 @@ export interface users_paymentsAttributes {

export type users_paymentsPk = "id";
export type users_paymentsId = users_payments[users_paymentsPk];
export type users_paymentsOptionalAttributes = "id" | "transactionHash" | "createdAt" | "updatedAt";
export type users_paymentsOptionalAttributes = "id" | "ammount" | "transactionHash" | "createdAt" | "updatedAt";
export type users_paymentsCreationAttributes = Optional<users_paymentsAttributes, users_paymentsOptionalAttributes>;

export class users_payments extends Model<users_paymentsAttributes, users_paymentsCreationAttributes> implements users_paymentsAttributes {
id!: number;
address!: string;
ammount!: number;
ammount?: number;
issueId!: number;
transactionHash?: string;
createdAt!: Date;
Expand All @@ -45,8 +45,8 @@ export class users_payments extends Model<users_paymentsAttributes, users_paymen
allowNull: false
},
ammount: {
type: DataTypes.INTEGER,
allowNull: false
type: DataTypes.DOUBLE,
allowNull: true
},
issueId: {
type: DataTypes.INTEGER,
Expand Down
99 changes: 99 additions & 0 deletions src/modules/points-system/handle-funded-funding-points.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { Bounty } from "@taikai/dappkit";
import { Op, Sequelize } from "sequelize";

import models from "src/db";
import { issuesAttributes } from "src/db/models/issues";
import { usersAttributes } from "src/db/models/users";

import { savePointEvent } from "src/modules/points-system/save-point-event";
import { removePointEntry } from "src/modules/points-system/remove-point-event";
import { getOrUpdateLastTokenPrice } from "src/modules/tokens";

import logger from "src/utils/logger-handler";

interface HandleFundedFundingPointsProps {
bounty: Bounty;
issue: issuesAttributes;
}

type FundingEventInfo = {
taskId: number,
benefactorId: number,
amount: string;
tokenPrice: number;
}

const {
NEXT_PUBLIC_CURRENCY_MAIN: currency = "eur",
} = process.env;

export async function handleFundedFundingPoints({
bounty,
issue,
}: HandleFundedFundingPointsProps) {
try {
const pointsEventsOfIssue = await models.points_events.findAll({
where: {
actionName: "funded_funding_request",
info: {
taskId: {
[Op.eq]: issue.id
}
}
}
});

const users: usersAttributes[] = [];
const findUser = (address: string) => users.find(user => user?.address?.toLowerCase() === address.toLowerCase());

const benefactors = bounty.funding.map((funding, id) => ({ id, ...funding }));
for (const benefactor of benefactors) {
let user = findUser(benefactor.benefactor);

if (!user) {
const found = await models.users.findOne({
where: Sequelize.where( Sequelize.fn("LOWER", Sequelize.col("address")),
"=",
benefactor.benefactor.toLowerCase())
});

if (!found)
continue;

user = found;
users.push(found);
}

const hasPointEvent = pointsEventsOfIssue
.find(({ userId, info }) => (info as FundingEventInfo)?.benefactorId === benefactor.id && userId === user?.id);

if (!hasPointEvent && +benefactor.amount > 0) {
const tokenPrice = await getOrUpdateLastTokenPrice(issue.transactionalTokenId!, currency);
await savePointEvent("funded_funding_request", user.address!, {
taskId: issue.id,
benefactorId: benefactor.id,
amount: benefactor.amount,
tokenPrice,
currency
}, (pointsPerAction, scalingFactor) => pointsPerAction * scalingFactor * +benefactor.amount * tokenPrice);

logger.info(`handleFundedFundingPoints: point saved`, {
taskId: issue.id,
benefactorId: benefactor.id,
amount: benefactor.amount,
tokenPrice,
currency
});
} else if (hasPointEvent && +benefactor.amount === 0)
await removePointEntry(hasPointEvent.id);
logger.info(`handleFundedFundingPoints: point removed because fund was retracted`, {
taskId: issue.id,
benefactorId: benefactor.id,
amount: benefactor.amount,
currency,
});
}
} catch(error) {
logger.error(`handleFundedFundingPoints: failed to handle funded funding points`, { error, bounty, issue });
}
}
36 changes: 36 additions & 0 deletions src/modules/points-system/remove-point-event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import models from "src/db";
import logger from "src/utils/logger-handler";

export async function removePointEntry(pointEntryId: number) {
const pointEntry = await models.points_events.findOne({
where: {
id: pointEntryId
}
});

if (!pointEntry) {
logger.warn(`removePointEntry: Entry with id ${pointEntryId} not found on points_events`);
return;
}

if (pointEntry.pointsCounted) {
const user = await models.users.findOne({
where: {
id: pointEntry.userId
}
});

if (!user) {
logger.warn(`removePointEntry: User with id ${pointEntry.userId} not found`);
return;
}

user.totalPoints = user.totalPoints! - pointEntry.pointsWon;

await user.save();
}

await pointEntry.destroy();

logger.info(`PointsEvents ${pointEntryId} removed ${pointEntry.pointsCounted ? "and user totalPoints updated" : ""}`);
}
2 changes: 1 addition & 1 deletion src/modules/points-system/save-point-event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import db from "src/db";
import logger from "src/utils/logger-handler";

type PointsEvents = "locked" | "delegated" | "created_marketplace" | "created_task" | "created_deliverable" |
"created_proposal" | "accepted_proposal";
"created_proposal" | "accepted_proposal" | "funded_funding_request" | "give_funding_reward";

export async function savePointEvent( event: PointsEvents,
participantAddress: string,
Expand Down

0 comments on commit 4dddd4e

Please sign in to comment.