@augustdigital/sdk 9.5.0 → 9.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 (38) hide show
  1. package/lib/adapters/evm/index.d.ts +1 -1
  2. package/lib/adapters/evm/index.js +29 -29
  3. package/lib/core/analytics/app-name.js +1 -1
  4. package/lib/core/analytics/constants.d.ts +10 -0
  5. package/lib/core/analytics/constants.js +17 -1
  6. package/lib/core/analytics/index.d.ts +7 -4
  7. package/lib/core/analytics/index.js +8 -4
  8. package/lib/core/analytics/instrumentation.js +8 -4
  9. package/lib/core/analytics/method-taxonomy.js +13 -0
  10. package/lib/core/analytics/metrics.d.ts +25 -10
  11. package/lib/core/analytics/metrics.js +41 -15
  12. package/lib/core/analytics/sentry.d.ts +24 -3
  13. package/lib/core/analytics/sentry.js +49 -18
  14. package/lib/core/analytics/types.d.ts +11 -4
  15. package/lib/core/analytics/user-identity.d.ts +19 -5
  16. package/lib/core/analytics/user-identity.js +35 -21
  17. package/lib/core/analytics/version.d.ts +1 -1
  18. package/lib/core/analytics/version.js +1 -1
  19. package/lib/core/auth/verify.js +5 -1
  20. package/lib/core/base.class.js +3 -0
  21. package/lib/core/constants/core.d.ts +3 -0
  22. package/lib/core/constants/core.js +3 -0
  23. package/lib/core/constants/swap-router.d.ts +16 -0
  24. package/lib/core/constants/swap-router.js +16 -0
  25. package/lib/core/errors/index.d.ts +36 -1
  26. package/lib/core/errors/index.js +40 -1
  27. package/lib/core/fetcher.d.ts +13 -0
  28. package/lib/core/fetcher.js +33 -0
  29. package/lib/main.d.ts +5 -0
  30. package/lib/main.js +5 -0
  31. package/lib/modules/vaults/getters.d.ts +55 -0
  32. package/lib/modules/vaults/getters.js +58 -14
  33. package/lib/modules/vaults/main.d.ts +4 -0
  34. package/lib/modules/vaults/main.js +4 -0
  35. package/lib/modules/vaults/write.actions.d.ts +57 -1
  36. package/lib/modules/vaults/write.actions.js +86 -2
  37. package/lib/sdk.d.ts +13032 -12801
  38. package/package.json +2 -2
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSDKVersion = getSDKVersion;
3
4
  exports.resetErrorDedupe = resetErrorDedupe;
4
5
  exports.initializeSentry = initializeSentry;
5
6
  exports.updateUser = updateUser;
6
7
  exports.clearUser = clearUser;
7
8
  exports.isAnalyticsEnabled = isAnalyticsEnabled;
9
+ exports.getSpanAttributes = getSpanAttributes;
8
10
  exports.getSentry = getSentry;
9
11
  exports.resetAnalytics = resetAnalytics;
10
12
  exports.captureSdkException = captureSdkException;
@@ -22,9 +24,15 @@ let isEnabled = true;
22
24
  let currentIdentity = null;
23
25
  /** Cached API key for identity updates */
24
26
  let cachedApiKey;
27
+ /** Identity tags resolved at init, mirrored onto every `sdk.method` span. */
28
+ let spanAttributes = {};
25
29
  /**
26
- * Get the SDK version from the generated version file.
27
- * Falls back to 'development' if file doesn't exist (during local dev).
30
+ * The August SDK build version, read from the generated `version.ts`.
31
+ * Falls back to `'development'` in an unbuilt checkout, where that file does
32
+ * not exist. Shared by the Sentry `release` tag and the
33
+ * `x-august-sdk-version` request header so the two can never disagree.
34
+ *
35
+ * @internal
28
36
  */
29
37
  function getSDKVersion() {
30
38
  try {
@@ -326,7 +334,8 @@ function createSentrySink() {
326
334
  * error rather than silently anonymous events.
327
335
  *
328
336
  * @param config - Analytics configuration. Pass `tracesSampleRate` to
329
- * override the default of `0.1`.
337
+ * override the default of `0.1`, and `surface` to label the emitting
338
+ * surface (`sdk.surface` tag; defaults to `'sdk'`).
330
339
  * @param environment - Current environment (DEV or PROD).
331
340
  * @param appName - App-name slug, required. Set as the global `app.name`
332
341
  * tag on every event and used to derive `partner.id`. Identifier-shaped:
@@ -463,17 +472,6 @@ function initializeSentry(config, environment, appName, walletAddress, apiKey) {
463
472
  }
464
473
  return event;
465
474
  },
466
- beforeSendTransaction(transaction) {
467
- // Sub-sample the high-frequency network bookkeeping category (switchNetwork,
468
- // updateWallet, clearWallet, setSigner) to 10% of whatever tracesSampleRate
469
- // already passed — these fire on every wallet/chain update and are low signal
470
- // for the partner-usage dashboard relative to read/write calls.
471
- if (transaction.tags?.['sdk.category'] === 'network' &&
472
- Math.random() > 0.1) {
473
- return null;
474
- }
475
- return transaction;
476
- },
477
475
  });
