All files / src/RewardsProvider BlockfrostRewardsProvider.ts

87.5% Statements 21/24
62.5% Branches 5/8
100% Functions 12/12
85.71% Lines 18/21

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 5817x   17x         17x   17x   3x       1x 1x 1x                             3x 3x   4x 4x       4x       4x 104x 4x             3x 3x      
import { Cardano, Reward, RewardAccountBalanceArgs, RewardsHistoryArgs, RewardsProvider } from '@cardano-sdk/core';
 
import { BlockfrostClient, BlockfrostProvider, fetchSequentially, isBlockfrostNotFoundError } from '../blockfrost';
import { Logger } from 'ts-log';
import { Range } from '@cardano-sdk/util';
import type { Responses } from '@blockfrost/blockfrost-js';
 
const stringToBigInt = (str: string) => BigInt(str);
 
export class BlockfrostRewardsProvider extends BlockfrostProvider implements RewardsProvider {
  constructor(client: BlockfrostClient, logger: Logger) {
    super(client, logger);
  }
 
  public async rewardAccountBalance({ rewardAccount }: RewardAccountBalanceArgs) {
    try {
      const accountResponse = await this.request<Responses['account_content']>(`accounts/${rewardAccount.toString()}`);
      return BigInt(accountResponse.withdrawable_amount);
    } catch (error) {
      Iif (isBlockfrostNotFoundError(error)) {
        return 0n;
      }
      throw this.toProviderError(error);
    }
  }
  protected async accountRewards(
    stakeAddress: Cardano.RewardAccount,
    {
      lowerBound = Cardano.EpochNo(0),
      upperBound = Cardano.EpochNo(Number.MAX_SAFE_INTEGER)
    }: Range<Cardano.EpochNo> = {}
  ): Promise<Reward[]> {
    const batchSize = 100;
    return fetchSequentially<Reward, Responses['account_reward_content'][0]>({
      haveEnoughItems: (_, rewardsPage) => {
        const lastReward = rewardsPage[rewardsPage.length - 1];
        return !lastReward || lastReward.epoch >= upperBound;
      },
      paginationOptions: { count: batchSize },
      request: (paginationQueryString) =>
        this.request<Responses['account_reward_content']>(
          `accounts/${stakeAddress.toString()}/rewards?${paginationQueryString}`
        ),
      responseTranslator: (rewardsPage) =>
        rewardsPage
          .filter(({ epoch }) => lowerBound <= epoch && epoch <= upperBound)
          .map(({ epoch, amount }) => ({
            epoch: Cardano.EpochNo(epoch),
            rewards: stringToBigInt(amount)
          }))
    });
  }
  public async rewardsHistory({ rewardAccounts, epochs }: RewardsHistoryArgs) {
    const allAddressRewards = await Promise.all(rewardAccounts.map((address) => this.accountRewards(address, epochs)));
    return new Map(allAddressRewards.map((epochRewards, i) => [rewardAccounts[i], epochRewards]));
  }
}