@koralabs/kora-labs-common 6.9.4 → 6.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/aws/jwtSigner.d.ts +0 -2
  2. package/aws/kmsEnvironment.d.ts +0 -8
  3. package/aws/kmsEnvironment.js +4 -4
  4. package/aws/objectStore.d.ts +0 -8
  5. package/chain/cache/ShortTermCache.d.ts +17 -0
  6. package/chain/cache/ShortTermCache.js +46 -0
  7. package/chain/config.d.ts +9 -0
  8. package/chain/config.js +40 -0
  9. package/chain/datum/imageDatum.d.ts +14 -0
  10. package/chain/datum/imageDatum.js +87 -0
  11. package/chain/failover/ChainProviderFailover.d.ts +50 -0
  12. package/chain/failover/ChainProviderFailover.js +91 -0
  13. package/chain/failover/interfaces.d.ts +62 -0
  14. package/chain/failover/interfaces.js +2 -0
  15. package/chain/index.d.ts +9 -0
  16. package/chain/index.js +28 -0
  17. package/chain/providerConfig.d.ts +22 -0
  18. package/chain/providerConfig.js +13 -0
  19. package/chain/providers/Blockfrost.d.ts +59 -0
  20. package/chain/providers/Blockfrost.js +142 -0
  21. package/chain/providers/Koios.d.ts +68 -0
  22. package/chain/providers/Koios.js +214 -0
  23. package/chain/transport/fetchProviderJson.d.ts +26 -0
  24. package/chain/transport/fetchProviderJson.js +165 -0
  25. package/environment/index.js +4 -4
  26. package/fn/index.d.ts +0 -2
  27. package/fn/index.js +1 -1
  28. package/handles/api.js +0 -1
  29. package/handles/constants.js +1 -1
  30. package/handles/interfaces/ScriptDetails.js +1 -1
  31. package/handles/interfaces/api.js +3 -3
  32. package/handles/interfaces/index.d.ts +4 -4
  33. package/handles/interfaces/index.js +5 -5
  34. package/handles/policies.js +1 -1
  35. package/handles/utils.d.ts +1 -1
  36. package/logger/index.js +1 -1
  37. package/mpt/index.js +17 -7
  38. package/mpt/labelSet.d.ts +0 -2
  39. package/package.json +7 -7
  40. package/types/index.js +4 -4
  41. package/types/profile-header.js +2 -2
  42. package/utils/cbor/index.d.ts +4 -6
  43. package/utils/cbor/index.js +18 -8
  44. package/utils/crypto/index.d.ts +2 -4
  45. package/utils/crypto/index.js +23 -13
  46. package/utils/index.d.ts +5 -5
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Blockfrost = void 0;
4
+ const types_1 = require("../../types");
5
+ const fetchProviderJson_1 = require("../transport/fetchProviderJson");
6
+ const imageDatum_1 = require("../datum/imageDatum");
7
+ const providerConfig_1 = require("../providerConfig");
8
+ class Blockfrost {
9
+ constructor(config) {
10
+ this.config = config;
11
+ this.name = 'Blockfrost';
12
+ }
13
+ async fetchBlockfrost(path) {
14
+ var _a;
15
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
16
+ const url = `https://cardano-${this.config.network.toLowerCase()}.blockfrost.io/api/v0/${normalizedPath}`;
17
+ return (0, fetchProviderJson_1.fetchProviderJson)({
18
+ provider: 'Blockfrost',
19
+ url,
20
+ headers: {
21
+ 'Content-Type': 'application/json',
22
+ project_id: (_a = this.config.blockfrostApiKey) !== null && _a !== void 0 ? _a : ''
23
+ }
24
+ });
25
+ }
26
+ async getDrepMetadata(drepId) {
27
+ return await this.fetchBlockfrost(`/governance/dreps/${drepId}/metadata`);
28
+ }
29
+ async getLatestBlock() {
30
+ return await this.fetchBlockfrost('blocks/latest');
31
+ }
32
+ async getLatestTransactionForAsset(policyId, hex) {
33
+ var _a;
34
+ const res = await this.fetchBlockfrost(`/assets/${policyId}${hex}/transactions?order=desc`);
35
+ const [tx] = res;
36
+ return (_a = tx === null || tx === void 0 ? void 0 : tx.tx_hash) !== null && _a !== void 0 ? _a : null;
37
+ }
38
+ async getAssets(policyId) {
39
+ var _a;
40
+ const res = await this.fetchBlockfrost(`/assets/policy/${policyId}`);
41
+ if (!Array.isArray(res))
42
+ return [];
43
+ return ((_a = res === null || res === void 0 ? void 0 : res.reduce((agg, item) => {
44
+ const { asset, quantity } = item;
45
+ const [, hex] = asset.split(policyId);
46
+ if (hex.includes(types_1.AssetNameLabel.LBL_100) || quantity === '0')
47
+ return agg;
48
+ agg.push({ policyId, hex });
49
+ return agg;
50
+ }, [])) !== null && _a !== void 0 ? _a : []);
51
+ }
52
+ async getAssetUtxo(policyId, hex) {
53
+ var _a;
54
+ // A BURN that only spends the asset is NOT listed by /assets/{asset}/transactions, so
55
+ // getLatestTransactionForAsset returns the pre-burn output tx and the asset is still in its
56
+ // (now-spent) outputs — we'd resurrect a de-indexed handle. /assets DOES reflect the burn
57
+ // (quantity 0), so gate on current supply first: a burned (or absent) asset resolves to null.
58
+ const info = await this.fetchBlockfrost(`/assets/${policyId}${hex}`);
59
+ if (!info || info.status_code === 404 || String(info.quantity) === '0')
60
+ return null;
61
+ const tx = await this.getLatestTransactionForAsset(policyId, hex);
62
+ if (!tx)
63
+ return null;
64
+ const res = await this.fetchBlockfrost(`/txs/${tx}/utxos`);
65
+ const asset = (_a = res === null || res === void 0 ? void 0 : res.outputs) === null || _a === void 0 ? void 0 : _a.find((output) => output.amount.some((a) => a.unit === `${policyId}${hex}`));
66
+ // Defensive: if the latest tx lists the asset only in inputs, return null.
67
+ if (!asset)
68
+ return null;
69
+ return { ...asset, tx_hash: tx };
70
+ }
71
+ async getDatumFromHash(datumHash) {
72
+ const result = await this.fetchBlockfrost(`/scripts/datum/${datumHash}/cbor`);
73
+ if (result.status)
74
+ return null;
75
+ return result.cbor;
76
+ }
77
+ async getAssetDatum(policyId, hex) {
78
+ var _a;
79
+ const asset = await this.getAssetUtxo(policyId, hex);
80
+ return (_a = asset === null || asset === void 0 ? void 0 : asset.inline_datum) !== null && _a !== void 0 ? _a : null;
81
+ }
82
+ async getCip25AssetImage(policyId, hex) {
83
+ var _a, _b;
84
+ const res = await this.fetchBlockfrost(`/assets/${policyId}${hex}`);
85
+ let image = (_b = (_a = res === null || res === void 0 ? void 0 : res.onchain_metadata) === null || _a === void 0 ? void 0 : _a.image) !== null && _b !== void 0 ? _b : null;
86
+ if (Array.isArray(image))
87
+ image = image.join('');
88
+ return image;
89
+ }
90
+ async getRefScriptCbor(tx, policyId, hex) {
91
+ const res = await this.fetchBlockfrost(`/txs/${tx}/utxos`);
92
+ const output = res.outputs.find((o) => o.amount.some((a) => a.unit === `${policyId}${hex}`));
93
+ const scriptHash = output === null || output === void 0 ? void 0 : output.reference_script_hash;
94
+ const { cbor } = await this.fetchBlockfrost(`/scripts/${scriptHash}/cbor`);
95
+ return cbor;
96
+ }
97
+ async getTxUtxos(tx) {
98
+ return await this.fetchBlockfrost(`/txs/${tx}/utxos`);
99
+ }
100
+ async getAssetsByStakeKey(stakeKey) {
101
+ const result = [];
102
+ let hasResults = true;
103
+ let page = 1;
104
+ while (hasResults) {
105
+ const items = await this.fetchBlockfrost(`/accounts/${stakeKey}/addresses/assets?count=100&page=${page}`);
106
+ if (items.length === 0)
107
+ hasResults = false;
108
+ result.push(...items.map((item) => ({ asset: item.unit, amount: item.quantity })));
109
+ page++;
110
+ }
111
+ return result;
112
+ }
113
+ async getBackgroundImageDetails(policyId, hex) {
114
+ var _a;
115
+ const data = await this.fetchBlockfrost(`/assets/${policyId}${hex}`);
116
+ const updateHex = hex.startsWith(types_1.AssetNameLabel.LBL_222) || hex.startsWith(types_1.AssetNameLabel.LBL_444)
117
+ ? `${types_1.AssetNameLabel.LBL_100}${hex.replace(types_1.AssetNameLabel.LBL_222, '').replace(types_1.AssetNameLabel.LBL_444, '')}`
118
+ : hex;
119
+ const datum = await this.getAssetDatum(policyId, updateHex);
120
+ if (datum) {
121
+ return (0, imageDatum_1.getImageDataFromDatum)((_a = this.config.apiHost) !== null && _a !== void 0 ? _a : (0, providerConfig_1.defaultApiHost)(this.config.network), datum);
122
+ }
123
+ let image = '';
124
+ if ((data === null || data === void 0 ? void 0 : data.onchain_metadata) && (data === null || data === void 0 ? void 0 : data.onchain_metadata.image)) {
125
+ image = data === null || data === void 0 ? void 0 : data.onchain_metadata.image;
126
+ }
127
+ return { image, metadata: data === null || data === void 0 ? void 0 : data.onchain_metadata };
128
+ }
129
+ async getAddressInfo(bech32Address) {
130
+ const result = await this.fetchBlockfrost(`/addresses/${bech32Address}`);
131
+ return {
132
+ address: result.address,
133
+ amount: result.amount,
134
+ stake_address: result.stake_address,
135
+ script: result.script
136
+ };
137
+ }
138
+ async getAddressUTxOs(bech32Address) {
139
+ return await this.fetchBlockfrost(`/addresses/${bech32Address}/utxos`);
140
+ }
141
+ }
142
+ exports.Blockfrost = Blockfrost;
@@ -0,0 +1,68 @@
1
+ import { ICreatorDefaults } from '../../handles/interfaces';
2
+ import { AccountAsset, AddressInfo, ChainProvider, ChainProviderAsset, ChainProviderUtxo } from '../failover/interfaces';
3
+ import { ChainProviderConfig } from '../providerConfig';
4
+ interface KoiosAsset {
5
+ quantity: string;
6
+ policy_id: string;
7
+ asset_name: string;
8
+ }
9
+ interface KoiosTxOutput {
10
+ value: string;
11
+ tx_hash: string;
12
+ tx_index: number;
13
+ asset_list: KoiosAsset[];
14
+ datum_hash: string | null;
15
+ stake_addr: string;
16
+ inline_datum: {
17
+ bytes: string | null;
18
+ value: string | null;
19
+ } | null;
20
+ payment_addr: {
21
+ bech32: string;
22
+ };
23
+ reference_script: {
24
+ bytes: string | null;
25
+ hash: string | null;
26
+ } | null;
27
+ }
28
+ export declare const buildKoiosHeaders: (bearerToken: string | undefined) => {
29
+ Authorization?: string | undefined;
30
+ 'Content-Type': string;
31
+ };
32
+ export declare const buildKoiosAddressUtxoRequestBody: (bech32Address: string) => {
33
+ _addresses: string[];
34
+ _extended: boolean;
35
+ };
36
+ export declare class Koios implements ChainProvider {
37
+ private readonly config;
38
+ name: string;
39
+ constructor(config: ChainProviderConfig);
40
+ private fetchKoios;
41
+ getLatestBlock(): Promise<{
42
+ height: number;
43
+ slot: number;
44
+ time: number;
45
+ }>;
46
+ getLatestTransactionForAsset(policyId: string, hex: string): Promise<string | null>;
47
+ getAssets(policyId: string): Promise<{
48
+ policyId: string;
49
+ hex: string;
50
+ }[]>;
51
+ getAssetUtxo(policyId: string, hex: string): Promise<ChainProviderAsset | null>;
52
+ getDatumFromHash(datumHash: string): Promise<string | null>;
53
+ getAssetDatum(policyId: string, hex: string): Promise<string | null>;
54
+ getRefScriptCbor(tx: string, policyId: string, hex: string): Promise<string>;
55
+ buildChainProviderAsset(output: KoiosTxOutput): Promise<ChainProviderAsset>;
56
+ getTxUtxos(tx: string): Promise<ChainProviderUtxo>;
57
+ getAssetsByStakeKey(stakeKey: string): Promise<AccountAsset[]>;
58
+ getAssetMetadata(policyId: string, hex: string): Promise<Record<string, unknown> | undefined>;
59
+ getCip25AssetImage(policyId: string, hex: string): Promise<string | null>;
60
+ getBackgroundImageDetails(policyId: string, hex: string): Promise<{
61
+ image: string;
62
+ creatorDefaults?: ICreatorDefaults;
63
+ metadata?: Record<string, unknown>;
64
+ }>;
65
+ getAddressInfo(bech32Address: string): Promise<AddressInfo>;
66
+ getAddressUTxOs(bech32Address: string): Promise<ChainProviderAsset[]>;
67
+ }
68
+ export {};
@@ -0,0 +1,214 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Koios = exports.buildKoiosAddressUtxoRequestBody = exports.buildKoiosHeaders = void 0;
4
+ const types_1 = require("../../types");
5
+ const fetchProviderJson_1 = require("../transport/fetchProviderJson");
6
+ const imageDatum_1 = require("../datum/imageDatum");
7
+ const providerConfig_1 = require("../providerConfig");
8
+ const buildKoiosHeaders = (bearerToken) => ({
9
+ 'Content-Type': 'application/json',
10
+ ...(bearerToken ? { Authorization: `Bearer ${bearerToken}` } : {})
11
+ });
12
+ exports.buildKoiosHeaders = buildKoiosHeaders;
13
+ const buildKoiosAddressUtxoRequestBody = (bech32Address) => ({
14
+ _addresses: [bech32Address],
15
+ _extended: true
16
+ });
17
+ exports.buildKoiosAddressUtxoRequestBody = buildKoiosAddressUtxoRequestBody;
18
+ class Koios {
19
+ constructor(config) {
20
+ this.config = config;
21
+ this.name = 'Koios';
22
+ }
23
+ async fetchKoios(path, method = 'GET', body) {
24
+ const network = this.config.network.toLowerCase();
25
+ const host = network === 'mainnet' ? 'api' : network;
26
+ const normalizedPath = path.startsWith('/') ? path : `/${path}`;
27
+ const url = `https://${host}.koios.rest/api/v1${normalizedPath}`;
28
+ return (0, fetchProviderJson_1.fetchProviderJson)({
29
+ provider: 'Koios',
30
+ url,
31
+ method,
32
+ headers: (0, exports.buildKoiosHeaders)(this.config.koiosBearerToken),
33
+ body
34
+ });
35
+ }
36
+ async getLatestBlock() {
37
+ const blocks = await this.fetchKoios('/blocks');
38
+ const latestBlock = blocks[0];
39
+ return { height: latestBlock.block_height, slot: latestBlock.abs_slot, time: latestBlock.block_time };
40
+ }
41
+ async getLatestTransactionForAsset(policyId, hex) {
42
+ var _a;
43
+ const res = await this.fetchKoios(`/asset_txs?_asset_policy=${policyId}&_asset_name=${hex}`);
44
+ const [tx] = res;
45
+ return (_a = tx === null || tx === void 0 ? void 0 : tx.tx_hash) !== null && _a !== void 0 ? _a : null;
46
+ }
47
+ async getAssets(policyId) {
48
+ var _a;
49
+ const res = await this.fetchKoios(`/policy_asset_list?_asset_policy=${policyId}`);
50
+ if (!Array.isArray(res))
51
+ return [];
52
+ return ((_a = res === null || res === void 0 ? void 0 : res.reduce((agg, item) => {
53
+ const { asset_name, total_supply } = item;
54
+ if (asset_name.includes(types_1.AssetNameLabel.LBL_100) || total_supply === '0')
55
+ return agg;
56
+ agg.push({ policyId, hex: asset_name });
57
+ return agg;
58
+ }, [])) !== null && _a !== void 0 ? _a : []);
59
+ }
60
+ async getAssetUtxo(policyId, hex) {
61
+ var _a, _b, _c;
62
+ const tx = await this.getLatestTransactionForAsset(policyId, hex);
63
+ if (!tx)
64
+ return null;
65
+ const payload = { _tx_hashes: [tx], _metadata: true, _assets: true, _scripts: true, _bytecode: true };
66
+ const res = await this.fetchKoios(`/tx_info`, 'POST', JSON.stringify(payload));
67
+ const [firstTransaction] = res;
68
+ const output = (_a = firstTransaction === null || firstTransaction === void 0 ? void 0 : firstTransaction.outputs) === null || _a === void 0 ? void 0 : _a.find((o) => o.asset_list.some((a) => a.policy_id === policyId && a.asset_name === hex));
69
+ if (!output)
70
+ return null;
71
+ const amount = output.asset_list.map((a) => ({
72
+ unit: `${a.policy_id}${a.asset_name}`,
73
+ quantity: a.quantity
74
+ }));
75
+ amount.push({ unit: 'lovelace', quantity: output.value });
76
+ const chainProviderAsset = {
77
+ address: output.payment_addr.bech32,
78
+ amount,
79
+ inline_datum: (_c = (_b = output.inline_datum) === null || _b === void 0 ? void 0 : _b.bytes) !== null && _c !== void 0 ? _c : null,
80
+ output_index: output.tx_index,
81
+ tx_hash: firstTransaction.tx_hash,
82
+ data_hash: output.datum_hash
83
+ };
84
+ return { ...chainProviderAsset, tx_hash: tx };
85
+ }
86
+ async getDatumFromHash(datumHash) {
87
+ const result = await this.fetchKoios(`/datum_info`, 'POST', JSON.stringify({ _datum_hashes: [datumHash] }));
88
+ const [firstResult] = result;
89
+ return firstResult ? firstResult.bytes : null;
90
+ }
91
+ async getAssetDatum(policyId, hex) {
92
+ var _a, _b;
93
+ const assetUtxos = await this.fetchKoios(`/asset_utxos`, 'POST', JSON.stringify({ _asset_list: [[policyId, hex]], _extended: true }));
94
+ const utxo = assetUtxos === null || assetUtxos === void 0 ? void 0 : assetUtxos.find((u) => u.asset_list.some((a) => a.policy_id === policyId && a.asset_name === hex));
95
+ if ((_a = utxo === null || utxo === void 0 ? void 0 : utxo.inline_datum) === null || _a === void 0 ? void 0 : _a.bytes)
96
+ return utxo.inline_datum.bytes;
97
+ if (!((_b = utxo === null || utxo === void 0 ? void 0 : utxo.inline_datum) === null || _b === void 0 ? void 0 : _b.bytes) && (utxo === null || utxo === void 0 ? void 0 : utxo.datum_hash))
98
+ return this.getDatumFromHash(utxo.datum_hash);
99
+ return null;
100
+ }
101
+ async getRefScriptCbor(tx, policyId, hex) {
102
+ var _a;
103
+ const { outputs } = await this.getTxUtxos(tx);
104
+ const output = outputs.find((o) => o.amount.some((a) => a.unit === `${policyId}${hex}`));
105
+ return (_a = output === null || output === void 0 ? void 0 : output.reference_script_cbor) !== null && _a !== void 0 ? _a : '';
106
+ }
107
+ async buildChainProviderAsset(output) {
108
+ var _a, _b, _c, _d, _e, _f, _g;
109
+ const amount = output.asset_list.map((a) => ({
110
+ unit: `${a.policy_id}${a.asset_name}`,
111
+ quantity: a.quantity
112
+ }));
113
+ amount.push({ unit: 'lovelace', quantity: output.value });
114
+ const inline_datum = !((_a = output === null || output === void 0 ? void 0 : output.inline_datum) === null || _a === void 0 ? void 0 : _a.bytes) && (output === null || output === void 0 ? void 0 : output.datum_hash)
115
+ ? await this.getDatumFromHash(output.datum_hash)
116
+ : (_c = (_b = output.inline_datum) === null || _b === void 0 ? void 0 : _b.bytes) !== null && _c !== void 0 ? _c : null;
117
+ return {
118
+ address: output.payment_addr.bech32,
119
+ amount,
120
+ inline_datum,
121
+ output_index: output.tx_index,
122
+ tx_hash: output.tx_hash,
123
+ data_hash: output.datum_hash,
124
+ reference_script_hash: (_e = (_d = output.reference_script) === null || _d === void 0 ? void 0 : _d.hash) !== null && _e !== void 0 ? _e : null,
125
+ reference_script_cbor: (_g = (_f = output.reference_script) === null || _f === void 0 ? void 0 : _f.bytes) !== null && _g !== void 0 ? _g : null
126
+ };
127
+ }
128
+ async getTxUtxos(tx) {
129
+ var _a;
130
+ const res = await this.fetchKoios(`/tx_info`, 'POST', JSON.stringify({ _tx_hashes: [tx], _metadata: true, _assets: true }));
131
+ const [firstTransaction] = res;
132
+ const outputs = [];
133
+ for (const output of (_a = firstTransaction === null || firstTransaction === void 0 ? void 0 : firstTransaction.outputs) !== null && _a !== void 0 ? _a : []) {
134
+ outputs.push(await this.buildChainProviderAsset(output));
135
+ }
136
+ return { outputs };
137
+ }
138
+ async getAssetsByStakeKey(stakeKey) {
139
+ const items = await this.fetchKoios(`/account_assets`, 'POST', JSON.stringify({ _stake_addresses: [stakeKey] }));
140
+ return items.reduce((agg, item) => {
141
+ const { policy_id, asset_name, quantity } = item;
142
+ if (quantity === '0')
143
+ return agg;
144
+ agg.push({ asset: `${policy_id}${asset_name}`, amount: quantity });
145
+ return agg;
146
+ }, []);
147
+ }
148
+ async getAssetMetadata(policyId, hex) {
149
+ var _a, _b, _c, _d, _e, _f, _g;
150
+ const assetInfo = await this.fetchKoios(`/asset_info`, 'POST', JSON.stringify({ _asset_list: [[policyId, hex]] }));
151
+ const [result] = assetInfo;
152
+ let metadata = (_c = (_b = (_a = result === null || result === void 0 ? void 0 : result.minting_tx_metadata) === null || _a === void 0 ? void 0 : _a['721']) === null || _b === void 0 ? void 0 : _b[policyId]) === null || _c === void 0 ? void 0 : _c[hex];
153
+ if (!metadata) {
154
+ const assetName = Buffer.from(hex, 'hex').toString('utf-8');
155
+ metadata = (_f = (_e = (_d = result === null || result === void 0 ? void 0 : result.minting_tx_metadata) === null || _d === void 0 ? void 0 : _d['721']) === null || _e === void 0 ? void 0 : _e[policyId]) === null || _f === void 0 ? void 0 : _f[assetName];
156
+ if (!metadata && (result === null || result === void 0 ? void 0 : result.token_registry_metadata)) {
157
+ metadata = { image: `data:image/png;base64,${(_g = result.token_registry_metadata.logo) !== null && _g !== void 0 ? _g : ''}` };
158
+ }
159
+ }
160
+ return metadata;
161
+ }
162
+ async getCip25AssetImage(policyId, hex) {
163
+ var _a;
164
+ const metadata = await this.getAssetMetadata(policyId, hex);
165
+ if (!metadata)
166
+ return null;
167
+ let image = (_a = metadata.image) !== null && _a !== void 0 ? _a : null;
168
+ if (Array.isArray(image))
169
+ image = image.join('');
170
+ return image;
171
+ }
172
+ async getBackgroundImageDetails(policyId, hex) {
173
+ var _a;
174
+ const updateHex = hex.startsWith(types_1.AssetNameLabel.LBL_222) || hex.startsWith(types_1.AssetNameLabel.LBL_444)
175
+ ? `${types_1.AssetNameLabel.LBL_100}${hex.replace(types_1.AssetNameLabel.LBL_222, '').replace(types_1.AssetNameLabel.LBL_444, '')}`
176
+ : hex;
177
+ const datum = await this.getAssetDatum(policyId, updateHex);
178
+ const metadata = await this.getAssetMetadata(policyId, hex);
179
+ let image = '';
180
+ if (metadata && metadata.image)
181
+ image = metadata.image;
182
+ if (datum) {
183
+ return (0, imageDatum_1.getImageDataFromDatum)((_a = this.config.apiHost) !== null && _a !== void 0 ? _a : (0, providerConfig_1.defaultApiHost)(this.config.network), datum);
184
+ }
185
+ return { image, metadata };
186
+ }
187
+ async getAddressInfo(bech32Address) {
188
+ const result = (await this.fetchKoios(`/address_info`, 'POST', JSON.stringify({ _addresses: [bech32Address] })))[0];
189
+ const balance = {};
190
+ result.utxo_set.forEach((utxo) => {
191
+ (utxo.asset_list || []).forEach((asset) => {
192
+ const { policy_id, asset_name, quantity } = asset;
193
+ const unit = `${policy_id}${asset_name || ''}`;
194
+ balance[unit] = (balance[unit] || BigInt(0)) + BigInt(quantity);
195
+ });
196
+ });
197
+ const amount = Object.entries(balance).reduce((acc, cur) => [...acc, { unit: cur[0], quantity: cur[1].toString() }], []);
198
+ return {
199
+ address: result.address,
200
+ amount: [{ unit: 'lovelace', quantity: result.balance }].concat(amount.sort((a, b) => (a.unit > b.unit ? 1 : -1))),
201
+ stake_address: result.stake_address,
202
+ script: result.script_address
203
+ };
204
+ }
205
+ async getAddressUTxOs(bech32Address) {
206
+ const result = await this.fetchKoios(`/address_utxos`, 'POST', JSON.stringify((0, exports.buildKoiosAddressUtxoRequestBody)(bech32Address)));
207
+ const utxos = [];
208
+ for (const output of result) {
209
+ utxos.push(await this.buildChainProviderAsset(output));
210
+ }
211
+ return utxos;
212
+ }
213
+ }
214
+ exports.Koios = Koios;
@@ -0,0 +1,26 @@
1
+ export type ProviderName = 'Koios' | 'Blockfrost';
2
+ type ProviderFetcher = (url: string, init?: {
3
+ method?: string;
4
+ headers?: Record<string, string>;
5
+ body?: string;
6
+ }) => Promise<{
7
+ ok: boolean;
8
+ status: number;
9
+ statusText: string;
10
+ text: () => Promise<string>;
11
+ }>;
12
+ export interface ProviderRequestOptions {
13
+ provider: ProviderName;
14
+ url: string;
15
+ method?: string;
16
+ headers?: Record<string, string>;
17
+ body?: string;
18
+ maxRetries?: number;
19
+ retryBaseDelayMs?: number;
20
+ maxRps?: number;
21
+ rateLimitKey?: string;
22
+ fetcher?: ProviderFetcher;
23
+ }
24
+ export declare const isRetriableError: (provider: ProviderName, error: any) => boolean;
25
+ export declare const fetchProviderJson: <T>({ provider, url, method, headers, body, maxRetries, retryBaseDelayMs, maxRps, rateLimitKey, fetcher }: ProviderRequestOptions) => Promise<T>;
26
+ export {};
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fetchProviderJson = exports.isRetriableError = void 0;
4
+ const utils_1 = require("../../utils");
5
+ const DEFAULT_MAX_RPS = 5;
6
+ const DEFAULT_MAX_RETRIES = 2;
7
+ const DEFAULT_RETRY_BASE_DELAY_MS = 500;
8
+ const RETRIABLE_STATUS_CODES = new Set([403, 408, 425, 429, 500, 502, 503, 504]);
9
+ const RETRIABLE_MESSAGE_SNIPPETS = [
10
+ 'terminated',
11
+ 'socket',
12
+ 'econnreset',
13
+ 'fetch failed',
14
+ 'gateway timeout',
15
+ 'too many requests',
16
+ 'payload too large',
17
+ 'timed out acquiring connection from connection pool',
18
+ 'rate limit',
19
+ 'service unavailable',
20
+ 'temporarily unavailable'
21
+ ];
22
+ const rateLimitStates = new Map();
23
+ const delay = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
24
+ const getRateLimitState = (key) => {
25
+ const existing = rateLimitStates.get(key);
26
+ if (existing)
27
+ return existing;
28
+ const state = { pending: [], processing: false, maxRps: DEFAULT_MAX_RPS };
29
+ rateLimitStates.set(key, state);
30
+ return state;
31
+ };
32
+ const processRateLimitedQueue = async (key) => {
33
+ const state = getRateLimitState(key);
34
+ if (state.processing)
35
+ return;
36
+ state.processing = true;
37
+ try {
38
+ while (state.pending.length > 0) {
39
+ const pending = state.pending.splice(0, state.pending.length);
40
+ const delayInMilliseconds = Math.ceil(1000 / Math.max(1, state.maxRps));
41
+ await (0, utils_1.asyncForEach)(pending, async (queuedTask) => {
42
+ try {
43
+ const value = await queuedTask.task();
44
+ queuedTask.resolve(value);
45
+ }
46
+ catch (error) {
47
+ queuedTask.reject(error);
48
+ }
49
+ }, delayInMilliseconds);
50
+ }
51
+ }
52
+ finally {
53
+ state.processing = false;
54
+ if (state.pending.length > 0) {
55
+ void processRateLimitedQueue(key);
56
+ }
57
+ }
58
+ };
59
+ const runRateLimitedTask = (key, maxRps, task) => new Promise((resolve, reject) => {
60
+ const state = getRateLimitState(key);
61
+ state.maxRps = maxRps;
62
+ state.pending.push({ task, resolve, reject });
63
+ void processRateLimitedQueue(key);
64
+ });
65
+ const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
66
+ const parseJson = (text) => {
67
+ if (!text)
68
+ return { value: null, parseError: null };
69
+ try {
70
+ return { value: JSON.parse(text), parseError: null };
71
+ }
72
+ catch (error) {
73
+ return { value: null, parseError: error };
74
+ }
75
+ };
76
+ const getProviderPayloadError = (provider, value) => {
77
+ if (!isRecord(value))
78
+ return null;
79
+ if (provider === 'Koios') {
80
+ if (typeof value.code === 'string' && typeof value.message === 'string') {
81
+ return value;
82
+ }
83
+ return null;
84
+ }
85
+ if (typeof value.error === 'string' && (typeof value.status_code === 'number' || typeof value.message === 'string')) {
86
+ return value;
87
+ }
88
+ return null;
89
+ };
90
+ const createProviderError = ({ provider, status, statusText, payload, responseText, cause }) => {
91
+ const error = new Error(`${provider} request failed: ${status} ${statusText}`.trim());
92
+ if (cause)
93
+ error.cause = cause;
94
+ error.status = status;
95
+ error.statusText = statusText;
96
+ if (payload !== undefined) {
97
+ error[`${provider.toLowerCase()}Response`] = payload;
98
+ }
99
+ if (responseText) {
100
+ error.responseText = responseText.slice(0, 512);
101
+ }
102
+ return error;
103
+ };
104
+ const messageLooksRetriable = (value) => RETRIABLE_MESSAGE_SNIPPETS.some((snippet) => value.includes(snippet));
105
+ const isRetriableError = (provider, error) => {
106
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
107
+ const status = Number((_j = (_g = (_e = (_c = (_b = (_a = error === null || error === void 0 ? void 0 : error.status) !== null && _a !== void 0 ? _a : error === null || error === void 0 ? void 0 : error.statusCode) !== null && _b !== void 0 ? _b : error === null || error === void 0 ? void 0 : error.status_code) !== null && _c !== void 0 ? _c : (_d = error === null || error === void 0 ? void 0 : error.koiosResponse) === null || _d === void 0 ? void 0 : _d.status) !== null && _e !== void 0 ? _e : (_f = error === null || error === void 0 ? void 0 : error.koiosResponse) === null || _f === void 0 ? void 0 : _f.status_code) !== null && _g !== void 0 ? _g : (_h = error === null || error === void 0 ? void 0 : error.blockfrostResponse) === null || _h === void 0 ? void 0 : _h.status) !== null && _j !== void 0 ? _j : (_k = error === null || error === void 0 ? void 0 : error.blockfrostResponse) === null || _k === void 0 ? void 0 : _k.status_code);
108
+ if (RETRIABLE_STATUS_CODES.has(status))
109
+ return true;
110
+ const message = `${(_l = error === null || error === void 0 ? void 0 : error.message) !== null && _l !== void 0 ? _l : ''} ${(_o = (_m = error === null || error === void 0 ? void 0 : error.cause) === null || _m === void 0 ? void 0 : _m.message) !== null && _o !== void 0 ? _o : ''}`.toLowerCase();
111
+ if (messageLooksRetriable(message))
112
+ return true;
113
+ const code = `${(_p = error === null || error === void 0 ? void 0 : error.code) !== null && _p !== void 0 ? _p : ''}`.toLowerCase();
114
+ const causeCode = `${(_r = (_q = error === null || error === void 0 ? void 0 : error.cause) === null || _q === void 0 ? void 0 : _q.code) !== null && _r !== void 0 ? _r : ''}`.toLowerCase();
115
+ if (['und_err_socket', 'econnreset', 'etimedout', 'eai_again'].includes(code))
116
+ return true;
117
+ if (['und_err_socket', 'econnreset', 'etimedout', 'eai_again'].includes(causeCode))
118
+ return true;
119
+ const payload = provider === 'Koios' ? error === null || error === void 0 ? void 0 : error.koiosResponse : error === null || error === void 0 ? void 0 : error.blockfrostResponse;
120
+ const payloadMessage = `${(_t = (_s = payload === null || payload === void 0 ? void 0 : payload.message) !== null && _s !== void 0 ? _s : payload === null || payload === void 0 ? void 0 : payload.error) !== null && _t !== void 0 ? _t : ''}`.toLowerCase();
121
+ if (messageLooksRetriable(payloadMessage))
122
+ return true;
123
+ if (provider === 'Koios' && `${(_u = payload === null || payload === void 0 ? void 0 : payload.code) !== null && _u !== void 0 ? _u : ''}`.toLowerCase() === 'pgrst003')
124
+ return true;
125
+ return false;
126
+ };
127
+ exports.isRetriableError = isRetriableError;
128
+ const defaultFetch = (url, init) => globalThis.fetch(url, init);
129
+ const fetchProviderJson = async ({ provider, url, method = 'GET', headers, body, maxRetries = DEFAULT_MAX_RETRIES, retryBaseDelayMs = DEFAULT_RETRY_BASE_DELAY_MS, maxRps = DEFAULT_MAX_RPS, rateLimitKey = provider, fetcher = defaultFetch }) => {
130
+ var _a, _b, _c, _d;
131
+ let attempt = 0;
132
+ // Retry loop: exits only via return (success) or throw (non-retriable / retries exhausted).
133
+ // eslint-disable-next-line no-constant-condition
134
+ while (true) {
135
+ try {
136
+ const response = await runRateLimitedTask(rateLimitKey, maxRps, () => fetcher(url, { method, headers, body }));
137
+ const responseText = await response.text();
138
+ const { value: parsed, parseError } = parseJson(responseText);
139
+ if (parseError && responseText) {
140
+ throw createProviderError({
141
+ provider,
142
+ status: response.status,
143
+ statusText: response.statusText,
144
+ responseText,
145
+ cause: parseError
146
+ });
147
+ }
148
+ const payloadError = getProviderPayloadError(provider, parsed);
149
+ if (!response.ok || payloadError) {
150
+ const status = Number((_b = (_a = payloadError === null || payloadError === void 0 ? void 0 : payloadError.status_code) !== null && _a !== void 0 ? _a : payloadError === null || payloadError === void 0 ? void 0 : payloadError.status) !== null && _b !== void 0 ? _b : response.status);
151
+ const statusText = response.statusText || `${(_d = (_c = payloadError === null || payloadError === void 0 ? void 0 : payloadError.error) !== null && _c !== void 0 ? _c : payloadError === null || payloadError === void 0 ? void 0 : payloadError.message) !== null && _d !== void 0 ? _d : 'Request failed'}`;
152
+ throw createProviderError({ provider, status, statusText, payload: payloadError !== null && payloadError !== void 0 ? payloadError : parsed, responseText });
153
+ }
154
+ return parsed;
155
+ }
156
+ catch (error) {
157
+ if (attempt >= maxRetries || !(0, exports.isRetriableError)(provider, error)) {
158
+ throw error;
159
+ }
160
+ attempt++;
161
+ await delay(retryBaseDelayMs * attempt);
162
+ }
163
+ }
164
+ };
165
+ exports.fetchProviderJson = fetchProviderJson;
@@ -9,7 +9,7 @@ var ComputeEnvironment;
9
9
  ComputeEnvironment["AWS_EC2"] = "aws_ec2";
