Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release: Feature import donations from qf round #1251

Merged
merged 3 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/example.env
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ SOLANA_NODE_RPC_URL=
SOLANA_MAINNET_NODE_RPC_URL=
SOLANA_DEVNET_NODE_RPC_URL=
SOLANA_TEST_NODE_RPC_URL=

ENABLE_IMPORT_LOST_DONATIONS=
IMPORT_LOST_DONATIONS_CRONJOB_EXPRESSION=
LOST_DONATIONS_TX_HASHES=
Expand Down
2 changes: 1 addition & 1 deletion config/test.env
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ SOLANA_TEST_NODE_RPC_URL=

ENABLE_IMPORT_LOST_DONATIONS=false
IMPORT_LOST_DONATIONS_CRONJOB_EXPRESSION=
LOST_DONATIONS_TX_HASHES=0x4012421fbc2cecc85804f3b98bdd31bef04589dbac8292deca33e699868af01f,0x067e91368272dc73bc715a21a2af863a333cde20f410189fa53bceaa9cb8c86b
LOST_DONATIONS_TX_HASHES=0x4012421fbc2cecc85804f3b98bdd31bef04589dbac8292deca33e699868af01f
LOST_DONATIONS_QF_ROUND=
LOST_DONATIONS_NETWORK_ID=10

Expand Down
47 changes: 47 additions & 0 deletions migration/1704991714385-addCoingeckoIdToOptimismTokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
import { NETWORK_IDS } from '../src/provider';

const tokenUpdates = {
ETH: 'ethereum',
OP: 'optimism',
WETH: 'weth',
DAI: 'dai',
LINK: 'chainlink',
WBTC: 'wrapped-bitcoin',
SNX: 'havven',
USDT: 'tether',
USDC: 'usd-coin',
};

