Skip to content

Commit

Permalink
Merge branch 'master' into upgrade-dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
aminlatifi authored Feb 9, 2024
2 parents e1aff2f + f76d8cd commit e83d2b6
Show file tree
Hide file tree
Showing 17 changed files with 275 additions and 11 deletions.
1 change: 1 addition & 0 deletions .github/workflows/develop-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ jobs:
SOLANA_TEST_NODE_RPC_URL: ${{ secrets.SOLANA_TEST_NODE_RPC_URL }}
SOLANA_DEVNET_NODE_RPC_URL: ${{ secrets.SOLANA_DEVNET_NODE_RPC_URL }}
SOLANA_MAINNET_NODE_RPC_URL: ${{ secrets.SOLANA_MAINNET_NODE_RPC_URL }}
MPETH_GRAPHQL_PRICES_URL: ${{ secrets.MPETH_GRAPHQL_PRICES_URL }}

publish:
needs: test
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/master-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ jobs:
SOLANA_TEST_NODE_RPC_URL: ${{ secrets.SOLANA_TEST_NODE_RPC_URL }}
SOLANA_DEVNET_NODE_RPC_URL: ${{ secrets.SOLANA_DEVNET_NODE_RPC_URL }}
SOLANA_MAINNET_NODE_RPC_URL: ${{ secrets.SOLANA_MAINNET_NODE_RPC_URL }}
MPETH_GRAPHQL_PRICES_URL: ${{ secrets.MPETH_GRAPHQL_PRICES_URL }}

publish:
needs: test
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/run-tests-on-pr.yml.bck
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,4 @@ jobs:
OPTIMISTIC_SCAN_API_KEY: ${{ secrets.OPTIMISTIC_SCAN_API_KEY }}
CELO_SCAN_API_KEY: ${{ secrets.CELO_SCAN_API_KEY }}
CELO_ALFAJORES_SCAN_API_KEY: ${{ secrets.CELO_ALFAJORES_SCAN_API_KEY }}
MPETH_GRAPHQL_PRICES_URL: ${{ secrets.MPETH_GRAPHQL_PRICES_URL }}
1 change: 1 addition & 0 deletions .github/workflows/staging-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ jobs:
SOLANA_TEST_NODE_RPC_URL: ${{ secrets.SOLANA_TEST_NODE_RPC_URL }}
SOLANA_DEVNET_NODE_RPC_URL: ${{ secrets.SOLANA_DEVNET_NODE_RPC_URL }}
SOLANA_MAINNET_NODE_RPC_URL: ${{ secrets.SOLANA_MAINNET_NODE_RPC_URL }}
MPETH_GRAPHQL_PRICES_URL: ${{ secrets.MPETH_GRAPHQL_PRICES_URL }}

publish:
needs: test
Expand Down
10 changes: 10 additions & 0 deletions config/example.env
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,13 @@ DONATION_SAVE_BACKUP_DATABASE=

# Default value is saveBackup
DONATION_SAVE_BACKUP_ADAPTER=saveBackup

ENABLE_UPDATE_RECURRING_DONATION_STREAM=true

# Default value is 1
NUMBER_OF_UPDATE_RECURRING_DONATION_CONCURRENT_JOB=1

# Default value is 0 0 * * * that means one day at 00:00
UPDATE_RECURRING_DONATIONS_STREAM=0 0 * * *

MPETH_GRAPHQL_PRICES_URL=
87 changes: 87 additions & 0 deletions migration/1706820821887-addmpEthToDatabaseTokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
import { NETWORK_IDS } from '../src/provider';
import { ChainType } from '../src/types/network';
import { Token } from '../src/entities/token';

const mpEthTokens = [
{
name: 'mpETH',
symbol: 'mpETH',
address: '0x48afbbd342f64ef8a9ab1c143719b63c2ad81710',
decimals: 18,
isGivbackEligible: true,
networkId: NETWORK_IDS.MAIN_NET,
chainType: ChainType.EVM,
},
{
name: 'mpETH',
symbol: 'mpETH',
address: '0x819845b60a192167ed1139040b4f8eca31834f27',
decimals: 18,
isGivbackEligible: true,
networkId: NETWORK_IDS.OPTIMISTIC,
chainType: ChainType.EVM,
},
];