478
476
  // Set initial user identity
479
477
  updateUser(walletAddress, environment);
@@ -500,13 +498,32 @@ function initializeSentry(config, environment, appName, walletAddress, apiKey) {
500
498
  * dashboards, and alert rules keep resolving; remove in the next major.
501
499
  */
502
500
  safeSetTag('sdk.version', sdkVersion);
503
- safeSetTag('sdk.runtime', (0, sentry_runtime_1.getSentryRuntime)());
501
+ const runtime = (0, sentry_runtime_1.getSentryRuntime)();
502
+ safeSetTag('sdk.runtime', runtime);
504
503
  // `app.name` is the dimension we filter on in Sentry to attribute
505
504
  // events to a specific consuming application — see SDK quickstart docs.
506
505
  safeSetTag('app.name', validatedAppName);
507
506
  // `unverified:` prefix until the backend API-key → partner-id endpoint exists.
508
- safeSetTag('partner.id', `unverified:${validatedAppName}`);
509
- safeSetTag('partner.tier', 'unverified');
507
+ const partnerId = `unverified:${validatedAppName}`;
508
+ safeSetTag('partner.id', partnerId);
509
+ // First-party apps are labelled so `partner.tier:unverified` already
510
+ // means "external integrator" before verification exists.
511
+ const partnerTier = constants_1.FIRST_PARTY_APP_NAMES.has(validatedAppName)
512
+ ? 'internal'
513
+ : 'unverified';
514
+ safeSetTag('partner.tier', partnerTier);
515
+ const surface = config.surface ?? 'sdk';
516
+ safeSetTag('sdk.surface', surface);
517
+ // Scope tags land only on the root span of a trace; child `sdk.method`
518
+ // spans get these copied on as attributes by `withMethodTracking`.
519
+ spanAttributes = {
520
+ 'app.name': validatedAppName,
521
+ 'partner.id': partnerId,
522
+ 'partner.tier': partnerTier,
523
+ 'sdk.runtime': runtime,
524
+ 'sdk.surface': surface,
525
+ 'august.sdk_version': sdkVersion,
526
+ };
510
527
  // Bridge the SDK's structured logger into Sentry now that the SDK is live.
511
528
  // Without this, Logger.log.error/warn are no-ops in production (no
512
529
  // SDKLogger/structured logger is wired by default), so SDK-internal
@@ -594,6 +611,18 @@ function clearUser() {
594
611
  function isAnalyticsEnabled() {
595
612
  return isEnabled && isInitialized;
596
613
  }
614
+ /**
615
+ * Identity tags resolved by `initializeSentry`, for `withMethodTracking` to
616
+ * copy onto each span as attributes. Sentry's span dataset carries scope tags
617
+ * on the root span only, so without this copy child `sdk.method` rows cannot
618
+ * be filtered by `app.name` or `partner.id`.
619
+ *
620
+ * @returns The tag snapshot, or `{}` until analytics has been enabled.
621
+ * @internal
622
+ */
623
+ function getSpanAttributes() {
624
+ return spanAttributes;
625
+ }
597
626
  /**
598
627
  * Get the current Sentry instance for advanced usage.
599
628
  * Returns null if analytics is disabled.
@@ -611,6 +640,7 @@ function resetAnalytics() {
611
640
  isEnabled = true;
612
641
  currentIdentity = null;
613
642
  cachedApiKey = undefined;
643
+ spanAttributes = {};
614
644
  logger_1.Logger.setSentrySink(null);
615
645
  (0, sentry_runtime_1.resetSentryRuntime)();
616
646
  // The error rate limiter is process-lifetime state; a reset analytics layer
@@ -624,7 +654,8 @@ function resetAnalytics() {
624
654
  * surface. No-ops when analytics is disabled.
625
655
  *
626
656
  * @param error - Thrown value. Non-Error values are wrapped.
627
- * @param options - `surface` ('cli' | 'sdk' | 'react' | string), extra `tags`,
657
+ * @param options - `surface` ('cli' | 'mcp' | 'sdk' | 'react' | string)
658
+ * overriding the init-time `sdk.surface` tag for this event, extra `tags`,
628
659
  * `extra` data, and a `flushTimeoutMs` to await before resolving (needed
629
660
  * in Node when the process is about to exit).
630
661
  */
@@ -7,17 +7,24 @@ export interface IAnalyticsConfig {
7
7
  /** Set to false to disable analytics. Default: true */
8
8
  enabled?: boolean;
9
9
  /**
10
- * Sentry trace sample rate. Defaults to `1.0` for the Phase 1/2 rollout;
11
- * values outside `[0, 1]` are clamped.
10
+ * Sentry trace sample rate. Defaults to `0.1`; values outside `[0, 1]`
11
+ * are clamped.
12
12
  */
13
13
  tracesSampleRate?: number;
14
+ /**
15
+ * Which August surface is emitting telemetry, recorded once at init as the
16
+ * sticky `sdk.surface` tag on every event and span. The CLI passes `'cli'`
17
+ * and the MCP server `'mcp'`; applications embedding the SDK directly can
18
+ * leave the default `'sdk'`.
19
+ */
20
+ surface?: 'sdk' | 'cli' | 'mcp' | 'react' | string;
14
21
  }
15
22
  /**
16
23
  * User identity for analytics tracking.
17
- * Multiple identifiers are used with priority: apiKeyHash > wallet > fingerprint > session
24
+ * Multiple identifiers are used with priority: apiKeyHash \> wallet \> domain \> fingerprint \> session
18
25
  */
19
26
  export interface IUserIdentity {
20
- /** SHA-256 hash prefix of API key (first 16 chars). Never stores raw key. */
27
+ /** Hex-encoded SHA-256 hash of the API key. Never stores the raw key. */
21
28
  apiKeyHash?: string;
22
29
  /** User's wallet address (lowercased) */
23
30
  walletAddress?: string;
@@ -1,11 +1,19 @@
1
1
  import type { IEnv } from '../../types';
2
2
  import type { IUserIdentity } from './types';
3
3
  /**
4
- * Mask an API key, keeping only the last 5 characters visible.
5
- * Example: "abcdefghij12345" -> "**********12345"
4
+ * Hash an API key with SHA-256 for use as a Sentry-safe identity token.
6
5
  *
7
- * @param apiKey - The raw API key to mask
8
- * @returns Masked API key with last 5 characters visible
6
+ * Only the hash is ever returned. The most recently hashed key and its hash
7
+ * are cached in memory, so a repeat call skips the digest and a different key
8
+ * replaces the cache instead of returning the previous key's hash.
9
+ *
10
+ * When the runtime has no usable `crypto.createHash` (e.g. a browser bundle
11
+ * without a `crypto` shim) this returns `''` rather than throwing, so the
12
+ * Sentry identity falls back to the next identifier instead of being dropped.
13
+ *
14
+ * @param apiKey - The raw API key to hash
15
+ * @returns Hex-encoded SHA-256 hash of `apiKey`, or `''` for an empty input or
16
+ * when hashing is unavailable
9
17
  */
10
18
  export declare function hashApiKey(apiKey: string): string;
11
19
  /**
@@ -33,7 +41,13 @@ export declare function generateFingerprint(): string | undefined;
33
41
  export declare function createUserIdentity(apiKey?: string, walletAddress?: string, environment?: IEnv): IUserIdentity;
34
42
  /**
35
43
  * Generate a unique user ID for Sentry from identity components.
36
- * Priority: apiKeyHash > wallet > fingerprint > session
44
+ * Priority: apiKeyHash \> wallet \> domain \> fingerprint \> session
45
+ *
46
+ * An API key identifies the integrator and a wallet identifies the end user,
47
+ * so both outrank the hosting domain — otherwise every visitor of one web app
48
+ * collapses into a single Sentry user. `domain` still ranks above the
49
+ * fingerprint, which nearly every runtime produces, so hostname attribution
50
+ * is not lost.
37
51
  *
38
52
  * @param identity - User identity object
39
53
  * @returns Prefixed user ID string
@@ -9,31 +9,39 @@ const crypto_1 = require("crypto");
9
9
  const uuid_1 = require("uuid");
10
10
  /** Session ID persists for SDK instance lifetime */
11
11
  let sessionId = null;
12
- /** Cached masked API key */
13
- let cachedMaskedApiKey = null;
12
+ /** Raw API key the cached hash below was computed from — lets a key change invalidate the cache. */
13
+ let cachedApiKey = null;
14
+ /** SHA-256 hash of {@link cachedApiKey}. */
15
+ let cachedApiKeyHash = null;
14
16
  /**
15
- * Mask an API key, keeping only the last 5 characters visible.
16
- * Example: "abcdefghij12345" -> "**********12345"
17
+ * Hash an API key with SHA-256 for use as a Sentry-safe identity token.
17
18
  *
18
- * @param apiKey - The raw API key to mask
19
- * @returns Masked API key with last 5 characters visible
19
+ * Only the hash is ever returned. The most recently hashed key and its hash
20
+ * are cached in memory, so a repeat call skips the digest and a different key
21
+ * replaces the cache instead of returning the previous key's hash.
22
+ *
23
+ * When the runtime has no usable `crypto.createHash` (e.g. a browser bundle
24
+ * without a `crypto` shim) this returns `''` rather than throwing, so the
25
+ * Sentry identity falls back to the next identifier instead of being dropped.
26
+ *
27
+ * @param apiKey - The raw API key to hash
28
+ * @returns Hex-encoded SHA-256 hash of `apiKey`, or `''` for an empty input or
29
+ * when hashing is unavailable
20
30
  */
21
31
  function hashApiKey(apiKey) {
22
32
  if (!apiKey)
23
33
  return '';
24
- // Use cached masked key if available
25
- if (cachedMaskedApiKey)
26
- return cachedMaskedApiKey;
27
- const visibleChars = 5;
28
- if (apiKey.length <= visibleChars) {
29
- cachedMaskedApiKey = apiKey;
34
+ if (cachedApiKey === apiKey && cachedApiKeyHash) {
35
+ return cachedApiKeyHash;
30
36
  }
31
- else {
32
- const masked = '*'.repeat(apiKey.length - visibleChars);
33
- const visible = apiKey.slice(-visibleChars);
34
- cachedMaskedApiKey = masked + visible;
37
+ try {
38
+ cachedApiKeyHash = (0, crypto_1.createHash)('sha256').update(apiKey).digest('hex');
39
+ cachedApiKey = apiKey;
40
+ return cachedApiKeyHash;
41
+ }
42
+ catch {
43
+ return '';
35
44
  }
36
- return cachedMaskedApiKey;
37
45
  }
38
46
  /**
39
47
  * Generate a session ID that persists for the SDK instance lifetime.
@@ -118,21 +126,27 @@ function createUserIdentity(apiKey, walletAddress, environment = 'PROD') {
118
126
  }
119
127
  /**
120
128
  * Generate a unique user ID for Sentry from identity components.
121
- * Priority: apiKeyHash > wallet > fingerprint > session
129
+ * Priority: apiKeyHash \> wallet \> domain \> fingerprint \> session
130
+ *
131
+ * An API key identifies the integrator and a wallet identifies the end user,
132
+ * so both outrank the hosting domain — otherwise every visitor of one web app
133
+ * collapses into a single Sentry user. `domain` still ranks above the
134
+ * fingerprint, which nearly every runtime produces, so hostname attribution
135
+ * is not lost.
122
136
  *
123
137
  * @param identity - User identity object
124
138
  * @returns Prefixed user ID string
125
139
  */
126
140
  function getUserId(identity) {
127
- if (identity.domain) {
128
- return `domain:${identity.domain}`;
129
- }
130
141
  if (identity.apiKeyHash) {
131
142
  return `apikey:${identity.apiKeyHash}`;
132
143
  }
133
144
  if (identity.walletAddress) {
134
145
  return `wallet:${identity.walletAddress}`;
135
146
  }
147
+ if (identity.domain) {
148
+ return `domain:${identity.domain}`;
149
+ }
136
150
  if (identity.fingerprint) {
137
151
  return `fp:${identity.fingerprint}`;
138
152
  }
@@ -3,4 +3,4 @@
3
3
  * Generated during publish from package.json version
4
4
  * This file is gitignored and created at publish time
5
5
  */
6
- export declare const SDK_VERSION = "9.5.0";
6
+ export declare const SDK_VERSION = "9.6.0";
@@ -6,5 +6,5 @@ exports.SDK_VERSION = void 0;
6
6
  * Generated during publish from package.json version
7
7
  * This file is gitignored and created at publish time
8
8
  */
9
- exports.SDK_VERSION = '9.5.0';
9
+ exports.SDK_VERSION = '9.6.0';
10
10
  //# sourceMappingURL=version.js.map
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.verifyInfuraKey = verifyInfuraKey;
4
4
  exports.verifyAugustKey = verifyAugustKey;
5
+ const analytics_1 = require("../analytics");
5
6
  const core_1 = require("../constants/core");
6
7
  const fetcher_1 = require("../fetcher");
7
8
  async function verifyInfuraKey(key) {
@@ -20,11 +21,14 @@ async function verifyAugustKey(key) {
20
21
  return false;
21
22
  try {
22
23
  const verify = await (0, fetcher_1.fetchAugustWithKey)(key, core_1.WEBSERVER_ENDPOINTS.auth.verify);
23
- return verify.status === 200;
24
+ const success = verify.status === 200;
25
+ (0, analytics_1.trackKeyVerification)('august', success);
26
+ return success;
24
27
  }
25
28
  catch {
26
29
  // Network errors, timeouts, and any future thrown auth errors all
27
30
  // resolve to "key not verified" here. Callers handle this as a boolean.
31
+ (0, analytics_1.trackKeyVerification)('august', false);
28
32
  return false;
29
33
  }
30
34
  }
@@ -71,6 +71,9 @@ class AugustBase {
71
71
  // prior staging instance would leak into a later prod instance in the same
72
72
  // process — the inverse of the isolation this option exists to provide.
73
73
  (0, fetcher_1.setPublicApiBaseUrl)(publicApiBaseUrl ?? null);
74
+ // Every backend request from this process now identifies the app
75
+ // (`x-august-app`) — same last-instance-wins contract as the base URL.
76
+ (0, fetcher_1.setSdkAppName)(this.appName);
74
77
  // ERC-8021 attribution follows the same reset-on-omit contract as
75
78
  // `publicApiBaseUrl`: called unconditionally so a prior instance's
76
79
  // builder codes never leak into a later instance in the same process.
@@ -33,6 +33,7 @@ export declare const WEBSERVER_URL: {
33
33
  };
34
34
  export declare const WEBSERVER_ENDPOINTS: {
35
35
  default: {
36
+ /** @deprecated No backend route. Removed in the next major release. */
36
37
  hello: string;
37
38
  protected: string;
38
39
  };
@@ -45,6 +46,7 @@ export declare const WEBSERVER_ENDPOINTS: {
45
46
  };
46
47
  subaccount: {
47
48
  list: (offset?: number, limit?: number) => string;
49
+ /** @deprecated No backend route. Removed in the next major release. */
48
50
  rewards: (subaccount: IAddress) => string;
49
51
  tokens: (subaccount: IAddress) => string;
50
52
  twap: {
@@ -55,6 +57,7 @@ export declare const WEBSERVER_ENDPOINTS: {
55
57
  debank: (subaccount: IAddress) => string;
56
58
  health_factor: (subaccount: IAddress) => string;
57
59
  summary: (subaccount: IAddress) => string;
60
+ /** @deprecated No backend route. Removed in the next major release. */
58
61
  batch: (subaccount: IAddress) => string;
59
62
  loans: (subaccount: IAddress, side?: "BORROWER" | "LENDER" | "BOTH", active?: boolean) => string;
60
63
  cefi: (subaccount: IAddress) => string;
@@ -63,6 +63,7 @@ exports.WEBSERVER_URL = {
63
63
  };
64
64
  exports.WEBSERVER_ENDPOINTS = {
65
65
  default: {
66
+ /** @deprecated No backend route. Removed in the next major release. */
66
67
  hello: '/hello',
67
68
  protected: '/protected',
68
69
  },
@@ -75,6 +76,7 @@ exports.WEBSERVER_ENDPOINTS = {
75
76
  },
76
77
  subaccount: {
77
78
  list: (offset = 0, limit = 100) => `/subaccount?offset=${offset}&limit=${limit}`,
79
+ /** @deprecated No backend route. Removed in the next major release. */
78
80
  rewards: (subaccount) => `/subaccount/${subaccountSegment(subaccount)}/rewards`,
79
81
  tokens: (subaccount) => `/subaccount/${subaccountSegment(subaccount)}/tokens`,
80
82
  twap: {
@@ -85,6 +87,7 @@ exports.WEBSERVER_ENDPOINTS = {
85
87
  debank: (subaccount) => `/subaccount/${subaccountSegment(subaccount)}/debank`,
86
88
  health_factor: (subaccount) => `/subaccount/${subaccountSegment(subaccount)}/health_factor`,
87
89
  summary: (subaccount) => `/subaccount/${subaccountSegment(subaccount)}/summary`,
90
+ /** @deprecated No backend route. Removed in the next major release. */
88
91
  batch: (subaccount) => `/subaccount/${subaccountSegment(subaccount)}/tx_batcher_integrations`,
89
92
  loans: (subaccount, side = 'BOTH', active = true) => `/subaccount/${subaccountSegment(subaccount)}/loans?side=${side}&active=${active}`,
90
93
  cefi: (subaccount) => `/subaccount/${subaccountSegment(subaccount)}/cefi_position`,
@@ -108,6 +108,22 @@ export declare const SWAP_ROUTER_MAX_SWAPS = 9;
108
108
  * every dispatch. Used to validate that native-token deposits target a
109
109
  * vault whose reference asset is the wrapped native — the only configuration
110
110
  * the contract's `depositNativeToken` / `swapAndDepositNativeToken` accept.
111
+ *
112
+ * **Wrap-only, WETH-vaults-only limitation.** Both payable entry points on the
113
+ * deployed router (`depositNativeToken` and `swapAndDepositNativeToken`) only
114
+ * wrap native ETH to this address and deposit — neither has an ETH→other-asset
115
+ * swap leg. `swapAndDepositNativeToken`'s ABI takes no swap payload, so a DEX
116
+ * swap is structurally impossible on that path too; its only difference from
117
+ * `depositNativeToken` is applying the vault swap fee on the pure wrap. The
118
+ * practical consequence: **native ETH can only be deposited into a vault whose
119
+ * reference asset is this wrapped-native token** (e.g. a WETH-reference
120
+ * vault). There is no atomic native-ETH deposit into a non-WETH-reference
121
+ * vault (e.g. a USDC vault) — deposit the wrapped native token instead
122
+ * (already whitelisted, and it routes through the swap leg for a vault whose
123
+ * reference asset differs). True atomic ETH→any-asset support would require a
124
+ * contract change (a payable `swapAndDeposit` variant), which is out of scope
125
+ * for this SDK. Use {@link isNativeSwapRouterDepositSupported} to check a
126
+ * vault's eligibility for the native path before offering it in a UI.
111
127
  */
112
128
  export declare const SWAP_ROUTER_WRAPPED_NATIVE: Readonly<Record<number, IAddress>>;
113
129
  /**
@@ -121,6 +121,22 @@ exports.SWAP_ROUTER_MAX_SWAPS = 9;
121
121
  * every dispatch. Used to validate that native-token deposits target a
122
122
  * vault whose reference asset is the wrapped native — the only configuration
123
123
  * the contract's `depositNativeToken` / `swapAndDepositNativeToken` accept.
124
+ *
125
+ * **Wrap-only, WETH-vaults-only limitation.** Both payable entry points on the
126
+ * deployed router (`depositNativeToken` and `swapAndDepositNativeToken`) only
127
+ * wrap native ETH to this address and deposit — neither has an ETH→other-asset
128
+ * swap leg. `swapAndDepositNativeToken`'s ABI takes no swap payload, so a DEX
129
+ * swap is structurally impossible on that path too; its only difference from
130
+ * `depositNativeToken` is applying the vault swap fee on the pure wrap. The
131
+ * practical consequence: **native ETH can only be deposited into a vault whose
132
+ * reference asset is this wrapped-native token** (e.g. a WETH-reference
133
+ * vault). There is no atomic native-ETH deposit into a non-WETH-reference
134
+ * vault (e.g. a USDC vault) — deposit the wrapped native token instead
135
+ * (already whitelisted, and it routes through the swap leg for a vault whose
136
+ * reference asset differs). True atomic ETH→any-asset support would require a
137
+ * contract change (a payable `swapAndDeposit` variant), which is out of scope
138
+ * for this SDK. Use {@link isNativeSwapRouterDepositSupported} to check a
139
+ * vault's eligibility for the native path before offering it in a UI.
124
140
  */
125
141
  exports.SWAP_ROUTER_WRAPPED_NATIVE = {
126
142
  1: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
@@ -15,7 +15,7 @@
15
15
  * ```
16
16
  */
17
17
  /** Stable error codes attached to SDK errors. */
18
- export type AugustErrorCode = 'AUTH_MISSING_KEY' | 'AUTH_INVALID_KEY' | 'AUTH_FORBIDDEN' | 'AUTH_UNAUTHORIZED' | 'INVALID_URL' | 'INVALID_INPUT' | 'INVALID_CHAIN' | 'INVALID_ADDRESS' | 'ACCOUNT_NOT_FUNDED' | 'NETWORK_ERROR' | 'TIMEOUT' | 'RATE_LIMITED' | 'SERVER_ERROR' | 'HISTORY_UNAVAILABLE' | 'UNKNOWN';
18
+ export type AugustErrorCode = 'AUTH_MISSING_KEY' | 'AUTH_INVALID_KEY' | 'AUTH_FORBIDDEN' | 'AUTH_UNAUTHORIZED' | 'INVALID_URL' | 'INVALID_INPUT' | 'INVALID_CHAIN' | 'INVALID_ADDRESS' | 'ACCOUNT_NOT_FUNDED' | 'NETWORK_ERROR' | 'TIMEOUT' | 'RATE_LIMITED' | 'SERVER_ERROR' | 'HISTORY_UNAVAILABLE' | 'PRICE_UNAVAILABLE' | 'UNKNOWN';
19
19
  /** Options accepted by every SDK error constructor. */
20
20
  export interface AugustErrorOptions {
21
21
  /** Original error preserved for stack-trace chaining. */
@@ -110,5 +110,40 @@ export declare class AugustHistoryUnavailableError extends AugustSDKError {
110
110
  */
111
111
  constructor(message: string, options?: AugustErrorOptions);
112
112
  }
113
+ /**
114
+ * The USD price needed to convert a computed value could not be resolved, so
115
+ * any USD figure derived from it would be fabricated rather than merely
116
+ * missing.
117
+ *
118
+ * Motivation — `getVaultUserLifetimePnl` used to substitute a price of `1`
119
+ * whenever the price server returned nothing, which reported every
120
+ * USD field of a non-dollar vault (lsETH, tETH, BTC) at $1 per asset: a
121
+ * position worth ~$40k read as ~$13, indistinguishable from a real result.
122
+ * Throwing this typed error makes a missing price explicit so callers can hide
123
+ * the USD value instead of displaying a wrong one.
124
+ *
125
+ * @example
126
+ * ```ts
127
+ * try {
128
+ * await sdk.getVaultUserLifetimePnl({ vault, wallet, chainId });
129
+ * } catch (err) {
130
+ * if (err instanceof AugustPriceUnavailableError) {
131
+ * // No USD price for the vault asset — hide the USD figures rather than
132
+ * // show a fabricated one.
133
+ * hideUsd(err.context);
134
+ * } else throw err;
135
+ * }
136
+ * ```
137
+ */
138
+ export declare class AugustPriceUnavailableError extends AugustSDKError {
139
+ /**
140
+ * @param message - Human-readable description of the missing price. Callers
141
+ * pass the same `#<method>::<vault>::<wallet>` prefix the surrounding getter
142
+ * uses so log grouping stays consistent.
143
+ * @param options - Standard SDK error options. `context` should carry the
144
+ * identifiers that scope the failure (e.g. `{ vault, wallet, asset }`).
145
+ */
146
+ constructor(message: string, options?: AugustErrorOptions);
147
+ }
113
148
  /** Type guard. Works across realms (e.g. Web Worker / VM contexts). */
114
149
  export declare function isAugustSDKError(err: unknown): err is AugustSDKError;
@@ -16,7 +16,7 @@
16
16
  * ```
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.AugustHistoryUnavailableError = exports.AugustServerError = exports.AugustRateLimitError = exports.AugustValidationError = exports.AugustTimeoutError = exports.AugustNetworkError = exports.AugustAuthError = exports.AugustSDKError = void 0;
19
+ exports.AugustPriceUnavailableError = exports.AugustHistoryUnavailableError = exports.AugustServerError = exports.AugustRateLimitError = exports.AugustValidationError = exports.AugustTimeoutError = exports.AugustNetworkError = exports.AugustAuthError = exports.AugustSDKError = void 0;
20
20
  exports.isAugustSDKError = isAugustSDKError;
21
21
  /** Base class for all SDK errors. */
22
22
  class AugustSDKError extends Error {
@@ -173,6 +173,45 @@ class AugustHistoryUnavailableError extends AugustSDKError {
173
173
  }
174
174
  }
175
175
  exports.AugustHistoryUnavailableError = AugustHistoryUnavailableError;
176
+ /**
177
+ * The USD price needed to convert a computed value could not be resolved, so
178
+ * any USD figure derived from it would be fabricated rather than merely
179
+ * missing.
180
+ *
181
+ * Motivation — `getVaultUserLifetimePnl` used to substitute a price of `1`
182
+ * whenever the price server returned nothing, which reported every
183
+ * USD field of a non-dollar vault (lsETH, tETH, BTC) at $1 per asset: a
184
+ * position worth ~$40k read as ~$13, indistinguishable from a real result.
185
+ * Throwing this typed error makes a missing price explicit so callers can hide
186
+ * the USD value instead of displaying a wrong one.
187
+ *
188
+ * @example
189
+ * ```ts
190
+ * try {
191
+ * await sdk.getVaultUserLifetimePnl({ vault, wallet, chainId });
192
+ * } catch (err) {
193
+ * if (err instanceof AugustPriceUnavailableError) {
194
+ * // No USD price for the vault asset — hide the USD figures rather than
195
+ * // show a fabricated one.
196
+ * hideUsd(err.context);
197
+ * } else throw err;
198
+ * }
199
+ * ```
200
+ */
201
+ class AugustPriceUnavailableError extends AugustSDKError {
202
+ /**
203
+ * @param message - Human-readable description of the missing price. Callers
204
+ * pass the same `#<method>::<vault>::<wallet>` prefix the surrounding getter
205
+ * uses so log grouping stays consistent.
206
+ * @param options - Standard SDK error options. `context` should carry the
207
+ * identifiers that scope the failure (e.g. `{ vault, wallet, asset }`).
208
+ */
209
+ constructor(message, options) {
210
+ super('PRICE_UNAVAILABLE', message, options);
211
+ this.name = 'AugustPriceUnavailableError';
212
+ }
213
+ }
214
+ exports.AugustPriceUnavailableError = AugustPriceUnavailableError;
176
215
  /** Type guard. Works across realms (e.g. Web Worker / VM contexts). */
177
216
  function isAugustSDKError(err) {
178
217
  return (err instanceof AugustSDKError ||
@@ -41,6 +41,19 @@ export declare function setSdkRequestTimeout(ms?: number | null): void;
41
41
  * otherwise the compiled-in `REQUEST_TIMEOUT_MS`.
42
42
  */
43
43
  export declare function getSdkRequestTimeout(): number;
44
+ /**
45
+ * Record the consuming application's `appName` so every request to the
46
+ * August backend carries it as `x-august-app`, next to
47
+ * `x-august-sdk-version`. This gives the backend per-integrator request
48
+ * counts that do not depend on Sentry sampling.
49
+ *
50
+ * Process-global, last writer wins — the same contract as
51
+ * `setSdkRequestTimeout` and `setPublicApiBaseUrl`. The `AugustSDK`
52
+ * constructor calls this with its validated `appName`.
53
+ *
54
+ * @param appName - Validated app-name slug, or `null` to stop sending it.
55
+ */
56
+ export declare function setSdkAppName(appName?: string | null): void;
44
57
  /**
45
58
  * Override the base URL used for `WEBSERVER_URL.public` requests — the
46
59
  * unauthenticated vault-catalog API that `getVault` / `fetchTokenizedVault`
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.setSafeCache = exports.getSafeCache = exports.CACHE = void 0;
4
4
  exports.setSdkRequestTimeout = setSdkRequestTimeout;
5
5
  exports.getSdkRequestTimeout = getSdkRequestTimeout;
6
+ exports.setSdkAppName = setSdkAppName;
6
7
  exports.setPublicApiBaseUrl = setPublicApiBaseUrl;
7
8
  exports.getPublicApiBaseUrl = getPublicApiBaseUrl;
8
9
  exports.fetchAugustWithKey = fetchAugustWithKey;
@@ -32,6 +33,7 @@ const core_2 = require("./constants/core");
32
33
  const vault_version_1 = require("./helpers/vault-version");
33
34
  const analytics_1 = require("./analytics");
34
35
  const sanitize_1 = require("./analytics/sanitize");
36
+ const sentry_1 = require("./analytics/sentry");
35
37
  const errors_1 = require("./errors");
36
38
  /** Map an HTTP status to the appropriate typed SDK error subclass. */
37
39
  function errorFromResponseStatus(status, message, options) {
@@ -76,6 +78,34 @@ function setSdkRequestTimeout(ms = null) {
76
78
  function getSdkRequestTimeout() {
77
79
  return SDK_REQUEST_TIMEOUT_OVERRIDE ?? core_2.REQUEST_TIMEOUT_MS;
78
80
  }
81
+ let SDK_APP_NAME = null;
82
+ /**
83
+ * Record the consuming application's `appName` so every request to the
84
+ * August backend carries it as `x-august-app`, next to
85
+ * `x-august-sdk-version`. This gives the backend per-integrator request
86
+ * counts that do not depend on Sentry sampling.
87
+ *
88
+ * Process-global, last writer wins — the same contract as
89
+ * `setSdkRequestTimeout` and `setPublicApiBaseUrl`. The `AugustSDK`
90
+ * constructor calls this with its validated `appName`.
91
+ *
92
+ * @param appName - Validated app-name slug, or `null` to stop sending it.
93
+ */
94
+ function setSdkAppName(appName = null) {
95
+ SDK_APP_NAME = appName;
96
+ }
97
+ /**
98
+ * Client-identification headers merged into every backend fetcher's
99
+ * defaults. Both values are low-cardinality slugs, never credentials.
100
+ */
101
+ function sdkClientHeaders() {
102
+ const headers = {
103
+ 'x-august-sdk-version': (0, sentry_1.getSDKVersion)(),
104
+ };
105
+ if (SDK_APP_NAME)
106
+ headers['x-august-app'] = SDK_APP_NAME;
107
+ return headers;
108
+ }
79
109
  let PUBLIC_API_BASE_OVERRIDE = null;
80
110
  /**
81
111
  * Override the base URL used for `WEBSERVER_URL.public` requests — the
@@ -370,6 +400,7 @@ async function fetchAugustWithKey(apiKey, relativeUrl, options) {
370
400
  throw new errors_1.AugustAuthError('AUTH_MISSING_KEY', 'fetchAugustWithKey: apiKey is required (set keys.august on the SDK constructor, or pass an explicit apiKey).');
371
401
  }
372
402
  const defaultHeaders = {
403
+ ...sdkClientHeaders(),
373
404
  'x-api-key': apiKey,
374
405
  'content-type': 'application/json',
375
406
  accept: 'application/json',
@@ -426,6 +457,7 @@ async function fetchAugustWithKey(apiKey, relativeUrl, options) {
426
457
  */
427
458
  async function fetchAugustPublic(relativeUrl, options) {
428
459
  const defaultHeaders = {
460
+ ...sdkClientHeaders(),
429
461
  'content-type': 'application/json',
430
462
  accept: 'application/json',
431
463
  };
@@ -480,6 +512,7 @@ async function fetchAugustPublic(relativeUrl, options) {
480
512
  */
481
513
  async function fetchAugustWithBearer(bearerToken, relativeUrl, options) {
482
514
  const defaultHeaders = {
515
+ ...sdkClientHeaders(),
483
516
  authorization: `Bearer ${bearerToken}`,
484
517
  'content-type': 'application/json',
485
518
  accept: 'application/json',
package/lib/main.d.ts CHANGED
@@ -355,6 +355,11 @@ export declare class AugustSDK extends AugustBase {
355
355
  * Calculates realized and unrealized PnL based on deposit/withdrawal history and current position.
356
356
  * @param props - Vault address, wallet address, and optional chain ID
357
357
  * @returns Lifetime PnL data including realized and unrealized PnL in both native token and USD
358
+ * @throws {@link AugustHistoryUnavailableError} (`HISTORY_UNAVAILABLE`) when a
359
+ * live position has no indexed deposits or withdrawals.
360
+ * @throws {@link AugustPriceUnavailableError} (`PRICE_UNAVAILABLE`) when the
361
+ * vault's underlying asset has no USD price — the USD fields are never
362
+ * computed from a stand-in price.
358
363
  */
359
364
  getVaultUserLifetimePnl(props: {
360
365
  vault: IAddress;