From 46cc87880fed5118560c76dd62e7b038eb5df689 Mon Sep 17 00:00:00 2001 From: Rhys Bartels-Waller Date: Thu, 8 Oct 2020 21:21:58 +1100 Subject: [PATCH] feature: Support rewards - top-level queries rewards and rewards_aggregate - StakePool.rewards and StakePool.rewards_aggregate --- .../hasura/project/metadata/tables.yaml | 23 ++++++- .../migrations/1589369664961_init/up.sql | 4 +- packages/api-cardano-db-hasura/schema.graphql | 69 +++++++++++++++++++ .../rewards/aggregateRewards.graphql | 19 +++++ .../rewards/rewardsForAddress.graphql | 15 ++++ .../stake_pools/allStakePoolFields.graphql | 15 ++++ .../src/executableSchema.ts | 20 ++++++ .../test/rewards.query.test.ts | 41 +++++++++++ .../test/stakePool.query.test.ts | 3 + 9 files changed, 204 insertions(+), 5 deletions(-) create mode 100644 packages/api-cardano-db-hasura/src/example_queries/rewards/aggregateRewards.graphql create mode 100644 packages/api-cardano-db-hasura/src/example_queries/rewards/rewardsForAddress.graphql create mode 100644 packages/api-cardano-db-hasura/test/rewards.query.test.ts diff --git a/packages/api-cardano-db-hasura/hasura/project/metadata/tables.yaml b/packages/api-cardano-db-hasura/hasura/project/metadata/tables.yaml index 4028f66a..263e17c4 100644 --- a/packages/api-cardano-db-hasura/hasura/project/metadata/tables.yaml +++ b/packages/api-cardano-db-hasura/hasura/project/metadata/tables.yaml @@ -175,19 +175,28 @@ select: rewards custom_column_names: {} object_relationships: - - name: transaction + - name: earnedIn using: manual_configuration: remote_table: schema: public - name: Transaction + name: Epoch column_mapping: - tx_id: id + epochNo: number + - name: stakePool + using: + manual_configuration: + remote_table: + schema: public + name: StakePool + column_mapping: + pool_hash: hash select_permissions: - role: cardano-graphql permission: columns: - address + - amount filter: {} limit: 100 allow_aggregations: true @@ -279,6 +288,14 @@ name: StakePoolRetirement column_mapping: hash: pool_hash + - name: rewards + using: + manual_configuration: + remote_table: + schema: public + name: Reward + column_mapping: + hash: pool_hash select_permissions: - role: cardano-graphql permission: diff --git a/packages/api-cardano-db-hasura/hasura/project/migrations/1589369664961_init/up.sql b/packages/api-cardano-db-hasura/hasura/project/migrations/1589369664961_init/up.sql index 09d26505..cd516690 100644 --- a/packages/api-cardano-db-hasura/hasura/project/migrations/1589369664961_init/up.sql +++ b/packages/api-cardano-db-hasura/hasura/project/migrations/1589369664961_init/up.sql @@ -59,13 +59,13 @@ FROM epoch; CREATE VIEW "Reward" AS SELECT reward.amount AS "amount", - reward.id AS "id", ( SELECT stake_address.view FROM stake_address WHERE stake_address.id = reward.addr_id ) AS "address", - reward.tx_id AS "tx_id" + reward.epoch_no AS "epochNo", + ( SELECT pool_hash.hash FROM pool_hash WHERE pool_hash.id = reward.pool_id ) AS "pool_hash" FROM reward; CREATE VIEW "SlotLeader" AS diff --git a/packages/api-cardano-db-hasura/schema.graphql b/packages/api-cardano-db-hasura/schema.graphql index b69aec75..152db72b 100644 --- a/packages/api-cardano-db-hasura/schema.graphql +++ b/packages/api-cardano-db-hasura/schema.graphql @@ -49,6 +49,18 @@ type Query { offset: Int where: Epoch_bool_exp ): Epoch_aggregate! + rewards ( + limit: Int + order_by: [Reward_order_by!] + offset: Int + where: Reward_bool_exp + ): [Reward]! + rewards_aggregate ( + limit: Int + order_by: [Reward_order_by!] + offset: Int + where: Reward_bool_exp + ): Reward_aggregate! stakeDeregistrations ( limit: Int order_by: [StakeDeregistration_order_by!] @@ -180,6 +192,55 @@ input Relay_bool_exp { port: Int_comparison_exp } +type Reward { + address: String! + amount: String! + earnedIn: Epoch! + stakePool: StakePool! +} + +type Reward_aggregate { + aggregate: Reward_aggregate_fields +} + +type Reward_aggregate_fields { + avg: Reward_avg_fields! + count: String! + max: Reward_max_fields! + min: Reward_min_fields! + sum: Reward_sum_fields! +} + +type Reward_avg_fields { + amount: Float +} + +type Reward_max_fields { + amount: String +} + +type Reward_min_fields { + amount: String +} + +type Reward_sum_fields { + amount: String +} + +input Reward_bool_exp { + address: text_comparison_exp + amount: text_comparison_exp + earnedIn: Epoch_bool_exp + stakePool: StakePool_bool_exp +} + +input Reward_order_by { + address: text_comparison_exp + amount: text_comparison_exp + earnedIn: Epoch_order_by + stakePool: StakePool_order_by +} + type StakeDeregistration { address: String! transaction: Transaction! @@ -228,6 +289,13 @@ type StakePool { relays: [Relay] retirements: [StakePoolRetirement] rewardAddress: String! + rewards: [Reward]! + rewards_aggregate ( + limit: Int + order_by: [Reward_order_by!] + offset: Int + where: Reward_bool_exp + ): Reward_aggregate! updatedIn: Transaction! url: URL } @@ -255,6 +323,7 @@ input StakePool_bool_exp { relays: Relay_bool_exp retirements: StakePoolRetirement_bool_exp rewardAddress: text_comparison_exp + rewards: Relay_bool_exp url: text_comparison_exp } diff --git a/packages/api-cardano-db-hasura/src/example_queries/rewards/aggregateRewards.graphql b/packages/api-cardano-db-hasura/src/example_queries/rewards/aggregateRewards.graphql new file mode 100644 index 00000000..9fdc5d45 --- /dev/null +++ b/packages/api-cardano-db-hasura/src/example_queries/rewards/aggregateRewards.graphql @@ -0,0 +1,19 @@ +query allRewardsAggregateFields { + rewards_aggregate { + aggregate { + avg { + amount + } + count + max { + amount + } + min { + amount + } + sum { + amount + } + } + } +} diff --git a/packages/api-cardano-db-hasura/src/example_queries/rewards/rewardsForAddress.graphql b/packages/api-cardano-db-hasura/src/example_queries/rewards/rewardsForAddress.graphql new file mode 100644 index 00000000..bf9a3c21 --- /dev/null +++ b/packages/api-cardano-db-hasura/src/example_queries/rewards/rewardsForAddress.graphql @@ -0,0 +1,15 @@ +query rewardsForAddress ( + $limit: Int! + $where: Reward_bool_exp +) { + rewards (limit: $limit, where: $where) { + address + amount + stakePool { + hash + } + earnedIn { + number + } + } +} diff --git a/packages/api-cardano-db-hasura/src/example_queries/stake_pools/allStakePoolFields.graphql b/packages/api-cardano-db-hasura/src/example_queries/stake_pools/allStakePoolFields.graphql index 5beff5cb..a3b56a2a 100644 --- a/packages/api-cardano-db-hasura/src/example_queries/stake_pools/allStakePoolFields.graphql +++ b/packages/api-cardano-db-hasura/src/example_queries/stake_pools/allStakePoolFields.graphql @@ -24,6 +24,21 @@ query allStakePoolFields ( inEffectFrom } rewardAddress + rewards { + address + amount + earnedIn { + number + } + } + rewards_aggregate { + aggregate { + count + sum { + amount + } + } + } updatedIn { hash } diff --git a/packages/api-cardano-db-hasura/src/executableSchema.ts b/packages/api-cardano-db-hasura/src/executableSchema.ts index f6a3b87e..f9f57a83 100644 --- a/packages/api-cardano-db-hasura/src/executableSchema.ts +++ b/packages/api-cardano-db-hasura/src/executableSchema.ts @@ -114,6 +114,26 @@ export async function buildSchema (hasuraClient: HasuraClient) { schema: hasuraSchema }) }, + rewards: (_root, args, context, info) => { + return delegateToSchema({ + args, + context, + fieldName: 'rewards', + info, + operation: 'query', + schema: hasuraSchema + }) + }, + rewards_aggregate: (_root, args, context, info) => { + return delegateToSchema({ + args, + context, + fieldName: 'rewards_aggregate', + info, + operation: 'query', + schema: hasuraSchema + }) + }, stakeDeregistrations: (_root, args, context, info) => { return delegateToSchema({ args, diff --git a/packages/api-cardano-db-hasura/test/rewards.query.test.ts b/packages/api-cardano-db-hasura/test/rewards.query.test.ts new file mode 100644 index 00000000..612e373f --- /dev/null +++ b/packages/api-cardano-db-hasura/test/rewards.query.test.ts @@ -0,0 +1,41 @@ +/* eslint-disable camelcase */ +import path from 'path' + +import { DocumentNode } from 'graphql' +import util from '@cardano-graphql/util' +import { TestClient } from '@cardano-graphql/util-dev' +import { buildClient } from './util' + +function loadQueryNode (name: string): Promise { + return util.loadQueryNode(path.resolve(__dirname, '..', 'src', 'example_queries', 'rewards'), name) +} + +describe('rewards', () => { + let client: TestClient + beforeAll(async () => { + client = await buildClient('http://localhost:3100', 'http://localhost:8090', 5442) + }) + + it('can return details for rewards scoped to an address', async () => { + const result = await client.query({ + query: await loadQueryNode('rewardsForAddress'), + variables: { limit: 5, where: { address: { _eq: 'stake1uyp6rqthh9n7y4rng75tz85t7djy7hny35fw27say5mfxygq3er9k' } } } + }) + const { rewards } = result.data + expect(rewards.length).toBe(5) + expect(rewards[0].stakePool.hash).toBeDefined() + expect(rewards[0].earnedIn.number).toBeDefined() + }) + + it('can return aggregated data on all delegations', async () => { + const result = await client.query({ + query: await loadQueryNode('aggregateRewards') + }) + const { rewards_aggregate } = result.data + expect(parseInt(rewards_aggregate.aggregate.avg.amount)).toBeDefined() + expect(parseInt(rewards_aggregate.aggregate.max.amount)).toBeDefined() + expect(parseInt(rewards_aggregate.aggregate.min.amount)).toBeDefined() + expect(parseInt(rewards_aggregate.aggregate.sum.amount)).toBeDefined() + expect(parseInt(rewards_aggregate.aggregate.count)).toBeGreaterThan(30000) + }) +}) diff --git a/packages/api-cardano-db-hasura/test/stakePool.query.test.ts b/packages/api-cardano-db-hasura/test/stakePool.query.test.ts index 5601df58..b3c293cd 100644 --- a/packages/api-cardano-db-hasura/test/stakePool.query.test.ts +++ b/packages/api-cardano-db-hasura/test/stakePool.query.test.ts @@ -32,6 +32,9 @@ describe('stakePools', () => { expect(stakePools[0].relays).toBeDefined() expect(stakePools[0].retirements).toBeDefined() expect(stakePools[0].rewardAddress.slice(0, 5)).toBe('stake') + expect(stakePools[0].rewards).toBeDefined() + expect(stakePools[0].rewards_aggregate.aggregate.count).toBeDefined() + expect(stakePools[0].rewards_aggregate.aggregate.sum.amount).toBeDefined() expect(stakePools[0].updatedIn.hash).toBeDefined() expect(stakePools[0].url).toBeDefined() })