Skip to content

Commit

Permalink
Merge pull request #739 from Giveth/f_1681_calculate_average_givback_…
Browse files Browse the repository at this point in the history
…factor_for_donations

Save average givback factor in donations
  • Loading branch information
mohammadranjbarz authored Nov 30, 2022
2 parents 0dcbf0a + 22612d9 commit a96c845
Show file tree
Hide file tree
Showing 13 changed files with 227 additions and 15 deletions.
3 changes: 3 additions & 0 deletions config/example.env
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,6 @@ FILL_BLOCK_NUMBERS_OF_SNAPSHOTS_CRONJOB_EXPRESSION=0 0 * * *
#Every 30 minutes
UPDATE_POWER_ROUND_CRONJOB_EXPRESSION=10 */30 * * * *
UPDATE_POWER_SNAPSHOT_SERVICE_ACTIVE=true

GIVBACK_MIN_FACTOR=0.5
GIVBACK_MAX_FACTOR=0.8
6 changes: 5 additions & 1 deletion config/test.env
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,8 @@ PROJECT_REVOKE_SERVICE_ACTIVE=false
UPDATE_POWER_ROUND_CRONJOB_EXPRESSION=0 0 * * *
UPDATE_POWER_SNAPSHOT_SERVICE_ACTIVE=false

ONRAMPER_SECRET=secreto
GIVBACK_MIN_FACTOR=0.5
GIVBACK_MAX_FACTOR=0.8

ONRAMPER_SECRET=secreto

2 changes: 2 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ const envVars = [
'GIVPOWER_BOOSTING_USER_PROJECTS_LIMIT',
'GIVPOWER_BOOSTING_PERCENTAGE_PRECISION',
'GIVPOWER_ROUND_DURATION',
'GIVBACK_MAX_FACTOR',
'GIVBACK_MIN_FACTOR',
];