export class addCoingeckoIdToOptimismTokens1704991714385
implements MigrationInterface
{
public async up(queryRunner: QueryRunner): Promise<void> {
const tokens = await queryRunner.query(
`SELECT * FROM token WHERE "networkId" = $1 OR "networkId" = $2`,
[NETWORK_IDS.OPTIMISTIC, NETWORK_IDS.OPTIMISM_GOERLI],
);

for (const token of tokens) {
const coingeckoId = tokenUpdates[token.symbol];
if (coingeckoId) {
await queryRunner.query(
`
UPDATE token
SET "coingeckoId" = $1
WHERE id = $2
`,
[coingeckoId, token.id],
);
}
}
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
UPDATE public.token
SET "coingeckoId" = NULL
WHERE "networkId" = 10 OR "networkId" = 420
`);
}
}
13 changes: 13 additions & 0 deletions migration/data/seedTokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1058,27 +1058,31 @@ const seedTokens: ITokenData[] = [
address: '0x0000000000000000000000000000000000000000',
decimals: 18,
networkId: NETWORK_IDS.OPTIMISM_GOERLI,
coingeckoId: 'ethereum',
},
{
name: 'OPTIMISM Goerli OP token',
symbol: 'OP',
address: '0x4200000000000000000000000000000000000042',
decimals: 18,
networkId: NETWORK_IDS.OPTIMISM_GOERLI,
coingeckoId: 'optimism',
},
{
name: 'Wrapped Ether',
symbol: 'WETH',
address: '0x4200000000000000000000000000000000000006',
decimals: 18,
networkId: NETWORK_IDS.OPTIMISM_GOERLI,
coingeckoId: 'weth',
},
{
name: 'Dai',
symbol: 'DAI',
address: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
decimals: 18,
networkId: NETWORK_IDS.OPTIMISM_GOERLI,
coingeckoId: 'dai',
},

// OPTIMISTIC tokens
Expand All @@ -1088,62 +1092,71 @@ const seedTokens: ITokenData[] = [
address: '0x0000000000000000000000000000000000000000',
decimals: 18,
networkId: NETWORK_IDS.OPTIMISTIC,
coingeckoId: 'ethereum',
},
{
name: 'OPTIMISTIC OP token',
symbol: 'OP',
address: '0x4200000000000000000000000000000000000042',
decimals: 18,
networkId: NETWORK_IDS.OPTIMISTIC,
coingeckoId: 'optimism',
},
{
name: 'Chainlink',
symbol: 'LINK',
address: '0x350a791bfc2c21f9ed5d10980dad2e2638ffa7f6',
decimals: 18,
networkId: NETWORK_IDS.OPTIMISTIC,
coingeckoId: 'chainlink',
},
{
name: 'Wrapped Bitcoin',
symbol: 'WBTC',
address: '0x68f180fcce6836688e9084f035309e29bf0a2095',
decimals: 8,
networkId: NETWORK_IDS.OPTIMISTIC,
coingeckoId: 'wrapped-bitcoin',
},
{
name: 'Synthetix Network',
symbol: 'SNX',
address: '0x8700daec35af8ff88c16bdf0418774cb3d7599b4',
decimals: 18,
networkId: NETWORK_IDS.OPTIMISTIC,
coingeckoId: 'havven',
},
{
name: 'Wrapped Ether',
symbol: 'WETH',
address: '0x4200000000000000000000000000000000000006',
decimals: 18,
networkId: NETWORK_IDS.OPTIMISTIC,
coingeckoId: 'weth',
},
{
name: 'Tether',
symbol: 'USDT',
address: '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58',
decimals: 6,
networkId: NETWORK_IDS.OPTIMISTIC,
coingeckoId: 'tether',
},
{
name: 'USD Coin',
symbol: 'USDC',
address: '0x7F5c764cBc14f9669B88837ca1490cCa17c31607',
decimals: 6,
networkId: NETWORK_IDS.OPTIMISTIC,
coingeckoId: 'usd-coin',
},
{
name: 'Dai',
symbol: 'DAI',
address: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
decimals: 18,
networkId: NETWORK_IDS.OPTIMISTIC,
coingeckoId: 'dai',
},
// CELO tokens
{
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "giveth-graphql-api",
"version": "1.19.0",
"version": "1.20.0",
"description": "Backend GraphQL server for Giveth originally forked from Topia",
"main": "./dist/index.js",
"dependencies": {
Expand Down Expand Up @@ -166,6 +166,7 @@
"test:projectAddressRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/projectAddressRepository.test.ts",
"test:donationService": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/qfRoundHistoryRepository.test.ts ./src/services/donationService.test.ts",
"test:userService": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/services/userService.test.ts",
"test:lostDonations": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/services/cronJobs/importLostDonationsJob.test.ts",
"test:reactionsService": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/services/reactionsService.test.ts",
"test:powerSnapshotService": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/services/powerSnapshotServices.test.ts",
"test:transactionsService": "NODE_ENV=test mocha ./test/pre-test-scripts.ts src/services/chains/index.test.ts",
Expand Down
23 changes: 23 additions & 0 deletions src/adapters/price/CoingeckoPriceAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
GetTokenPriceAtDateParams,
GetTokenPriceParams,
PriceAdapterInterface,
} from './PriceAdapterInterface';
Expand Down Expand Up @@ -33,6 +34,28 @@ export class CoingeckoPriceAdapter implements PriceAdapterInterface {
return result.priceUsd;
}

async getTokenPriceAtDate(
params: GetTokenPriceAtDateParams,
): Promise<number> {
try {
const result = await axios.get(
// symbol in here means coingecko id for instance for ETC token the coingecko id is ethereum-classic
`https://api.coingecko.com/api/v3/coins/${params.symbol}/history?date=${params.date}`,
);

const priceUsd = result?.data?.market_data?.current_price?.usd;
if (!priceUsd) {
throw new Error(
`History Price not found for ${params.symbol} in coingecko`,
);
}
return priceUsd;
} catch (e) {
logger.error('Error in CoingeckoHistoricPriceAdapter', e);
throw e;
}
}

