Skip to content

Commit

Permalink
Merge pull request #83 from input-output-hk/feature/utxo-rewards
Browse files Browse the repository at this point in the history
feat(core|blockfrost): modify utxo method on provider to return delegations & rewards
  • Loading branch information
rhyslbw committed Sep 10, 2021
2 parents c0c5d7e + e0a1bf0 commit 2f7ca7c
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 140 deletions.
15 changes: 11 additions & 4 deletions packages/blockfrost/src/blockfrostProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,21 @@ export const blockfrostProvider = (options: Options): CardanoProvider => {
}
};

const utxo: CardanoProvider['utxo'] = async (addresses) => {
const results = await Promise.all(
const utxoDelegationAndRewards: CardanoProvider['utxoDelegationAndRewards'] = async (addresses, stakeKeyHash) => {
const utxoResults = await Promise.all(
addresses.map(async (address) =>
blockfrost.addressesUtxosAll(address).then((result) => BlockfrostToOgmios.addressUtxoContent(address, result))
)
);
const utxo = utxoResults.flat(1);

return results.flat(1);
const accountResponse = await blockfrost.accounts(stakeKeyHash);
const delegationAndRewards = {
delegate: accountResponse.pool_id,
rewards: Number(accountResponse.withdrawable_amount)
};

return { utxo, delegationAndRewards };
};

const queryTransactionsByAddresses: CardanoProvider['queryTransactionsByAddresses'] = async (addresses) => {
Expand All @@ -55,7 +62,7 @@ export const blockfrostProvider = (options: Options): CardanoProvider => {

return {
submitTx,
utxo,
utxoDelegationAndRewards,
queryTransactionsByAddresses,
queryTransactionsByHashes
};
Expand Down
45 changes: 31 additions & 14 deletions packages/blockfrost/test/blockfrostProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ jest.mock('@blockfrost/blockfrost-js');
describe('blockfrostProvider', () => {
const apiKey = 'someapikey';

test('utxo', async () => {
const mockedResponse = [
test('utxoDelegationAndRewards', async () => {
const addressesUtxosAllMockResponse = [
{
tx_hash: '0f3abbc8fc19c2e61bab6059bf8a466e6e754833a08a62a6c56fe0e78f19d9d5',
tx_index: 0,
Expand Down Expand Up @@ -40,21 +40,35 @@ describe('blockfrostProvider', () => {
block: '500de01367988d4698266ca02148bf2308eab96c0876c9df00ee843772ccb326'
}
];
BlockFrostAPI.prototype.addressesUtxosAll = jest.fn().mockResolvedValue(mockedResponse);
BlockFrostAPI.prototype.addressesUtxosAll = jest.fn().mockResolvedValue(addressesUtxosAllMockResponse);

const client = blockfrostProvider({ projectId: apiKey, isTestnet: true });
const response = await client.utxo([
'addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3jcu5d8ps7zex2k2xt3uqxgjqnnj83ws8lhrn648jjxtwq2ytjqp'
]);
const accountsMockResponse = {
stake_address: 'stake_test1uqfu74w3wh4gfzu8m6e7j987h4lq9r3t7ef5gaw497uu85qsqfy27',
active: true,
active_epoch: 81,
controlled_amount: '95565690389731',
rewards_sum: '615803862289',
withdrawals_sum: '0',
reserves_sum: '0',
treasury_sum: '0',
withdrawable_amount: '615803862289',
pool_id: 'pool1y6chk7x7fup4ms9leesdr57r4qy9cwxuee0msan72x976a6u0nc'
};
BlockFrostAPI.prototype.accounts = jest.fn().mockResolvedValue(accountsMockResponse);

expect(response).toHaveLength(2);
const client = blockfrostProvider({ projectId: apiKey, isTestnet: true });
const response = await client.utxoDelegationAndRewards(
['addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3jcu5d8ps7zex2k2xt3uqxgjqnnj83ws8lhrn648jjxtwq2ytjqp'],
'stake_test1uqfu74w3wh4gfzu8m6e7j987h4lq9r3t7ef5gaw497uu85qsqfy27'
);

expect(response[0]).toHaveLength(2);
expect(response[0][0]).toMatchObject<Cardano.TxIn>({
expect(response.utxo).toBeTruthy();
expect(response.utxo[0]).toHaveLength(2);
expect(response.utxo[0][0]).toMatchObject<Cardano.TxIn>({
txId: '0f3abbc8fc19c2e61bab6059bf8a466e6e754833a08a62a6c56fe0e78f19d9d5',
index: 0
});
expect(response[0][1]).toMatchObject<Cardano.TxOut>({
expect(response.utxo[0][1]).toMatchObject<Cardano.TxOut>({
address:
'addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3jcu5d8ps7zex2k2xt3uqxgjqnnj83ws8lhrn648jjxtwq2ytjqp',
value: {
Expand All @@ -65,19 +79,22 @@ describe('blockfrostProvider', () => {
}
});

expect(response[1]).toHaveLength(2);
expect(response[1][0]).toMatchObject<Cardano.TxIn>({
expect(response.utxo[1]).toHaveLength(2);
expect(response.utxo[1][0]).toMatchObject<Cardano.TxIn>({
txId: '6f04f2cd96b609b8d5675f89fe53159bab859fb1d62bb56c6001ccf58d9ac128',
index: 0
});
expect(response[1][1]).toMatchObject<Cardano.TxOut>({
expect(response.utxo[1][1]).toMatchObject<Cardano.TxOut>({
address:
'addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3jcu5d8ps7zex2k2xt3uqxgjqnnj83ws8lhrn648jjxtwq2ytjqp',
value: {
coins: 1_097_647,
assets: {}
}
});

expect(response.delegationAndRewards.delegate).toEqual(accountsMockResponse.pool_id);
expect(response.delegationAndRewards.rewards).toEqual(Number(accountsMockResponse.withdrawable_amount));
});

test('queryTransactionsByAddresses', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,53 +35,9 @@ export const cardanoGraphqlDbSyncProvider = (uri: string): CardanoProvider => {
}
};

const utxo: CardanoProvider['utxo'] = async (addresses) => {
const query = gql`
query ($addresses: [String]!) {
utxos(where: { address: { _in: $addresses } }) {
transaction {
hash
}
index
address
value # coins
tokens {
asset {
assetId # asset key
}
quantity
}
}
}
`;

type Utxo = {
transaction: { hash: Cardano.Hash16 };
index: number;
address: Cardano.Address;
value: string;
tokens: {
asset: {
assetId: string;
};
quantity: string;
}[];
};
type Response = { utxos: Utxo[] };
type Variables = { addresses: string[] };

const response = await client.request<Response, Variables>(query, { addresses });

return response.utxos.map((uxto) => {
const assets: Cardano.Value['assets'] = {};

for (const t of uxto.tokens) assets[t.asset.assetId] = BigInt(t.quantity);

return [
{ txId: uxto.transaction.hash, index: uxto.index },
{ address: uxto.address, value: { coins: Number(uxto.value), assets } }
];
});
// eslint-disable-next-line unicorn/consistent-function-scoping
const utxoDelegationAndRewards: CardanoProvider['utxoDelegationAndRewards'] = async () => {
throw new Error('Not implemented yet.');
};

const queryTransactionsByAddresses: CardanoProvider['queryTransactionsByAddresses'] = async (addresses) => {
Expand Down Expand Up @@ -170,7 +126,7 @@ export const cardanoGraphqlDbSyncProvider = (uri: string): CardanoProvider => {

return {
submitTx,
utxo,
utxoDelegationAndRewards,
queryTransactionsByAddresses,
queryTransactionsByHashes
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,86 +1,13 @@
/* eslint-disable max-len */

import { GraphQLClient } from 'graphql-request';
import { Schema as Cardano } from '@cardano-ogmios/client';
import { Tx } from '@cardano-sdk/core';
import { cardanoGraphqlDbSyncProvider } from '../src';
jest.mock('graphql-request');

describe('cardanoGraphqlDbSyncProvider', () => {
const uri = 'http://someurl.com';

test('utxo', async () => {
const mockedResponse = {
utxos: [
{
transaction: {
hash: '6f04f2cd96b609b8d5675f89fe53159bab859fb1d62bb56c6001ccf58d9ac128'
},
index: 0,
address:
'addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3jcu5d8ps7zex2k2xt3uqxgjqnnj83ws8lhrn648jjxtwq2ytjqp',
value: '1097647',
tokens: []
},
{
transaction: {
hash: '0f3abbc8fc19c2e61bab6059bf8a466e6e754833a08a62a6c56fe0e78f19d9d5'
},
index: 0,
address:
'addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3jcu5d8ps7zex2k2xt3uqxgjqnnj83ws8lhrn648jjxtwq2ytjqp',
value: '50928877',
tokens: [
{
asset: {
assetId: 'b01fb3b8c3dd6b3705a5dc8bcd5a70759f70ad5d97a72005caeac3c652657675746f31333237'
},
quantity: '1'
}
]
}
]
};
GraphQLClient.prototype.request = jest.fn().mockResolvedValue(mockedResponse);
const client = cardanoGraphqlDbSyncProvider(uri);

const response = await client.utxo([
'addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3jcu5d8ps7zex2k2xt3uqxgjqnnj83ws8lhrn648jjxtwq2ytjqp'
]);

expect(response).toHaveLength(2);

expect(response[0]).toHaveLength(2);
expect(response[0][0]).toMatchObject<Cardano.TxIn>({
txId: '6f04f2cd96b609b8d5675f89fe53159bab859fb1d62bb56c6001ccf58d9ac128',
index: 0
});
expect(response[0][1]).toMatchObject<Cardano.TxOut>({
address:
'addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3jcu5d8ps7zex2k2xt3uqxgjqnnj83ws8lhrn648jjxtwq2ytjqp',
value: {
coins: 1_097_647,
assets: {}
}
});

expect(response[1]).toHaveLength(2);
expect(response[1][0]).toMatchObject<Cardano.TxIn>({
txId: '0f3abbc8fc19c2e61bab6059bf8a466e6e754833a08a62a6c56fe0e78f19d9d5',
index: 0
});
expect(response[1][1]).toMatchObject<Cardano.TxOut>({
address:
'addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3jcu5d8ps7zex2k2xt3uqxgjqnnj83ws8lhrn648jjxtwq2ytjqp',
value: {
coins: 50_928_877,
assets: {
b01fb3b8c3dd6b3705a5dc8bcd5a70759f70ad5d97a72005caeac3c652657675746f31333237: BigInt(1)
}
}
});
});

test('queryTransactionsByAddresses', async () => {
const mockedResponse = {
transactions: [
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/Provider/CardanoProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import { Tx } from '../Transaction';
export interface CardanoProvider {
/** @param signedTransaction signed and serialized cbor */
submitTx: (signedTransaction: string) => Promise<boolean>;
utxo: (addresses: Cardano.Address[]) => Promise<Cardano.Utxo>;
utxoDelegationAndRewards: (
addresses: Cardano.Address[],
stakeKeyHash: Cardano.Hash16
) => Promise<{ utxo: Cardano.Utxo; delegationAndRewards: Cardano.DelegationsAndRewards }>;
queryTransactionsByAddresses: (addresses: Cardano.Address[]) => Promise<Tx[]>;
queryTransactionsByHashes: (hashes: Cardano.Hash16[]) => Promise<Tx[]>;
}

0 comments on commit 2f7ca7c

Please sign in to comment.