10
10
  ComputeEnvironment["AWS_OTHER"] = "aws_other";
11
11
  ComputeEnvironment["OTHER"] = "other";
12
- })(ComputeEnvironment = exports.ComputeEnvironment || (exports.ComputeEnvironment = {}));
12
+ })(ComputeEnvironment || (exports.ComputeEnvironment = ComputeEnvironment = {}));
13
13
  class Environment {
14
14
  static async getComputeEnvironment() {
15
15
  if (process.env.AWS_LAMBDA_FUNCTION_NAME || process.env.AWS_EXECUTION_ENV) {
@@ -73,14 +73,14 @@ class Environment {
73
73
  const response = await fetch('http://169.254.169.254/latest/meta-data/tags/instance/Name');
74
74
  return response.text();
75
75
  }
76
- catch (err) {
76
+ catch (_a) {
77
77
  // swallow
78
78
  }
79
79
  try {
80
80
  const response = await fetch('http://169.254.169.254/latest/meta-data/instance-id');
81
81
  return response.text();
82
82
  }
83
- catch (err) {
83
+ catch (_b) {
84
84
  // swallow
85
85
  }
86
86
  return null;
@@ -90,7 +90,7 @@ class Environment {
90
90
  const response = await fetch('http://169.254.170.2/v2/metadata');
91
91
  return await response.json();
92
92
  }
93
- catch (err) {
93
+ catch (_a) {
94
94
  return null;
95
95
  }
96
96
  }
package/fn/index.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
3
1
  interface AlbEvent {
4
2
  requestContext: {
5
3
  elb: {
package/fn/index.js CHANGED
@@ -61,7 +61,7 @@ const invokeExpressViaAlb = async (albHandler, ctx, input) => {
61
61
  !/^text\//.test(ctype) &&
62
62
  !/(json|xml|x-www-form-urlencoded|javascript|graphql)/.test(ctype);
63
63
  const event = {
64
- requestContext: { elb: { targetGroupArn: 'arn:kora:fn' } },
64
+ requestContext: { elb: { targetGroupArn: 'arn:kora:fn' } }, // marks this as an ALB event
65
65
  httpMethod: h.method,
66
66
  path: url.pathname,
67
67
  queryStringParameters: Object.keys(qs).length ? qs : null,