async getTokenPrice(params: GetTokenPriceParams): Promise<number> {
try {
const cachedPrice = await this.readTokenFromCache(params.symbol);
Expand Down
6 changes: 6 additions & 0 deletions src/adapters/price/PriceAdapterInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ export interface GetTokenPriceParams {
symbol: string;
networkId: number;
}

export interface GetTokenPriceAtDateParams {
symbol: string;
date: string;
}

export interface PriceAdapterInterface {
getTokenPrice(params: GetTokenPriceParams): Promise<number>;
}
5 changes: 5 additions & 0 deletions src/server/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import { isTestEnv } from '../utils/utils';
import { runCheckActiveStatusOfQfRounds } from '../services/cronJobs/checkActiveStatusQfRounds';
import { runUpdateProjectCampaignsCacheJob } from '../services/cronJobs/updateProjectCampaignsCacheJob';
import { corsOptions } from './cors';
import { runSyncLostDonations } from '../services/cronJobs/importLostDonationsJob';

Resource.validate = validate;

Expand Down Expand Up @@ -335,6 +336,10 @@ export async function bootstrap() {
runSyncPoignArtDonations();
}

if ((config.get('ENABLE_IMPORT_LOST_DONATIONS') as string) === 'true') {
runSyncLostDonations();
}

if (
(config.get('FILL_POWER_SNAPSHOT_BALANCE_SERVICE_ACTIVE') as string) ===
'true'
Expand Down
70 changes: 70 additions & 0 deletions src/services/cronJobs/importLostDonationsJob.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { assert } from 'chai';
import {
createProjectData,
generateRandomEtheriumAddress,
saveProjectDirectlyToDb,
saveUserDirectlyToDb,
} from '../../../test/testUtils';
import { importLostDonations } from './importLostDonationsJob';
import { Donation } from '../../entities/donation';

describe('importLostDonations() test cases', importLostDonationsTestCases);

function importLostDonationsTestCases() {
it('should create a eth simple transfer donation and erc20 token transfer donation', async () => {
// eth donation
const transactionIdEth =
'0x4012421fbc2cecc85804f3b98bdd31bef04589dbac8292deca33e699868af01f';
const toWalletAddressEth = '0x6e8873085530406995170da467010565968c7c62';
const walletAddressEth = '0x317bbc1927be411cd05615d2ffdf8d320c6c4052';
const walletAddress2Eth = generateRandomEtheriumAddress();
const userEth = await saveUserDirectlyToDb(walletAddressEth);
const user2Eth = await saveUserDirectlyToDb(walletAddress2Eth);
const project1 = await saveProjectDirectlyToDb({
// test project with real tx
...createProjectData(),
admin: String(user2Eth.id),
walletAddress: toWalletAddressEth,
});

// // optimism donation from safe SKIP
// const transactionIdOP =
// '0x067e91368272dc73bc715a21a2af863a333cde20f410189fa53bceaa9cb8c86b';
// const toWalletAddressOP = '0xa64f2228ccec96076c82abb903021c33859082f8';
// const walletAddressOP = '0x40891ce6e8574bb9118913a8a304195437f36213';
// const walletAddress2OP = generateRandomEtheriumAddress();
// const userOP = await saveUserDirectlyToDb(walletAddressOP);
// const user2OP = await saveUserDirectlyToDb(walletAddress2OP);
// const project2 = await saveProjectDirectlyToDb({
// // test project with real tx
// ...createProjectData(),
// admin: String(user2OP.id),
// walletAddress: toWalletAddressOP,
// });
await importLostDonations();

const createdDonationEth = await Donation.createQueryBuilder('donation')
.where(`donation."transactionId" = :transactionIdEth`, {
transactionIdEth,
})
.getOne();

// const createdDonationOP = await Donation.createQueryBuilder('donation')
// .where(`donation."transactionId" = :transactionIdOP`, { transactionIdOP })
// .getOne();

assert.equal(createdDonationEth?.toWalletAddress, toWalletAddressEth);
assert.equal(createdDonationEth?.fromWalletAddress, walletAddressEth);
assert.equal(createdDonationEth?.transactionId, transactionIdEth);
assert.equal(createdDonationEth?.projectId, project1.id);
assert.isTrue(createdDonationEth?.amount! > 0);
assert.isTrue(createdDonationEth?.valueUsd! > 0);

// assert.equal(createdDonationOP?.toWalletAddress, toWalletAddressOP);
// assert.equal(createdDonationOP?.fromWalletAddress, walletAddressOP);
// assert.equal(createdDonationOP?.transactionId, transactionIdOP);
// assert.equal(createdDonationOP?.projectId, project2.id);
// assert.isTrue(createdDonationOP?.amount! > 0);
// assert.isTrue(createdDonationOP?.valueUsd! > 0);
});
}
Loading
Loading