Skip to content

Commit c824001

Browse files
authored
Merge pull request #2004 from kadirchan/main
feat: Allow fetch functions to pass headers
2 parents 1dc90e4 + dcd73ce commit c824001

File tree

6 files changed

+536
-110
lines changed

6 files changed

+536
-110
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
2222
- `setFee` and `setFeePerSnarkCost` for `Transaction` and `PendingTransaction` https://github.com/o1-labs/o1js/pull/1968
2323
- Doc comments for various ZkProgram methods https://github.com/o1-labs/o1js/pull/1974
2424
- `MerkleList.popOption()` for popping the last element and also learning if there was one https://github.com/o1-labs/o1js/pull/1997
25+
- Added custom header support for `Fetch` methods such as `fetchEvents`, `fetchActions` etc. and to `Mina` instance. Also added two new methods `setMinaDefaultHeaders` and `setArchiveDefaultHeaders` https://github.com/o1-labs/o1js/pull/2004
2526

2627
### Changed
2728

src/lib/mina/fetch.ts

Lines changed: 101 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ export {
5656
getCachedGenesisConstants,
5757
addCachedAccount,
5858
networkConfig,
59+
setMinaDefaultHeaders,
60+
setArchiveDefaultHeaders,
5961
setGraphqlEndpoint,
6062
setGraphqlEndpoints,
6163
setMinaGraphqlFallbackEndpoints,
@@ -77,6 +79,8 @@ type NetworkConfig = {
7779
archiveEndpoint: string;
7880
archiveFallbackEndpoints: string[];
7981
lightnetAccountManagerEndpoint: string;
82+
minaDefaultHeaders: HeadersInit;
83+
archiveDefaultHeaders: HeadersInit;
8084
};
8185

8286
type ActionsQueryInputs = {
@@ -91,6 +95,8 @@ let networkConfig = {
9195
archiveEndpoint: '',
9296
archiveFallbackEndpoints: [] as string[],
9397
lightnetAccountManagerEndpoint: '',
98+
minaDefaultHeaders: {},
99+
archiveDefaultHeaders: {},
94100
} satisfies NetworkConfig;
95101

96102
function checkForValidUrl(url: string) {
@@ -102,20 +108,58 @@ function checkForValidUrl(url: string) {
102108
}
103109
}
104110

111+
/**
112+
* Sets up the default headers to be used for all Mina node GraphQL requests, example usage:
113+
* ```typescript
114+
* setMinaDefaultHeaders({ Authorization: 'Bearer example-token' });
115+
* ```
116+
*
117+
* It can be overridden by passing headers to the individual fetch functions, example usage:
118+
* ```typescript
119+
* setMinaDefaultHeaders({ Authorization: 'Bearer default-token' });
120+
* await fetchAccount({publicKey}, minaEndpoint, { headers: { Authorization: 'Bearer override-token' } });
121+
* ```
122+
* @param headers Arbitrary sized headers to be used for all Mina node GraphQL requests.
123+
*/
124+
function setMinaDefaultHeaders(headers: HeadersInit) {
125+
networkConfig.minaDefaultHeaders = headers;
126+
}
127+
128+
/**
129+
* Sets up the default headers to be used for all Archive node GraphQL requests, example usage:
130+
* ```typescript
131+
* setArchiveDefaultHeaders({ Authorization: 'Bearer example-token' });
132+
* ```
133+
*
134+
* It can be overridden by passing headers to the individual fetch functions, example usage:
135+
* ```typescript
136+
* setArchiveDefaultHeaders({ Authorization: 'Bearer default-token' });
137+
* await fetchEvents({publicKey}, archiveEndpoint, { headers: { Authorization: 'Bearer override-token' } });
138+
* ```
139+
* @param headers Arbitrary sized headers to be used for all Mina Archive node GraphQL requests.
140+
*/
141+
function setArchiveDefaultHeaders(headers: HeadersInit) {
142+
networkConfig.archiveDefaultHeaders = headers;
143+
}
144+
105145
function setGraphqlEndpoints([
106146
graphqlEndpoint,
107147
...fallbackEndpoints
108148
]: string[]) {
109149
setGraphqlEndpoint(graphqlEndpoint);
110150
setMinaGraphqlFallbackEndpoints(fallbackEndpoints);
111151
}
112-
function setGraphqlEndpoint(graphqlEndpoint: string) {
152+
function setGraphqlEndpoint(
153+
graphqlEndpoint: string,
154+
minaDefaultHeaders?: HeadersInit
155+
) {
113156
if (!checkForValidUrl(graphqlEndpoint)) {
114157
throw new Error(
115158
`Invalid GraphQL endpoint: ${graphqlEndpoint}. Please specify a valid URL.`
116159
);
117160
}
118161
networkConfig.minaEndpoint = graphqlEndpoint;
162+
if (minaDefaultHeaders) setMinaDefaultHeaders(minaDefaultHeaders);
119163
}
120164
function setMinaGraphqlFallbackEndpoints(graphqlEndpoints: string[]) {
121165
if (graphqlEndpoints.some((endpoint) => !checkForValidUrl(endpoint))) {
@@ -130,13 +174,17 @@ function setMinaGraphqlFallbackEndpoints(graphqlEndpoints: string[]) {
130174
* Sets up a GraphQL endpoint to be used for fetching information from an Archive Node.
131175
*
132176
*/
133-
function setArchiveGraphqlEndpoint(graphqlEndpoint: string) {
177+
function setArchiveGraphqlEndpoint(
178+
graphqlEndpoint: string,
179+
archiveDefaultHeaders?: HeadersInit
180+
) {
134181
if (!checkForValidUrl(graphqlEndpoint)) {
135182
throw new Error(
136183
`Invalid GraphQL endpoint: ${graphqlEndpoint}. Please specify a valid URL.`
137184
);
138185
}
139186
networkConfig.archiveEndpoint = graphqlEndpoint;
187+
if (archiveDefaultHeaders) setArchiveDefaultHeaders(archiveDefaultHeaders);
140188
}
141189
function setArchiveGraphqlFallbackEndpoints(graphqlEndpoints: string[]) {
142190
if (graphqlEndpoints.some((endpoint) => !checkForValidUrl(endpoint))) {
@@ -173,13 +221,13 @@ function setLightnetAccountManagerEndpoint(endpoint: string) {
173221
* @param accountInfo.publicKey The specified publicKey to get account information on
174222
* @param accountInfo.tokenId The specified tokenId to get account information on
175223
* @param graphqlEndpoint The graphql endpoint to fetch from
176-
* @param config An object that exposes an additional timeout option
224+
* @param config An object that exposes an additional timeout and header options
177225
* @returns zkapp information on the specified account or an error is thrown
178226
*/
179227
async function fetchAccount(
180228
accountInfo: { publicKey: string | PublicKey; tokenId?: string | Field },
181229
graphqlEndpoint = networkConfig.minaEndpoint,
182-
{ timeout = defaultTimeout } = {}
230+
{ timeout = defaultTimeout, headers }: FetchConfig = {}
183231
): Promise<
184232
| { account: Types.Account; error: undefined }
185233
| { account: undefined; error: FetchError }
@@ -198,6 +246,7 @@ async function fetchAccount(
198246
graphqlEndpoint,
199247
{
200248
timeout,
249+
headers: { ...networkConfig.minaDefaultHeaders, ...headers },
201250
}
202251
);
203252
}
@@ -236,7 +285,7 @@ async function fetchAccountInternal(
236285
};
237286
}
238287

239-
type FetchConfig = { timeout?: number };
288+
type FetchConfig = { timeout?: number; headers?: HeadersInit };
240289
type FetchResponse<TDataResponse = any> = { data: TDataResponse; errors?: any };
241290
type FetchError = {
242291
statusCode: number;
@@ -458,11 +507,15 @@ function accountCacheKey(
458507
/**
459508
* Fetches the last block on the Mina network.
460509
*/
461-
async function fetchLastBlock(graphqlEndpoint = networkConfig.minaEndpoint) {
510+
async function fetchLastBlock(
511+
graphqlEndpoint = networkConfig.minaEndpoint,
512+
headers?: HeadersInit
513+
) {
462514
let [resp, error] = await makeGraphqlRequest<LastBlockQueryResponse>(
463515
lastBlockQuery,
464516
graphqlEndpoint,
465-
networkConfig.minaFallbackEndpoints
517+
networkConfig.minaFallbackEndpoints,
518+
{ headers: { ...networkConfig.minaDefaultHeaders, ...headers } }
466519
);
467520
if (error) throw Error(error.statusText);
468521
let lastBlock = resp?.data?.bestChain?.[0];
@@ -478,11 +531,21 @@ async function fetchLastBlock(graphqlEndpoint = networkConfig.minaEndpoint) {
478531
return network;
479532
}
480533

481-
async function fetchCurrentSlot(graphqlEndpoint = networkConfig.minaEndpoint) {
534+
/**
535+
* Fetches the current slot number of the Mina network.
536+
* @param graphqlEndpoint GraphQL endpoint to fetch from
537+
* @param headers optional headers to pass to the fetch request
538+
* @returns The current slot number
539+
*/
540+
async function fetchCurrentSlot(
541+
graphqlEndpoint = networkConfig.minaEndpoint,
542+
headers?: HeadersInit
543+
) {
482544
let [resp, error] = await makeGraphqlRequest<CurrentSlotResponse>(
483545
currentSlotQuery,
484546
graphqlEndpoint,
485-
networkConfig.minaFallbackEndpoints
547+
networkConfig.minaFallbackEndpoints,
548+
{ headers: { ...networkConfig.minaDefaultHeaders, ...headers } }
486549
);
487550
if (error) throw Error(`Error making GraphQL request: ${error.statusText}`);
488551
let bestChain = resp?.data?.bestChain;
@@ -502,7 +565,8 @@ async function fetchLatestBlockZkappStatus(
502565
await makeGraphqlRequest<LastBlockQueryFailureCheckResponse>(
503566
lastBlockQueryFailureCheck(blockLength),
504567
graphqlEndpoint,
505-
networkConfig.minaFallbackEndpoints
568+
networkConfig.minaFallbackEndpoints,
569+
{ headers: networkConfig.minaDefaultHeaders }
506570
);
507571
if (error) throw Error(`Error making GraphQL request: ${error.statusText}`);
508572
let bestChain = resp?.data;
@@ -597,12 +661,14 @@ function parseEpochData({
597661
*/
598662
async function fetchTransactionStatus(
599663
txId: string,
600-
graphqlEndpoint = networkConfig.minaEndpoint
664+
graphqlEndpoint = networkConfig.minaEndpoint,
665+
headers?: HeadersInit
601666
): Promise<TransactionStatus> {
602667
let [resp, error] = await makeGraphqlRequest<TransactionStatusQueryResponse>(
603668
transactionStatusQuery(txId),
604669
graphqlEndpoint,
605-
networkConfig.minaFallbackEndpoints
670+
networkConfig.minaFallbackEndpoints,
671+
{ headers: { ...networkConfig.minaDefaultHeaders, ...headers } }
606672
);
607673
if (error) throw Error(error.statusText);
608674
let txStatus = resp?.data?.transactionStatus;
@@ -618,14 +684,15 @@ async function fetchTransactionStatus(
618684
function sendZkapp(
619685
json: string,
620686
graphqlEndpoint = networkConfig.minaEndpoint,
621-
{ timeout = defaultTimeout } = {}
687+
{ timeout = defaultTimeout, headers }: FetchConfig = {}
622688
) {
623689
return makeGraphqlRequest<SendZkAppResponse>(
624690
sendZkappQuery(json),
625691
graphqlEndpoint,
626692
networkConfig.minaFallbackEndpoints,
627693
{
628694
timeout,
695+
headers: { ...networkConfig.minaDefaultHeaders, ...headers },
629696
}
630697
);
631698
}
@@ -637,6 +704,7 @@ function sendZkapp(
637704
* @param [accountInfo.tokenId] - The optional token ID for the account.
638705
* @param [graphqlEndpoint=networkConfig.archiveEndpoint] - The GraphQL endpoint to query. Defaults to the Archive Node GraphQL API.
639706
* @param [filterOptions={}] - The optional filter options object.
707+
* @param headers - Optional headers to pass to the fetch request
640708
* @returns A promise that resolves to an array of objects containing event data, block information and transaction information for the account.
641709
* @throws If the GraphQL request fails or the response is invalid.
642710
* @example
@@ -649,7 +717,8 @@ function sendZkapp(
649717
async function fetchEvents(
650718
accountInfo: { publicKey: string; tokenId?: string },
651719
graphqlEndpoint = networkConfig.archiveEndpoint,
652-
filterOptions: EventActionFilterOptions = {}
720+
filterOptions: EventActionFilterOptions = {},
721+
headers?: HeadersInit
653722
) {
654723
if (!graphqlEndpoint)
655724
throw Error(
@@ -663,7 +732,8 @@ async function fetchEvents(
663732
filterOptions
664733
),
665734
graphqlEndpoint,
666-
networkConfig.archiveFallbackEndpoints
735+
networkConfig.archiveFallbackEndpoints,
736+
{ headers: { ...networkConfig.archiveDefaultHeaders, ...headers } }
667737
);
668738
if (error) throw Error(error.statusText);
669739
let fetchedEvents = response?.data.events;
@@ -697,6 +767,7 @@ async function fetchEvents(
697767
*
698768
* @param accountInfo - An {@link ActionsQueryInputs} containing the public key, and optional query parameters for the actions query
699769
* @param graphqlEndpoint - The GraphQL endpoint to fetch from. Defaults to the configured Mina endpoint.
770+
* @param headers - Optional headers to pass to the fetch request
700771
*
701772
* @returns A promise that resolves to an object containing the final actions hash for the account, and a list of actions
702773
* @throws Will throw an error if the GraphQL endpoint is invalid or if the fetch request fails.
@@ -710,7 +781,8 @@ async function fetchEvents(
710781
*/
711782
async function fetchActions(
712783
accountInfo: ActionsQueryInputs,
713-
graphqlEndpoint = networkConfig.archiveEndpoint
784+
graphqlEndpoint = networkConfig.archiveEndpoint,
785+
headers?: HeadersInit
714786
): Promise<
715787
| {
716788
actions: string[][];
@@ -731,7 +803,8 @@ async function fetchActions(
731803
let [response, error] = await makeGraphqlRequest<ActionQueryResponse>(
732804
getActionsQuery(publicKey, actionStates, tokenId),
733805
graphqlEndpoint,
734-
networkConfig.archiveFallbackEndpoints
806+
networkConfig.archiveFallbackEndpoints,
807+
{ headers: { ...networkConfig.archiveDefaultHeaders, ...headers } }
735808
);
736809
// As of 2025-01-07, minascan is running a version of the node which supports `sequenceNumber` and `zkappAccountUpdateIds` fields
737810
// We could consider removing this fallback since no other nodes are widely used
@@ -746,7 +819,8 @@ async function fetchActions(
746819
/* _excludeTransactionInfo= */ true
747820
),
748821
graphqlEndpoint,
749-
networkConfig.archiveFallbackEndpoints
822+
networkConfig.archiveFallbackEndpoints,
823+
{ headers: { ...networkConfig.archiveDefaultHeaders, ...headers } }
750824
);
751825
if (error)
752826
throw Error(
@@ -867,12 +941,14 @@ export function createActionsList(
867941
* Fetches genesis constants.
868942
*/
869943
async function fetchGenesisConstants(
870-
graphqlEndpoint = networkConfig.minaEndpoint
944+
graphqlEndpoint = networkConfig.minaEndpoint,
945+
headers?: HeadersInit
871946
): Promise<GenesisConstants> {
872947
let [resp, error] = await makeGraphqlRequest<GenesisConstantsResponse>(
873948
genesisConstantsQuery,
874949
graphqlEndpoint,
875-
networkConfig.minaFallbackEndpoints
950+
networkConfig.minaFallbackEndpoints,
951+
{ headers: { ...networkConfig.minaDefaultHeaders, ...headers } }
876952
);
877953
if (error) throw Error(error.statusText);
878954
const genesisConstants = resp?.data?.genesisConstants;
@@ -1029,7 +1105,7 @@ async function makeGraphqlRequest<TDataResponse = any>(
10291105
query: string,
10301106
graphqlEndpoint = networkConfig.minaEndpoint,
10311107
fallbackEndpoints: string[],
1032-
{ timeout = defaultTimeout } = {} as FetchConfig
1108+
{ timeout = defaultTimeout, headers } = {} as FetchConfig
10331109
) {
10341110
if (graphqlEndpoint === 'none')
10351111
throw Error(
@@ -1049,7 +1125,10 @@ async function makeGraphqlRequest<TDataResponse = any>(
10491125
try {
10501126
let response = await fetch(url, {
10511127
method: 'POST',
1052-
headers: { 'Content-Type': 'application/json' },
1128+
headers: {
1129+
'Content-Type': 'application/json',
1130+
...headers,
1131+
},
10531132
body,
10541133
signal: controller.signal,
10551134
});

0 commit comments

Comments
 (0)