@ember-finance/sdk 2.15.0 → 2.17.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.
- package/dist/src/api/v2/apis/vaults-api.d.ts +8 -8
- package/dist/src/api/v2/apis/vaults-api.js +4 -6
- package/dist/src/api/v2/models/atomic-vault-info.d.ts +84 -0
- package/dist/src/api/v2/models/atomic-vault-info.js +14 -0
- package/dist/src/api/v2/models/index.d.ts +1 -0
- package/dist/src/api/v2/models/index.js +1 -0
- package/dist/src/api/v2/models/sui-protocol-info.d.ts +9 -0
- package/dist/src/placements/pointers.js +129 -45
- package/package.json +1 -1
|
@@ -48,13 +48,13 @@ export declare const VaultsApiAxiosParamCreator: (configuration?: Configuration)
|
|
|
48
48
|
/**
|
|
49
49
|
* Pointers only: which protocol venues list the coin. No amounts are returned — clients read those from chain, so a stale row here can drop a venue but can never report a wrong balance.
|
|
50
50
|
* @summary Get venues that list an Ember receipt coin
|
|
51
|
-
* @param {string} coin Sui coin type or EVM token address of the receipt coin
|
|
51
|
+
* @param {string} [coin] Sui coin type or EVM token address of the receipt coin. Omit it to get every active placement, sorted by coin. A blank value is rejected.
|
|
52
52
|
* @param {string} [protocol] Restrict to one protocol adapter id, e.g. \"bluefin-spot\"
|
|
53
53
|
* @param {string} [chainIdentifier] Narrow to one chain, e.g. \"sui-mainnet\".
|
|
54
54
|
* @param {*} [options] Override http request option.
|
|
55
55
|
* @throws {RequiredError}
|
|
56
56
|
*/
|
|
57
|
-
getCoinPlacements: (coin
|
|
57
|
+
getCoinPlacements: (coin?: string, protocol?: string, chainIdentifier?: string, options?: RawAxiosRequestConfig) => Promise<RequestArgs>;
|
|
58
58
|
/**
|
|
59
59
|
*
|
|
60
60
|
* @summary Get the price of the coins
|
|
@@ -215,13 +215,13 @@ export declare const VaultsApiFp: (configuration?: Configuration) => {
|
|
|
215
215
|
/**
|
|
216
216
|
* Pointers only: which protocol venues list the coin. No amounts are returned — clients read those from chain, so a stale row here can drop a venue but can never report a wrong balance.
|
|
217
217
|
* @summary Get venues that list an Ember receipt coin
|
|
218
|
-
* @param {string} coin Sui coin type or EVM token address of the receipt coin
|
|
218
|
+
* @param {string} [coin] Sui coin type or EVM token address of the receipt coin. Omit it to get every active placement, sorted by coin. A blank value is rejected.
|
|
219
219
|
* @param {string} [protocol] Restrict to one protocol adapter id, e.g. \"bluefin-spot\"
|
|
220
220
|
* @param {string} [chainIdentifier] Narrow to one chain, e.g. \"sui-mainnet\".
|
|
221
221
|
* @param {*} [options] Override http request option.
|
|
222
222
|
* @throws {RequiredError}
|
|
223
223
|
*/
|
|
224
|
-
getCoinPlacements(coin
|
|
224
|
+
getCoinPlacements(coin?: string, protocol?: string, chainIdentifier?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<CoinPlacementsResponse>>;
|
|
225
225
|
/**
|
|
226
226
|
*
|
|
227
227
|
* @summary Get the price of the coins
|
|
@@ -382,13 +382,13 @@ export declare const VaultsApiFactory: (configuration?: Configuration, basePath?
|
|
|
382
382
|
/**
|
|
383
383
|
* Pointers only: which protocol venues list the coin. No amounts are returned — clients read those from chain, so a stale row here can drop a venue but can never report a wrong balance.
|
|
384
384
|
* @summary Get venues that list an Ember receipt coin
|
|
385
|
-
* @param {string} coin Sui coin type or EVM token address of the receipt coin
|
|
385
|
+
* @param {string} [coin] Sui coin type or EVM token address of the receipt coin. Omit it to get every active placement, sorted by coin. A blank value is rejected.
|
|
386
386
|
* @param {string} [protocol] Restrict to one protocol adapter id, e.g. \"bluefin-spot\"
|
|
387
387
|
* @param {string} [chainIdentifier] Narrow to one chain, e.g. \"sui-mainnet\".
|
|
388
388
|
* @param {*} [options] Override http request option.
|
|
389
389
|
* @throws {RequiredError}
|
|
390
390
|
*/
|
|
391
|
-
getCoinPlacements(coin
|
|
391
|
+
getCoinPlacements(coin?: string, protocol?: string, chainIdentifier?: string, options?: RawAxiosRequestConfig): AxiosPromise<CoinPlacementsResponse>;
|
|
392
392
|
/**
|
|
393
393
|
*
|
|
394
394
|
* @summary Get the price of the coins
|
|
@@ -552,14 +552,14 @@ export declare class VaultsApi extends BaseAPI {
|
|
|
552
552
|
/**
|
|
553
553
|
* Pointers only: which protocol venues list the coin. No amounts are returned — clients read those from chain, so a stale row here can drop a venue but can never report a wrong balance.
|
|
554
554
|
* @summary Get venues that list an Ember receipt coin
|
|
555
|
-
* @param {string} coin Sui coin type or EVM token address of the receipt coin
|
|
555
|
+
* @param {string} [coin] Sui coin type or EVM token address of the receipt coin. Omit it to get every active placement, sorted by coin. A blank value is rejected.
|
|
556
556
|
* @param {string} [protocol] Restrict to one protocol adapter id, e.g. \"bluefin-spot\"
|
|
557
557
|
* @param {string} [chainIdentifier] Narrow to one chain, e.g. \"sui-mainnet\".
|
|
558
558
|
* @param {*} [options] Override http request option.
|
|
559
559
|
* @throws {RequiredError}
|
|
560
560
|
* @memberof VaultsApi
|
|
561
561
|
*/
|
|
562
|
-
getCoinPlacements(coin
|
|
562
|
+
getCoinPlacements(coin?: string, protocol?: string, chainIdentifier?: string, options?: RawAxiosRequestConfig): Promise<import("axios").AxiosResponse<CoinPlacementsResponse, any, {}>>;
|
|
563
563
|
/**
|
|
564
564
|
*
|
|
565
565
|
* @summary Get the price of the coins
|
|
@@ -55,15 +55,13 @@ export const VaultsApiAxiosParamCreator = function (configuration) {
|
|
|
55
55
|
/**
|
|
56
56
|
* Pointers only: which protocol venues list the coin. No amounts are returned — clients read those from chain, so a stale row here can drop a venue but can never report a wrong balance.
|
|
57
57
|
* @summary Get venues that list an Ember receipt coin
|
|
58
|
-
* @param {string} coin Sui coin type or EVM token address of the receipt coin
|
|
58
|
+
* @param {string} [coin] Sui coin type or EVM token address of the receipt coin. Omit it to get every active placement, sorted by coin. A blank value is rejected.
|
|
59
59
|
* @param {string} [protocol] Restrict to one protocol adapter id, e.g. \"bluefin-spot\"
|
|
60
60
|
* @param {string} [chainIdentifier] Narrow to one chain, e.g. \"sui-mainnet\".
|
|
61
61
|
* @param {*} [options] Override http request option.
|
|
62
62
|
* @throws {RequiredError}
|
|
63
63
|
*/
|
|
64
64
|
getCoinPlacements: async (coin, protocol, chainIdentifier, options = {}) => {
|
|
65
|
-
// verify required parameter 'coin' is not null or undefined
|
|
66
|
-
assertParamExists("getCoinPlacements", "coin", coin);
|
|
67
65
|
const localVarPath = `/api/v2/vaults/placements`;
|
|
68
66
|
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
|
69
67
|
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
|
@@ -713,7 +711,7 @@ export const VaultsApiFp = function (configuration) {
|
|
|
713
711
|
/**
|
|
714
712
|
* Pointers only: which protocol venues list the coin. No amounts are returned — clients read those from chain, so a stale row here can drop a venue but can never report a wrong balance.
|
|
715
713
|
* @summary Get venues that list an Ember receipt coin
|
|
716
|
-
* @param {string} coin Sui coin type or EVM token address of the receipt coin
|
|
714
|
+
* @param {string} [coin] Sui coin type or EVM token address of the receipt coin. Omit it to get every active placement, sorted by coin. A blank value is rejected.
|
|
717
715
|
* @param {string} [protocol] Restrict to one protocol adapter id, e.g. \"bluefin-spot\"
|
|
718
716
|
* @param {string} [chainIdentifier] Narrow to one chain, e.g. \"sui-mainnet\".
|
|
719
717
|
* @param {*} [options] Override http request option.
|
|
@@ -974,7 +972,7 @@ export const VaultsApiFactory = function (configuration, basePath, axios) {
|
|
|
974
972
|
/**
|
|
975
973
|
* Pointers only: which protocol venues list the coin. No amounts are returned — clients read those from chain, so a stale row here can drop a venue but can never report a wrong balance.
|
|
976
974
|
* @summary Get venues that list an Ember receipt coin
|
|
977
|
-
* @param {string} coin Sui coin type or EVM token address of the receipt coin
|
|
975
|
+
* @param {string} [coin] Sui coin type or EVM token address of the receipt coin. Omit it to get every active placement, sorted by coin. A blank value is rejected.
|
|
978
976
|
* @param {string} [protocol] Restrict to one protocol adapter id, e.g. \"bluefin-spot\"
|
|
979
977
|
* @param {string} [chainIdentifier] Narrow to one chain, e.g. \"sui-mainnet\".
|
|
980
978
|
* @param {*} [options] Override http request option.
|
|
@@ -1217,7 +1215,7 @@ export class VaultsApi extends BaseAPI {
|
|
|
1217
1215
|
/**
|
|
1218
1216
|
* Pointers only: which protocol venues list the coin. No amounts are returned — clients read those from chain, so a stale row here can drop a venue but can never report a wrong balance.
|
|
1219
1217
|
* @summary Get venues that list an Ember receipt coin
|
|
1220
|
-
* @param {string} coin Sui coin type or EVM token address of the receipt coin
|
|
1218
|
+
* @param {string} [coin] Sui coin type or EVM token address of the receipt coin. Omit it to get every active placement, sorted by coin. A blank value is rejected.
|
|
1221
1219
|
* @param {string} [protocol] Restrict to one protocol adapter id, e.g. \"bluefin-spot\"
|
|
1222
1220
|
* @param {string} [chainIdentifier] Narrow to one chain, e.g. \"sui-mainnet\".
|
|
1223
1221
|
* @param {*} [options] Override http request option.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ember Protocol Vaults API
|
|
3
|
+
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
|
4
|
+
*
|
|
5
|
+
* The version of the OpenAPI document: 2.0.0
|
|
6
|
+
*
|
|
7
|
+
*
|
|
8
|
+
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
|
9
|
+
* https://openapi-generator.tech
|
|
10
|
+
* Do not edit the class manually.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* An Atomic Liquidity Vault on Sui. Not a VaultInfo with extra fields: an ALV mints no receipt coin, so it has no ReceiptCoinType, and it carries a rate manager because its rate is set externally rather than derived from a receipt supply.
|
|
14
|
+
* @export
|
|
15
|
+
* @interface AtomicVaultInfo
|
|
16
|
+
*/
|
|
17
|
+
export interface AtomicVaultInfo {
|
|
18
|
+
/**
|
|
19
|
+
* The name of the vault
|
|
20
|
+
* @type {string}
|
|
21
|
+
* @memberof AtomicVaultInfo
|
|
22
|
+
*/
|
|
23
|
+
Name?: string;
|
|
24
|
+
/**
|
|
25
|
+
* The object identifier of the vault
|
|
26
|
+
* @type {string}
|
|
27
|
+
* @memberof AtomicVaultInfo
|
|
28
|
+
*/
|
|
29
|
+
ObjectId?: string;
|
|
30
|
+
/**
|
|
31
|
+
* The coin type the vault holds and pays out in
|
|
32
|
+
* @type {string}
|
|
33
|
+
* @memberof AtomicVaultInfo
|
|
34
|
+
*/
|
|
35
|
+
CollateralCoinType?: string;
|
|
36
|
+
/**
|
|
37
|
+
* The number of decimals for the collateral coin
|
|
38
|
+
* @type {number}
|
|
39
|
+
* @memberof AtomicVaultInfo
|
|
40
|
+
*/
|
|
41
|
+
CollateralCoinDecimals?: number;
|
|
42
|
+
/**
|
|
43
|
+
* The admin identifier for the vault
|
|
44
|
+
* @type {string}
|
|
45
|
+
* @memberof AtomicVaultInfo
|
|
46
|
+
*/
|
|
47
|
+
Admin?: string;
|
|
48
|
+
/**
|
|
49
|
+
* The operator identifier for the vault
|
|
50
|
+
* @type {string}
|
|
51
|
+
* @memberof AtomicVaultInfo
|
|
52
|
+
*/
|
|
53
|
+
Operator?: string;
|
|
54
|
+
/**
|
|
55
|
+
* The address allowed to update the vault\'s rate
|
|
56
|
+
* @type {string}
|
|
57
|
+
* @memberof AtomicVaultInfo
|
|
58
|
+
*/
|
|
59
|
+
RateManager?: string;
|
|
60
|
+
/**
|
|
61
|
+
* The maximum rate change per update
|
|
62
|
+
* @type {string}
|
|
63
|
+
* @memberof AtomicVaultInfo
|
|
64
|
+
*/
|
|
65
|
+
MaxRateChangePerUpdate?: string;
|
|
66
|
+
/**
|
|
67
|
+
* The fee percentage for the vault
|
|
68
|
+
* @type {string}
|
|
69
|
+
* @memberof AtomicVaultInfo
|
|
70
|
+
*/
|
|
71
|
+
FeePercentage?: string;
|
|
72
|
+
/**
|
|
73
|
+
* The minimum withdrawal shares
|
|
74
|
+
* @type {string}
|
|
75
|
+
* @memberof AtomicVaultInfo
|
|
76
|
+
*/
|
|
77
|
+
MinWithdrawalShares?: string;
|
|
78
|
+
/**
|
|
79
|
+
* List of sub-account identifiers
|
|
80
|
+
* @type {Array<string>}
|
|
81
|
+
* @memberof AtomicVaultInfo
|
|
82
|
+
*/
|
|
83
|
+
SubAccounts?: Array<string>;
|
|
84
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
/**
|
|
4
|
+
* Ember Protocol Vaults API
|
|
5
|
+
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
|
6
|
+
*
|
|
7
|
+
* The version of the OpenAPI document: 2.0.0
|
|
8
|
+
*
|
|
9
|
+
*
|
|
10
|
+
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
|
11
|
+
* https://openapi-generator.tech
|
|
12
|
+
* Do not edit the class manually.
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
@@ -12,6 +12,7 @@ export * from "./apy-history.js";
|
|
|
12
12
|
export * from "./apy-history-response.js";
|
|
13
13
|
export * from "./apy-period-average.js";
|
|
14
14
|
export * from "./asset.js";
|
|
15
|
+
export * from "./atomic-vault-info.js";
|
|
15
16
|
export * from "./borrowed.js";
|
|
16
17
|
export * from "./chain.js";
|
|
17
18
|
export * from "./chain-vault-details.js";
|
|
@@ -12,6 +12,7 @@ export * from "./apy-history.js";
|
|
|
12
12
|
export * from "./apy-history-response.js";
|
|
13
13
|
export * from "./apy-period-average.js";
|
|
14
14
|
export * from "./asset.js";
|
|
15
|
+
export * from "./atomic-vault-info.js";
|
|
15
16
|
export * from "./borrowed.js";
|
|
16
17
|
export * from "./chain.js";
|
|
17
18
|
export * from "./chain-vault-details.js";
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* https://openapi-generator.tech
|
|
10
10
|
* Do not edit the class manually.
|
|
11
11
|
*/
|
|
12
|
+
import type { AtomicVaultInfo } from "./atomic-vault-info.js";
|
|
12
13
|
import type { UltraCoinInfo } from "./ultra-coin-info.js";
|
|
13
14
|
import type { VaultInfo } from "./vault-info.js";
|
|
14
15
|
import type { VaultProtocol } from "./vault-protocol.js";
|
|
@@ -38,4 +39,12 @@ export interface SuiProtocolInfo {
|
|
|
38
39
|
Vaults?: {
|
|
39
40
|
[key: string]: VaultInfo;
|
|
40
41
|
};
|
|
42
|
+
/**
|
|
43
|
+
* Map of Atomic Liquidity Vaults by id
|
|
44
|
+
* @type {{ [key: string]: AtomicVaultInfo; }}
|
|
45
|
+
* @memberof SuiProtocolInfo
|
|
46
|
+
*/
|
|
47
|
+
AtomicVaults?: {
|
|
48
|
+
[key: string]: AtomicVaultInfo;
|
|
49
|
+
};
|
|
41
50
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { environmentConfig } from "../common/constants.js";
|
|
2
|
-
import {
|
|
2
|
+
import { isAbortError, isTransientStatus, retryAfterMs } from "./batch.js";
|
|
3
|
+
import { coinTypesMatch, normalizeCoinType } from "./sui.js";
|
|
3
4
|
/**
|
|
4
5
|
* Pointers held in memory. Useful for tests, and for pinning a known venue set
|
|
5
6
|
* without a round-trip.
|
|
@@ -35,69 +36,152 @@ export function httpPointerSource(options = {}) {
|
|
|
35
36
|
const path = options.path ?? "/api/v2/vaults/placements";
|
|
36
37
|
const cacheMs = options.cacheMs ?? 30000;
|
|
37
38
|
/**
|
|
38
|
-
* One request
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* fetched the same list five times and threw four of them away. Reading one
|
|
42
|
-
* vault meant eight requests where two would do, and a caller sweeping every
|
|
43
|
-
* vault made hundreds — enough to be rate-limited, and a rejected discover
|
|
44
|
-
* drops that venue from the answer.
|
|
39
|
+
* One request for every coin. The endpoint answers with the whole registry
|
|
40
|
+
* when `coin` is omitted, so reading every vault costs one call rather than
|
|
41
|
+
* one per receipt coin.
|
|
45
42
|
*
|
|
46
43
|
* In-flight requests are shared rather than merely cached, so the adapters
|
|
47
44
|
* racing each other at the start of a `forAccount` collapse into one call.
|
|
48
45
|
*/
|
|
49
46
|
const inFlight = new Map();
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
// Set when the server rejects the unfiltered call, i.e. it predates it.
|
|
48
|
+
// Rechecked after a while, so a long-lived source picks up a server that
|
|
49
|
+
// has since been upgraded.
|
|
50
|
+
let perCoinUntil = 0;
|
|
51
|
+
const shared = (key, load) => {
|
|
52
52
|
const hit = inFlight.get(key);
|
|
53
53
|
if (hit && Date.now() - hit.at < cacheMs)
|
|
54
54
|
return hit.rows;
|
|
55
|
-
const rows = (
|
|
56
|
-
const url = new URL(`${baseUrl.replace(/\/$/, "")}${path}`);
|
|
57
|
-
url.searchParams.set("coin", coin.coin);
|
|
58
|
-
const response = await fetch(url.toString(), {
|
|
59
|
-
headers: { Accept: "application/json" }
|
|
60
|
-
});
|
|
61
|
-
if (!response.ok) {
|
|
62
|
-
throw new Error(`pointer source ${response.status}`);
|
|
63
|
-
}
|
|
64
|
-
const body = (await response.json());
|
|
65
|
-
// The chain type comes from the coin, not the wire: an adapter only
|
|
66
|
-
// ever runs on its own chain, so a server that named it again could
|
|
67
|
-
// only ever disagree.
|
|
68
|
-
return (body.placements ?? []).map(placement => ({
|
|
69
|
-
id: placement.id,
|
|
70
|
-
protocol: placement.protocol,
|
|
71
|
-
chain: {
|
|
72
|
-
type: coin.chain.type,
|
|
73
|
-
chainId: placement.chainId,
|
|
74
|
-
identifier: placement.chainIdentifier
|
|
75
|
-
},
|
|
76
|
-
venue: placement.venue,
|
|
77
|
-
coin: placement.coin,
|
|
78
|
-
decimals: requireDecimals(placement),
|
|
79
|
-
protocolName: placement.protocolName,
|
|
80
|
-
protocolLogoUrl: placement.protocolLogoUrl,
|
|
81
|
-
source: placement.source,
|
|
82
|
-
label: placement.label,
|
|
83
|
-
shareToken: placement.shareToken,
|
|
84
|
-
url: placement.url,
|
|
85
|
-
rpcUrl: placement.rpcUrl
|
|
86
|
-
}));
|
|
87
|
-
})();
|
|
55
|
+
const rows = load();
|
|
88
56
|
// A failure is not cached: the next adapter should retry rather than
|
|
89
57
|
// inherit a rejection it never caused.
|
|
90
58
|
rows.catch(() => inFlight.delete(key));
|
|
91
59
|
inFlight.set(key, { at: Date.now(), rows });
|
|
92
60
|
return rows;
|
|
93
61
|
};
|
|
62
|
+
const request = (coin) => {
|
|
63
|
+
const url = new URL(`${baseUrl.replace(/\/$/, "")}${path}`);
|
|
64
|
+
if (coin !== undefined)
|
|
65
|
+
url.searchParams.set("coin", coin);
|
|
66
|
+
return fetchPlacements(url.toString());
|
|
67
|
+
};
|
|
68
|
+
const rowsFor = async (coin) => {
|
|
69
|
+
if (Date.now() >= perCoinUntil) {
|
|
70
|
+
try {
|
|
71
|
+
return await shared("*", () => request());
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
if (!(error instanceof PointerSourceError) || error.status !== 400) {
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
perCoinUntil = Date.now() + FALLBACK_RECHECK_MS;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return shared(coin.coin.toLowerCase(), () => request(coin.coin));
|
|
81
|
+
};
|
|
94
82
|
return {
|
|
95
83
|
async placements(protocol, coin) {
|
|
96
|
-
const rows = await
|
|
97
|
-
|
|
84
|
+
const rows = await rowsFor(coin);
|
|
85
|
+
const wanted = coinKey(coin.coin);
|
|
86
|
+
// One venue stored under two spellings of the coin would otherwise
|
|
87
|
+
// be read twice and double-count the position.
|
|
88
|
+
const seen = new Set();
|
|
89
|
+
return (rows
|
|
90
|
+
.filter(row => {
|
|
91
|
+
if (row.protocol !== protocol || coinKey(row.coin) !== wanted) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
const key = [row.chainIdentifier, row.venue, row.shareToken ?? ""]
|
|
95
|
+
.join(":")
|
|
96
|
+
.toLowerCase();
|
|
97
|
+
if (seen.has(key))
|
|
98
|
+
return false;
|
|
99
|
+
seen.add(key);
|
|
100
|
+
return true;
|
|
101
|
+
})
|
|
102
|
+
// The chain type comes from the coin, not the wire: an adapter
|
|
103
|
+
// only ever runs on its own chain, so a server that named it
|
|
104
|
+
// again could only ever disagree.
|
|
105
|
+
.map(row => ({
|
|
106
|
+
id: row.id,
|
|
107
|
+
protocol: row.protocol,
|
|
108
|
+
chain: {
|
|
109
|
+
type: coin.chain.type,
|
|
110
|
+
chainId: row.chainId,
|
|
111
|
+
identifier: row.chainIdentifier
|
|
112
|
+
},
|
|
113
|
+
venue: row.venue,
|
|
114
|
+
coin: row.coin,
|
|
115
|
+
decimals: requireDecimals(row),
|
|
116
|
+
protocolName: row.protocolName,
|
|
117
|
+
protocolLogoUrl: row.protocolLogoUrl,
|
|
118
|
+
source: row.source,
|
|
119
|
+
label: row.label,
|
|
120
|
+
shareToken: row.shareToken,
|
|
121
|
+
url: row.url,
|
|
122
|
+
rpcUrl: row.rpcUrl
|
|
123
|
+
})));
|
|
98
124
|
}
|
|
99
125
|
};
|
|
100
126
|
}
|
|
127
|
+
class PointerSourceError extends Error {
|
|
128
|
+
constructor(status) {
|
|
129
|
+
super(`pointer source ${status}`);
|
|
130
|
+
this.status = status;
|
|
131
|
+
this.name = "PointerSourceError";
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const FETCH_ATTEMPTS = 3;
|
|
135
|
+
const RETRY_BASE_MS = 250;
|
|
136
|
+
const RETRY_MAX_MS = 2000;
|
|
137
|
+
const FALLBACK_RECHECK_MS = 5 * 60000;
|
|
138
|
+
/**
|
|
139
|
+
* Retries transient failures. Every vault's venues now ride on this one
|
|
140
|
+
* request, and callers keep a failed discover rather than retrying it.
|
|
141
|
+
*/
|
|
142
|
+
async function fetchPlacements(url) {
|
|
143
|
+
for (let attempt = 1;; attempt++) {
|
|
144
|
+
let waitMs;
|
|
145
|
+
try {
|
|
146
|
+
const response = await fetch(url, {
|
|
147
|
+
headers: { Accept: "application/json" }
|
|
148
|
+
});
|
|
149
|
+
if (response.ok) {
|
|
150
|
+
const body = (await response.json());
|
|
151
|
+
return body.placements ?? [];
|
|
152
|
+
}
|
|
153
|
+
if (!isTransientStatus(response.status) || attempt >= FETCH_ATTEMPTS) {
|
|
154
|
+
throw new PointerSourceError(response.status);
|
|
155
|
+
}
|
|
156
|
+
waitMs = retryAfterMs(response.headers?.get("retry-after"));
|
|
157
|
+
// Retrying before the server asked would only add to the load it
|
|
158
|
+
// is shedding and fail again.
|
|
159
|
+
if (waitMs !== undefined && waitMs > RETRY_MAX_MS) {
|
|
160
|
+
throw new PointerSourceError(response.status);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
// fetch rejects only on network failure; retry that, not our own
|
|
165
|
+
// errors or an abort.
|
|
166
|
+
if (error instanceof PointerSourceError ||
|
|
167
|
+
isAbortError(error) ||
|
|
168
|
+
attempt >= FETCH_ATTEMPTS) {
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const backoff = Math.min(RETRY_BASE_MS * 2 ** (attempt - 1), RETRY_MAX_MS);
|
|
173
|
+
await new Promise(resolve => setTimeout(resolve, waitMs ?? backoff));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Coin identity across spellings. `coinTypesMatch` folds a Sui address; EVM
|
|
178
|
+
* addresses are compared case-insensitively and with or without `0x`, since
|
|
179
|
+
* the API returns them lowercased and the vaults list checksums them.
|
|
180
|
+
*/
|
|
181
|
+
function coinKey(coin) {
|
|
182
|
+
const normalized = normalizeCoinType(coin);
|
|
183
|
+
return normalized.includes("::") ? normalized : normalized.replace(/^0x/, "");
|
|
184
|
+
}
|
|
101
185
|
/**
|
|
102
186
|
* Merges what every source returns, de-duplicated by venue. Earlier sources win,
|
|
103
187
|
* so a caller can pin rows ahead of a remote set. A source that throws is
|