Skip to content

Commit

Permalink
Merge pull request #1124 from Giveth/feature_add_network_elegibility_…
Browse files Browse the repository at this point in the history
…to_qf

Feature: Add eligible donations to qfround entity
  • Loading branch information
CarlosQ96 authored Sep 14, 2023
2 parents e89a515 + ca31852 commit c4f8012
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 2 deletions.
16 changes: 16 additions & 0 deletions migration/1694295208252-AddEligibleNetworksToQfRoundEntity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { MigrationInterface, QueryRunner, TableColumn } from 'typeorm';

export class AddEligibleNetworksToQfRoundEntity1694295208252
implements MigrationInterface
{
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE "qf_round"
ADD COLUMN IF NOT EXIST "eligibleNetworks" integer array DEFAULT ARRAY[]::integer[]
`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropColumn('qf_round', 'eligibleNetworks');
}
}
32 changes: 32 additions & 0 deletions migration/1694635872128-AddEligibleNetworksToPreviousQfRounds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
import config from '../src/config';

export class AddEligibleNetworksToPreviousQfRounds1694635872128
implements MigrationInterface
{
public async up(queryRunner: QueryRunner): Promise<void> {
const environment = config.get('ENVIRONMENT') as string;

// Define the eligible network IDs based on the conditions
const eligibleNetworks =
environment !== 'production'
? [1, 3, 5, 100, 137, 10, 420, 56, 42220, 44787] // Include testnets for staging
: [1, 137, 56, 42220, 100, 10]; // Exclude testnets for non-staging

// Convert the eligibleNetworks array to a comma-separated string
const eligibleNetworksString = eligibleNetworks.join(', ');

// Update the "qf_round" table with the new eligibleNetworks values
await queryRunner.query(`
UPDATE "qf_round"
SET eligibleNetworks = '{${eligibleNetworksString}}'
`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
UPDATE "qf_round"
SET eligibleNetworks = '{}'
`);
}
}
14 changes: 13 additions & 1 deletion src/entities/qfRound.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Field, ID, ObjectType } from 'type-graphql';
import { Field, ID, ObjectType, Int } from 'type-graphql';
import {
PrimaryGeneratedColumn,
Column,
Expand Down Expand Up @@ -36,6 +36,10 @@ export class QfRound extends BaseEntity {
@Column()
minimumPassportScore: number;

@Field(type => [Int], { nullable: true }) // Define the new field as an array of integers
@Column('integer', { array: true, default: [] })
eligibleNetworks: number[];

@Field(type => Date)
@Column()
beginDate: Date;
Expand All @@ -52,4 +56,12 @@ export class QfRound extends BaseEntity {

@ManyToMany(type => Project, project => project.qfRounds)
projects: Project[];

// only projects with status active can be listed automatically
isEligibleNetwork(donationNetworkId: number): Boolean {
// when not specified, all are valid
if (this.eligibleNetworks.length === 0) return true;

return this.eligibleNetworks.includes(donationNetworkId);
}
}
96 changes: 96 additions & 0 deletions src/resolvers/donationResolver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,102 @@ function createDonationTestCases() {
qfRound.isActive = false;
await qfRound.save();
});

it('should create a donation in an active qfRound when qfround has network eligiblity on XDAI', async () => {
const project = await saveProjectDirectlyToDb(createProjectData());
const qfRound = await QfRound.create({
isActive: true,
name: new Date().toString(),
minimumPassportScore: 8,
allocatedFund: 100,
eligibleNetworks: [100], // accepts ONLY xdai to mark as part of QFround
beginDate: new Date(),
endDate: moment().add(2, 'day'),
}).save();
project.qfRounds = [qfRound];
await project.save();
const referrerId = generateRandomString();
const referrerWalletAddress =
await getChainvineAdapter().getWalletAddressFromReferrer(referrerId);

const user = await User.create({
walletAddress: generateRandomEtheriumAddress(),
loginType: 'wallet',
firstName: 'first name',
}).save();

const user2 = await User.create({
walletAddress: referrerWalletAddress,
loginType: 'wallet',
firstName: 'first name',
}).save();

const referredEvent = await firstOrCreateReferredEventByUserId(user.id);
referredEvent.startTime = new Date();
await referredEvent.save();

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

assert.equal(donation?.qfRound?.id as number, qfRound.id);

// should ignore non xdai donations because its not an eligible network
const saveDonationResponseNotXdai = await axios.post(
graphqlUrl,
{
query: createDonationMutation,
variables: {
projectId: project.id,
transactionNetworkId: NETWORK_IDS.CELO,
transactionId: generateRandomTxHash(),
nonce: 1,
amount: 10,
token: 'GIV',
referrerId,
},
},
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
);
assert.isOk(saveDonationResponseNotXdai.data.data.createDonation);
const donationNotFromQF = await Donation.findOne({
where: {
id: saveDonationResponseNotXdai.data.data.createDonation,
},
});
assert.isNull(donationNotFromQF?.qfRound);
qfRound.isActive = false;
await qfRound.save();
});
it('should create a donation in an active qfRound, when project is not listed', async () => {
const project = await saveProjectDirectlyToDb(createProjectData());
const qfRound = await QfRound.create({
Expand Down
5 changes: 4 additions & 1 deletion src/resolvers/donationResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -727,7 +727,10 @@ export class DonationResolver {
const activeQfRoundForProject = await relatedActiveQfRoundForProject(
projectId,
);
if (activeQfRoundForProject) {
if (
activeQfRoundForProject &&
activeQfRoundForProject.isEligibleNetwork(Number(transactionNetworkId))
) {
donation.qfRound = activeQfRoundForProject;
}
await donation.save();
Expand Down
20 changes: 20 additions & 0 deletions src/server/adminJs/tabs/qfRoundTab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
getRelatedProjectsOfQfRound,
} from '../../../repositories/qfRoundRepository';
import { RecordJSON } from 'adminjs/src/frontend/interfaces/record-json.interface';
import { NETWORK_IDS } from '../../../provider';

export const refreshMaterializedViews = async (
response,
Expand Down Expand Up @@ -70,6 +71,25 @@ export const qfRoundTab = {
minimumPassportScore: {
isVisible: true,
},
eligibleNetworks: {
isVisible: true,
type: 'array',
availableValues: [
{ value: NETWORK_IDS.MAIN_NET, label: 'MAINNET' },
{ value: NETWORK_IDS.ROPSTEN, label: 'ROPSTEN' },
{ value: NETWORK_IDS.GOERLI, label: 'GOERLI' },
{ value: NETWORK_IDS.POLYGON, label: 'POLYGON' },
{ value: NETWORK_IDS.OPTIMISTIC, label: 'OPTIMISTIC' },
{ value: NETWORK_IDS.OPTIMISM_GOERLI, label: 'OPTIMISM GOERLI' },
{ value: NETWORK_IDS.CELO, label: 'CELO' },
{
value: NETWORK_IDS.CELO_ALFAJORES,
label: 'ALFAJORES (Test CELO)',
},
{ value: NETWORK_IDS.XDAI, label: 'XDAI' },
{ value: NETWORK_IDS.BSC, label: 'BSC' },
],
},
projects: {
type: 'mixed',
isVisible: {
Expand Down

0 comments on commit c4f8012

Please sign in to comment.