// tslint:disable-next-line:class-name
Expand Down
16 changes: 16 additions & 0 deletions src/entities/donation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,22 @@ export class Donation extends BaseEntity {
@Column({ type: 'real', nullable: true })
priceUsd: number;

@Field({ nullable: true })
@Column({ type: 'real', nullable: true })
givbackFactor: number;

@Field({ nullable: true })
@Column({ nullable: true })
powerRound: number;

@Field({ nullable: true })
@Column({ type: 'real', nullable: true })
projectRank?: number;

@Field({ nullable: true })
@Column({ type: 'real', nullable: true })
bottomRankInRound?: number;

@Index()
@Field(type => Project)
@ManyToOne(type => Project, { eager: true })
Expand Down
60 changes: 54 additions & 6 deletions src/repositories/projectPowerViewRepository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ import {
} from './powerBoostingRepository';
import { setPowerRound } from './powerRoundRepository';
import {
getTopPowerRank,
getBottomRank,
getProjectPowers,
refreshProjectPowerView,
refreshProjectFuturePowerView,
getProjectFuturePowers,
findProjectPowerViewByProjectId,
} from './projectPowerViewRepository';
import { Project, ProjStatus } from '../entities/project';
import { getConnection } from 'typeorm';
Expand All @@ -34,12 +35,17 @@ describe(
projectPowerViewRepositoryTestCases,
);

describe(
'findProjectPowerViewByProjectId test',
findProjectPowerViewByProjectIdTestCases,
);

describe(
'projectFuturePowerViewRepository test',
projectFuturePowerViewRepositoryTestCases,
);

describe('getTopPowerRank test cases', getTopPowerRankTestCases);
describe('getBottomPowerRank test cases', getBottomPowerRankTestCases);

function projectPowerViewRepositoryTestCases() {
beforeEach(async () => {
Expand Down Expand Up @@ -243,6 +249,48 @@ function projectPowerViewRepositoryTestCases() {
});
}

function findProjectPowerViewByProjectIdTestCases() {
beforeEach(async () => {
await getConnection().query('truncate power_snapshot cascade');
await PowerBalanceSnapshot.clear();
await PowerBoostingSnapshot.clear();
});

it('Return project rank correctly', async () => {
const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress());
const project1 = await saveProjectDirectlyToDb(createProjectData());

const roundNumber = project1.id * 10;

await insertSinglePowerBoosting({
user,
project: project1,
percentage: 10,
});

await takePowerBoostingSnapshot();
const incompleteSnapshots = await findInCompletePowerSnapShots();
const snapshot = incompleteSnapshots[0];

snapshot.blockNumber = 1;
snapshot.roundNumber = roundNumber;
await snapshot.save();

await insertSinglePowerBalanceSnapshot({
userId: user.id,
powerSnapshotId: snapshot.id,
balance: 100,
});

await setPowerRound(roundNumber);
await refreshProjectPowerView();
const projectPower = await findProjectPowerViewByProjectId(project1.id);
assert.isOk(projectPower);
assert.equal(projectPower?.powerRank, 1);
assert.equal(projectPower?.totalPower, 10);
});
}

function projectFuturePowerViewRepositoryTestCases() {
beforeEach(async () => {
await getConnection().query('truncate power_snapshot cascade');
Expand Down Expand Up @@ -426,14 +474,14 @@ function projectFuturePowerViewRepositoryTestCases() {
});
}

function getTopPowerRankTestCases() {
function getBottomPowerRankTestCases() {
beforeEach(async () => {
await getConnection().query('truncate power_snapshot cascade');
await PowerBalanceSnapshot.clear();
await PowerBoostingSnapshot.clear();
});

it('Should return topPowerRank correctly', async () => {
it('Should return bottomPowerRank correctly', async () => {
const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress());
const project1 = await saveProjectDirectlyToDb(createProjectData());
const project2 = await saveProjectDirectlyToDb(createProjectData());
Expand Down Expand Up @@ -470,7 +518,7 @@ function getTopPowerRankTestCases() {
await setPowerRound(roundNumber);
await refreshProjectPowerView();

const topPowerRank = await getTopPowerRank();
assert.equal(topPowerRank, 3);
const bottomPowerRank = await getBottomRank();
assert.equal(bottomPowerRank, 3);
});
}
9 changes: 8 additions & 1 deletion src/repositories/projectPowerViewRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,21 @@ export const getProjectPowers = async (
): Promise<ProjectPowerView[]> => {
return ProjectPowerView.find({ take, skip });
};

export const findProjectPowerViewByProjectId = async (
projectId: number,
): Promise<ProjectPowerView | undefined> => {
return ProjectPowerView.findOne(projectId);
};

export const getProjectFuturePowers = async (
take: number = 50,
skip: number = 0,
): Promise<ProjectPowerView[]> => {
return ProjectFuturePowerView.find({ take, skip });
};

export const getTopPowerRank = async (): Promise<number> => {
export const getBottomRank = async (): Promise<number> => {
try {
const powerRank = await getConnection().manager.query(`
SELECT MAX("powerRank") FROM project_power_view
Expand Down
88 changes: 88 additions & 0 deletions src/resolvers/donationResolver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ import { User } from '../entities/user';
import { Organization, ORGANIZATION_LABELS } from '../entities/organization';
import { ProjStatus } from '../entities/project';
import { Token } from '../entities/token';
import {
insertSinglePowerBoosting,
takePowerBoostingSnapshot,
} from '../repositories/powerBoostingRepository';
import {
findInCompletePowerSnapShots,
insertSinglePowerBalanceSnapshot,
} from '../repositories/powerSnapshotRepository';
import { setPowerRound } from '../repositories/powerRoundRepository';
import { refreshProjectPowerView } from '../repositories/projectPowerViewRepository';
import { getConnection } from 'typeorm';
import { PowerBalanceSnapshot } from '../entities/powerBalanceSnapshot';
import { PowerBoostingSnapshot } from '../entities/powerBoostingSnapshot';

// tslint:disable-next-line:no-var-requires
const moment = require('moment');
Expand Down Expand Up @@ -444,6 +457,81 @@ function createDonationTestCases() {
);
assert.isTrue(donation?.isTokenEligibleForGivback);
});
it('should create GIV donation and fill averageGivbackFactor', async () => {
const project = await saveProjectDirectlyToDb(createProjectData());
const project2 = await saveProjectDirectlyToDb(createProjectData());
const user = await User.create({
walletAddress: generateRandomEtheriumAddress(),
loginType: 'wallet',
firstName: 'first name',
}).save();

// Clear previous snapshots
await getConnection().query('truncate power_snapshot cascade');
await PowerBalanceSnapshot.clear();
await PowerBoostingSnapshot.clear();

// Fill ranking and power snapshot
const roundNumber = project.id * 10;
await insertSinglePowerBoosting({
user,
project,
percentage: 80,
});
await insertSinglePowerBoosting({
user,
project: project2,
percentage: 20,
});

await takePowerBoostingSnapshot();
const incompleteSnapshots = await findInCompletePowerSnapShots();
const snapshot = incompleteSnapshots[0];

snapshot.blockNumber = 1;
snapshot.roundNumber = roundNumber;
await snapshot.save();
await insertSinglePowerBalanceSnapshot({
userId: user.id,
powerSnapshotId: snapshot.id,
balance: 100,
});
await setPowerRound(roundNumber);
await refreshProjectPowerView();

const accessToken = await generateTestAccessToken(user.id);
const saveDonationResponse = await axios.post(
graphqlUrl,
{
query: createDonationMutation,
variables: {
projectId: project.id,
transactionNetworkId: NETWORK_IDS.XDAI,
transactionId: generateRandomTxHash(),
nonce: 1,
amount: 10,
token: 'GIV',
},
},
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
);
assert.isOk(saveDonationResponse.data.data.createDonation);
const donation = await Donation.findOne(
saveDonationResponse.data.data.createDonation,
);

// because this project is rank1
assert.equal(
donation?.givbackFactor,
Number(process.env.GIVBACK_MAX_FACTOR),
);
assert.equal(donation?.powerRound, roundNumber);
assert.equal(donation?.projectRank, 1);
});
it('should create GIV donation for giveth project on mainnet successfully', async () => {
const project = await saveProjectDirectlyToDb(createProjectData());
const user = await User.create({
Expand Down
1 change: 1 addition & 0 deletions src/resolvers/donationResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import { MainCategory } from '../entities/mainCategory';
import { SegmentAnalyticsSingleton } from '../services/segment/segmentAnalyticsSingleton';
import { getNotificationAdapter } from '../adapters/adaptersFactory';
import { findProjectById } from '../repositories/projectRepository';
import { calculateGivbackFactor } from '../services/givbackService';

@ObjectType()
class PaginateDonations {
Expand Down
8 changes: 4 additions & 4 deletions src/resolvers/powerBoostingResolver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from '../../test/testUtils';
import axios, { AxiosResponse } from 'axios';
import {
getTopPowerRankQuery,
getBottomPowerRankQuery,
getPowerBoostingsQuery,
setMultiplePowerBoostingMutation,
setSinglePowerBoostingMutation,
Expand Down Expand Up @@ -41,7 +41,7 @@ describe(
setMultiplePowerBoostingTestCases,
);
describe('getPowerBoosting test cases', getPowerBoostingTestCases);
describe('getTopPowerRank test cases', getTopPowerRankTestCases);
describe('getBottomPowerRank test cases', getBottomPowerRankTestCases);

// Clean percentages after setting
const removePowerBoostings = async (boosts: PowerBoosting[]): Promise<void> => {
Expand Down Expand Up @@ -1086,7 +1086,7 @@ function getPowerBoostingTestCases() {
assert.isTrue(powerBoostings[1].percentage <= powerBoostings[2].percentage);
});
}
async function getTopPowerRankTestCases() {
async function getBottomPowerRankTestCases() {
beforeEach(async () => {
await getConnection().query('truncate power_snapshot cascade');
await PowerBalanceSnapshot.clear();
Expand Down Expand Up @@ -1144,7 +1144,7 @@ async function getTopPowerRankTestCases() {
await refreshProjectPowerView();

const result = await axios.post(graphqlUrl, {
query: getTopPowerRankQuery,
query: getBottomPowerRankQuery,
});
assert.isOk(result);
assert.equal(result.data.data.getTopPowerRank, 4);
Expand Down
4 changes: 2 additions & 2 deletions src/resolvers/powerBoostingResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { Max, Min } from 'class-validator';
import { Service } from 'typedi';
import { OrderField, SortingField } from '../entities/project';
import { logger } from '../utils/logger';
import { getTopPowerRank } from '../repositories/projectPowerViewRepository';
import { getBottomRank } from '../repositories/projectPowerViewRepository';

enum PowerBoostingOrderDirection {
ASC = 'ASC',
Expand Down Expand Up @@ -168,6 +168,6 @@ export class PowerBoostingResolver {

@Query(returns => Number)
async getTopPowerRank(): Promise<Number> {
return getTopPowerRank();
return getBottomRank();
}
}
8 changes: 8 additions & 0 deletions src/services/donationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { findDonationById } from '../repositories/donationRepository';
import { getNotificationAdapter } from '../adapters/adaptersFactory';
import { getTokenPrices } from 'monoswap';
import SentryLogger from '../sentryLogger';
import { calculateGivbackFactor } from './givbackService';

export const TRANSAK_COMPLETED_STATUS = 'COMPLETED';

Expand Down Expand Up @@ -69,6 +70,13 @@ export const updateDonationPricesAndValues = async (
},
);
}
const { givbackFactor, projectRank, bottomRankInRound, powerRound } =
await calculateGivbackFactor(project.id);
donation.givbackFactor = givbackFactor;
donation.projectRank = projectRank;
donation.bottomRankInRound = bottomRankInRound;
donation.powerRound = powerRound;

return await donation.save();
};

Expand Down
Loading

0 comments on commit a96c845

Please sign in to comment.