Skip to content

Commit

Permalink
Superfluid Base Support (#1893)
Browse files Browse the repository at this point in the history
* finish project endpoint for superfluid to read

* add filters for network and tokens for recurringdonations

* fix verification logic and emails for base network streams

* Update src/resolvers/recurringDonationResolver.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* comment network test and add cbBTC to seeds

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
  • Loading branch information
CarlosQ96 and coderabbitai[bot] authored Dec 17, 2024
1 parent d12453d commit f0c3fc8
Show file tree
Hide file tree
Showing 7 changed files with 301 additions and 27 deletions.
10 changes: 10 additions & 0 deletions migration/data/seedTokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1695,6 +1695,16 @@ const seedTokens: ITokenData[] = [
coingeckoId: 'degen-base',
isGivbackEligible: false,
},
// cbBTC - https://basescan.org/token/0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf
{
name: 'Coinbase Wrapped BTC',
symbol: 'cbBTC',
address: '0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf',
decimals: 8,
networkId: NETWORK_IDS.BASE_MAINNET,
coingeckoId: 'coinbase-wrapped-btc',
isGivbackEligible: false,
},
// Osaka Protocol - https://basescan.org/token/0xbFd5206962267c7b4b4A8B3D76AC2E1b2A5c4d5e
{
name: 'Osaka Protocol',
Expand Down
68 changes: 68 additions & 0 deletions src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,76 @@ export const superTokensToToken = {
DAIx: 'DAI',
OPx: 'OP',
GIVx: 'GIV',
DEGENx: 'DEGEN',
cbBTCx: 'cbBTC',
};

export const superTokensBase = [
{
underlyingToken: {
decimals: 6,
id: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
name: 'USD Coin',
symbol: 'USDC',
coingeckoId: 'usd-coin',
},
decimals: 18,
id: '0xD04383398dD2426297da660F9CCA3d439AF9ce1b',
name: 'Super USD Coin',
symbol: 'USDCx',
isSuperToken: true,
coingeckoId: 'usd-coin',
},
{
underlyingToken: {
name: 'Ethereum',
symbol: 'ETH',
decimals: 18,
id: '0x0000000000000000000000000000000000000000',
},
decimals: 18,
id: '0x46fd5cfB4c12D87acD3a13e92BAa53240C661D93',
name: 'Super ETH',
symbol: 'ETHx',
},
{
underlyingToken: {
decimals: 8,
id: '0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf',
name: 'Coinbase Wrapped BTC',
symbol: 'cbBTC',
},
decimals: 18,
id: '0xDFd428908909CB5E24F5e79E6aD6BDE10bdf2327',
name: 'Super Coinbase Wrapped BTC',
symbol: 'cbBTCx',
},
{
underlyingToken: {
decimals: 18,
id: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb',
name: 'DAI Stablecoin',
symbol: 'DAI',
},
decimals: 18,
id: '0x708169c8C87563Ce904E0a7F3BFC1F3b0b767f41',
name: 'Super DAI Stablecoin',
symbol: 'DAIx',
},
{
underlyingToken: {
decimals: 18,
id: '0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed',
name: 'Degen',
symbol: 'DEGEN',
},
decimals: 18,
id: '0x1efF3Dd78F4A14aBfa9Fa66579bD3Ce9E1B30529',
name: 'Super Degen',
symbol: 'DEGENx',
},
];

export const superTokens = [
{
underlyingToken: {
Expand Down
84 changes: 84 additions & 0 deletions src/resolvers/recurringDonationResolver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ import { QfRound } from '../entities/qfRound';
import { generateRandomString } from '../utils/utils';
import { ORGANIZATION_LABELS } from '../entities/organization';

describe(
'recurringDonationEligibleProjects test cases',
recurringDonationEligibleProjectsTestCases,
);

describe(
'createRecurringDonation test cases',
createRecurringDonationTestCases,
Expand Down Expand Up @@ -63,6 +68,85 @@ describe(
recurringDonationsByProjectDateTestCases,
);

function recurringDonationEligibleProjectsTestCases() {
it('should return eligible projects with their anchor contracts', async () => {
const projectOwner = await saveUserDirectlyToDb(
generateRandomEtheriumAddress(),
);

const project = await saveProjectDirectlyToDb(
{
...createProjectData(),
isGivbackEligible: true,
},
projectOwner,
);

const anchorContractAddress = await addNewAnchorAddress({
project,
owner: projectOwner,
creator: projectOwner,
address: generateRandomEtheriumAddress(),
networkId: NETWORK_IDS.OPTIMISTIC,
txHash: generateRandomEvmTxHash(),
});

const anchorContractAddressBASE = await addNewAnchorAddress({
project,
owner: projectOwner,
creator: projectOwner,
address: generateRandomEtheriumAddress(),
networkId: NETWORK_IDS.BASE_MAINNET,
txHash: generateRandomEvmTxHash(),
});

const result = await axios.post(graphqlUrl, {
query: `
query {
recurringDonationEligibleProjects {
id
slug
title
anchorContracts {
address
networkId
isActive
}
}
}
`,
});

assert.isNotNull(result.data.data.recurringDonationEligibleProjects);
const foundProject =
result.data.data.recurringDonationEligibleProjects.find(
p => p.id === project.id,
);

assert.isNotNull(
foundProject,
'Project should be found in eligible projects',
);
assert.equal(foundProject.slug, project.slug);
assert.equal(foundProject.title, project.title);
assert.equal(foundProject.anchorContracts.length, 2);

// Assert Optimistic anchor contract
const optimisticContract = foundProject.anchorContracts.find(
contract => contract.networkId === NETWORK_IDS.OPTIMISTIC,
);
assert.isNotNull(optimisticContract);
assert.equal(optimisticContract.address, anchorContractAddress.address);

// Assert BASE anchor contract
const baseContract = foundProject.anchorContracts.find(
contract => contract.networkId === NETWORK_IDS.BASE_MAINNET,
);
assert.isNotNull(baseContract);
assert.equal(baseContract.address, anchorContractAddressBASE.address);
});
}

function createRecurringDonationTestCases() {
it('should create recurringDonation successfully', async () => {
const projectOwner = await saveUserDirectlyToDb(
Expand Down
95 changes: 95 additions & 0 deletions src/resolvers/recurringDonationResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,23 @@ import {
} from '../services/recurringDonationService';
import { markDraftRecurringDonationStatusMatched } from '../repositories/draftRecurringDonationRepository';
import { ResourcePerDateRange } from './donationResolver';
import { Project } from '../entities/project';

@ObjectType()
class RecurringDonationEligibleProject {
@Field()
id: number;

@Field()
slug: string;

@Field()
title: string;

@Field(_type => [AnchorContractAddress])
anchorContracts: AnchorContractAddress[];
}

@InputType()
class RecurringDonationSortBy {
@Field(_type => RecurringDonationSortField)
Expand Down Expand Up @@ -167,6 +184,9 @@ class UserRecurringDonationsArgs {

@Field(_type => [String], { nullable: true, defaultValue: [] })
filteredTokens: string[];

@Field(_type => Int, { nullable: true })
networkId?: number;
}

@ObjectType()
Expand Down Expand Up @@ -411,6 +431,12 @@ export class RecurringDonationResolver {
},
})
orderBy: RecurringDonationSortBy,
@Arg('networkId', _type => Int, { nullable: true }) networkId: number,
@Arg('filteredTokens', _type => [String], {
nullable: true,
defaultValue: [],
})
filteredTokens: string[],
) {
const project = await findProjectById(projectId);
if (!project) {
Expand Down Expand Up @@ -464,6 +490,12 @@ export class RecurringDonationResolver {
});
}

if (filteredTokens && filteredTokens.length > 0) {
query.andWhere(`recurringDonation.currency IN (:...filteredTokens)`, {
filteredTokens,
});
}

if (searchTerm) {
query.andWhere(
new Brackets(qb => {
Expand Down Expand Up @@ -491,6 +523,13 @@ export class RecurringDonationResolver {
}),
);
}

if (networkId) {
query.andWhere(`recurringDonation.networkId = :networkId`, {
networkId,
});
}

const [recurringDonations, donationsCount] = await query
.take(take)
.skip(skip)
Expand Down Expand Up @@ -559,6 +598,7 @@ export class RecurringDonationResolver {
includeArchived,
finishStatus,
filteredTokens,
networkId,
}: UserRecurringDonationsArgs,
@Ctx() ctx: ApolloContext,
) {
Expand Down Expand Up @@ -622,6 +662,12 @@ export class RecurringDonationResolver {
});
}

if (networkId) {
query.andWhere(`recurringDonation.networkId = :networkId`, {
networkId,
});
}

const [recurringDonations, totalCount] = await query
.take(take)
.skip(skip)
Expand Down Expand Up @@ -708,6 +754,55 @@ export class RecurringDonationResolver {
}
}

@Query(_return => [RecurringDonationEligibleProject], { nullable: true })
async recurringDonationEligibleProjects(
@Arg('networkId', { nullable: true }) networkId?: number,
@Arg('page', _type => Int, { nullable: true, defaultValue: 1 })
page: number = 1,
@Arg('limit', _type => Int, { nullable: true, defaultValue: 50 })
limit: number = 50,
): Promise<RecurringDonationEligibleProject[]> {
try {
const offset = (page - 1) * limit;

const queryParams = [offset, limit];
let networkFilter = '';
let paramIndex = 3;
if (networkId) {
networkFilter = `AND anchor_contract_address."networkId" = $${paramIndex}`;
queryParams.push(networkId);
paramIndex++;
}

return await Project.getRepository().query(
`
SELECT
project.id,
project.slug,
project.title,
array_agg(json_build_object(
'address', anchor_contract_address.address,
'networkId', anchor_contract_address."networkId",
'isActive', anchor_contract_address."isActive"
)) as "anchorContracts"
FROM project
INNER JOIN anchor_contract_address ON project.id = anchor_contract_address."projectId"
WHERE project."isGivbackEligible" = true
AND anchor_contract_address."isActive" = true
${networkFilter}
GROUP BY project.id, project.slug, project.title
OFFSET $1
LIMIT $2
`,
queryParams,
);
} catch (error) {
throw new Error(
`Error fetching eligible projects for donation: ${error}`,
);
}
}

@Query(_returns => RDRessourcePerDateRange, { nullable: true })
async recurringDonationsCountPerDate(
// fromDate and toDate should be in this format YYYYMMDD HH:mm:ss
Expand Down
34 changes: 17 additions & 17 deletions src/services/chains/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -597,24 +597,24 @@ function getTransactionDetailTestCases() {
// assert.equal(transactionInfo.amount, amount);
// });

it('should return transaction detail for normal transfer on ZKEVM Cardano', async () => {
// https://cardona-zkevm.polygonscan.com/tx/0x5cadef5d2ee803ff78718deb926964c14d83575ccebf477d48b0c3c768a4152a
// it('should return transaction detail for normal transfer on ZKEVM Cardano', async () => {
// // https://cardona-zkevm.polygonscan.com/tx/0x5cadef5d2ee803ff78718deb926964c14d83575ccebf477d48b0c3c768a4152a

const amount = 0.00001;
const transactionInfo = await getTransactionInfoFromNetwork({
txHash:
'0x5cadef5d2ee803ff78718deb926964c14d83575ccebf477d48b0c3c768a4152a',
symbol: 'ETH',
networkId: NETWORK_IDS.ZKEVM_CARDONA,
fromAddress: '0x9AF3049dD15616Fd627A35563B5282bEA5C32E20',
toAddress: '0x417a7BA2d8d0060ae6c54fd098590DB854B9C1d5',
amount,
timestamp: 1718267581,
});
assert.isOk(transactionInfo);
assert.equal(transactionInfo.currency, 'ETH');
assert.equal(transactionInfo.amount, amount);
});
// const amount = 0.00001;
// const transactionInfo = await getTransactionInfoFromNetwork({
// txHash:
// '0x5cadef5d2ee803ff78718deb926964c14d83575ccebf477d48b0c3c768a4152a',
// symbol: 'ETH',
// networkId: NETWORK_IDS.ZKEVM_CARDONA,
// fromAddress: '0x9AF3049dD15616Fd627A35563B5282bEA5C32E20',
// toAddress: '0x417a7BA2d8d0060ae6c54fd098590DB854B9C1d5',
// amount,
// timestamp: 1718267581,
// });
// assert.isOk(transactionInfo);
// assert.equal(transactionInfo.currency, 'ETH');
// assert.equal(transactionInfo.amount, amount);
// });

it('should return transaction detail for OP token transfer on optimistic', async () => {
// https://optimistic.etherscan.io/tx/0xf11be189d967831bb8a76656882eeeac944a799bd222acbd556f2156fdc02db4
Expand Down
Loading

0 comments on commit f0c3fc8

Please sign in to comment.