@augustdigital/sdk 8.3.2 → 8.6.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 (213) hide show
  1. package/README.md +207 -117
  2. package/lib/abis/ERC20_Bytes32.d.ts +4 -0
  3. package/lib/abis/ERC20_Bytes32.js +4 -0
  4. package/lib/abis/ERC4626.d.ts +1 -0
  5. package/lib/abis/ERC4626.js +1 -0
  6. package/lib/abis/ERC721.d.ts +1 -0
  7. package/lib/abis/ERC721.js +1 -0
  8. package/lib/abis/FeeOracle.js +1 -0
  9. package/lib/abis/LendingPool.js +1 -0
  10. package/lib/abis/LendingPoolV2.js +1 -0
  11. package/lib/abis/Multicall3.js +3 -0
  12. package/lib/abis/OFT.d.ts +20 -0
  13. package/lib/abis/OFT.js +20 -0
  14. package/lib/abis/SmartAccount.d.ts +1 -0
  15. package/lib/abis/SmartAccount.js +3 -0
  16. package/lib/abis/SwapRouter.d.ts +1 -0
  17. package/lib/abis/SwapRouter.js +1 -0
  18. package/lib/abis/UniversalSignatureValidator.js +3 -0
  19. package/lib/abis/index.d.ts +5 -0
  20. package/lib/abis/index.js +5 -0
  21. package/lib/adapters/evm/getters.d.ts +17 -2
  22. package/lib/adapters/evm/getters.js +35 -3
  23. package/lib/adapters/evm/index.d.ts +266 -4
  24. package/lib/adapters/evm/index.js +270 -1
  25. package/lib/adapters/evm/utils.d.ts +6 -0
  26. package/lib/adapters/evm/utils.js +7 -0
  27. package/lib/adapters/solana/constants.d.ts +1 -1
  28. package/lib/adapters/solana/constants.js +4 -1
  29. package/lib/adapters/solana/getters.d.ts +9 -1
  30. package/lib/adapters/solana/getters.js +21 -0
  31. package/lib/adapters/solana/idl/vault-idl.js +9 -0
  32. package/lib/adapters/solana/index.d.ts +59 -4
  33. package/lib/adapters/solana/index.js +61 -0
  34. package/lib/adapters/solana/types.d.ts +1 -1
  35. package/lib/adapters/solana/utils.d.ts +31 -3
  36. package/lib/adapters/solana/utils.js +79 -4
  37. package/lib/adapters/solana/vault.actions.d.ts +23 -4
  38. package/lib/adapters/solana/vault.actions.js +47 -3
  39. package/lib/adapters/stellar/actions.d.ts +26 -1
  40. package/lib/adapters/stellar/actions.js +33 -0
  41. package/lib/adapters/stellar/constants.d.ts +26 -0
  42. package/lib/adapters/stellar/constants.js +29 -0
  43. package/lib/adapters/stellar/getters.d.ts +56 -0
  44. package/lib/adapters/stellar/getters.js +81 -0
  45. package/lib/adapters/stellar/index.d.ts +70 -0
  46. package/lib/adapters/stellar/index.js +71 -0
  47. package/lib/adapters/stellar/soroban.d.ts +21 -1
  48. package/lib/adapters/stellar/soroban.js +47 -1
  49. package/lib/adapters/stellar/submit.d.ts +29 -0
  50. package/lib/adapters/stellar/submit.js +90 -5
  51. package/lib/adapters/stellar/types.d.ts +27 -0
  52. package/lib/adapters/stellar/types.js +3 -0
  53. package/lib/adapters/stellar/utils.d.ts +10 -0
  54. package/lib/adapters/stellar/utils.js +10 -0
  55. package/lib/adapters/sui/getters.d.ts +7 -1
  56. package/lib/adapters/sui/getters.js +6 -0
  57. package/lib/adapters/sui/index.d.ts +16 -1
  58. package/lib/adapters/sui/index.js +17 -0
  59. package/lib/adapters/sui/transformer.d.ts +8 -2
  60. package/lib/adapters/sui/transformer.js +7 -0
  61. package/lib/adapters/sui/types.d.ts +1 -1
  62. package/lib/adapters/sui/utils.d.ts +6 -0
  63. package/lib/adapters/sui/utils.js +6 -0
  64. package/lib/core/analytics/chain-name.d.ts +8 -0
  65. package/lib/core/analytics/chain-name.js +8 -0
  66. package/lib/core/analytics/constants.d.ts +4 -0
  67. package/lib/core/analytics/constants.js +4 -0
  68. package/lib/core/analytics/env.d.ts +25 -0
  69. package/lib/core/analytics/env.js +26 -0
  70. package/lib/core/analytics/index.d.ts +26 -0
  71. package/lib/core/analytics/index.js +35 -0
  72. package/lib/core/analytics/instrumentation.d.ts +26 -0
  73. package/lib/core/analytics/instrumentation.js +66 -2
  74. package/lib/core/analytics/method-taxonomy.d.ts +16 -0
  75. package/lib/core/analytics/method-taxonomy.js +18 -0
  76. package/lib/core/analytics/metrics.d.ts +23 -0
  77. package/lib/core/analytics/metrics.js +40 -0
  78. package/lib/core/analytics/sanitize.d.ts +38 -0
  79. package/lib/core/analytics/sanitize.js +46 -0
  80. package/lib/core/analytics/sentry-runtime.d.ts +11 -0
  81. package/lib/core/analytics/sentry-runtime.js +20 -1
  82. package/lib/core/analytics/sentry.d.ts +47 -2
  83. package/lib/core/analytics/sentry.js +115 -2
  84. package/lib/core/analytics/types.d.ts +28 -1
  85. package/lib/core/analytics/user-identity.d.ts +36 -2
  86. package/lib/core/analytics/user-identity.js +43 -1
  87. package/lib/core/analytics/version.d.ts +6 -1
  88. package/lib/core/analytics/version.js +6 -1
  89. package/lib/core/auth/verify.js +5 -0
  90. package/lib/core/base.class.d.ts +78 -3
  91. package/lib/core/base.class.js +66 -4
  92. package/lib/core/cache.d.ts +5 -0
  93. package/lib/core/cache.js +6 -0
  94. package/lib/core/constants/adapters.d.ts +16 -1
  95. package/lib/core/constants/adapters.js +28 -8
  96. package/lib/core/constants/core.d.ts +14 -1
  97. package/lib/core/constants/core.js +18 -0
  98. package/lib/core/constants/swap-router.d.ts +83 -1
  99. package/lib/core/constants/swap-router.js +91 -1
  100. package/lib/core/constants/vaults.d.ts +57 -1
  101. package/lib/core/constants/vaults.js +66 -1
  102. package/lib/core/constants/web3.d.ts +4 -1
  103. package/lib/core/constants/web3.js +15 -5
  104. package/lib/core/errors/index.d.ts +38 -2
  105. package/lib/core/errors/index.js +38 -0
  106. package/lib/core/fetcher.d.ts +136 -2
  107. package/lib/core/fetcher.js +229 -25
  108. package/lib/core/helpers/adapters.d.ts +10 -1
  109. package/lib/core/helpers/adapters.js +11 -0
  110. package/lib/core/helpers/chain-address.d.ts +10 -0
  111. package/lib/core/helpers/chain-address.js +11 -0
  112. package/lib/core/helpers/core.d.ts +49 -4
  113. package/lib/core/helpers/core.js +74 -7
  114. package/lib/core/helpers/explorer-link.d.ts +15 -1
  115. package/lib/core/helpers/explorer-link.js +14 -0
  116. package/lib/core/helpers/signer.d.ts +27 -1
  117. package/lib/core/helpers/signer.js +39 -0
  118. package/lib/core/helpers/swap-router.d.ts +33 -1
  119. package/lib/core/helpers/swap-router.js +32 -0
  120. package/lib/core/helpers/vault-version.d.ts +2 -1
  121. package/lib/core/helpers/vault-version.js +2 -0
  122. package/lib/core/helpers/vaults.d.ts +9 -1
  123. package/lib/core/helpers/vaults.js +22 -8
  124. package/lib/core/helpers/web3.d.ts +154 -2
  125. package/lib/core/helpers/web3.js +192 -15
  126. package/lib/core/logger/index.d.ts +55 -0
  127. package/lib/core/logger/index.js +19 -0
  128. package/lib/core/logger/slack.d.ts +3 -0
  129. package/lib/core/logger/slack.js +3 -0
  130. package/lib/core/vault-metadata.d.ts +7 -1
  131. package/lib/core/vault-metadata.js +6 -0
  132. package/lib/core/version-check.d.ts +52 -0
  133. package/lib/core/version-check.js +82 -1
  134. package/lib/evm/methods/crossChainVault.d.ts +90 -0
  135. package/lib/evm/methods/crossChainVault.js +187 -2
  136. package/lib/evm/methods/crossChainVaultRegistry.d.ts +93 -0
  137. package/lib/evm/methods/crossChainVaultRegistry.js +240 -0
  138. package/lib/evm/methods/index.d.ts +1 -0
  139. package/lib/evm/methods/index.js +1 -0
  140. package/lib/evm/types/crossChain.d.ts +202 -0
  141. package/lib/evm/types/crossChain.js +12 -1
  142. package/lib/index.d.ts +16 -0
  143. package/lib/index.js +19 -0
  144. package/lib/main.d.ts +289 -6
  145. package/lib/main.js +312 -0
  146. package/lib/modules/api/index.d.ts +1 -0
  147. package/lib/modules/api/index.js +6 -0
  148. package/lib/modules/api/main.d.ts +52 -0
  149. package/lib/modules/api/main.js +130 -0
  150. package/lib/modules/sub-accounts/fetcher.d.ts +17 -2
  151. package/lib/modules/sub-accounts/fetcher.js +15 -0
  152. package/lib/modules/sub-accounts/main.d.ts +35 -2
  153. package/lib/modules/sub-accounts/main.js +39 -1
  154. package/lib/modules/sub-accounts/utils.d.ts +4 -1
  155. package/lib/modules/sub-accounts/utils.js +3 -0
  156. package/lib/modules/vaults/adapter.helpers.d.ts +21 -3
  157. package/lib/modules/vaults/adapter.helpers.js +34 -0
  158. package/lib/modules/vaults/fetcher.d.ts +45 -0
  159. package/lib/modules/vaults/fetcher.js +91 -4
  160. package/lib/modules/vaults/getters.d.ts +295 -0
  161. package/lib/modules/vaults/getters.js +567 -26
  162. package/lib/modules/vaults/index.d.ts +12 -0
  163. package/lib/modules/vaults/index.js +12 -0
  164. package/lib/modules/vaults/main.d.ts +292 -4
  165. package/lib/modules/vaults/main.js +382 -8
  166. package/lib/modules/vaults/read.actions.d.ts +170 -2
  167. package/lib/modules/vaults/read.actions.js +143 -0
  168. package/lib/modules/vaults/types.d.ts +34 -0
  169. package/lib/modules/vaults/utils/call-data-decoder.d.ts +48 -1
  170. package/lib/modules/vaults/utils/call-data-decoder.js +56 -0
  171. package/lib/modules/vaults/utils/date-utils.d.ts +39 -0
  172. package/lib/modules/vaults/utils/date-utils.js +47 -1
  173. package/lib/modules/vaults/utils.d.ts +85 -0
  174. package/lib/modules/vaults/utils.js +139 -7
  175. package/lib/modules/vaults/write.actions.d.ts +363 -3
  176. package/lib/modules/vaults/write.actions.js +399 -3
  177. package/lib/polyfills.js +4 -2
  178. package/lib/sdk.d.ts +23847 -0
  179. package/lib/services/coingecko/fetcher.d.ts +15 -1
  180. package/lib/services/coingecko/fetcher.js +30 -9
  181. package/lib/services/debank/fetcher.d.ts +15 -1
  182. package/lib/services/debank/fetcher.js +12 -0
  183. package/lib/services/debank/utils.d.ts +1 -1
  184. package/lib/services/debank/utils.js +18 -1
  185. package/lib/services/layerzero/deposits.d.ts +12 -1
  186. package/lib/services/layerzero/deposits.js +34 -11
  187. package/lib/services/layerzero/redeems.d.ts +11 -1
  188. package/lib/services/layerzero/redeems.js +13 -0
  189. package/lib/services/layerzero/utils.d.ts +8 -0
  190. package/lib/services/layerzero/utils.js +11 -0
  191. package/lib/services/octavfi/fetcher.d.ts +8 -1
  192. package/lib/services/octavfi/fetcher.js +25 -0
  193. package/lib/services/octavfi/types.d.ts +1 -1
  194. package/lib/services/octavfi/utils.d.ts +14 -2
  195. package/lib/services/octavfi/utils.js +44 -10
  196. package/lib/services/subgraph/fetcher.js +4 -2
  197. package/lib/services/subgraph/vaults.d.ts +12 -0
  198. package/lib/services/subgraph/vaults.js +119 -2
  199. package/lib/services/swap-quotes/index.d.ts +80 -0
  200. package/lib/services/swap-quotes/index.js +27 -1
  201. package/lib/services/swap-quotes/paraswap.d.ts +25 -0
  202. package/lib/services/swap-quotes/paraswap.js +22 -0
  203. package/lib/types/pools.d.ts +5 -2
  204. package/lib/types/staking.d.ts +1 -1
  205. package/lib/types/sub-accounts.d.ts +1 -1
  206. package/lib/types/subgraph.d.ts +10 -1
  207. package/lib/types/typed-contract.d.ts +64 -0
  208. package/lib/types/vaults.d.ts +163 -2
  209. package/lib/types/vaults.js +10 -0
  210. package/lib/types/web3.d.ts +9 -1
  211. package/lib/types/web3.js +1 -0
  212. package/lib/types/webserver.d.ts +47 -2
  213. package/package.json +6 -5
