From 79a3be70955e99d077cc3b156037437452ca8bf8 Mon Sep 17 00:00:00 2001 From: Mohammad Ranjbar Z Date: Sun, 28 Jan 2024 16:52:11 +0330 Subject: [PATCH 1/9] Fix verifying spl token transfer donations --- src/services/chains/index.test.ts | 23 +++++++++++++++++-- .../chains/solana/transactionService.ts | 6 +++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/services/chains/index.test.ts b/src/services/chains/index.test.ts index 625634137..0504ce2ef 100644 --- a/src/services/chains/index.test.ts +++ b/src/services/chains/index.test.ts @@ -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({ @@ -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({ @@ -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 diff --git a/src/services/chains/solana/transactionService.ts b/src/services/chains/solana/transactionService.ts index 4908928ff..ccb9e5921 100644 --- a/src/services/chains/solana/transactionService.ts +++ b/src/services/chains/solana/transactionService.ts @@ -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; From 774e21d9ada1f50f5f044fc68bacd87656800f57 Mon Sep 17 00:00:00 2001 From: mohammadranjbarz Date: Mon, 29 Jan 2024 11:29:06 +0330 Subject: [PATCH 2/9] Return english error message if there is no verification form for a project (#1274) Related to https://github.com/Giveth/giveth-dapps-v2/issues/3582#issuecomment-1913614715 --- src/resolvers/projectVerificationFormResolver.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/resolvers/projectVerificationFormResolver.ts b/src/resolvers/projectVerificationFormResolver.ts index 34495644f..e13431039 100644 --- a/src/resolvers/projectVerificationFormResolver.ts +++ b/src/resolvers/projectVerificationFormResolver.ts @@ -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, @@ -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; From e02b04a614b158ca2c3c88918b8c540d55342229 Mon Sep 17 00:00:00 2001 From: Carlos Date: Mon, 29 Jan 2024 23:07:11 -0500 Subject: [PATCH 3/9] improve import lost donations with network customizable --- src/services/cronJobs/importLostDonationsJob.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/cronJobs/importLostDonationsJob.ts b/src/services/cronJobs/importLostDonationsJob.ts index 7e550fe6f..e66e4c4e9 100644 --- a/src/services/cronJobs/importLostDonationsJob.ts +++ b/src/services/cronJobs/importLostDonationsJob.ts @@ -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) || @@ -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; From 323672b938f01810b263c6cb4c504c44946ece10 Mon Sep 17 00:00:00 2001 From: Carlos Date: Tue, 30 Jan 2024 12:08:35 -0500 Subject: [PATCH 4/9] add continue on error from import --- src/services/cronJobs/importLostDonationsJob.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/services/cronJobs/importLostDonationsJob.ts b/src/services/cronJobs/importLostDonationsJob.ts index e66e4c4e9..1a205b827 100644 --- a/src/services/cronJobs/importLostDonationsJob.ts +++ b/src/services/cronJobs/importLostDonationsJob.ts @@ -239,6 +239,7 @@ 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); @@ -246,6 +247,7 @@ export const importLostDonations = async () => { await updateTotalDonationsOfProject(project.id); } catch (e) { logger.error('importLostDonations() error'); + continue; } } From 6e94d8528f18d534cae332b8cd6c6ab2bb4768f6 Mon Sep 17 00:00:00 2001 From: Carlos Date: Tue, 30 Jan 2024 13:41:51 -0500 Subject: [PATCH 5/9] fix userId query in import of donations --- src/services/cronJobs/importLostDonationsJob.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/services/cronJobs/importLostDonationsJob.ts b/src/services/cronJobs/importLostDonationsJob.ts index 1a205b827..388b11bab 100644 --- a/src/services/cronJobs/importLostDonationsJob.ts +++ b/src/services/cronJobs/importLostDonationsJob.ts @@ -214,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, From ee10acf5af05cb838935c09374b428aef7b5344d Mon Sep 17 00:00:00 2001 From: Amin Latifi Date: Wed, 31 Jan 2024 17:09:04 +0330 Subject: [PATCH 6/9] Verify Donation Queue Job ID Production (#1277) * Verify transfer spl-token on solana, supporting multi chain solana related to #1175 * fix solana tests on devnet * add solana additional tokens * fix solana token transfer test * improve joi schema chaintype validator * Add another spl-token transfer test case, change validator of createDonation webservice related to https://github.com/Giveth/impact-graph/pull/1232#discussion_r1444472435 * Refactor and change a nested if-else with witch case * Removed only tags from tests * Update src/utils/errorMessages.ts Co-authored-by: Amin Latifi * Update src/utils/locales/en.json Co-authored-by: Amin Latifi * Fill networkId for solana addresses when create/update projects * Add some test tokens for solana dev chain * Import lost donations into DB cronjob (#1236) * import donations from env vars and add tests * Read LOST_DONATIONS_TX_HASHES from .env instead of config * Fix build error --------- Co-authored-by: Amin Latifi Co-authored-by: Mohammad Ranjbar Z * setup notification center disabling and reduce notifications * Change filling networkId for solana * Fix migration files for adding spl tokens on solana * Modify create donation test cases for check filling networkId of solana donations * comment out notification center methods interface * Revert "comment out notification center methods interface" This reverts commit 4a6dd8c4082fceee0b822c1f14a9c0a0b6968d41. * set logic as before, comment notification center methods not required * move donation received logic to notification adapter * Refactor get networkId for solana addresses and implement getAppropriateNetworkId * Fix filling solana donations price (#1252) * Fix filling solana donations price related to https://github.com/Giveth/giveth-dapps-v2/issues/3394#issuecomment-1896557906 * Add informative logs for filling value usd part * Add log for createDonation webservice * change type of transactionNetworkId to number in ajv schema * Update createDonation to use correct networkId * Added jobId to donation verification queue * fix test env * Import from Donation Backup Service (#1253) * Added Donation Save Backup Adapter * add backup service import cronjob * add interface of used params from mongo data * Change loading urls for backup service * Refactor donationSaveBackupAdapter and adding mock adapter * Removed unused package script * Refactored createBackupDonation to reuse resolver * Fix createBackupDonation() and write test case for that * Add importError to failed donation mongo backup --------- Co-authored-by: mohammadranjbarz Co-authored-by: Carlos * Fix query of getting failed donations from mongo API * Add more logs for importing failed donations * Change info logs to debug logs * 1.21.0 * Add importDate to donation entity * Fill importDate of donation correctly * Put importing failed donations in try...catch * Added back jobId to verifyDonationsQueue, along with removeOnComplete and removeOnFail --------- Co-authored-by: Mohammad Ranjbar Z Co-authored-by: Carlos Co-authored-by: CarlosQ96 <92376054+CarlosQ96@users.noreply.github.com> --- src/services/cronJobs/syncDonationsWithNetwork.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/services/cronJobs/syncDonationsWithNetwork.ts b/src/services/cronJobs/syncDonationsWithNetwork.ts index da2850d86..7ff03b7aa 100644 --- a/src/services/cronJobs/syncDonationsWithNetwork.ts +++ b/src/services/cronJobs/syncDonationsWithNetwork.ts @@ -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, }, ); }); From bdcfff4884ab745b84f63a5e5832a7913f10c1d1 Mon Sep 17 00:00:00 2001 From: CarlosQ96 <92376054+CarlosQ96@users.noreply.github.com> Date: Tue, 6 Feb 2024 15:09:43 -0500 Subject: [PATCH 7/9] Release mpeth token (#1305) * add mpeth prices fetch and token * add env var to tests * add tests for fetching price method (check status) --- .github/workflows/develop-pipeline.yml | 1 + .github/workflows/master-pipeline.yml | 1 + .github/workflows/run-tests-on-pr.yml.bck | 1 + .github/workflows/staging-pipeline.yml | 1 + config/example.env | 10 +++ .../1706820821887-addmpEthToDatabaseTokens.ts | 77 +++++++++++++++++++ src/services/donationService.test.ts | 64 +++++++++++++++ src/services/donationService.ts | 5 ++ src/services/mpEthPriceService.test.ts | 12 +++ src/services/mpEthPriceService.ts | 37 +++++++++ test/testUtils.ts | 14 ++++ 11 files changed, 223 insertions(+) create mode 100644 migration/1706820821887-addmpEthToDatabaseTokens.ts create mode 100644 src/services/mpEthPriceService.test.ts create mode 100644 src/services/mpEthPriceService.ts diff --git a/.github/workflows/develop-pipeline.yml b/.github/workflows/develop-pipeline.yml index c377286ae..3df6a977d 100644 --- a/.github/workflows/develop-pipeline.yml +++ b/.github/workflows/develop-pipeline.yml @@ -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 diff --git a/.github/workflows/master-pipeline.yml b/.github/workflows/master-pipeline.yml index 77c44f8d7..3195a1695 100644 --- a/.github/workflows/master-pipeline.yml +++ b/.github/workflows/master-pipeline.yml @@ -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 diff --git a/.github/workflows/run-tests-on-pr.yml.bck b/.github/workflows/run-tests-on-pr.yml.bck index be434d0e4..76fc70f7b 100644 --- a/.github/workflows/run-tests-on-pr.yml.bck +++ b/.github/workflows/run-tests-on-pr.yml.bck @@ -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 }} diff --git a/.github/workflows/staging-pipeline.yml b/.github/workflows/staging-pipeline.yml index 96f245e76..97966af4e 100644 --- a/.github/workflows/staging-pipeline.yml +++ b/.github/workflows/staging-pipeline.yml @@ -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 diff --git a/config/example.env b/config/example.env index 892420053..d5607c64a 100644 --- a/config/example.env +++ b/config/example.env @@ -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= diff --git a/migration/1706820821887-addmpEthToDatabaseTokens.ts b/migration/1706820821887-addmpEthToDatabaseTokens.ts new file mode 100644 index 000000000..a71b6beb5 --- /dev/null +++ b/migration/1706820821887-addmpEthToDatabaseTokens.ts @@ -0,0 +1,77 @@ +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', + mainnetAddress: '0x48afbbd342f64ef8a9ab1c143719b63c2ad81710', + decimals: 18, + isGivbackEligible: true, + networkId: NETWORK_IDS.OPTIMISTIC, + chainType: ChainType.EVM, + }, +]; + +export class addmpEthToDatabaseTokens1706820821887 + implements MigrationInterface +{ + public async up(queryRunner: QueryRunner): Promise { + 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 { + 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(',')})`, + ); + } +} diff --git a/src/services/donationService.test.ts b/src/services/donationService.test.ts index 5f6c7b023..dde8ae31f 100644 --- a/src/services/donationService.test.ts +++ b/src/services/donationService.test.ts @@ -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; diff --git a/src/services/donationService.ts b/src/services/donationService.ts index 491f55e13..c1e2c85a4 100644 --- a/src/services/donationService.ts +++ b/src/services/donationService.ts @@ -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'; @@ -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); diff --git a/src/services/mpEthPriceService.test.ts b/src/services/mpEthPriceService.test.ts new file mode 100644 index 000000000..12ed411bf --- /dev/null +++ b/src/services/mpEthPriceService.test.ts @@ -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); + }); +} diff --git a/src/services/mpEthPriceService.ts b/src/services/mpEthPriceService.ts new file mode 100644 index 000000000..c3d695cf6 --- /dev/null +++ b/src/services/mpEthPriceService.ts @@ -0,0 +1,37 @@ +import { logger } from '../utils/logger'; +import Axios, { AxiosResponse } from 'axios'; +import axiosRetry from 'axios-retry'; + +const mpEthSubgraphUrl = process.env.MPETH_GRAPHQL_PRICES_URL as string; + +// Maximum timeout of axios. +const axiosTimeout = 20000; + +const query = { + query: ` + { + tokens(where:{id:"0x819845b60a192167ed1139040b4f8eca31834f27"}) { + id + name + symbol + decimals + lastPriceUSD + } + } + `, +}; + +export const fetchMpEthPrice = async () => { + try { + const result = await Axios.post(mpEthSubgraphUrl, query, { + headers: { + 'Content-Type': 'application/json', + }, + timeout: axiosTimeout, + }); + return Number(result.data.data.tokens[0].lastPriceUSD); + } catch (e) { + logger.error('fetching Giv Price fetchGivPrice() err', e); + throw e; + } +}; diff --git a/test/testUtils.ts b/test/testUtils.ts index 83b5a0435..d81e1ddb6 100644 --- a/test/testUtils.ts +++ b/test/testUtils.ts @@ -1265,6 +1265,13 @@ export const SEED_DATA = { decimals: 18, isStableCoin: true, }, + { + name: 'mpETH', + symbol: 'mpETH', + address: '0x48afbbd342f64ef8a9ab1c143719b63c2ad81710', + decimals: 18, + isStableCoin: true, + }, ], ropsten: [ { @@ -1329,6 +1336,13 @@ export const SEED_DATA = { decimals: 18, isStableCoin: true, }, + { + name: 'mpETH', + symbol: 'mpETH', + address: '0x819845b60a192167ed1139040b4f8eca31834f27', + decimals: 18, + isStableCoin: true, + }, ], etc: [ { From f1c1fd12a2c326ac1a66c727d34d9543c572adcc Mon Sep 17 00:00:00 2001 From: Carlos Date: Tue, 6 Feb 2024 16:15:49 -0500 Subject: [PATCH 8/9] fix migration as mpeth already exists --- migration/1706820821887-addmpEthToDatabaseTokens.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/migration/1706820821887-addmpEthToDatabaseTokens.ts b/migration/1706820821887-addmpEthToDatabaseTokens.ts index a71b6beb5..70387118a 100644 --- a/migration/1706820821887-addmpEthToDatabaseTokens.ts +++ b/migration/1706820821887-addmpEthToDatabaseTokens.ts @@ -17,7 +17,6 @@ const mpEthTokens = [ name: 'mpETH', symbol: 'mpETH', address: '0x819845b60a192167ed1139040b4f8eca31834f27', - mainnetAddress: '0x48afbbd342f64ef8a9ab1c143719b63c2ad81710', decimals: 18, isGivbackEligible: true, networkId: NETWORK_IDS.OPTIMISTIC, @@ -29,6 +28,17 @@ export class addmpEthToDatabaseTokens1706820821887 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { + 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(` From f76d8cda2258bbc8a85d4fc97324eb54f78812c4 Mon Sep 17 00:00:00 2001 From: Carlos Date: Wed, 7 Feb 2024 11:43:50 -0500 Subject: [PATCH 9/9] make chaintype nullable on managing funds (errors generated) --- src/entities/projectVerificationForm.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/entities/projectVerificationForm.ts b/src/entities/projectVerificationForm.ts index e38bf3213..97783fd36 100644 --- a/src/entities/projectVerificationForm.ts +++ b/src/entities/projectVerificationForm.ts @@ -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; }