diff --git a/.env.example b/.env.example index e0ca966..182a688 100644 --- a/.env.example +++ b/.env.example @@ -2,6 +2,9 @@ GIVETHIO_BASE_URL=https://mainnet.serve.giveth.io TRACE_BASE_URL=https://feathers.beta.giveth.io PURPLE_LIST=0x1ad91ee08f21be3de0ba2ba6918e714da6b45836,0x63ecc058D97ED7bAb75616B77C96734D0B823f87 MAINNET_NODE_URL=https://mainnet.infura.io/v3/infuraApiKey +ENVIRONMENT=staging +ZKEVM_NODE_HTTP_URL= +ZKEVM_CARDONA_HTTP_URL= XDAI_NODE_HTTP_URL= PINATA_API_KEY= diff --git a/README.md b/README.md index 3576cdf..f4f959e 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,6 @@ You just need to run docker-compose-develop.yml, and you dont need any dependenc ## Preview It's the environment for calculate givback from aggregate donations fo https://giveth.io and https://trace.giveth.io #### Production -https://givback.develop.giveth.io/api-docs +https://givbacks.giveth.io/api-docs #### Staging -https://givback.develop.giveth.io/staging/api-docs +https://givback.develop.giveth.io/api-docs diff --git a/src/commonServices.ts b/src/commonServices.ts index 43a7fb6..933c5f0 100644 --- a/src/commonServices.ts +++ b/src/commonServices.ts @@ -1,5 +1,5 @@ // List of peoples who should not give givbacks -import {FormattedDonation, GivbackFactorParams} from "./types/general"; +import {FormattedDonation, GivbackFactorParams, GivethIoDonation} from "./types/general"; const {gql, request} = require("graphql-request"); const givethiobaseurl = process.env.GIVETHIO_BASE_URL @@ -57,4 +57,50 @@ export const donationValueAfterGivFactor = (params: { (usdValue * givFactor).toFixed(7) ) } +export const groupDonationsByParentRecurringId = ( + donations: GivethIoDonation[] +): GivethIoDonation[] => { + const groupedDonations: GivethIoDonation[] = []; + // Create a map to group donations by parentRecurringDonationId + const donationMap = donations.reduce((map, donation) => { + const parentRecurringDonationId = donation.recurringDonation?.id; + if (parentRecurringDonationId) { + if (!map[parentRecurringDonationId]) { + map[parentRecurringDonationId] = []; + } + map[parentRecurringDonationId].push(donation); + } else { + // If there is no parentRecurringDonationId, add directly to the grouped donations + groupedDonations.push({ + ...donation, + amount: donation.amount, // Convert amount to number + }); + } + return map; + }, {} as Record); + + // Iterate through the map to create grouped donations + for (const parentId in donationMap) { + const donationsGroup = donationMap[parentId]; + + // Use the data of the first donation in the group + const firstDonation = donationsGroup[0]; + + // Sum the amounts, valueUsd, and valueUsdAfterGivbackFactor + const totalAmount = donationsGroup.reduce((sum, donation) => sum + parseFloat(donation.amount), 0); + const totalValueUsd = donationsGroup.reduce((sum, donation) => sum + donation.valueUsd, 0); + + // Create a new grouped donation object + const groupedDonation: GivethIoDonation = { + ...firstDonation, + amount: String(totalAmount), + valueUsd: totalValueUsd, + numberOfStreamedDonations: donationsGroup.length, + }; + + groupedDonations.push(groupedDonation); + } + + return groupedDonations; +}; diff --git a/src/givethIoService.ts b/src/givethIoService.ts index 3a188a5..b7ca4ae 100644 --- a/src/givethIoService.ts +++ b/src/givethIoService.ts @@ -8,7 +8,9 @@ import { } from "./types/general"; import {GIVETH_TOKEN_DISTRO_ADDRESS} from "./subgraphService"; import TokenDistroJSON from '../abi/TokenDistroV2.json' + const Ethers = require("ethers"); +const {isAddress} = require("ethers"); require('dotenv').config() @@ -25,7 +27,7 @@ import { calculateReferralRewardFromRemainingAmount, calculateReferralReward, getNetworkNameById, - filterRawDonationsByChain + filterRawDonationsByChain, isDonationAmountValid } from "./utils"; const givethiobaseurl = process.env.GIVETHIO_BASE_URL @@ -37,6 +39,19 @@ const gnosisProvider = new Ethers.JsonRpcProvider(xdaiNodeHttpUrl); const tokenDistroGC = new Ethers.Contract(GIVETH_TOKEN_DISTRO_ADDRESS, TokenDistroJSON.abi, gnosisProvider); +export const isEvmAddress = (address: string): boolean => { + return isAddress(address); +}; + +const isStellarDonationAndUserLoggedInWithEvmAddress = (donation: GivethIoDonation): boolean => { + console.log('isStellarDonationAndUserLoggedIn', donation) + return Boolean(donation.transactionNetworkId === 1500 && donation?.user?.walletAddress && isEvmAddress(donation?.user?.walletAddress)) +} + +const donationGiverAddress = (donation: GivethIoDonation): string => { + return isStellarDonationAndUserLoggedInWithEvmAddress(donation) ? donation.user.walletAddress : donation.fromWalletAddress +} + /** * * @returns {Promise<[{amount:400, currency:"GIV",createdAt:"", @@ -48,12 +63,14 @@ export const getEligibleDonations = async ( params: { beginDate: string, endDate: string, + minEligibleValueUsd: number, + givethCommunityProjectSlug: string, niceWhitelistTokens?: string[], niceProjectSlugs?: string[], eligible?: boolean, disablePurpleList?: boolean, justCountListed?: boolean, - chain?: "all-other-chains" | "gnosis" + chain?: "all-other-chains" | "gnosis" | "zkEVM" }): Promise => { try { @@ -64,7 +81,9 @@ export const getEligibleDonations = async ( niceProjectSlugs, disablePurpleList, justCountListed, - chain + chain, + minEligibleValueUsd, + givethCommunityProjectSlug } = params const eligible = params.eligible === undefined ? true : params.eligible const timeFormat = 'YYYY/MM/DD-HH:mm:ss'; @@ -95,7 +114,8 @@ export const getEligibleDonations = async ( amount givbackFactor chainType - isProjectVerified + anonymous + isProjectGivbackEligible projectRank powerRound bottomRankInRound @@ -116,6 +136,7 @@ export const getEligibleDonations = async ( user { name email + walletAddress } fromWalletAddress status @@ -131,20 +152,28 @@ export const getEligibleDonations = async ( moment(donation.createdAt) < secondDate && moment(donation.createdAt) > firstDate && donation.valueUsd - && donation.chainType == 'EVM' - && donation.isProjectVerified + && isDonationAmountValid({ + donation, + minEligibleValueUsd, + givethCommunityProjectSlug, + } ) + && (donation.chainType == 'EVM' || isStellarDonationAndUserLoggedInWithEvmAddress(donation)) + && donation.isProjectGivbackEligible && donation.status === 'verified' ) let donationsToNotVerifiedProjects: GivethIoDonation[] = rawDonationsFilterByChain .filter( (donation: GivethIoDonation) => - moment(donation.createdAt) < secondDate - && moment(donation.createdAt) > firstDate - && donation.valueUsd - && donation.chainType == 'EVM' - && !donation.isProjectVerified - && donation.status === 'verified' + ( + moment(donation.createdAt) < secondDate + && moment(donation.createdAt) > firstDate + && donation.valueUsd + && (donation.chainType == 'EVM' || isStellarDonationAndUserLoggedInWithEvmAddress(donation)) + && (!donation.isProjectGivbackEligible || donation.valueUsd >= minEligibleValueUsd) + && donation.status === 'verified' + ) + ) if (niceWhitelistTokens) { @@ -193,6 +222,7 @@ export const getEligibleDonations = async ( currency: item.currency, createdAt: moment(item.createdAt).format('YYYY-MM-DD-hh:mm:ss'), valueUsd: item.valueUsd, + anonymous: item.anonymous, bottomRankInRound: item.bottomRankInRound, givbacksRound: item.powerRound, projectRank: item.projectRank, @@ -201,7 +231,7 @@ export const getEligibleDonations = async ( usdValue: item.valueUsd, givFactor: item.givbackFactor }), - giverAddress: item.fromWalletAddress, + giverAddress: donationGiverAddress(item), txHash: item.transactionId, network: getNetworkNameById(item.transactionNetworkId), source: 'giveth.io', @@ -212,6 +242,7 @@ export const getEligibleDonations = async ( isReferrerGivbackEligible: item.isReferrerGivbackEligible, referrerWallet: item.referrerWallet, + numberOfStreamedDonations: item.numberOfStreamedDonations, parentRecurringDonationId: item?.recurringDonation?.id, parentRecurringDonationTxHash: item?.recurringDonation?.txHash } @@ -221,6 +252,7 @@ export const getEligibleDonations = async ( const givbackFactor = item.givbackFactor || 0.5; return { amount: item.amount, + anonymous: item.anonymous, currency: item.currency, createdAt: moment(item.createdAt).format('YYYY-MM-DD-hh:mm:ss'), valueUsd: item.valueUsd, @@ -232,7 +264,7 @@ export const getEligibleDonations = async ( projectRank: item.projectRank, bottomRankInRound: item.powerRound, givbacksRound: item.powerRound, - giverAddress: item.fromWalletAddress, + giverAddress: donationGiverAddress(item), txHash: item.transactionId, network: getNetworkNameById(item.transactionNetworkId), source: 'giveth.io', @@ -290,7 +322,7 @@ export const getVerifiedPurpleListDonations = async (beginDate: string, endDate: transactionNetworkId amount chainType - isProjectVerified + isProjectGivbackEligible project { slug verified @@ -313,8 +345,8 @@ export const getVerifiedPurpleListDonations = async (beginDate: string, endDate: moment(donation.createdAt) < secondDate && moment(donation.createdAt) > firstDate && donation.valueUsd - && donation.chainType == 'EVM' - && donation.isProjectVerified + && (donation.chainType == 'EVM' || isStellarDonationAndUserLoggedInWithEvmAddress(donation)) + && donation.isProjectGivbackEligible && donation.status === 'verified' ) @@ -326,7 +358,7 @@ export const getVerifiedPurpleListDonations = async (beginDate: string, endDate: createdAt: moment(item.createdAt).format('YYYY-MM-DD-hh:mm:ss'), valueUsd: item.valueUsd, givbackFactor: item.givbackFactor, - giverAddress: item.fromWalletAddress, + giverAddress: donationGiverAddress(item), txHash: item.transactionId, network: getNetworkNameById(item.transactionNetworkId), source: 'giveth.io', @@ -351,11 +383,12 @@ export const getDonationsReport = async (params: { // example: 2021/07/01-00:00:00 beginDate: string, endDate: string, - + minEligibleValueUsd: number, + givethCommunityProjectSlug:string, niceWhitelistTokens?: string[], niceProjectSlugs?: string[], applyChainvineReferral?: boolean, - chain?: "all-other-chains" | "gnosis" + chain?: "all-other-chains" | "gnosis" | "zkEVM" }): Promise => { const { beginDate, @@ -363,15 +396,20 @@ export const getDonationsReport = async (params: { niceWhitelistTokens, niceProjectSlugs, applyChainvineReferral, - chain + chain, + givethCommunityProjectSlug, + minEligibleValueUsd } = params try { + const response = await getEligibleDonations( { beginDate, endDate, niceWhitelistTokens, niceProjectSlugs, disablePurpleList: Boolean(niceWhitelistTokens), + minEligibleValueUsd, + givethCommunityProjectSlug, chain }) diff --git a/src/index.ts b/src/index.ts index 1a593b7..56637e0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -62,9 +62,11 @@ app.get(`/calculate`, maxAddressesPerFunctionCall, niceWhitelistTokens, niceProjectSlugs, nicePerDollar, + givethCommunityProjectSlug } = req.query; const givAvailable = Number(req.query.givAvailable) const givPrice = Number(req.query.givPrice) + const minEligibleValueUsd = Number(req.query.minEligibleValueUsd) const givWorth = givAvailable * givPrice const tokens = (niceWhitelistTokens as string).split(',') @@ -76,6 +78,8 @@ app.get(`/calculate`, endDate: endDate as string, niceWhitelistTokens: tokens, niceProjectSlugs: slugs, + minEligibleValueUsd, + givethCommunityProjectSlug: givethCommunityProjectSlug as string }) const niceDonationsGroupByGiverAddress = _.groupBy(givethDonationsForNice, 'giverAddress') @@ -122,19 +126,35 @@ app.get(`/calculate`, beginDate: startDate as string, endDate: endDate as string, applyChainvineReferral: true, - chain: "gnosis" + chain: "gnosis", + givethCommunityProjectSlug:givethCommunityProjectSlug as string, + minEligibleValueUsd + }); const otherChainDonations = await getDonationsReport({ beginDate: startDate as string, endDate: endDate as string, applyChainvineReferral: true, - chain: "all-other-chains" + chain: "all-other-chains", + givethCommunityProjectSlug:givethCommunityProjectSlug as string, + minEligibleValueUsd + }); + + const zkEVMDonations = await getDonationsReport({ + beginDate: startDate as string, + endDate: endDate as string, + applyChainvineReferral: true, + chain: "zkEVM", + givethCommunityProjectSlug:givethCommunityProjectSlug as string, + minEligibleValueUsd }); const totalDonations = await getDonationsReport({ beginDate: startDate as string, endDate: endDate as string, applyChainvineReferral: true, + givethCommunityProjectSlug:givethCommunityProjectSlug as string, + minEligibleValueUsd }); const totalDonationsAmount = totalDonations.reduce((previousValue: number, currentValue: MinimalDonation) => { @@ -151,6 +171,7 @@ app.get(`/calculate`, const groupByGiverAddressForTotalDonations = _.groupBy(totalDonations, 'giverAddress') const groupByGiverAddressForOptimismDonations = _.groupBy(gnosisDonations, 'giverAddress') const groupByGiverAddressForAllOtherChainsDonations = _.groupBy(otherChainDonations, 'giverAddress') + const groupByGiverAddressForzkEVMDonations = _.groupBy(zkEVMDonations, 'giverAddress') const optimismMinimalDonations = getDonationsForSmartContractParams({ @@ -168,6 +189,11 @@ app.get(`/calculate`, groupByGiverAddress: groupByGiverAddressForTotalDonations }) + const zkEVMChainMinimalDonations = getDonationsForSmartContractParams({ + maxGivbackFactorPercentage, + groupByGiverAddress: groupByGiverAddressForzkEVMDonations + }) + const totalMinimalDonationsSortedByUsdValue = totalMinimalDonations.sort((a, b) => { return b.totalDonationsUsdValueAfterGivFactor - a.totalDonationsUsdValueAfterGivFactor }); @@ -190,6 +216,11 @@ app.get(`/calculate`, raisedValueSum }) + const zkEVMChainMinimalDonationsWithShare = convertMinimalDonationToDonationResponse({ + minimalDonationsArray: zkEVMChainMinimalDonations, + givPrice, + raisedValueSum + }) const allOtherChainsDonationsWithShare = convertMinimalDonationToDonationResponse({ minimalDonationsArray: allOtherChainsMinimalDonations, @@ -215,6 +246,7 @@ app.get(`/calculate`, // https://github.com/Giveth/givback-calculation/issues/35#issuecomment-1716106403 const optimismRelayerAddress = '0xf13e93af5e706ab3073e393e77bb2d7ce7bec01f' const gnosisRelayerAddress = '0xd0e81E3EE863318D0121501ff48C6C3e3Fd6cbc7' + const zkEVMRelayerAddress = '0x0000000000000000000000000000000000000000' const response = { raisedValueSumExcludedPurpleList: Math.ceil(raisedValueSum), givDistributed, @@ -243,6 +275,18 @@ app.get(`/calculate`, ), givbacks: allOtherChainsDonationsWithShare }, + zkEVM: { + smartContractParams: await createSmartContractCallAddBatchParams( + { + nrGIVAddress, + donationsWithShare: zkEVMChainMinimalDonationsWithShare.filter(givback => givback.givback > 0), + givRelayerAddress: zkEVMRelayerAddress, + network:'zkEVM' + }, + Number(maxAddressesPerFunctionCall) || 200 + ), + givbacks: zkEVMChainMinimalDonationsWithShare + }, niceTokens: niceDonationsWithShareFormatted, // niceRaisedValueSumExcludedPurpleList: Math.ceil(raisedValueForGivethioDonationsSum), // niceGivethioDonationsAmountForNice: Math.ceil(givethioDonationsAmountForNice), @@ -275,6 +319,20 @@ app.get(`/calculate`, res.setHeader('Content-disposition', "attachment; filename=" + fileName); res.setHeader('Content-type', 'application/json'); res.send(csv) + } else if(download === 'zkEVM'){ + console.log('zkEVM response',response.zkEVM); + const csv = parse(response.zkEVM.givbacks.map((item: DonationResponse) => { + return { + givDistributed, + givPrice, + givbackUsdValue: givPrice * item.givback, + ...item + } + })); + const fileName = `givbackReport_zkEVM_${startDate}-${endDate}.csv`; + res.setHeader('Content-disposition', "attachment; filename=" + fileName); + res.setHeader('Content-type', 'application/json'); + res.send(csv) } else if (download === 'NICE') { const csv = parse(response.niceTokens); const fileName = `givbackReport_NICE_${startDate}-${endDate}.csv`; @@ -298,6 +356,8 @@ const getEligibleAndNonEligibleDonations = async (req: Request, res: Response, e endDate, startDate, download, justCountListed, chain } = req.query; + const minEligibleValueUsd = Number(req.query.minEligibleValueUsd) + const givethCommunityProjectSlug = req.query.givethCommunityProjectSlug const givethIoDonations = await getEligibleDonations( { @@ -305,8 +365,9 @@ const getEligibleAndNonEligibleDonations = async (req: Request, res: Response, e endDate: endDate as string, eligible, justCountListed: justCountListed === 'yes', - chain: chain as "all-other-chains" | "gnosis" - + chain: chain as "all-other-chains" | "gnosis" | "zkEVM", + givethCommunityProjectSlug:givethCommunityProjectSlug as string, + minEligibleValueUsd }); const donations = givethIoDonations.sort((a: FormattedDonation, b: FormattedDonation) => { @@ -334,8 +395,9 @@ const getEligibleDonationsForNiceToken = async (req: Request, res: Response, eli try { const { endDate, startDate, download, justCountListed, niceWhitelistTokens, - niceProjectSlugs, nicePerDollar + niceProjectSlugs, nicePerDollar, givethCommunityProjectSlug } = req.query; + const minEligibleValueUsd = Number(req.query.minEligibleValueUsd) const tokens = (niceWhitelistTokens as string).split(',') const slugs = (niceProjectSlugs as string).split(',') @@ -347,6 +409,8 @@ const getEligibleDonationsForNiceToken = async (req: Request, res: Response, eli endDate: endDate as string, eligible: true, justCountListed: justCountListed === 'yes', + minEligibleValueUsd, + givethCommunityProjectSlug: givethCommunityProjectSlug as string }); const donations = @@ -552,9 +616,12 @@ app.get(`/calculate-updated`, maxAddressesPerFunctionCall, niceWhitelistTokens, niceProjectSlugs, nicePerDollar, + givethCommunityProjectSlug, } = req.query; + const givAvailable = Number(req.query.givAvailable) + const minEligibleValueUsd = Number(req.query.minEligibleValueUsd) const {start, end} = await getGIVbacksRound(Number(roundNumber)) const endDate = moment(end, 'YYYY/MM/DD-HH:mm:ss') const endDateTimestamp = endDate.unix() @@ -582,6 +649,8 @@ app.get(`/calculate-updated`, endDate: end, niceWhitelistTokens: tokens, niceProjectSlugs: slugs, + givethCommunityProjectSlug:givethCommunityProjectSlug as string, + minEligibleValueUsd }) const niceDonationsGroupByGiverAddress = _.groupBy(givethDonationsForNice, 'giverAddress') @@ -628,13 +697,26 @@ app.get(`/calculate-updated`, beginDate: start, endDate: end, applyChainvineReferral: true, - chain: "gnosis" + chain: "gnosis", + givethCommunityProjectSlug:givethCommunityProjectSlug as string, + minEligibleValueUsd }); const otherChainDonations = await getDonationsReport({ beginDate: start, endDate: end, applyChainvineReferral: true, - chain: "all-other-chains" + chain: "all-other-chains", + givethCommunityProjectSlug:givethCommunityProjectSlug as string, + minEligibleValueUsd + }); + + const zkEVMDonations = await getDonationsReport({ + beginDate: start, + endDate: end, + applyChainvineReferral: true, + chain: "zkEVM", + givethCommunityProjectSlug:givethCommunityProjectSlug as string, + minEligibleValueUsd }); console.log('***new webservice donations*** new', { @@ -648,6 +730,8 @@ app.get(`/calculate-updated`, beginDate: start, endDate: end, applyChainvineReferral: true, + givethCommunityProjectSlug:givethCommunityProjectSlug as string, + minEligibleValueUsd }); const totalDonationsAmount = totalDonations.reduce((previousValue: number, currentValue: MinimalDonation) => { @@ -672,6 +756,7 @@ app.get(`/calculate-updated`, const groupByGiverAddressForTotalDonations = _.groupBy(totalDonations, 'giverAddress') const groupByGiverAddressForOptimismDonations = _.groupBy(gnosisDonations, 'giverAddress') const groupByGiverAddressForAllOtherChainsDonations = _.groupBy(otherChainDonations, 'giverAddress') + const groupByGiverAddressForzkEVMDonations = _.groupBy(zkEVMDonations, 'giverAddress') const optimismMinimalDonations = getDonationsForSmartContractParams({ @@ -684,6 +769,11 @@ app.get(`/calculate-updated`, groupByGiverAddress: groupByGiverAddressForAllOtherChainsDonations }) + const zkEVMChainMinimalDonations = getDonationsForSmartContractParams({ + maxGivbackFactorPercentage, + groupByGiverAddress: groupByGiverAddressForzkEVMDonations + }) + const totalMinimalDonations = getDonationsForSmartContractParams({ maxGivbackFactorPercentage, groupByGiverAddress: groupByGiverAddressForTotalDonations @@ -717,6 +807,13 @@ app.get(`/calculate-updated`, givPrice, raisedValueSum }) + + const zkEVMDonationsWithShare = convertMinimalDonationToDonationResponse({ + minimalDonationsArray: zkEVMChainMinimalDonations, + givPrice, + raisedValueSum + }) + console.log('**allOtherChainsDonationsWithShare**', allOtherChainsDonationsWithShare.length) console.log('**allOtherChainsMinimalDonations**', allOtherChainsMinimalDonations.length) @@ -736,6 +833,8 @@ app.get(`/calculate-updated`, const givDistributed = Math.ceil(raisedValueSumAfterGivFactor / givPrice); const optimismRelayerAddress = '0xf13e93af5e706ab3073e393e77bb2d7ce7bec01f' const gnosisRelayerAddress = '0xd0e81E3EE863318D0121501ff48C6C3e3Fd6cbc7' + // TODO : Set the relayer address for zkEVM. + const zkEVMRelayerAddress = '0x0000000000000000000000000000000000000000' const response = { start, @@ -743,6 +842,18 @@ app.get(`/calculate-updated`, raisedValueSumExcludedPurpleList: Math.ceil(raisedValueSum), givDistributed, givethioDonationsAmount: Math.ceil(totalDonationsAmount), + zkEVM: { + smartContractParams: await createSmartContractCallAddBatchParams( + { + nrGIVAddress, + donationsWithShare: zkEVMDonationsWithShare.filter(givback => givback.givback > 0), + givRelayerAddress: zkEVMRelayerAddress, + network:'gnosis' + }, + Number(maxAddressesPerFunctionCall) || 200 + ), + givbacks: zkEVMDonationsWithShare + }, gnosis: { smartContractParams: await createSmartContractCallAddBatchParams( { @@ -799,6 +910,19 @@ app.get(`/calculate-updated`, res.setHeader('Content-disposition', "attachment; filename=" + fileName); res.setHeader('Content-type', 'application/json'); res.send(csv) + } else if (download === "zkEVM") { + const csv = parse(response.zkEVM.givbacks.map((item: DonationResponse) => { + return { + givDistributed, + givPrice, + givbackUsdValue: givPrice * item.givback, + ...item + } + })); + const fileName = `givbackReport_zkEVM_${start}-${end}.csv`; + res.setHeader('Content-disposition', "attachment; filename=" + fileName); + res.setHeader('Content-type', 'application/json'); + res.send(csv) } else if (download === 'NICE') { const csv = parse(response.niceTokens); const fileName = `givbackReport_NICE_${start}-${end}.csv`; diff --git a/src/swagger.json b/src/swagger.json index 84aabc2..28c29af 100644 --- a/src/swagger.json +++ b/src/swagger.json @@ -58,12 +58,24 @@ "default": "the-giveth-community-of-makers", "in": "query" }, + { + "name": "minEligibleValueUsd", + "type": "number", + "default": "4", + "in": "query" + }, + { + "name": "givethCommunityProjectSlug", + "type": "string", + "default": "the-giveth-community-of-makers", + "in": "query" + }, { "name": "download", "type": "string", "description": "If you want to see json here set no, but if you want to download a file set yes", "default": "no", - "enum": ["no", "gnosis","all-other-chains", "NICE" ], + "enum": ["no", "gnosis","all-other-chains", "NICE", "zkEVM" ], "in": "query" } ], @@ -84,7 +96,7 @@ "type": "string", "description": "For Gnosis we need to distribute on Gnosis chain so we should have different response", "default": "all-other-chains", - "enum": ["all-other-chains","gnosis"], + "enum": ["all-other-chains","gnosis","zkEVM"], "in": "query" }, { @@ -99,6 +111,19 @@ "default": "2022/01/21-15:59:59", "in": "query" }, + + { + "name": "minEligibleValueUsd", + "type": "number", + "default": "4", + "in": "query" + }, + { + "name": "givethCommunityProjectSlug", + "type": "string", + "default": "the-giveth-community-of-makers", + "in": "query" + }, { "name": "download", "type": "string", @@ -187,7 +212,7 @@ "type": "string", "description": "For Gnosis we need to distribute on Gnosis chain so we should have different response", "default": "all-other-chains", - "enum": ["all-other-chains","gnosis"], + "enum": ["all-other-chains","gnosis","zkEVM"], "in": "query" }, { @@ -202,6 +227,19 @@ "default": "2022/01/21-15:59:59", "in": "query" }, + + { + "name": "minEligibleValueUsd", + "type": "number", + "default": "4", + "in": "query" + }, + { + "name": "givethCommunityProjectSlug", + "type": "string", + "default": "the-giveth-community-of-makers", + "in": "query" + }, { "name": "download", "type": "string", @@ -395,12 +433,24 @@ "default": "the-giveth-community-of-makers", "in": "query" }, + { + "name": "minEligibleValueUsd", + "type": "number", + "default": "4", + "in": "query" + }, + { + "name": "givethCommunityProjectSlug", + "type": "string", + "default": "the-giveth-community-of-makers", + "in": "query" + }, { "name": "download", "type": "string", "description": "If you want to see json here set no, but if you want to download a file set yes", "default": "no", - "enum": ["no", "gnosis","all-other-chains", "NICE" ], + "enum": ["no", "gnosis","all-other-chains", "NICE", "zkEVM"], "in": "query" } ], diff --git a/src/types/general.ts b/src/types/general.ts index ffec42a..35c01a3 100644 --- a/src/types/general.ts +++ b/src/types/general.ts @@ -1,5 +1,3 @@ -import moment from "moment"; - export interface FormattedDonation { amount: string, currency: string, @@ -23,6 +21,7 @@ export interface FormattedDonation { referrerWallet?: string referrer?: boolean, referred?: boolean, + anonymous: boolean, parentRecurringDonationId?: string, parentRecurringDonationTxHash?: string, @@ -47,7 +46,8 @@ export interface GivethIoDonation { source: string, user: { name: string, - email: string + email: string, + walletAddress: string } recurringDonation ?: { @@ -66,9 +66,11 @@ export interface GivethIoDonation { // giverName: string // giverEmail: string, status: string, - isProjectVerified: boolean, + anonymous: boolean, + isProjectGivbackEligible: boolean, isReferrerGivbackEligible?: boolean, referrerWallet?: string + numberOfStreamedDonations ?: number } export interface DonationResponse { diff --git a/src/utils.ts b/src/utils.ts index 42a02e6..e63a21f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -2,6 +2,7 @@ import {DonationResponse, GivethIoDonation, MinimalDonation} from "./types/gener import {hexlify, ethers} from "ethers"; import { keccak256 } from "@ethersproject/keccak256"; import { toUtf8Bytes } from "@ethersproject/strings"; +import {groupDonationsByParentRecurringId} from "./commonServices"; const Web3 = require('web3'); @@ -80,7 +81,7 @@ export const createSmartContractCallAddBatchParams = async (params: { nrGIVAddress: string, donationsWithShare: DonationResponse[], givRelayerAddress: string, - network : 'gnosis' | 'optimism' + network : 'gnosis' | 'optimism' | 'zkEVM' }, maxAddressesPerFunctionCall: number): Promise<{ result: string, hashParams: string @@ -209,9 +210,12 @@ const xdaiWeb3 = new Web3(xdaiWeb3NodeUrl); const optimismWeb3NodeUrl = process.env.OPTIMISM_NODE_HTTP_URL const optimismWeb3 = new Web3(optimismWeb3NodeUrl); +const isProduction = process.env.ENVIRONMENT !== 'staging'; +const zkEVMWeb3NodeUrl = isProduction ? process.env.ZKEVM_NODE_HTTP_URL: process.env.ZKEVM_CARDONA_HTTP_URL; +const zkEVMWeb3 = new Web3(zkEVMWeb3NodeUrl); -export const getLastNonceForWalletAddress = async (walletAddress: string, chain: 'gnosis' | 'optimism'): Promise => { - const web3Provider = chain === 'optimism' ? optimismWeb3 : xdaiWeb3 +export const getLastNonceForWalletAddress = async (walletAddress: string, chain: 'gnosis' | 'optimism' | 'zkEVM'): Promise => { + const web3Provider = getWeb3Provider(chain); const userTransactionsCount = await web3Provider.eth.getTransactionCount( walletAddress ); @@ -224,6 +228,20 @@ export const getLastNonceForWalletAddress = async (walletAddress: string, chain: return Math.max(userTransactionsCount - 1, 0) } +const getWeb3Provider = (chain: 'gnosis' | 'optimism' | 'zkEVM'): any => { + let web3Provider = xdaiWeb3; + switch(chain) { + case 'optimism': + web3Provider = optimismWeb3 + break; + case 'zkEVM': + web3Provider = zkEVMWeb3; + break; + default: + web3Provider = xdaiWeb3; + } + return web3Provider; +} export const getNetworkNameById = (networkId: number): string => { switch (networkId) { @@ -237,8 +255,12 @@ export const getNetworkNameById = (networkId: number): string => { return 'optimism' case 420 : return 'optimism-goerli' + case 11155420 : + return 'optimism-sepolia' case 100 : return 'gnosis' + case 1500 : + return 'stellar' case 137: return 'polygon' case 42220: @@ -262,14 +284,19 @@ export const getNetworkNameById = (networkId: number): string => { } } -export const filterRawDonationsByChain = (gqlResult: { donations: GivethIoDonation[] }, chain ?: "all-other-chains" | "gnosis"): GivethIoDonation[] => { +const ZKEVM_Network_ID = isProduction? 1101: 2442; + +export const filterRawDonationsByChain = (gqlResult: { donations: GivethIoDonation[] }, chain ?: "all-other-chains" | "gnosis" | "zkEVM"): GivethIoDonation[] => { + const donations = groupDonationsByParentRecurringId(gqlResult.donations) if (chain === 'gnosis') { - return gqlResult.donations.filter(donation => donation.transactionNetworkId === 100) + return donations.filter(donation => donation.transactionNetworkId === 100) + } else if (chain === 'zkEVM') { + return donations.filter(donation => donation.transactionNetworkId === ZKEVM_Network_ID) } else if (chain === "all-other-chains") { // Exclude Optimism donations and return all other donations - return gqlResult.donations.filter(donation => donation.transactionNetworkId !== 100) + return donations.filter(donation => donation.transactionNetworkId !== 100 && donation.transactionNetworkId !== ZKEVM_Network_ID) } else { - return gqlResult.donations + return donations } @@ -295,3 +322,10 @@ export const getBlockByTimestamp = async (timestamp: number, chainId: number) :P return 0 } } + +export const isDonationAmountValid = (params: { + donation: GivethIoDonation, minEligibleValueUsd: number, givethCommunityProjectSlug:string +}): boolean => { + const { donation, minEligibleValueUsd, givethCommunityProjectSlug } = params + return donation.valueUsd >= minEligibleValueUsd || donation.project.slug === givethCommunityProjectSlug +}