export class addmpEthToDatabaseTokens1706820821887
implements MigrationInterface
{
public async up(queryRunner: QueryRunner): Promise<void> {
const exists = await queryRunner.query(`
SELECT * FROM token
WHERE
("address" = '0x48afbbd342f64ef8a9ab1c143719b63c2ad81710' AND "networkId" = ${NETWORK_IDS.MAIN_NET}) OR
("address" = '0x819845b60a192167ed1139040b4f8eca31834f27' AND "networkId" = ${NETWORK_IDS.OPTIMISTIC})
`);

if (exists && exists.length === 2) {
return;
}

await queryRunner.manager.save(Token, mpEthTokens);

const tokens = await queryRunner.query(`
SELECT * FROM token
WHERE
("address" = '0x48afbbd342f64ef8a9ab1c143719b63c2ad81710' AND "networkId" = ${NETWORK_IDS.MAIN_NET}) OR
("address" = '0x819845b60a192167ed1139040b4f8eca31834f27' AND "networkId" = ${NETWORK_IDS.OPTIMISTIC})
`);
const givethOrganization = (
await queryRunner.query(`SELECT * FROM organization
WHERE label='giveth'`)
)[0];

const traceOrganization = (
await queryRunner.query(`SELECT * FROM organization
WHERE label='trace'`)
)[0];

for (const token of tokens) {
await queryRunner.query(`INSERT INTO organization_tokens_token ("tokenId","organizationId") VALUES
(${token.id}, ${givethOrganization.id}),
(${token.id}, ${traceOrganization.id})
;`);
}
}

public async down(queryRunner: QueryRunner): Promise<void> {
const tokens = await queryRunner.query(`
SELECT * FROM token
WHERE
("address" = '0x48afbbd342f64ef8a9ab1c143719b63c2ad81710' AND "networkId" = ${NETWORK_IDS.MAIN_NET}) OR
("address" = '0x819845b60a192167ed1139040b4f8eca31834f27' AND "networkId" = ${NETWORK_IDS.OPTIMISTIC})
`);
await queryRunner.query(
`DELETE FROM organization_tokens_token WHERE "tokenId" IN (${tokens
.map(token => token.id)
.join(',')})`,
);

await queryRunner.query(
`DELETE FROM token WHERE "id" IN (${tokens
.map(token => token.id)
.join(',')})`,
);
}
}
2 changes: 1 addition & 1 deletion src/entities/projectVerificationForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export class FormRelatedAddress {
address: string;
@Field({ nullable: true })
networkId: number;
@Field(type => ChainType, { defaultValue: ChainType.EVM })
@Field(type => ChainType, { defaultValue: ChainType.EVM, nullable: true })
chainType?: ChainType;
}

Expand Down
12 changes: 8 additions & 4 deletions src/resolvers/projectVerificationFormResolver.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { Arg, Ctx, Mutation, Query, Resolver } from 'type-graphql';
import { ApolloContext } from '../types/ApolloContext';
import { i18n, translationErrorMessagesKeys } from '../utils/errorMessages';
import {
errorMessages,
i18n,
translationErrorMessagesKeys,
} from '../utils/errorMessages';
import {
createProjectVerificationRequestValidator,
getCurrentProjectVerificationRequestValidator,
Expand Down Expand Up @@ -324,10 +328,10 @@ export class ProjectVerificationFormResolver {

const verificationForm = await getVerificationFormByProjectId(project.id);
if (!verificationForm) {
// Because frontend use hard coded english error message, we dont translate this error message
// otherwise we need to handle all translation in frontend as well https://github.com/Giveth/giveth-dapps-v2/issues/3582#issuecomment-1913614715
throw new Error(
i18n.__(
translationErrorMessagesKeys.THERE_IS_NOT_ANY_ONGOING_PROJECT_VERIFICATION_FORM_FOR_THIS_PROJECT,
),
errorMessages.THERE_IS_NOT_ANY_ONGOING_PROJECT_VERIFICATION_FORM_FOR_THIS_PROJECT,
);
}
return verificationForm;
Expand Down
23 changes: 21 additions & 2 deletions src/services/chains/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -934,7 +934,7 @@ function getTransactionDetailTestCases() {
);
});

