Skip to content

Commit 1db47b9

Browse files
authored
Merge pull request #1532 from input-output-hk/feat/client-side-blockfrost-providers
LW-11697 Browser compatible Blockfrost Providers
2 parents f9d460a + 6f1acc2 commit 1db47b9

File tree

59 files changed

+989
-1443
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+989
-1443
lines changed

.github/workflows/continuous-integration-blockfrost-e2e.yaml

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,18 @@ env:
1212
STAKE_POOL_TEST_CONNECTION_STRING: 'postgresql://postgres:doNoUseThisSecret!@localhost:5435/stake_pool_test'
1313
TEST_CLIENT_ASSET_PROVIDER: 'blockfrost'
1414
TEST_CLIENT_ASSET_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:3015"}'
15-
TEST_CLIENT_CHAIN_HISTORY_PROVIDER: 'ws'
16-
TEST_CLIENT_CHAIN_HISTORY_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4000/"}'
15+
TEST_CLIENT_CHAIN_HISTORY_PROVIDER: 'blockfrost'
16+
TEST_CLIENT_CHAIN_HISTORY_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:3015"}'
1717
TEST_CLIENT_HANDLE_PROVIDER: 'http'
1818
TEST_CLIENT_HANDLE_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4011/"}'
19-
TEST_CLIENT_NETWORK_INFO_PROVIDER: 'ws'
20-
TEST_CLIENT_NETWORK_INFO_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4000/"}'
21-
TEST_CLIENT_REWARDS_PROVIDER: 'http'
22-
TEST_CLIENT_REWARDS_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4000/"}'
19+
TEST_CLIENT_NETWORK_INFO_PROVIDER: 'blockfrost'
20+
TEST_CLIENT_NETWORK_INFO_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:3015"}'
21+
TEST_CLIENT_REWARDS_PROVIDER: 'blockfrost'
22+
TEST_CLIENT_REWARDS_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:3015"}'
2323
TEST_CLIENT_TX_SUBMIT_PROVIDER: 'http'
2424
TEST_CLIENT_TX_SUBMIT_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4000/"}'
25-
TEST_CLIENT_UTXO_PROVIDER: 'ws'
26-
TEST_CLIENT_UTXO_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4000/"}'
25+
TEST_CLIENT_UTXO_PROVIDER: 'blockfrost'
26+
TEST_CLIENT_UTXO_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:3015"}'
2727
TEST_CLIENT_STAKE_POOL_PROVIDER: 'http'
2828
TEST_CLIENT_STAKE_POOL_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4000/"}'
2929
WS_PROVIDER_URL: 'http://localhost:4100/ws'
@@ -86,7 +86,6 @@ jobs:
8686
- name: 🔬 Test - e2e - wallet at epoch 3
8787
run: |
8888
yarn workspace @cardano-sdk/e2e test:wallet:epoch3
89-
yarn workspace @cardano-sdk/e2e test:blockfrost:providers
9089
9190
- name: Dump docker logs
9291
if: ${{ cancelled() || failure() }}

compose/common.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ x-sdk-environment: &sdk-environment
121121
services:
122122
blockfrost-ryo:
123123
build:
124-
context: 'https://github.com/mirceahasegan/blockfrost-backend-ryo.git#feat/custom-network-support'
124+
context: 'https://github.com/mkazlauskas/blockfrost-backend-ryo.git#feat/custom-network-support'
125125
dockerfile: Dockerfile
126126
environment:
127127
BLOCKFROST_CONFIG_SERVER_LISTEN_ADDRESS: 0.0.0.0