@@ -30,6 +30,7 @@ const vault_version_1 = require("./helpers/vault-version");
30
30
  const analytics_1 = require("./analytics");
31
31
  const sanitize_1 = require("./analytics/sanitize");
32
32
  const errors_1 = require("./errors");
33
+ /** Map an HTTP status to the appropriate typed SDK error subclass. */
33
34
  function errorFromResponseStatus(status, message, options) {
34
35
  if (status === 401) {
35
36
  return new errors_1.AugustAuthError('AUTH_UNAUTHORIZED', message, options);
@@ -43,6 +44,15 @@ function errorFromResponseStatus(status, message, options) {
43
44
  return new errors_1.AugustServerError(status, message, options);
44
45
  }
45
46
  let SDK_REQUEST_TIMEOUT_OVERRIDE = null;
47
+ /**
48
+ * Override the default request timeout used by every August fetcher helper
49
+ * when the caller doesn't supply a per-call `timeoutMs`. Pass `null` (or
50
+ * call with no arguments) to clear the override and fall back to the
51
+ * compiled-in `REQUEST_TIMEOUT_MS`.
52
+ *
53
+ * @param ms - Positive number of milliseconds to wait before aborting, or
54
+ * `null` to clear.
55
+ */
46
56
  function setSdkRequestTimeout(ms = null) {
47
57
  if (ms === null) {
48
58
  SDK_REQUEST_TIMEOUT_OVERRIDE = null;
@@ -56,9 +66,21 @@ function setSdkRequestTimeout(ms = null) {
56
66
  }
57
67
  SDK_REQUEST_TIMEOUT_OVERRIDE = ms;
58
68
  }
69
+ /**
70
+ * Read the active default request timeout — the SDK-level override if set,
71
+ * otherwise the compiled-in `REQUEST_TIMEOUT_MS`.
72
+ */
59
73
  function getSdkRequestTimeout() {
60
74
  return SDK_REQUEST_TIMEOUT_OVERRIDE ?? core_2.REQUEST_TIMEOUT_MS;
61
75
  }
76
+ /**
77
+ * Combine the per-request timeout signal with any caller-supplied signal.
78
+ * Uses `AbortSignal.any` (Node 22+) with a manual relay fallback.
79
+ *
80
+ * Returns `timedOut()` (a closure over an internal flag set when the timeout
81
+ * fires) so the caller can distinguish "we timed out" from "caller cancelled",
82
+ * and `cleanup()` to release the fallback-relay listener.
83
+ */
62
84
  function buildRequestAbortSignal(options) {
63
85
  const timeoutMs = options?.timeoutMs ?? SDK_REQUEST_TIMEOUT_OVERRIDE ?? core_2.REQUEST_TIMEOUT_MS;
64
86
  const timeoutController = new AbortController();
@@ -68,18 +90,23 @@ function buildRequestAbortSignal(options) {
68
90
  timeoutController.abort();
69
91
  }, timeoutMs);
70
92
  let signal = timeoutController.signal;
71
- let relayCleanup = () => { };
93
+ // No-op by default; replaced with a real listener-removal closure only when
94
+ // the manual relay fallback path below registers an 'abort' listener.
95
+ let relayCleanup = () => undefined;
72
96
  if (options?.signal) {
97
+ // Capture in a local so the cleanup closure below keeps the non-nullable
98
+ // narrowing (a closure over `options.signal` would otherwise be re-widened).
99
+ const callerSignal = options.signal;
73
100
  if (typeof AbortSignal.any === 'function') {
74
- signal = AbortSignal.any([timeoutController.signal, options.signal]);
101
+ signal = AbortSignal.any([timeoutController.signal, callerSignal]);
75
102
  }
76
- else if (options.signal.aborted) {
103
+ else if (callerSignal.aborted) {
77
104
  timeoutController.abort();
78
105
  }
79
106
  else {
80
107
  const relay = () => timeoutController.abort();
81
- options.signal.addEventListener('abort', relay, { once: true });
82
- relayCleanup = () => options.signal.removeEventListener('abort', relay);
108
+ callerSignal.addEventListener('abort', relay, { once: true });
109
+ relayCleanup = () => callerSignal.removeEventListener('abort', relay);
83
110
  }
84
111
  }
85
112
  return {
@@ -98,8 +125,20 @@ function warnOverrideDeprecation(callerTag) {
98
125
  if (_overrideDeprecationWarned)
99
126
  return;
100
127
  _overrideDeprecationWarned = true;
128
+ // Deliberately a direct console.warn, not Logger.log.warn: a deprecation
129
+ // notice must surface in the integrator's console in every environment.
130
+ // Logger.log.warn would downgrade it to a prod-silent Sentry breadcrumb.
131
+ // biome-ignore lint/suspicious/noConsole: integrator-facing deprecation notice must surface in every environment; Logger.log.warn would downgrade it to a prod-silent Sentry breadcrumb.
101
132
  console.warn(`[@augustdigital/sdk] ${callerTag}: option "override" is deprecated and will be removed in a future major version. Pass a valid apiKey instead.`);
102
133
  }
134
+ /**
135
+ * LRU cache for API responses and frequently accessed data.
136
+ * 24-hour TTL with 1000 item capacity.
137
+ */
138
+ /**
139
+ * Read a response header tolerantly. Real `fetch` returns a `Headers` instance,
140
+ * but some test mocks supply a plain object; both shapes are supported here.
141
+ */
103
142
  function readResponseHeader(headers, name) {
104
143
  if (!headers)
105
144
  return undefined;
@@ -114,17 +153,33 @@ const cache_1 = require("./cache");
114
153
  Object.defineProperty(exports, "CACHE", { enumerable: true, get: function () { return cache_1.CACHE; } });
115
154
  Object.defineProperty(exports, "getSafeCache", { enumerable: true, get: function () { return cache_1.getSafeCache; } });
116
155
  Object.defineProperty(exports, "setSafeCache", { enumerable: true, get: function () { return cache_1.setSafeCache; } });
156
+ /**
157
+ * Dedicated cache for token prices with short TTL to prevent rate limiting.
158
+ * 30-second TTL with 500 item capacity.
159
+ */
117
160
  const PRICE_CACHE = new lru_cache_1.LRUCache({
118
161
  max: 500,
119
- ttl: 1000 * 30,
162
+ ttl: 1000 * 30, // 30 seconds
120
163
  allowStale: false,
121
164
  });
165
+ /**
166
+ * Cache for failed price requests to prevent retry storms.
167
+ * 5-second TTL to allow quick retry after temporary failures.
168
+ */
122
169
  const PRICE_ERROR_CACHE = new lru_cache_1.LRUCache({
123
170
  max: 100,
124
- ttl: 1000 * 5,
171
+ ttl: 1000 * 5, // 5 seconds
125
172
  allowStale: false,
126
173
  });
174
+ /**
175
+ * In-flight price requests to prevent duplicate simultaneous API calls.
176
+ */
127
177
  const PRICE_REQUESTS = new Map();
178
+ /**
179
+ * Build an August backend URL, rejecting unknown server keys and any
180
+ * `relativeUrl` that would escape the configured origin.
181
+ * Throws `AugustValidationError` with `code: 'INVALID_URL'`.
182
+ */
128
183
  function buildAugustUrl(server, relativeUrl) {
129
184
  const serverKey = server;
130
185
  const base = core_2.WEBSERVER_URL[serverKey];
@@ -134,9 +189,12 @@ function buildAugustUrl(server, relativeUrl) {
134
189
  if (typeof relativeUrl !== 'string' || relativeUrl.length === 0) {
135
190
  throw new errors_1.AugustValidationError('INVALID_URL', 'relativeUrl must be a non-empty string');
136
191
  }
192
+ // Reject anything that looks like a scheme or protocol-relative authority.
137
193
  if (/^\s*[a-z][a-z0-9+.-]*:|^\s*\/\//i.test(relativeUrl)) {
138
194
  throw new errors_1.AugustValidationError('INVALID_URL', `relativeUrl must be a path, not an absolute or protocol-relative URL`);
139
195
  }
196
+ // Concat (rather than `new URL(rel, base)`) preserves base paths like /api/v1
197
+ // when relativeUrl starts with `/`. Origin check below is a second line.
140
198
  const candidate = `${base}${relativeUrl}`;
141
199
  let url;
142
200
  try {
@@ -151,6 +209,15 @@ function buildAugustUrl(server, relativeUrl) {
151
209
  }
152
210
  return url.toString();
153
211
  }
212
+ /**
213
+ * Make authenticated requests to August backend services using API key.
214
+ * Automatically logs errors with correlation IDs for debugging.
215
+ * @param apiKey - August API key for authentication
216
+ * @param relativeUrl - Endpoint path relative to base URL
217
+ * @param options - Request configuration including method and headers
218
+ * @returns Response object if successful
219
+ * @throws Error with correlation ID if request fails
220
+ */
154
221
  async function fetchAugustWithKey(apiKey, relativeUrl, options) {
155
222
  if (options?.override) {
156
223
  warnOverrideDeprecation('fetchAugustWithKey');
@@ -178,6 +245,7 @@ async function fetchAugustWithKey(apiKey, relativeUrl, options) {
178
245
  signal: requestSignal,
179
246
  });
180
247
  cleanup();
248
+ // Track API call
181
249
  (0, analytics_1.trackApiCall)(relativeUrl, options?.method ?? 'GET', startTime, res.status, server);
182
250
  const correlationId = readResponseHeader(res.headers, 'x-correlation-id');
183
251
  const logger = logger_1.Logger.getLogger();
@@ -206,6 +274,14 @@ async function fetchAugustWithKey(apiKey, relativeUrl, options) {
206
274
  throw err;
207
275
  }
208
276
  }
277
+ /**
278
+ * Make unauthenticated requests to August public API endpoints.
279
+ * Used for fetching vault metadata and public market data.
280
+ * @param relativeUrl - Endpoint path relative to public API base URL
281
+ * @param options - Request configuration including method and headers
282
+ * @returns Response object if successful
283
+ * @throws Error with correlation ID if request fails
284
+ */
209
285
  async function fetchAugustPublic(relativeUrl, options) {
210
286
  const defaultHeaders = {
211
287
  'content-type': 'application/json',
@@ -253,6 +329,15 @@ async function fetchAugustPublic(relativeUrl, options) {
253
329
  throw err;
254
330
  }
255
331
  }
332
+ /**
333
+ * Make authenticated requests using Bearer token authentication.
334
+ * Typically used for user-specific operations requiring OAuth tokens.
335
+ * @param bearerToken - OAuth bearer token
336
+ * @param relativeUrl - Endpoint path relative to base URL
337
+ * @param options - Request configuration including method and headers
338
+ * @returns Response object if successful
339
+ * @throws Error with correlation ID if request fails
340
+ */
256
341
  async function fetchAugustWithBearer(bearerToken, relativeUrl, options) {
257
342
  const defaultHeaders = {
258
343
  authorization: `Bearer ${bearerToken}`,
@@ -302,6 +387,10 @@ async function fetchAugustWithBearer(bearerToken, relativeUrl, options) {
302
387
  throw err;
303
388
  }
304
389
  }
390
+ /**
391
+ * Check if an error is network-related and suitable for retry.
392
+ * Identifies timeouts, connection resets, and fetch failures.
393
+ */
305
394
  function isRetryableError(error) {
306
395
  return ((error instanceof TypeError && error.message.includes('fetch failed')) ||
307
396
  error.name === 'AbortError' ||
@@ -311,6 +400,16 @@ function isRetryableError(error) {
311
400
  error.message.includes('ENOTFOUND') ||
312
401
  error.message.includes('ECONNREFUSED'));
313
402
  }
403
+ /**
404
+ * Wrap async operations with exponential backoff retry logic.
405
+ * Retries on network errors with increasing delays between attempts.
406
+ * @param fn - Async function to execute with retry logic
407
+ * @param maxRetries - Maximum number of retry attempts
408
+ * @param baseDelay - Initial delay in milliseconds (doubles each retry)
409
+ * @param shouldRetry - Custom function to determine if error is retryable
410
+ * @returns Result of successful function execution
411
+ * @throws Last error if all retries exhausted
412
+ */
314
413
  async function withRetry(fn, maxRetries = 3, baseDelay = 1000, shouldRetry = isRetryableError) {
315
414
  let lastError;
316
415
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
@@ -328,12 +427,22 @@ async function withRetry(fn, maxRetries = 3, baseDelay = 1000, shouldRetry = isR
328
427
  throw error;
329
428
  }
330
429
  }
331
- throw lastError;
430
+ // Unreachable in practice: the final attempt re-throws inside the loop. This
431
+ // satisfies the compiler's control-flow analysis without a non-null assertion.
432
+ throw lastError ?? new Error('withRetry exhausted retries without an error');
332
433
  }
434
+ /**
435
+ * Fetch tokenized vault configurations from backend API.
436
+ * Results are cached to reduce API calls. Optionally filter by specific vault address.
437
+ * @param pool - Optional vault address to filter results
438
+ * @param headers - Monitoring headers for request tracking
439
+ * @returns Array of tokenized vault configurations
440
+ */
333
441
  async function fetchTokenizedVault(pool, headers, loadSubaccounts, loadSnapshots) {
334
442
  if (!pool) {
335
443
  return [];
336
444
  }
445
+ // Build cache key based on loading options
337
446
  const keyParts = [`tokenized-vault-${pool}`];
338
447
  if (loadSubaccounts === false)
339
448
  keyParts.push('no-subaccounts');
@@ -346,6 +455,7 @@ async function fetchTokenizedVault(pool, headers, loadSubaccounts, loadSnapshots
346
455
  tokenizedVault = cachedResponse;
347
456
  }
348
457
  if (!tokenizedVault) {
458
+ // Build endpoint URL with optional query parameters
349
459
  let endpoint = core_2.WEBSERVER_ENDPOINTS.public.tokenizedVault.byVaultAddress(pool);
350
460
  const params = new URLSearchParams();
351
461
  if (loadSubaccounts === false) {
@@ -356,21 +466,31 @@ async function fetchTokenizedVault(pool, headers, loadSubaccounts, loadSnapshots
356
466
  }
357
467
  const queryString = params.toString();
358
468
  if (queryString) {
359
- endpoint += '?' + queryString;
469
+ endpoint += `?${queryString}`;
360
470
  }
361
471
  const tokenizedVaultsResponse = await fetchAugustPublic(endpoint, {
362
472
  headers: headers,
363
473
  });
364
474
  tokenizedVault = (await tokenizedVaultsResponse.json());
365
- cache_1.CACHE.set(key, tokenizedVault, { ttl: 1000 * 60 * 10 });
475
+ cache_1.CACHE.set(key, tokenizedVault, { ttl: 1000 * 60 * 10 }); // Cache for 10 minutes
366
476
  }
477
+ // Filter by specific vault if address provided
367
478
  if (pool &&
368
479
  ((0, ethers_1.isAddress)(pool) || (0, chain_address_1.isSolanaAddress)(pool) || (0, chain_address_1.isStellarAddress)(pool))) {
369
480
  return tokenizedVault ? [tokenizedVault] : [];
370
481
  }
371
482
  return [tokenizedVault];
372
483
  }
484
+ /**
485
+ * Fetch tokenized vault configurations from backend API.
486
+ * Results are cached to reduce API calls. Optionally filter by specific vault address.
487
+ * @param pool - Optional vault address to filter results
488
+ * @param headers - Monitoring headers for request tracking
489
+ * @param loadSubaccounts - Optional flag to load subaccounts and EOA operators (defaults to true on API)
490
+ * @returns Array of tokenized vault configurations
491
+ */
373
492
  async function fetchTokenizedVaults(pool, headers, loadSubaccounts, loadSnapshots) {
493
+ // Build cache key based on loading options
374
494
  const keyParts = ['tokenized-vaults'];
375
495
  if (pool)
376
496
  keyParts.push(pool);
@@ -385,6 +505,7 @@ async function fetchTokenizedVaults(pool, headers, loadSubaccounts, loadSnapshot
385
505
  tokenizedVaults = cachedResponse;
386
506
  }
387
507
  if (!tokenizedVaults) {
508
+ // Build endpoint URL with optional query parameters
388
509
  let endpoint = core_2.WEBSERVER_ENDPOINTS.public.tokenizedVault.list;
389
510
  const params = new URLSearchParams();
390
511
  if (loadSubaccounts === false) {
@@ -395,16 +516,17 @@ async function fetchTokenizedVaults(pool, headers, loadSubaccounts, loadSnapshot
395
516
  }
396
517
  const queryString = params.toString();
397
518
  if (queryString) {
398
- endpoint += '?' + queryString;
519
+ endpoint += `?${queryString}`;
399
520
  }
400
521
  const tokenizedVaultsResponse = await fetchAugustPublic(endpoint, {
401
522
  headers: headers,
402
523
  });
403
524
  tokenizedVaults =
404
525
  (await tokenizedVaultsResponse.json());
405
- const ttl = 1000 * 60 * 15;
526
+ const ttl = 1000 * 60 * 15; // 15 minute cache
406
527
  cache_1.CACHE.set(key, tokenizedVaults, { ttl });
407
528
  }
529
+ // Filter by specific vault if address provided
408
530
  if (pool &&
409
531
  ((0, ethers_1.isAddress)(pool) || (0, chain_address_1.isSolanaAddress)(pool) || (0, chain_address_1.isStellarAddress)(pool))) {
410
532
  const foundObj = tokenizedVaults.find((t) => t.address.toLowerCase() === pool.toLowerCase());
@@ -412,57 +534,93 @@ async function fetchTokenizedVaults(pool, headers, loadSubaccounts, loadSnapshot
412
534
  }
413
535
  return tokenizedVaults;
414
536
  }
537
+ /**
538
+ * Fetch current USD price for a token symbol or vault LP token.
539
+ * For vault LP tokens, calculates share price using totalAssets/totalSupply ratio.
540
+ * Falls back to CoinGecko if primary source fails.
541
+ * @param symbol - Token symbol or contract address
542
+ * @param provider - Optional web3 provider for on-chain price calculations
543
+ * @param coinGeckoKey - Optional CoinGecko API key for fallback pricing
544
+ * @param headers - Monitoring headers for request tracking
545
+ * @returns Token price in USD
546
+ */
415
547
  async function fetchTokenPrice(symbol, provider, coinGeckoKey, headers) {
416
548
  if (!symbol) {
417
549
  logger_1.Logger.log.error('fetchTokenPrice', 'symbol parameter is undefined');
418
550
  return 0;
419
551
  }
552
+ // Normalize symbol and apply aliases BEFORE generating cache key
420
553
  let normalizedSymbol = symbol.toLowerCase();
421
554
  normalizedSymbol = normalizedSymbol === 'xbtc' ? 'btc' : normalizedSymbol;
422
555
  normalizedSymbol = normalizedSymbol === 'strbtc' ? 'btc' : normalizedSymbol;
556
+ // Generate cache key based on normalized symbol and provider presence
423
557
  const cacheKey = `price:${normalizedSymbol}:${provider ? 'with-provider' : 'no-provider'}`;
558
+ // Check error cache to prevent retry storms after failures
424
559
  const cachedError = PRICE_ERROR_CACHE.get(cacheKey);
425
560
  if (cachedError) {
426
561
  logger_1.Logger.log.info('fetchTokenPrice', `Error cache HIT for ${normalizedSymbol} - throwing cached error`);
427
562
  throw cachedError;
428
563
  }
564
+ // Check price cache first to avoid rate limiting
429
565
  const cachedPrice = PRICE_CACHE.get(cacheKey);
430
566
  if (cachedPrice !== undefined) {
431
567
  logger_1.Logger.log.info('fetchTokenPrice', `Cache HIT for ${normalizedSymbol}: $${cachedPrice}`);
432
568
  return cachedPrice;
433
569
  }
570
+ // Check for in-flight request to deduplicate simultaneous calls
434
571
  const inflightRequest = PRICE_REQUESTS.get(cacheKey);
435
572
  if (inflightRequest) {
436
573
  logger_1.Logger.log.info('fetchTokenPrice', `In-flight request deduplication for ${normalizedSymbol}`);
437
574
  return inflightRequest;
438
575
  }
576
+ // Create the price fetching promise
439
577
  const pricePromise = (async () => {
440
578
  try {
441
579
  const price = await _fetchTokenPriceInternal(symbol, provider, coinGeckoKey, headers);
580
+ // Cache the successful result
442
581
  PRICE_CACHE.set(cacheKey, price);
582
+ // Clear any previous error cache on success
443
583
  PRICE_ERROR_CACHE.delete(cacheKey);
444
584
  return price;
445
585
  }
446
586
  catch (error) {
587
+ // Cache the error temporarily to prevent retry storms
447
588
  const err = error instanceof Error ? error : new Error(String(error));
448
589
  PRICE_ERROR_CACHE.set(cacheKey, err);
449
590
  logger_1.Logger.log.info('fetchTokenPrice', `Cached error for ${normalizedSymbol}: ${err.message}`);
450
591
  throw err;
451
592
  }
452
593
  finally {
594
+ // Clean up in-flight request tracking
453
595
  PRICE_REQUESTS.delete(cacheKey);
454
596
  }
455
597
  })();
598
+ // Track this request to prevent duplicates
456
599
  PRICE_REQUESTS.set(cacheKey, pricePromise);
457
600
  return pricePromise;
458
601
  }
602
+ // Normalize both raw integers to the same decimal scale BEFORE dividing —
603
+ // dividing the raw bigints first floors the ratio to ~1 and loses all
604
+ // fractional precision. Returns 1 for an empty vault (priced 1:1).
605
+ function normalizedSharePrice(totalAssets, totalSupply, decimals) {
606
+ const supply = Number((0, core_1.toNormalizedBn)(totalSupply, decimals).normalized);
607
+ if (!supply)
608
+ return 1;
609
+ return Number((0, core_1.toNormalizedBn)(totalAssets, decimals).normalized) / supply;
610
+ }
611
+ /**
612
+ * Internal price fetching implementation.
613
+ * This is separated to allow caching and request deduplication in the public function.
614
+ */
459
615
  async function _fetchTokenPriceInternal(symbol, provider, coinGeckoKey, headers) {
616
+ // Fetch vault metadata to check if symbol is a vault LP token
460
617
  const tokenizedVaults = await fetchTokenizedVaults(undefined, headers, false, false);
461
618
  const vaultArray = tokenizedVaults.map((vault) => ({
462
619
  vault: vault.address,
463
620
  symbol: vault.receipt_token_symbol,
464
621
  }));
465
622
  const foundVaultLpAsset = vaultArray.find((v) => v.symbol?.toLowerCase() === symbol?.toLowerCase());
623
+ // @todo: handle multi-asset vaults
466
624
  const tokenizedVault = tokenizedVaults.find((vault) => vault.address === foundVaultLpAsset?.vault);
467
625
  const version = (0, vault_version_1.getVaultVersionV2)(tokenizedVault);
468
626
  if (foundVaultLpAsset?.vault && provider && version === 'evm-2') {
@@ -474,19 +632,22 @@ async function _fetchTokenPriceInternal(symbol, provider, coinGeckoKey, headers)
474
632
  const sharePrice = await vaultContract.getSharePrice();
475
633
  return Number((0, core_1.toNormalizedBn)(sharePrice, decimals).normalized);
476
634
  }
477
- catch (e) {
635
+ catch {
478
636
  throw new Error('failed to fetch token price for evm-2 vault');
479
637
  }
480
638
  }
639
+ // Apply symbol normalization and aliases for API calls
481
640
  let _symbol = symbol?.toLowerCase();
482
641
  _symbol = _symbol === 'xbtc' ? 'btc' : _symbol;
483
642
  _symbol = _symbol === 'strbtc' ? 'btc' : _symbol;
484
643
  try {
644
+ // Fetch base token price from August backend
485
645
  const res = await fetch(`https://prices.augustdigital.io/price/${_symbol}`, { headers: headers });
486
646
  if (res.status !== 200) {
487
647
  throw new Error(`fetching ${_symbol} price: ${res?.statusText}`);
488
648
  }
489
649
  const json = (await res.json());
650
+ // For vault LP tokens, calculate share price using on-chain ratio
490
651
  if (foundVaultLpAsset && provider) {
491
652
  const vaultContract = new ethers_2.Contract((0, ethers_1.getAddress)(foundVaultLpAsset.vault), abis_1.ABI_LENDING_POOL_V2, provider);
492
653
  const [totalAssets, totalSupply, rawDecimals] = await Promise.all([
@@ -495,12 +656,13 @@ async function _fetchTokenPriceInternal(symbol, provider, coinGeckoKey, headers)
495
656
  vaultContract.decimals(),
496
657
  ]);
497
658
  const decimals = Number(rawDecimals);
498
- const ratio = (totalAssets || BigInt(0)) / (totalSupply || BigInt(0)) || 1;
499
- return json.price * Number((0, core_1.toNormalizedBn)(ratio, decimals).normalized);
659
+ const sharePrice = normalizedSharePrice(totalAssets, totalSupply, decimals);
660
+ return json.price * sharePrice;
500
661
  }
501
662
  return json.price;
502
663
  }
503
- catch (e) {
664
+ catch {
665
+ // Fallback to CoinGecko if primary source fails
504
666
  const fallbackPrice = await (0, fetcher_1.fetchTokenPricesFromCoinGecko)(symbol, coinGeckoKey);
505
667
  if (foundVaultLpAsset && provider) {
506
668
  const vaultContract = new ethers_2.Contract((0, ethers_1.getAddress)(foundVaultLpAsset.vault), abis_1.ABI_LENDING_POOL_V2, provider);
@@ -510,8 +672,8 @@ async function _fetchTokenPriceInternal(symbol, provider, coinGeckoKey, headers)
510
672
  vaultContract.decimals(),
511
673
  ]);
512
674
  const decimals = Number(rawDecimals);
513
- const ratio = (totalAssets || BigInt(0)) / (totalSupply || BigInt(0)) || 1;
514
- return fallbackPrice * Number((0, core_1.toNormalizedBn)(ratio, decimals).normalized);
675
+ const sharePrice = normalizedSharePrice(totalAssets, totalSupply, decimals);
676
+ return fallbackPrice * sharePrice;
515
677
  }
516
678
  if (fallbackPrice) {
517
679
  return fallbackPrice;
@@ -521,6 +683,13 @@ async function _fetchTokenPriceInternal(symbol, provider, coinGeckoKey, headers)
521
683
  }
522
684
  }
523
685
  }
686
+ /**
687
+ * Fetch token price by contract address and chain ID from August price server.
688
+ * @param address - Token contract address
689
+ * @param chainId - Network chain ID
690
+ * @param headers - Optional monitoring headers for request tracking
691
+ * @returns Token price in USD, or 0 if not found
692
+ */
524
693
  async function fetchTokenPriceByAddress(address, chainId, headers) {
525
694
  if (!address || !chainId) {
526
695
  return 0;
@@ -547,32 +716,58 @@ async function fetchTokenPriceByAddress(address, chainId, headers) {
547
716
  return 0;
548
717
  }
549
718
  }
719
+ /**
720
+ * Fetch active loans for a specific vault from backend API.
721
+ * Results are cached per vault and chain to reduce API calls.
722
+ * @param pool - Vault address
723
+ * @param chainId - Network chain ID
724
+ * @param headers - Monitoring headers for request tracking
725
+ * @returns Array of active loan objects
726
+ */
550
727
  async function fetchTokenizedVaultLoans(pool, chainId, headers) {
551
728
  const key = `loans-${chainId}-${pool}`;
552
729
  let loans;
553
- if (cache_1.CACHE.get(key)) {
554
- loans = cache_1.CACHE.get(key);
730
+ const cached = await (0, cache_1.getSafeCache)(key);
731
+ if (cached) {
732
+ loans = cached;
555
733
  }
556
734
  else {
557
735
  const tokenizedVaultsResponse = await fetchAugustPublic(core_2.WEBSERVER_ENDPOINTS.public.tokenizedVault.loans(pool, chainId, true), { headers: headers });
558
736
  loans = (await tokenizedVaultsResponse.json()).loans;
559
- cache_1.CACHE.set(key, loans);
737
+ await (0, cache_1.setSafeCache)(key, loans);
560
738
  }
561
739
  return loans?.filter((l) => l.state.toLowerCase() === 'active');
562
740
  }
741
+ /**
742
+ * Fetch subaccount loans for a specific tokenized vault (lending pool).
743
+ * Retrieves all active subaccount loan data from the backend.
744
+ * @param pool - Vault address
745
+ * @param chainId - Network chain ID
746
+ * @param headers - Monitoring headers for request tracking
747
+ * @returns Array of active subaccount loan objects
748
+ */
563
749
  async function fetchTokenizedVaultSubaccountLoans(pool, chainId, headers) {
564
750
  const key = `subaccount-loans-${chainId}-${pool}`;
565
751
  let loans;
566
- if (cache_1.CACHE.get(key)) {
567
- loans = cache_1.CACHE.get(key);
752
+ const cached = await (0, cache_1.getSafeCache)(key);
753
+ if (cached) {
754
+ loans = cached;
568
755
  }
569
756
  else {
570
757
  const tokenizedVaultsResponse = await fetchAugustPublic(core_2.WEBSERVER_ENDPOINTS.public.tokenizedVault.subaccountLoans(pool, chainId, true), { headers: headers });
571
758
  loans = (await tokenizedVaultsResponse.json()).loans;
572
- cache_1.CACHE.set(key, loans);
759
+ await (0, cache_1.setSafeCache)(key, loans);
573
760
  }
574
761
  return loans?.filter((l) => l.state.toLowerCase() === 'active');
575
762
  }
763
+ /**
764
+ * Fetch historical timeseries data for a specific vault.
765
+ * Returns TVL, APY, PnL, and share price data over time.
766
+ * @param vaultAddress - Vault contract address
767
+ * @param nDays - Number of days of historical data to fetch (default 30, min 1)
768
+ * @param headers - Monitoring headers for request tracking
769
+ * @returns Historical timeseries data with date keys
770
+ */
576
771
  async function fetchVaultHistoricalTimeseries(vaultAddress, nDays, headers) {
577
772
  const key = `vault-historical-timeseries-${vaultAddress}-${nDays}`;
578
773
  const cachedResponse = await (0, cache_1.getSafeCache)(key);
@@ -584,9 +779,18 @@ async function fetchVaultHistoricalTimeseries(vaultAddress, nDays, headers) {
584
779
  throw new Error(`Failed to fetch historical timeseries data: ${response.statusText}`);
585
780
  }
586
781
  const json = (await response.json());
587
- cache_1.CACHE.set(key, json, { ttl: 1000 * 60 * 10 });
782
+ cache_1.CACHE.set(key, json, { ttl: 1000 * 60 * 10 }); // Cache for 10 minutes
588
783
  return json;
589
784
  }
785
+ /**
786
+ * Execute multiple promises with retry logic and allow partial failures.
787
+ * Unlike Promise.all, continues execution if some promises fail.
788
+ * Failed promises return null to maintain array index alignment.
789
+ * @param promises - Array of promises to execute
790
+ * @param maxRetries - Maximum retry attempts per promise
791
+ * @param baseDelay - Initial delay for exponential backoff
792
+ * @returns Array of results with null for failed promises
793
+ */
590
794
  async function promiseSettle(promises, maxRetries = 3, baseDelay = 1000) {
591
795
  const promisesWithRetry = promises.map((promise, index) => withRetry(async () => {
592
796
  try {
@@ -1,4 +1,13 @@
1
- import { IAddress, IVaultAdapterConfig } from '../../types';
1
+ import type { IAddress, IVaultAdapterConfig } from '../../types';
2
+ /**
3
+ * Get adapter configuration for a vault
4
+ */
2
5
  export declare function getVaultAdapterConfig(vaultAddress: IAddress): IVaultAdapterConfig | undefined;
6
+ /**
7
+ * Check if vault has adapter support
8
+ */
3
9
  export declare function vaultHasAdapter(vaultAddress: IAddress): boolean;
10
+ /**
11
+ * Get deposit tokens for a vault (includes underlying + adapter tokens)
12
+ */
4
13
  export declare function getVaultDepositTokens(vaultAddress: IAddress, underlyingToken: IAddress): IAddress[];
@@ -4,19 +4,30 @@ exports.getVaultAdapterConfig = getVaultAdapterConfig;
4
4
  exports.vaultHasAdapter = vaultHasAdapter;
5
5
  exports.getVaultDepositTokens = getVaultDepositTokens;
6
6
  const adapters_1 = require("../constants/adapters");
7
+ /**
8
+ * Get adapter configuration for a vault
9
+ */
7
10
  function getVaultAdapterConfig(vaultAddress) {
8
11
  return adapters_1.VAULT_ADAPTER_CONFIGS[vaultAddress];
9
12
  }
13
+ /**
14
+ * Check if vault has adapter support
15
+ */
10
16
  function vaultHasAdapter(vaultAddress) {
11
17
  return !!adapters_1.VAULT_ADAPTER_CONFIGS[vaultAddress];
12
18
  }
19
+ /**
20
+ * Get deposit tokens for a vault (includes underlying + adapter tokens)
21
+ */
13
22
  function getVaultDepositTokens(vaultAddress, underlyingToken) {
14
23
  const adapterConfig = getVaultAdapterConfig(vaultAddress);
15
24
  if (!adapterConfig) {
16
25
  return [underlyingToken];
17
26
  }
27
+ // Return all tokens including the underlying
18
28
  const allTokens = [underlyingToken];
19
29
  if (adapterConfig.tokens && adapterConfig.tokens.length > 0) {
30
+ // Use configured tokens list
20
31
  adapterConfig.tokens.forEach((token) => {
21
32
  if (!allTokens.includes(token)) {
22
33
  allTokens.push(token);
@@ -1,3 +1,13 @@
1
+ /**
2
+ * Check if a string is a valid Solana base58 address (including PDAs).
3
+ * Returns `false` for EVM hex addresses.
4
+ */
1
5
  export declare function isSolanaAddress(address: string): boolean;
6
+ /**
7
+ * Check if a string is a valid Stellar address.
8
+ * Muxed accounts (M prefix, per SEP-0023) are intentionally excluded — they are
9
+ * not valid vault or contract addresses.
10
+ */
2
11
  export declare function isStellarAddress(address: string): boolean;
12
+ /** Check if a string is a valid Sui address (0x + 64 hex chars). */
3
13
  export declare function isSuiAddress(address: string): boolean;
@@ -5,7 +5,12 @@ exports.isStellarAddress = isStellarAddress;
5
5
  exports.isSuiAddress = isSuiAddress;
6
6
  const ethers_1 = require("ethers");
7
7
  const web3_js_1 = require("@solana/web3.js");
8
+ /** Stellar uses RFC 4648 base32; 56 chars, prefixed by G (public key) or C (contract/Soroban). */
8
9
  const STELLAR_ADDRESS_RE = /^[A-Z2-7]{56}$/;
10
+ /**
11
+ * Check if a string is a valid Solana base58 address (including PDAs).
12
+ * Returns `false` for EVM hex addresses.
13
+ */
9
14
  function isSolanaAddress(address) {
10
15
  if (!address)
11
16
  return false;
@@ -19,6 +24,11 @@ function isSolanaAddress(address) {
19
24
  return false;
20
25
  }
21
26
  }
27
+ /**
28
+ * Check if a string is a valid Stellar address.
29
+ * Muxed accounts (M prefix, per SEP-0023) are intentionally excluded — they are
30
+ * not valid vault or contract addresses.
31
+ */
22
32
  function isStellarAddress(address) {
23
33
  if (!address || typeof address !== 'string')
24
34
  return false;
@@ -28,6 +38,7 @@ function isStellarAddress(address) {
28
38
  return false;
29
39
  return STELLAR_ADDRESS_RE.test(address);
30
40
  }
41
+ /** Check if a string is a valid Sui address (0x + 64 hex chars). */
31
42
  function isSuiAddress(address) {
32
43
  if (!address || typeof address !== 'string')
33
44
  return false;