it('should return transaction detail for spl-token transfer on Solana #1', async () => {
it('should return transaction detail for spl-token transfer on Solana devnet #1', async () => {
// https://solscan.io/tx/2tm14GVsDwXpMzxZzpEWyQnfzcUEv1DZQVQb6VdbsHcV8StoMbBtuQTkW1LJ8RhKKrAL18gbm181NgzuusiQfZ16?cluster=devnet
const amount = 7;
const transactionInfo = await getTransactionInfoFromNetwork({
Expand All @@ -953,7 +953,7 @@ function getTransactionDetailTestCases() {
assert.equal(transactionInfo.amount, amount);
});

it('should return transaction detail for spl-token transfer on Solana #2', async () => {
it('should return transaction detail for spl-token transfer on Solana devnet #2', async () => {
// https://solscan.io/tx/3m6f1g2YK6jtbfVfuYsfDbhVzNAqozF8JJyjp1VuFDduecojqeCVK4htKnLTSk3qBwSqYUvgLpBTVpeLJRvNmeTg?cluster=devnet
const amount = 0.00000005;
const transactionInfo = await getTransactionInfoFromNetwork({
Expand All @@ -972,6 +972,25 @@ function getTransactionDetailTestCases() {
assert.equal(transactionInfo.amount, amount);
});

it('should return transaction detail for RAY spl token transfer on Solana mainnet', async () => {
// https://solscan.io/tx/4ApdD7usYH5Cp7hsaWGKjnJW3mfyNpRw4S4NJbzwa2CQfnUkjY11sR2G1W3rvXmCzXwu3yNLz2CfkCHY5sQPdWzq
const amount = 0.005;
const transactionInfo = await getTransactionInfoFromNetwork({
txHash:
'4ApdD7usYH5Cp7hsaWGKjnJW3mfyNpRw4S4NJbzwa2CQfnUkjY11sR2G1W3rvXmCzXwu3yNLz2CfkCHY5sQPdWzq',
symbol: 'RAY',
chainType: ChainType.SOLANA,
networkId: NETWORK_IDS.SOLANA_MAINNET,
fromAddress: 'FAMREy7d73N5jPdoKowQ4QFm6DKPWuYxZh6cwjNAbpkY',
toAddress: '6U29tmuvaGsTQqamf9Vt4o15JHTNq5RdJxoRW6NJxRdx',
timestamp: 1706429516,
amount,
});
assert.isOk(transactionInfo);
assert.equal(transactionInfo.currency, 'RAY');
assert.equal(transactionInfo.amount, amount);
});

it('should return error when transaction time is newer than sent timestamp for spl-token transfer on Solana', async () => {
// https://explorer.solana.com/tx/2tm14GVsDwXpMzxZzpEWyQnfzcUEv1DZQVQb6VdbsHcV8StoMbBtuQTkW1LJ8RhKKrAL18gbm181NgzuusiQfZ16?cluster=devnet

Expand Down
6 changes: 4 additions & 2 deletions src/services/chains/solana/transactionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,14 @@ async function getTransactionDetailForSplTokenTransfer(
balance =>
balance.owner === params.toAddress && balance.mint === token?.address,
);
if (!data || !toAddressPostBalance || !toAddressPreBalance) {
if (!data || !toAddressPostBalance) {
return null;
}

// toAddressBalance might be null if this is first time that destination wallet receives this token
const amount =
toAddressPostBalance.uiTokenAmount?.uiAmount -
toAddressPreBalance.uiTokenAmount?.uiAmount;
(toAddressPreBalance?.uiTokenAmount?.uiAmount || 0);
const parsedData = data as ParsedInstruction;

const txInfo = parsedData.parsed.info;
Expand Down
6 changes: 5 additions & 1 deletion src/services/cronJobs/importLostDonationsJob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ abiDecoder.addABI(erc20ABI);

const QF_ROUND_ID = config.get('LOST_DONATIONS_QF_ROUND');
const NETWORK_ID = config.get('LOST_DONATIONS_NETWORK_ID');
const NATIVE_NETWORK_TOKEN = config.get('LOST_DONATIONS_NATIVE_NETWORK_TOKEN');

const cronJobTime =
(config.get('IMPORT_LOST_DONATIONS_CRONJOB_EXPRESSION') as string) ||
Expand Down Expand Up @@ -140,7 +141,7 @@ export const importLostDonations = async () => {
) {
// it's an eth transfer native token
const nativeToken = await Token.createQueryBuilder('token')
.where(`token.id = 198`)
.where(`token.id = :token`, { token: NATIVE_NETWORK_TOKEN })
.getOne();

tokenInDB = nativeToken;
Expand Down Expand Up @@ -213,6 +214,7 @@ export const importLostDonations = async () => {
toWalletAddress: donationParams.to.toLowerCase(),
transactionId: tx.toLowerCase(),
projectId: project.id,
userId: dbUser.id,
currency: donationParams.currency,
tokenAddress: tokenInDB?.address,
amount: donationParams.amount,
Expand All @@ -238,13 +240,15 @@ export const importLostDonations = async () => {
} catch (e) {
logger.debug('Error saving donation for for tx: ', tx);
logger.debug('Error saving donation: ', e);
continue;
}

await updateUserTotalDonated(dbUser.id);
await updateUserTotalReceived(project.adminUser?.id);
await updateTotalDonationsOfProject(project.id);
} catch (e) {
logger.error('importLostDonations() error');
continue;
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/services/cronJobs/syncDonationsWithNetwork.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ const addJobToCheckPendingDonationsWithNetwork = async () => {
donationId: donation.id,
},
{
// jobId: `verify-donation-id-${donation.id}`,
jobId: `verify-donation-id-${donation.id}`,
removeOnComplete: true,
removeOnFail: true,
},
);
});
Expand Down
64 changes: 64 additions & 0 deletions src/services/donationService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,70 @@ function fillOldStableCoinDonationsPriceTestCases() {
expect(donation.valueUsd).to.gt(0);
});

it('should fill price for mpETH donation on the MAINNET network', async () => {
const token = 'mpETH';
const amount = 2;
let donation = await saveDonationDirectlyToDb(
{
...createDonationData(),
currency: token,
valueUsd: undefined,
valueEth: undefined,
amount,
},
SEED_DATA.FIRST_USER.id,
SEED_DATA.FIRST_PROJECT.id,
);

const project = (await Project.findOne({
where: { id: SEED_DATA.FIRST_PROJECT.id },
})) as Project;

await updateDonationPricesAndValues(
donation,
project,
null,
token,
CHAIN_ID.MAINNET,
amount,
);
donation = (await findDonationById(donation.id))!;
expect(donation.valueUsd).to.gt(0);
expect(donation.priceUsd).to.below(donation.valueUsd);
});

it('should fill price for mpETH donation on the OPTIMISM network', async () => {
const token = 'mpETH';
const amount = 2;
let donation = await saveDonationDirectlyToDb(
{
...createDonationData(),
currency: token,
valueUsd: undefined,
valueEth: undefined,
amount,
},
SEED_DATA.FIRST_USER.id,
SEED_DATA.FIRST_PROJECT.id,
);

const project = (await Project.findOne({
where: { id: SEED_DATA.FIRST_PROJECT.id },
})) as Project;

await updateDonationPricesAndValues(
donation,
project,
null,
token,
CHAIN_ID.OPTIMISM,
amount,
);
donation = (await findDonationById(donation.id))!;
expect(donation.valueUsd).to.gt(0);
expect(donation.priceUsd).to.below(donation.valueUsd);
});

it('should fill price for Celo donation on the CELO Alfajores network', async () => {
const token = 'CELO';
const amount = 100;
Expand Down
5 changes: 5 additions & 0 deletions src/services/donationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import { fetchSafeTransactionHash } from './safeServices';
import { ChainType } from '../types/network';
import { NETWORK_IDS } from '../provider';
import { getTransactionInfoFromNetwork } from './chains';
import { fetchMpEthPrice } from './mpEthPriceService';

export const TRANSAK_COMPLETED_STATUS = 'COMPLETED';

Expand All @@ -65,6 +66,10 @@ export const updateDonationPricesAndValues = async (
if (token?.isStableCoin) {
donation.priceUsd = 1;
donation.valueUsd = Number(amount);
} else if (currency === 'mpETH') {
const mpEthPriceInUsd = await fetchMpEthPrice();
donation.priceUsd = toFixNumber(mpEthPriceInUsd, 4);
donation.valueUsd = toFixNumber(donation.amount * mpEthPriceInUsd, 4);
} else if (currency === 'GIV') {
const { givPriceInUsd } = await fetchGivPrice();
donation.priceUsd = toFixNumber(givPriceInUsd, 4);
Expand Down
12 changes: 12 additions & 0 deletions src/services/mpEthPriceService.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { assert, expect } from 'chai';
import { fetchMpEthPrice } from './mpEthPriceService';

describe('fetchMpEthPrice test cases', fetchMpEthPriceTestCases);

function fetchMpEthPriceTestCases() {
it('should fetch the price from velodrome subgraph for mpeth', async () => {
const mpEthPrice = await fetchMpEthPrice();
assert.isOk(mpEthPrice);
expect(mpEthPrice).to.gt(0);
});
}
Loading

0 comments on commit e83d2b6

Please sign in to comment.