packages/cardano-services-client/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"test:debug": "DEBUG=true yarn test"
4040
},
4141
"devDependencies": {
42+
"@blockfrost/blockfrost-js": "^5.7.0",
4243
"@cardano-sdk/util-dev": "workspace:~",
4344
"@types/lodash": "^4.14.182",
4445
"@types/node-fetch": "^2.6.12",
@@ -57,6 +58,7 @@
5758
},
5859
"dependencies": {
5960
"@cardano-sdk/core": "workspace:~",
61+
"@cardano-sdk/crypto": "workspace:~",
6062
"@cardano-sdk/util": "workspace:~",
6163
"axios": "^1.7.4",
6264
"class-validator": "^0.14.0",
Lines changed: 39 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
// eslint-disable-next-line jsdoc/check-param-names
22
import * as Crypto from '@cardano-sdk/crypto';
3-
import { BlockfrostProvider, BlockfrostProviderDependencies } from '../../util/BlockfrostProvider/BlockfrostProvider';
3+
44
import {
5+
BlockfrostClient,
6+
BlockfrostProvider,
57
BlockfrostToCore,
68
BlockfrostTransactionContent,
79
blockfrostMetadataToTxMetadata,
8-
blockfrostToProviderError,
9-
fetchByAddressSequentially,
10+
fetchSequentially,
1011
isBlockfrostNotFoundError
11-
} from '../../util';
12+
} from '../blockfrost';
1213
import {
1314
BlocksByIdsArgs,
1415
Cardano,
@@ -22,22 +23,19 @@ import {
2223
TransactionsByIdsArgs,
2324
createSlotEpochCalc
2425
} from '@cardano-sdk/core';
25-
import { DB_MAX_SAFE_INTEGER } from '../DbSyncChainHistory/queries';
26-
import { Responses } from '@blockfrost/blockfrost-js';
27-
import { Schemas } from '@blockfrost/blockfrost-js/lib/types/open-api';
26+
import { Logger } from 'ts-log';
2827
import omit from 'lodash/omit.js';
28+
import type { Responses } from '@blockfrost/blockfrost-js';
29+
import type { Schemas } from '@blockfrost/blockfrost-js/lib/types/open-api';
2930

3031
type WithCertIndex<T> = T & { cert_index: number };
31-
32-
export interface BlockfrostChainHistoryProviderDependencies extends BlockfrostProviderDependencies {
33-
networkInfoProvider: NetworkInfoProvider;
34-
}
32+
export const DB_MAX_SAFE_INTEGER = 2_147_483_647;
3533

3634
export class BlockfrostChainHistoryProvider extends BlockfrostProvider implements ChainHistoryProvider {
3735
private networkInfoProvider: NetworkInfoProvider;
3836

39-
constructor({ logger, blockfrost, networkInfoProvider }: BlockfrostChainHistoryProviderDependencies) {
40-
super({ blockfrost, logger });
37+
constructor(client: BlockfrostClient, networkInfoProvider: NetworkInfoProvider, logger: Logger) {
38+
super(client, logger);
4139
this.networkInfoProvider = networkInfoProvider;
4240
}
4341

@@ -46,7 +44,7 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
4644
redeemer_count
4745
}: Responses['tx_content']): Promise<Cardano.Redeemer[] | undefined> {
4846
if (!redeemer_count) return;
49-
return this.blockfrost.txsRedeemers(hash).then((response) =>
47+
return this.request<Responses['tx_content_redeemers']>(`txs/${hash}/redeemers`).then((response) =>
5048
response.map(
5149
({ purpose, script_hash, unit_mem, unit_steps, tx_index }): Cardano.Redeemer => ({
5250
data: Buffer.from(script_hash),
@@ -79,7 +77,7 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
7977
hash
8078
}: Responses['tx_content']): Promise<Cardano.Withdrawal[] | undefined> {
8179
if (!withdrawal_count) return;
82-
return this.blockfrost.txsWithdrawals(hash).then((response) =>
80+
return this.request<Responses['tx_content_withdrawals']>(`txs/${hash}/withdrawals`).then((response) =>
8381
response.map(
8482
({ address, amount }): Cardano.Withdrawal => ({
8583
quantity: BigInt(amount),
@@ -107,7 +105,7 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
107105
}
108106

109107
protected async fetchPoolRetireCerts(hash: string): Promise<WithCertIndex<Cardano.PoolRetirementCertificate>[]> {
110-
return this.blockfrost.txsPoolRetires(hash).then((response) =>
108+
return this.request<Responses['tx_content_pool_retires']>(`txs/${hash}/pool_retires`).then((response) =>
111109
response.map(({ pool_id, retiring_epoch, cert_index }) => ({
112110
__typename: Cardano.CertificateType.PoolRetirement,
113111
cert_index,
@@ -118,7 +116,7 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
118116
}
119117

120118
protected async fetchPoolUpdateCerts(hash: string): Promise<WithCertIndex<Cardano.PoolRegistrationCertificate>[]> {
121-
return this.blockfrost.txsPoolUpdates(hash).then((response) =>
119+
return this.request<Responses['tx_content_pool_certs']>(`txs/${hash}/pool_updates`).then((response) =>
122120
response.map(({ pool_id, cert_index, fixed_cost, margin_cost, pledge, reward_account, vrf_key }) => ({
123121
__typename: Cardano.CertificateType.PoolRegistration,
124122
cert_index,
@@ -138,10 +136,9 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
138136
}
139137

140138
async fetchCBOR(hash: string): Promise<string> {
141-
return this.blockfrost
142-
.instance<Schemas['script_cbor']>(`txs/${hash}/cbor`)
139+
return this.request<Responses['tx_content_cbor']>(`txs/${hash}/cbor`)
143140
.then((response) => {
144-
if (response.body.cbor) return response.body.cbor;
141+
if (response) return response.cbor;
145142
throw new Error('CBOR is null');
146143
})
147144
.catch((_error) => {
@@ -163,7 +160,7 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
163160
}
164161

165162
protected async fetchMirCerts(hash: string): Promise<WithCertIndex<Cardano.MirCertificate>[]> {
166-
return this.blockfrost.txsMirs(hash).then((response) =>
163+
return this.request<Responses['tx_content_mirs']>(`txs/${hash}/mirs`).then((response) =>
167164
response.map(({ address, amount, cert_index, pot }) => ({
168165
__typename: Cardano.CertificateType.MIR,
169166
cert_index,
@@ -176,7 +173,7 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
176173
}
177174

178175
protected async fetchStakeCerts(hash: string): Promise<WithCertIndex<Cardano.StakeAddressCertificate>[]> {
179-
return this.blockfrost.txsStakes(hash).then((response) =>
176+
return this.request<Responses['tx_content_stake_addr']>(`txs/${hash}/stakes`).then((response) =>
180177
response.map(({ address, cert_index, registration }) => ({
181178
__typename: registration
182179
? Cardano.CertificateType.StakeRegistration
@@ -191,7 +188,7 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
191188
}
192189

193190
protected async fetchDelegationCerts(hash: string): Promise<WithCertIndex<Cardano.StakeDelegationCertificate>[]> {
194-
return this.blockfrost.txsDelegations(hash).then((response) =>
191+
return this.request<Responses['tx_content_delegations']>(`txs/${hash}/delegations`).then((response) =>
195192
response.map(({ address, pool_id, cert_index }) => ({
196193
__typename: Cardano.CertificateType.StakeDelegation,
197194
cert_index,
@@ -230,8 +227,7 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
230227

231228
protected async fetchJsonMetadataAsAuxiliaryData(txHash: string): Promise<Cardano.AuxiliaryData | undefined> {
232229
const UNDEFINED = undefined;
233-
return this.blockfrost
234-
.txsMetadata(txHash)
230+
return this.request<Responses['tx_content_metadata']>(`txs/${txHash}/metadata`)
235231
.then((m) => {
236232
const metadata = blockfrostMetadataToTxMetadata(m);
237233
return metadata && metadata.size > 0
@@ -257,7 +253,7 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
257253
}
258254

259255
protected async fetchEpochParameters(epochNo: Cardano.EpochNo): Promise<Schemas['epoch_param_content']> {
260-
return await this.blockfrost.epochsParameters(epochNo);
256+
return await this.request<Responses['epoch_param_content']>(`epochs/${epochNo}/parameters`);
261257
}
262258

263259
protected async processCertificates(
@@ -375,7 +371,7 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
375371
const txFromCBOR = await this.fetchDetailsFromCBOR(id);
376372
if (!txFromCBOR) return;
377373

378-
const utxos: Schemas['tx_content_utxo'] = (await this.blockfrost.txsUtxos(id)) as Schemas['tx_content_utxo'];
374+
const utxos = await this.request<Responses['tx_content_utxo']>(`txs/${id}/utxos`);
379375

380376
// We can't use txFromCBOR.body.inputs since it misses HydratedTxIn.address
381377
const { inputs, outputs, collaterals } = this.transactionUtxos(utxos, txFromCBOR);
@@ -418,11 +414,11 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
418414

419415
protected async fetchTransaction(txId: Cardano.TransactionId): Promise<Cardano.HydratedTx> {
420416
try {
421-
const txContent = await this.blockfrost.txs(txId.toString());
417+
const txContent = await this.request<Responses['tx_content']>(`txs/${txId.toString()}`);
422418

423419
return (await this.transactionDetailsUsingCBOR(txContent)) ?? (await this.transactionDetailsUsingAPIs(txContent));
424420
} catch (error) {
425-
throw blockfrostToProviderError(error);
421+
throw this.toProviderError(error);
426422
}
427423
}
428424

@@ -444,7 +440,9 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
444440

445441
public async blocksByHashes({ ids }: BlocksByIdsArgs): Promise<Cardano.ExtendedBlockInfo[]> {
446442
try {
447-
const responses = await Promise.all(ids.map((id) => this.blockfrost.blocks(id.toString())));
443+
const responses = await Promise.all(
444+
ids.map((id) => this.request<Responses['block_content']>(`blocks/${id.toString()}`))
445+
);
448446
return responses.map((response) => {
449447
if (!response.epoch || !response.epoch_slot || !response.height || !response.slot || !response.block_vrf) {
450448
throw new ProviderError(ProviderFailure.Unknown, null, 'Queried unsupported block');
@@ -470,15 +468,15 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
470468
};
471469
});
472470
} catch (error) {
473-
throw blockfrostToProviderError(error);
471+
throw this.toProviderError(error);
474472
}
475473
}
476474

477475
public async transactionsByHashes({ ids }: TransactionsByIdsArgs): Promise<Cardano.HydratedTx[]> {
478476
try {
479477
return Promise.all(ids.map((id) => this.fetchTransaction(id)));
480478
} catch (error) {
481-
throw blockfrostToProviderError(error);
479+
throw this.toProviderError(error);
482480
}
483481
}
484482

@@ -495,21 +493,18 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
495493

496494
const addressTransactions = await Promise.all(
497495
addresses.map(async (address) =>
498-
fetchByAddressSequentially<
499-
{ tx_hash: string; tx_index: number; block_height: number },
500-
BlockfrostTransactionContent
501-
>({
502-
address,
496+
fetchSequentially<{ tx_hash: string; tx_index: number; block_height: number }, BlockfrostTransactionContent>({
503497
haveEnoughItems: blockRange?.lowerBound
504498
? (transactions) =>
505499
transactions.length > 0 &&
506500
transactions[transactions.length - 1].block_height < blockRange!.lowerBound!
507501
: undefined,
508-
request: (addr: Cardano.PaymentAddress, paginationOptions) =>
509-
this.blockfrost.addressesTransactions(addr.toString(), paginationOptions, {
510-
from: blockRange?.lowerBound ? blockRange?.lowerBound.toString() : undefined,
511-
to: blockRange?.upperBound ? blockRange?.upperBound.toString() : undefined
512-
})
502+
request: (paginationQueryString) => {
503+
let queryString = `addresses/${address}/transactions?${paginationQueryString}`;
504+
if (blockRange?.lowerBound) queryString += `&from=${blockRange.lowerBound.toString()}`;
505+
if (blockRange?.upperBound) queryString += `&to=${blockRange.upperBound.toString()}`;
506+
return this.request<Responses['address_transactions_content']>(queryString);
507+
}
513508
})
514509
)
515510
);
@@ -526,7 +521,7 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
526521

527522
return { pageResults, totalResultCount: allTransactions.length };
528523
} catch (error) {
529-
throw blockfrostToProviderError(error);
524+
throw this.toProviderError(error);
530525
}
531526
}
532527

@@ -581,6 +576,6 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
581576
}
582577

583578
private fetchUtxos(id: Cardano.TransactionId): Promise<Schemas['tx_content_utxo']> {
584-
return this.blockfrost.txsUtxos(id);
579+
return this.request<Responses['tx_content_utxo']>(`txs/${id}/utxos`);
585580
}
586581
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
export * from './BlockfrostChainHistoryProvider';
12
export * from './chainHistoryHttpProvider';

0 commit comments

Comments
 (0)