@dynamic-labs-wallet/core 1.0.28 → 1.0.30

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/index.cjs CHANGED
@@ -134,7 +134,6 @@ const FEATURE_FLAGS = {
134
134
  ENABLE_DELEGATED_KEY_SHARES_FLAG: 'enable-delegated-key-shares',
135
135
  ENABLE_FORWARD_MPC_CLIENT_FLAG: 'enable-forward-mpc-client',
136
136
  ENABLE_ROOM_CACHE_FLAG: 'enable-mpc-room-cache',
137
- ENABLE_SETTINGS_GATED_CHAINS_FLAG: 'enable-settings-gated-chains',
138
137
  ENABLE_SHARE_SET_RESHARE: 'enable-share-set-reshare',
139
138
  ENABLE_TRON_MPC_FLAG: 'enable-tron-mpc',
140
139
  VERIFY_BACKUP_RECOVERABILITY_FLAG: 'enable-verify-backup-recoverability'
@@ -768,8 +767,8 @@ const getElapsedTime = (startTime)=>{
768
767
  }
769
768
  if (event.type === SuccessEventType.CeremonyComplete) {
770
769
  ceremonyCompleteSeen = true;
771
- const { accountAddress, walletId, shareSetId, shareSetType } = event.data;
772
- onCeremonyComplete == null ? void 0 : onCeremonyComplete(accountAddress, walletId, shareSetId, shareSetType);
770
+ const { accountAddress, walletId, shareSetId, shareSetType, targetUserId, clientKeyshareBackupGrant } = event.data;
771
+ onCeremonyComplete == null ? void 0 : onCeremonyComplete(accountAddress, walletId, shareSetId, shareSetType, targetUserId, clientKeyshareBackupGrant);
773
772
  }
774
773
  if (event.type === 'error') {
775
774
  settled = true;
@@ -915,7 +914,7 @@ const getRequiredExternalKeyShareId = (ks)=>{
915
914
  * before it is posted and surfaces in Datadog exactly as the recovery path does.
916
915
  */ const assertDynamicLocationsHaveExternalKeyShareId = (locations)=>locations.filter((location)=>location.location === BackupLocation.DYNAMIC).forEach((location)=>getRequiredExternalKeyShareId(location));
917
916
 
918
- var version = "1.0.28";
917
+ var version = "1.0.30";
919
918
 
920
919
  class BaseClient {
921
920
  /**
@@ -1047,7 +1046,7 @@ class DynamicApiClient extends BaseClient {
1047
1046
  }
1048
1047
  });
1049
1048
  }
1050
- async createWalletAccount({ chainName, clientKeygenIds, dynamicRequestId, thresholdSignatureScheme, skipLock, bitcoinConfig, onError, onCeremonyComplete, traceContext }) {
1049
+ async createWalletAccount({ chainName, clientKeygenIds, dynamicRequestId, thresholdSignatureScheme, skipLock, bitcoinConfig, businessAccountId, onError, onCeremonyComplete, traceContext }) {
1051
1050
  const { addressType } = bitcoinConfig || {};
1052
1051
  const requestBody = _extends({
1053
1052
  chain: chainName,
@@ -1057,6 +1056,8 @@ class DynamicApiClient extends BaseClient {
1057
1056
  skipLock
1058
1057
  } : {}, addressType ? {
1059
1058
  addressType
1059
+ } : {}, businessAccountId ? {
1060
+ businessAccountId
1060
1061
  } : {});
1061
1062
  return createEventStreamPromise({
1062
1063
  apiClient: this.apiClient,
@@ -1387,7 +1388,7 @@ class DynamicApiClient extends BaseClient {
1387
1388
  });
1388
1389
  return data;
1389
1390
  }
1390
- async markKeySharesAsBackedUp({ walletId, shareSetId, locations, dynamicRequestId, traceContext, passwordUpdateBatchId }) {
1391
+ async markKeySharesAsBackedUp({ walletId, shareSetId, locations, dynamicRequestId, traceContext, passwordUpdateBatchId, businessAccountId }) {
1391
1392
  assertDynamicLocationsHaveExternalKeyShareId(locations);
1392
1393
  const { data } = await this.apiClient.post(`${this.waasRoutePrefix}/${walletId}/keyShares/backup/locations`, _extends({
1393
1394
  locations
@@ -1395,6 +1396,8 @@ class DynamicApiClient extends BaseClient {
1395
1396
  passwordUpdateBatchId
1396
1397
  }, shareSetId ? {
1397
1398
  shareSetId
1399
+ } : {}, businessAccountId ? {
1400
+ businessAccountId
1398
1401
  } : {}), {
1399
1402
  headers: {
1400
1403
  [DynamicRequestIdHeader]: dynamicRequestId,
@@ -1534,6 +1537,158 @@ class DynamicApiClient extends BaseClient {
1534
1537
  });
1535
1538
  return data;
1536
1539
  }
1540
+ // --- Business accounts (SDK routes, JWT-authed, membership-scoped) ---
1541
+ /** Create a business account; the caller's JWT user is seeded as the first owner. */ async createBusinessAccount({ request, traceContext }) {
1542
+ const { data } = await this.apiClient.post(this.businessAccountsRoutePrefix, request, {
1543
+ headers: {
1544
+ [DynamicRequestIdHeader]: uuid.v4().replace('-', ''),
1545
+ [DynamicTraceIdHeader]: traceContext == null ? void 0 : traceContext.traceId,
1546
+ [DynamicTraceElapsedTimeHeader]: getElapsedTime(traceContext == null ? void 0 : traceContext.startTime)
1547
+ }
1548
+ });
1549
+ return data;
1550
+ }
1551
+ /** List business accounts the caller is a member of. */ async listBusinessAccounts({ traceContext } = {}) {
1552
+ const { data } = await this.apiClient.get(this.businessAccountsRoutePrefix, {
1553
+ headers: {
1554
+ [DynamicRequestIdHeader]: uuid.v4().replace('-', ''),
1555
+ [DynamicTraceIdHeader]: traceContext == null ? void 0 : traceContext.traceId,
1556
+ [DynamicTraceElapsedTimeHeader]: getElapsedTime(traceContext == null ? void 0 : traceContext.startTime)
1557
+ }
1558
+ });
1559
+ return data;
1560
+ }
1561
+ /** Get a business account with embedded members + signers. 404 if the caller is not a member. */ async getBusinessAccountById({ businessAccountId, traceContext }) {
1562
+ const { data } = await this.apiClient.get(`${this.businessAccountsRoutePrefix}/${businessAccountId}`, {
1563
+ headers: {
1564
+ [DynamicRequestIdHeader]: uuid.v4().replace('-', ''),
1565
+ [DynamicTraceIdHeader]: traceContext == null ? void 0 : traceContext.traceId,
1566
+ [DynamicTraceElapsedTimeHeader]: getElapsedTime(traceContext == null ? void 0 : traceContext.startTime)
1567
+ }
1568
+ });
1569
+ return data;
1570
+ }
1571
+ /**
1572
+ * Add an existing wallet to a business account. No ceremony — the server
1573
+ * transfers ownership and registers the caller as owner + signer bound to
1574
+ * their existing share set. Omit `businessAccountId` to find-or-create the
1575
+ * per-wallet business account. Returns the business-account detail. Idempotent.
1576
+ */ async linkWalletToBusinessAccount({ walletId, businessAccountId, traceContext }) {
1577
+ const { data } = await this.apiClient.post(`${this.businessAccountsRoutePrefix}/linkWallet`, _extends({
1578
+ walletId
1579
+ }, businessAccountId ? {
1580
+ businessAccountId
1581
+ } : {}), {
1582
+ headers: {
1583
+ [DynamicRequestIdHeader]: uuid.v4().replace('-', ''),
1584
+ [DynamicTraceIdHeader]: traceContext == null ? void 0 : traceContext.traceId,
1585
+ [DynamicTraceElapsedTimeHeader]: getElapsedTime(traceContext == null ? void 0 : traceContext.startTime)
1586
+ }
1587
+ });
1588
+ return data;
1589
+ }
1590
+ /** Add a member to a business account by user id. Caller must be an owner/admin. */ async addBusinessAccountMember({ businessAccountId, userId, role, traceContext }) {
1591
+ const { data } = await this.apiClient.post(`${this.businessAccountsRoutePrefix}/${businessAccountId}/members`, _extends({
1592
+ userId
1593
+ }, role ? {
1594
+ role
1595
+ } : {}), {
1596
+ headers: {
1597
+ [DynamicRequestIdHeader]: uuid.v4().replace('-', ''),
1598
+ [DynamicTraceIdHeader]: traceContext == null ? void 0 : traceContext.traceId,
1599
+ [DynamicTraceElapsedTimeHeader]: getElapsedTime(traceContext == null ? void 0 : traceContext.startTime)
1600
+ }
1601
+ });
1602
+ return data;
1603
+ }
1604
+ /**
1605
+ * Add a signer to a business-account wallet. Resolve the target by `userId`, or by
1606
+ * `identifier` + `identifierType` (e.g. an email). Returns a PENDING signer
1607
+ * (HTTP 202, `shareSetId: null`) — the reshare ceremony that mints the share
1608
+ * is a follow-up and not yet performed server-side.
1609
+ */ async addBusinessAccountSigner({ businessAccountId, walletId, signerType, userId, identifier, identifierType, traceContext }) {
1610
+ const { data } = await this.apiClient.post(`${this.businessAccountsRoutePrefix}/${businessAccountId}/wallets/${walletId}/signers`, _extends({
1611
+ signerType
1612
+ }, userId ? {
1613
+ userId
1614
+ } : {}, identifier ? {
1615
+ identifier
1616
+ } : {}, identifierType ? {
1617
+ type: identifierType
1618
+ } : {}), {
1619
+ headers: {
1620
+ [DynamicRequestIdHeader]: uuid.v4().replace('-', ''),
1621
+ [DynamicTraceIdHeader]: traceContext == null ? void 0 : traceContext.traceId,
1622
+ [DynamicTraceElapsedTimeHeader]: getElapsedTime(traceContext == null ? void 0 : traceContext.startTime)
1623
+ }
1624
+ });
1625
+ return data;
1626
+ }
1627
+ /**
1628
+ * SSE ceremony variant of addSigner (WIP — DYNT-1072). Same-parties
1629
+ * 2/2 -> 2/2 reshare on the signers endpoint: the caller's existing client
1630
+ * share participates as a remaining party (clientKeygenIds = [existing], no
1631
+ * new party). The server runs the degenerate reshare with
1632
+ * `newServerKeygenIds=[]` and persists the result as a NEW isolated share set
1633
+ * for the target signer — it must NOT refresh the caller's set in place.
1634
+ *
1635
+ * Returns the room (createEventStreamPromise) + the new `shareSetId` via
1636
+ * onCeremonyComplete. The resulting client share is reshared locally and
1637
+ * seeded to the client-keyshare-service on the target's behalf (see
1638
+ * `seedBusinessAccountSignerShare`). Not wired into the live addSigner until the
1639
+ * redcoast signers SSE endpoint exists.
1640
+ */ async addBusinessAccountSignerCeremony({ businessAccountId, walletId, clientKeygenIds, oldThresholdSignatureScheme, signerType, userId, identifier, identifierType, dynamicRequestId, onError, onCeremonyComplete, traceContext }) {
1641
+ return createEventStreamPromise({
1642
+ apiClient: this.apiClient,
1643
+ dynamicRequestId,
1644
+ endpoint: `${this.businessAccountsRoutePrefix}/${businessAccountId}/wallets/${walletId}/signers`,
1645
+ body: _extends({
1646
+ signerType,
1647
+ clientKeygenIds,
1648
+ oldThresholdSignatureScheme,
1649
+ // FF-on share-set semantics: always reshare to a NEW 2/2 share set.
1650
+ newThresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_TWO
1651
+ }, userId ? {
1652
+ userId
1653
+ } : {}, identifier ? {
1654
+ identifier
1655
+ } : {}, identifierType ? {
1656
+ type: identifierType
1657
+ } : {}),
1658
+ successEventType: SuccessEventType.RoomCreated,
1659
+ timeoutMessage: 'Add signer reshare timed out',
1660
+ onError,
1661
+ onCeremonyComplete,
1662
+ traceContext
1663
+ });
1664
+ }
1665
+ /**
1666
+ * On-behalf KSS write for addSigner: store the added signer's freshly-reshared
1667
+ * client share in the client-keyshare-service keyed to the ADDED signer's
1668
+ * partition via `targetUserId` in the body. Uses the caller's relay auth (the
1669
+ * business-account-membership gate is FF-bypassed for now; the client-keyshare-service
1670
+ * reads `targetUserId` and inserts-if-empty). Returns the stored keyShareId(s)
1671
+ * to thread as `externalKeyShareId` into `markKeySharesAsBackedUp`.
1672
+ * Hardened auth (scoped grant token) is the follow-up — DYNT-1071/1072.
1673
+ */ async backupOnBehalfOfAddedSigner({ walletId, targetUserId, encryptedAccountCredentials, passwordEncrypted, encryptionVersion, clientKeyshareBackupGrant, signedSessionId, dynamicRequestId, traceContext }) {
1674
+ const { data } = await this.clientRelayApiClient.post(`${this.waasRoutePrefix}/${walletId}/keyShares/backup`, {
1675
+ encryptedAccountCredentials,
1676
+ passwordEncrypted,
1677
+ encryptionVersion,
1678
+ targetUserId
1679
+ }, {
1680
+ headers: _extends({
1681
+ [DynamicRequestIdHeader]: dynamicRequestId,
1682
+ [DynamicTraceIdHeader]: traceContext == null ? void 0 : traceContext.traceId,
1683
+ [DynamicTraceElapsedTimeHeader]: getElapsedTime(traceContext == null ? void 0 : traceContext.startTime)
1684
+ }, signedSessionId && {
1685
+ [DynamicClientSessionSignature]: signedSessionId
1686
+ }, clientKeyshareBackupGrant && {
1687
+ Authorization: `Bearer ${clientKeyshareBackupGrant}`
1688
+ })
1689
+ });
1690
+ return data;
1691
+ }
1537
1692
  async delegatedSignMessage({ walletId, shareSetId, message, isFormatted, dynamicRequestId, onError, context, traceContext }) {
1538
1693
  return createEventStreamPromise({
1539
1694
  apiClient: this.apiClient,
@@ -1629,6 +1784,103 @@ class DynamicApiClient extends BaseClient {
1629
1784
  });
1630
1785
  this.sdkRoutePrefix = isServerSdk ? SERVER_SDK_ROUTE_PREFIX : BROWSER_SDK_ROUTE_PREFIX;
1631
1786
  this.waasRoutePrefix = `${this.sdkRoutePrefix}/${this.environmentId}/waas`;
1787
+ this.businessAccountsRoutePrefix = `${this.sdkRoutePrefix}/${this.environmentId}/businessAccounts`;
1788
+ }
1789
+ }
1790
+
1791
+ // Local mirror of the business-account models generated into
1792
+ // `@dynamic-labs/sdk-api-core`. Swap these for the generated imports once that
1793
+ // package is bumped with the SDK routes (redcoast PR #9792).
1794
+ var BusinessAccountMemberRole = /*#__PURE__*/ function(BusinessAccountMemberRole) {
1795
+ BusinessAccountMemberRole["Owner"] = "owner";
1796
+ BusinessAccountMemberRole["Admin"] = "admin";
1797
+ BusinessAccountMemberRole["Viewer"] = "viewer";
1798
+ return BusinessAccountMemberRole;
1799
+ }({});
1800
+ var BusinessAccountSignerType = /*#__PURE__*/ function(BusinessAccountSignerType) {
1801
+ BusinessAccountSignerType["EndUser"] = "endUser";
1802
+ BusinessAccountSignerType["Server"] = "server";
1803
+ return BusinessAccountSignerType;
1804
+ }({});
1805
+
1806
+ // Subset redcoast's addSigner endpoint supports today (sdkController.ts).
1807
+ // Other identifier types in the TS union are reserved for future endpoints.
1808
+ const ADD_SIGNER_SUPPORTED_IDENTIFIER_TYPES = new Set([
1809
+ 'email',
1810
+ 'externalUserId',
1811
+ 'id'
1812
+ ]);
1813
+ /**
1814
+ * Business-account orchestration. Lives in core (not on the monolithic wallet
1815
+ * client) so both browser and node clients can compose it. Pure REST today.
1816
+ */ class BusinessAccountClient {
1817
+ /** Create a business account; the authenticated caller is seeded as the first owner member. */ async createBusinessAccount(request = {}, options) {
1818
+ return this.apiClient.createBusinessAccount({
1819
+ request,
1820
+ traceContext: options == null ? void 0 : options.traceContext
1821
+ });
1822
+ }
1823
+ /** List business accounts the caller is a member of. */ async listBusinessAccounts(options) {
1824
+ return this.apiClient.listBusinessAccounts({
1825
+ traceContext: options == null ? void 0 : options.traceContext
1826
+ });
1827
+ }
1828
+ /** Full account detail (members + signers). 404 if the caller is not a member. */ async getBusinessAccount(businessAccountId, options) {
1829
+ return this.apiClient.getBusinessAccountById({
1830
+ businessAccountId,
1831
+ traceContext: options == null ? void 0 : options.traceContext
1832
+ });
1833
+ }
1834
+ /**
1835
+ * Bring an existing wallet under a business account so signers can be added to it.
1836
+ * Pure REST, no ceremony — the owner already holds a valid share set, so the
1837
+ * server just transfers ownership and registers the caller as owner + signer.
1838
+ * Omit `businessAccountId` to find-or-create the per-wallet business account.
1839
+ * Idempotent.
1840
+ */ async addWalletToBusinessAccount({ walletId, businessAccountId, traceContext }) {
1841
+ return this.apiClient.linkWalletToBusinessAccount({
1842
+ walletId,
1843
+ businessAccountId,
1844
+ traceContext
1845
+ });
1846
+ }
1847
+ /** Add a member to a business account by user id. Caller must be an owner/admin. */ async addMember({ businessAccountId, userId, role, traceContext }) {
1848
+ return this.apiClient.addBusinessAccountMember({
1849
+ businessAccountId,
1850
+ userId,
1851
+ role,
1852
+ traceContext
1853
+ });
1854
+ }
1855
+ /**
1856
+ * Add a signer to a business-account wallet, resolved by `email` (or explicit
1857
+ * `userId`). Server creates the user if absent, auto-adds them as a member,
1858
+ * and writes a PENDING signer row. Returns the pending signer — the reshare
1859
+ * ceremony that mints the share lives in the browser SDK.
1860
+ */ async addSigner({ businessAccountId, walletId, targetSignerIdentity, signerType = 'endUser', traceContext }) {
1861
+ const { userId, identifier, identifierType } = targetSignerIdentity;
1862
+ if (!userId && !(identifier && identifierType)) {
1863
+ throw new Error('BusinessAccountClient.addSigner requires targetSignerIdentity.userId, or identifier + identifierType');
1864
+ }
1865
+ if (identifierType && !ADD_SIGNER_SUPPORTED_IDENTIFIER_TYPES.has(identifierType)) {
1866
+ throw new Error(`BusinessAccountClient.addSigner: identifierType '${identifierType}' is not supported. Use one of: ${[
1867
+ ...ADD_SIGNER_SUPPORTED_IDENTIFIER_TYPES
1868
+ ].join(', ')}.`);
1869
+ }
1870
+ this.logger.info('BusinessAccountClient.addSigner creating pending signer (reshare pending)');
1871
+ return this.apiClient.addBusinessAccountSigner({
1872
+ businessAccountId,
1873
+ walletId,
1874
+ signerType,
1875
+ userId,
1876
+ identifier,
1877
+ identifierType,
1878
+ traceContext
1879
+ });
1880
+ }
1881
+ constructor({ apiClient, logger }){
1882
+ this.apiClient = apiClient;
1883
+ this.logger = logger;
1632
1884
  }
1633
1885
  }
1634
1886
 
@@ -1899,6 +2151,9 @@ exports.BROWSER_SDK_ROUTE_PREFIX = BROWSER_SDK_ROUTE_PREFIX;
1899
2151
  exports.BackupLocation = BackupLocation;
1900
2152
  exports.BitcoinAddressType = BitcoinAddressType;
1901
2153
  exports.BitcoinNetwork = BitcoinNetwork;
2154
+ exports.BusinessAccountClient = BusinessAccountClient;
2155
+ exports.BusinessAccountMemberRole = BusinessAccountMemberRole;
2156
+ exports.BusinessAccountSignerType = BusinessAccountSignerType;
1902
2157
  exports.CreateRoomPartiesOptions = CreateRoomPartiesOptions;
1903
2158
  exports.DELEGATED_SHARE_COUNT = DELEGATED_SHARE_COUNT;
1904
2159
  exports.DYNAMIC_AUTH_BASE_API_URL_MAP = DYNAMIC_AUTH_BASE_API_URL_MAP;
package/index.esm.js CHANGED
@@ -134,7 +134,6 @@ const FEATURE_FLAGS = {
134
134
  ENABLE_DELEGATED_KEY_SHARES_FLAG: 'enable-delegated-key-shares',
135
135
  ENABLE_FORWARD_MPC_CLIENT_FLAG: 'enable-forward-mpc-client',
136
136
  ENABLE_ROOM_CACHE_FLAG: 'enable-mpc-room-cache',
137
- ENABLE_SETTINGS_GATED_CHAINS_FLAG: 'enable-settings-gated-chains',
138
137
  ENABLE_SHARE_SET_RESHARE: 'enable-share-set-reshare',
139
138
  ENABLE_TRON_MPC_FLAG: 'enable-tron-mpc',
140
139
  VERIFY_BACKUP_RECOVERABILITY_FLAG: 'enable-verify-backup-recoverability'
@@ -768,8 +767,8 @@ const getElapsedTime = (startTime)=>{
768
767
  }
769
768
  if (event.type === SuccessEventType.CeremonyComplete) {
770
769
  ceremonyCompleteSeen = true;
771
- const { accountAddress, walletId, shareSetId, shareSetType } = event.data;
772
- onCeremonyComplete == null ? void 0 : onCeremonyComplete(accountAddress, walletId, shareSetId, shareSetType);
770
+ const { accountAddress, walletId, shareSetId, shareSetType, targetUserId, clientKeyshareBackupGrant } = event.data;
771
+ onCeremonyComplete == null ? void 0 : onCeremonyComplete(accountAddress, walletId, shareSetId, shareSetType, targetUserId, clientKeyshareBackupGrant);
773
772
  }
774
773
  if (event.type === 'error') {
775
774
  settled = true;
@@ -915,7 +914,7 @@ const getRequiredExternalKeyShareId = (ks)=>{
915
914
  * before it is posted and surfaces in Datadog exactly as the recovery path does.
916
915
  */ const assertDynamicLocationsHaveExternalKeyShareId = (locations)=>locations.filter((location)=>location.location === BackupLocation.DYNAMIC).forEach((location)=>getRequiredExternalKeyShareId(location));
917
916
 
918
- var version = "1.0.28";
917
+ var version = "1.0.30";
919
918
 
920
919
  class BaseClient {
921
920
  /**
@@ -1047,7 +1046,7 @@ class DynamicApiClient extends BaseClient {
1047
1046
  }
1048
1047
  });
1049
1048
  }
1050
- async createWalletAccount({ chainName, clientKeygenIds, dynamicRequestId, thresholdSignatureScheme, skipLock, bitcoinConfig, onError, onCeremonyComplete, traceContext }) {
1049
+ async createWalletAccount({ chainName, clientKeygenIds, dynamicRequestId, thresholdSignatureScheme, skipLock, bitcoinConfig, businessAccountId, onError, onCeremonyComplete, traceContext }) {
1051
1050
  const { addressType } = bitcoinConfig || {};
1052
1051
  const requestBody = _extends({
1053
1052
  chain: chainName,
@@ -1057,6 +1056,8 @@ class DynamicApiClient extends BaseClient {
1057
1056
  skipLock
1058
1057
  } : {}, addressType ? {
1059
1058
  addressType
1059
+ } : {}, businessAccountId ? {
1060
+ businessAccountId
1060
1061
  } : {});
1061
1062
  return createEventStreamPromise({
1062
1063
  apiClient: this.apiClient,
@@ -1387,7 +1388,7 @@ class DynamicApiClient extends BaseClient {
1387
1388
  });
1388
1389
  return data;
1389
1390
  }
1390
- async markKeySharesAsBackedUp({ walletId, shareSetId, locations, dynamicRequestId, traceContext, passwordUpdateBatchId }) {
1391
+ async markKeySharesAsBackedUp({ walletId, shareSetId, locations, dynamicRequestId, traceContext, passwordUpdateBatchId, businessAccountId }) {
1391
1392
  assertDynamicLocationsHaveExternalKeyShareId(locations);
1392
1393
  const { data } = await this.apiClient.post(`${this.waasRoutePrefix}/${walletId}/keyShares/backup/locations`, _extends({
1393
1394
  locations
@@ -1395,6 +1396,8 @@ class DynamicApiClient extends BaseClient {
1395
1396
  passwordUpdateBatchId
1396
1397
  }, shareSetId ? {
1397
1398
  shareSetId
1399
+ } : {}, businessAccountId ? {
1400
+ businessAccountId
1398
1401
  } : {}), {
1399
1402
  headers: {
1400
1403
  [DynamicRequestIdHeader]: dynamicRequestId,
@@ -1534,6 +1537,158 @@ class DynamicApiClient extends BaseClient {
1534
1537
  });
1535
1538
  return data;
1536
1539
  }
1540
+ // --- Business accounts (SDK routes, JWT-authed, membership-scoped) ---
1541
+ /** Create a business account; the caller's JWT user is seeded as the first owner. */ async createBusinessAccount({ request, traceContext }) {
1542
+ const { data } = await this.apiClient.post(this.businessAccountsRoutePrefix, request, {
1543
+ headers: {
1544
+ [DynamicRequestIdHeader]: v4().replace('-', ''),
1545
+ [DynamicTraceIdHeader]: traceContext == null ? void 0 : traceContext.traceId,
1546
+ [DynamicTraceElapsedTimeHeader]: getElapsedTime(traceContext == null ? void 0 : traceContext.startTime)
1547
+ }
1548
+ });
1549
+ return data;
1550
+ }
1551
+ /** List business accounts the caller is a member of. */ async listBusinessAccounts({ traceContext } = {}) {
1552
+ const { data } = await this.apiClient.get(this.businessAccountsRoutePrefix, {
1553
+ headers: {
1554
+ [DynamicRequestIdHeader]: v4().replace('-', ''),
1555
+ [DynamicTraceIdHeader]: traceContext == null ? void 0 : traceContext.traceId,
1556
+ [DynamicTraceElapsedTimeHeader]: getElapsedTime(traceContext == null ? void 0 : traceContext.startTime)
1557
+ }
1558
+ });
1559
+ return data;
1560
+ }
1561
+ /** Get a business account with embedded members + signers. 404 if the caller is not a member. */ async getBusinessAccountById({ businessAccountId, traceContext }) {
1562
+ const { data } = await this.apiClient.get(`${this.businessAccountsRoutePrefix}/${businessAccountId}`, {
1563
+ headers: {
1564
+ [DynamicRequestIdHeader]: v4().replace('-', ''),
1565
+ [DynamicTraceIdHeader]: traceContext == null ? void 0 : traceContext.traceId,
1566
+ [DynamicTraceElapsedTimeHeader]: getElapsedTime(traceContext == null ? void 0 : traceContext.startTime)
1567
+ }
1568
+ });
1569
+ return data;
1570
+ }
1571
+ /**
1572
+ * Add an existing wallet to a business account. No ceremony — the server
1573
+ * transfers ownership and registers the caller as owner + signer bound to
1574
+ * their existing share set. Omit `businessAccountId` to find-or-create the
1575
+ * per-wallet business account. Returns the business-account detail. Idempotent.
1576
+ */ async linkWalletToBusinessAccount({ walletId, businessAccountId, traceContext }) {
1577
+ const { data } = await this.apiClient.post(`${this.businessAccountsRoutePrefix}/linkWallet`, _extends({
1578
+ walletId
1579
+ }, businessAccountId ? {
1580
+ businessAccountId
1581
+ } : {}), {
1582
+ headers: {
1583
+ [DynamicRequestIdHeader]: v4().replace('-', ''),
1584
+ [DynamicTraceIdHeader]: traceContext == null ? void 0 : traceContext.traceId,
1585
+ [DynamicTraceElapsedTimeHeader]: getElapsedTime(traceContext == null ? void 0 : traceContext.startTime)
1586
+ }
1587
+ });
1588
+ return data;
1589
+ }
1590
+ /** Add a member to a business account by user id. Caller must be an owner/admin. */ async addBusinessAccountMember({ businessAccountId, userId, role, traceContext }) {
1591
+ const { data } = await this.apiClient.post(`${this.businessAccountsRoutePrefix}/${businessAccountId}/members`, _extends({
1592
+ userId
1593
+ }, role ? {
1594
+ role
1595
+ } : {}), {
1596
+ headers: {
1597
+ [DynamicRequestIdHeader]: v4().replace('-', ''),
1598
+ [DynamicTraceIdHeader]: traceContext == null ? void 0 : traceContext.traceId,
1599
+ [DynamicTraceElapsedTimeHeader]: getElapsedTime(traceContext == null ? void 0 : traceContext.startTime)
1600
+ }
1601
+ });
1602
+ return data;
1603
+ }
1604
+ /**
1605
+ * Add a signer to a business-account wallet. Resolve the target by `userId`, or by
1606
+ * `identifier` + `identifierType` (e.g. an email). Returns a PENDING signer
1607
+ * (HTTP 202, `shareSetId: null`) — the reshare ceremony that mints the share
1608
+ * is a follow-up and not yet performed server-side.
1609
+ */ async addBusinessAccountSigner({ businessAccountId, walletId, signerType, userId, identifier, identifierType, traceContext }) {
1610
+ const { data } = await this.apiClient.post(`${this.businessAccountsRoutePrefix}/${businessAccountId}/wallets/${walletId}/signers`, _extends({
1611
+ signerType
1612
+ }, userId ? {
1613
+ userId
1614
+ } : {}, identifier ? {
1615
+ identifier
1616
+ } : {}, identifierType ? {
1617
+ type: identifierType
1618
+ } : {}), {
1619
+ headers: {
1620
+ [DynamicRequestIdHeader]: v4().replace('-', ''),
1621
+ [DynamicTraceIdHeader]: traceContext == null ? void 0 : traceContext.traceId,
1622
+ [DynamicTraceElapsedTimeHeader]: getElapsedTime(traceContext == null ? void 0 : traceContext.startTime)
1623
+ }
1624
+ });
1625
+ return data;
1626
+ }
1627
+ /**
1628
+ * SSE ceremony variant of addSigner (WIP — DYNT-1072). Same-parties
1629
+ * 2/2 -> 2/2 reshare on the signers endpoint: the caller's existing client
1630
+ * share participates as a remaining party (clientKeygenIds = [existing], no
1631
+ * new party). The server runs the degenerate reshare with
1632
+ * `newServerKeygenIds=[]` and persists the result as a NEW isolated share set
1633
+ * for the target signer — it must NOT refresh the caller's set in place.
1634
+ *
1635
+ * Returns the room (createEventStreamPromise) + the new `shareSetId` via
1636
+ * onCeremonyComplete. The resulting client share is reshared locally and
1637
+ * seeded to the client-keyshare-service on the target's behalf (see
1638
+ * `seedBusinessAccountSignerShare`). Not wired into the live addSigner until the
1639
+ * redcoast signers SSE endpoint exists.
1640
+ */ async addBusinessAccountSignerCeremony({ businessAccountId, walletId, clientKeygenIds, oldThresholdSignatureScheme, signerType, userId, identifier, identifierType, dynamicRequestId, onError, onCeremonyComplete, traceContext }) {
1641
+ return createEventStreamPromise({
1642
+ apiClient: this.apiClient,
1643
+ dynamicRequestId,
1644
+ endpoint: `${this.businessAccountsRoutePrefix}/${businessAccountId}/wallets/${walletId}/signers`,
1645
+ body: _extends({
1646
+ signerType,
1647
+ clientKeygenIds,
1648
+ oldThresholdSignatureScheme,
1649
+ // FF-on share-set semantics: always reshare to a NEW 2/2 share set.
1650
+ newThresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_TWO
1651
+ }, userId ? {
1652
+ userId
1653
+ } : {}, identifier ? {
1654
+ identifier
1655
+ } : {}, identifierType ? {
1656
+ type: identifierType
1657
+ } : {}),
1658
+ successEventType: SuccessEventType.RoomCreated,
1659
+ timeoutMessage: 'Add signer reshare timed out',
1660
+ onError,
1661
+ onCeremonyComplete,
1662
+ traceContext
1663
+ });
1664
+ }
1665
+ /**
1666
+ * On-behalf KSS write for addSigner: store the added signer's freshly-reshared
1667
+ * client share in the client-keyshare-service keyed to the ADDED signer's
1668
+ * partition via `targetUserId` in the body. Uses the caller's relay auth (the
1669
+ * business-account-membership gate is FF-bypassed for now; the client-keyshare-service
1670
+ * reads `targetUserId` and inserts-if-empty). Returns the stored keyShareId(s)
1671
+ * to thread as `externalKeyShareId` into `markKeySharesAsBackedUp`.
1672
+ * Hardened auth (scoped grant token) is the follow-up — DYNT-1071/1072.
1673
+ */ async backupOnBehalfOfAddedSigner({ walletId, targetUserId, encryptedAccountCredentials, passwordEncrypted, encryptionVersion, clientKeyshareBackupGrant, signedSessionId, dynamicRequestId, traceContext }) {
1674
+ const { data } = await this.clientRelayApiClient.post(`${this.waasRoutePrefix}/${walletId}/keyShares/backup`, {
1675
+ encryptedAccountCredentials,
1676
+ passwordEncrypted,
1677
+ encryptionVersion,
1678
+ targetUserId
1679
+ }, {
1680
+ headers: _extends({
1681
+ [DynamicRequestIdHeader]: dynamicRequestId,
1682
+ [DynamicTraceIdHeader]: traceContext == null ? void 0 : traceContext.traceId,
1683
+ [DynamicTraceElapsedTimeHeader]: getElapsedTime(traceContext == null ? void 0 : traceContext.startTime)
1684
+ }, signedSessionId && {
1685
+ [DynamicClientSessionSignature]: signedSessionId
1686
+ }, clientKeyshareBackupGrant && {
1687
+ Authorization: `Bearer ${clientKeyshareBackupGrant}`
1688
+ })
1689
+ });
1690
+ return data;
1691
+ }
1537
1692
  async delegatedSignMessage({ walletId, shareSetId, message, isFormatted, dynamicRequestId, onError, context, traceContext }) {
1538
1693
  return createEventStreamPromise({
1539
1694
  apiClient: this.apiClient,
@@ -1629,6 +1784,103 @@ class DynamicApiClient extends BaseClient {
1629
1784
  });
1630
1785
  this.sdkRoutePrefix = isServerSdk ? SERVER_SDK_ROUTE_PREFIX : BROWSER_SDK_ROUTE_PREFIX;
1631
1786
  this.waasRoutePrefix = `${this.sdkRoutePrefix}/${this.environmentId}/waas`;
1787
+ this.businessAccountsRoutePrefix = `${this.sdkRoutePrefix}/${this.environmentId}/businessAccounts`;
1788
+ }
1789
+ }
1790
+
1791
+ // Local mirror of the business-account models generated into
1792
+ // `@dynamic-labs/sdk-api-core`. Swap these for the generated imports once that
1793
+ // package is bumped with the SDK routes (redcoast PR #9792).
1794
+ var BusinessAccountMemberRole = /*#__PURE__*/ function(BusinessAccountMemberRole) {
1795
+ BusinessAccountMemberRole["Owner"] = "owner";
1796
+ BusinessAccountMemberRole["Admin"] = "admin";
1797
+ BusinessAccountMemberRole["Viewer"] = "viewer";
1798
+ return BusinessAccountMemberRole;
1799
+ }({});
1800
+ var BusinessAccountSignerType = /*#__PURE__*/ function(BusinessAccountSignerType) {
1801
+ BusinessAccountSignerType["EndUser"] = "endUser";
1802
+ BusinessAccountSignerType["Server"] = "server";
1803
+ return BusinessAccountSignerType;
1804
+ }({});
1805
+
1806
+ // Subset redcoast's addSigner endpoint supports today (sdkController.ts).
1807
+ // Other identifier types in the TS union are reserved for future endpoints.
1808
+ const ADD_SIGNER_SUPPORTED_IDENTIFIER_TYPES = new Set([
1809
+ 'email',
1810
+ 'externalUserId',
1811
+ 'id'
1812
+ ]);
1813
+ /**
1814
+ * Business-account orchestration. Lives in core (not on the monolithic wallet
1815
+ * client) so both browser and node clients can compose it. Pure REST today.
1816
+ */ class BusinessAccountClient {
1817
+ /** Create a business account; the authenticated caller is seeded as the first owner member. */ async createBusinessAccount(request = {}, options) {
1818
+ return this.apiClient.createBusinessAccount({
1819
+ request,
1820
+ traceContext: options == null ? void 0 : options.traceContext
1821
+ });
1822
+ }
1823
+ /** List business accounts the caller is a member of. */ async listBusinessAccounts(options) {
1824
+ return this.apiClient.listBusinessAccounts({
1825
+ traceContext: options == null ? void 0 : options.traceContext
1826
+ });
1827
+ }
1828
+ /** Full account detail (members + signers). 404 if the caller is not a member. */ async getBusinessAccount(businessAccountId, options) {
1829
+ return this.apiClient.getBusinessAccountById({
1830
+ businessAccountId,
1831
+ traceContext: options == null ? void 0 : options.traceContext
1832
+ });
1833
+ }
1834
+ /**
1835
+ * Bring an existing wallet under a business account so signers can be added to it.
1836
+ * Pure REST, no ceremony — the owner already holds a valid share set, so the
1837
+ * server just transfers ownership and registers the caller as owner + signer.
1838
+ * Omit `businessAccountId` to find-or-create the per-wallet business account.
1839
+ * Idempotent.
1840
+ */ async addWalletToBusinessAccount({ walletId, businessAccountId, traceContext }) {
1841
+ return this.apiClient.linkWalletToBusinessAccount({
1842
+ walletId,
1843
+ businessAccountId,
1844
+ traceContext
1845
+ });
1846
+ }
1847
+ /** Add a member to a business account by user id. Caller must be an owner/admin. */ async addMember({ businessAccountId, userId, role, traceContext }) {
1848
+ return this.apiClient.addBusinessAccountMember({
1849
+ businessAccountId,
1850
+ userId,
1851
+ role,
1852
+ traceContext
1853
+ });
1854
+ }
1855
+ /**
1856
+ * Add a signer to a business-account wallet, resolved by `email` (or explicit
1857
+ * `userId`). Server creates the user if absent, auto-adds them as a member,
1858
+ * and writes a PENDING signer row. Returns the pending signer — the reshare
1859
+ * ceremony that mints the share lives in the browser SDK.
1860
+ */ async addSigner({ businessAccountId, walletId, targetSignerIdentity, signerType = 'endUser', traceContext }) {
1861
+ const { userId, identifier, identifierType } = targetSignerIdentity;
1862
+ if (!userId && !(identifier && identifierType)) {
1863
+ throw new Error('BusinessAccountClient.addSigner requires targetSignerIdentity.userId, or identifier + identifierType');
1864
+ }
1865
+ if (identifierType && !ADD_SIGNER_SUPPORTED_IDENTIFIER_TYPES.has(identifierType)) {
1866
+ throw new Error(`BusinessAccountClient.addSigner: identifierType '${identifierType}' is not supported. Use one of: ${[
1867
+ ...ADD_SIGNER_SUPPORTED_IDENTIFIER_TYPES
1868
+ ].join(', ')}.`);
1869
+ }
1870
+ this.logger.info('BusinessAccountClient.addSigner creating pending signer (reshare pending)');
1871
+ return this.apiClient.addBusinessAccountSigner({
1872
+ businessAccountId,
1873
+ walletId,
1874
+ signerType,
1875
+ userId,
1876
+ identifier,
1877
+ identifierType,
1878
+ traceContext
1879
+ });
1880
+ }
1881
+ constructor({ apiClient, logger }){
1882
+ this.apiClient = apiClient;
1883
+ this.logger = logger;
1632
1884
  }
1633
1885
  }
1634
1886
 
@@ -1872,4 +2124,4 @@ const handleAxiosError = (error, message, context, _logger)=>{
1872
2124
  throw new WalletApiError(resolvedStatus, STATUS_MESSAGES[resolvedStatus]);
1873
2125
  };
1874
2126
 
1875
- export { AuthMode, BITCOIN_ADDRESS_TYPE_CONFIG, BITCOIN_DERIVATION_PATHS, BROWSER_SDK_ROUTE_PREFIX, BackupLocation, BitcoinAddressType, BitcoinNetwork, CreateRoomPartiesOptions, DELEGATED_SHARE_COUNT, DYNAMIC_AUTH_BASE_API_URL_MAP, DYNAMIC_AUTH_DEV_BASE_API_URL, DYNAMIC_AUTH_PREPROD_BASE_API_URL, DYNAMIC_AUTH_PROD_BASE_API_URL, DYNAMIC_CLIENT_RELAY_REDCOAST_API_KEY_MAP, DYNAMIC_CLIENT_RELAY_REDCOAST_APP_ID_MAP, DYNAMIC_FORWARD_MPC_DEV_ENCLAVE_URL, DYNAMIC_FORWARD_MPC_ENCLAVE_ATTESTATION_CONFIG_MAP, DYNAMIC_FORWARD_MPC_ENCLAVE_URL_MAP, DYNAMIC_FORWARD_MPC_PREPROD_ENCLAVE_URL, DYNAMIC_FORWARD_MPC_PROD_ENCLAVE_URL, DYNAMIC_KEYSHARES_RELAY_MAP, DYNAMIC_KEYSHARES_RELAY_PREPROD_BASE_API_URL, DYNAMIC_KEYSHARES_RELAY_PROD_BASE_API_URL, DynamicApiClient, DynamicClientSessionSignature, DynamicElevatedAccessTokenHeader, DynamicForwardMPCHeader, DynamicMfaTokenHeader, DynamicRequestIdHeader, DynamicTraceElapsedTimeHeader, DynamicTraceIdHeader, ENCRYPTED_SHARES_STORAGE_SUFFIX, ENVIRONMENT_ENUM, EVM_COMPATIBLE_CHAINS, FEATURE_FLAGS, IFRAME_DOMAIN_MAP, Logger, MIDNIGHT_DERIVATION_PATHS, MPC_CHAIN_CONFIG, MPC_CONFIG, MPC_RELAY_DEV_API_URL, MPC_RELAY_PREPROD_API_URL, MPC_RELAY_PROD_API_URL, MPC_RELAY_URL_MAP, NoopLogger, PREPROD_RELAY_API_KEY, PREPROD_RELAY_APP_ID, PROD_RELAY_API_KEY, PROD_RELAY_APP_ID, RELAY_API_KEY_HEADER, RELAY_APP_ID_HEADER, SDK_NAMESPACE, SERVER_SDK_ROUTE_PREFIX, SOLANA_RPC_URL, SuccessEventType, ThresholdSignatureScheme, URL_PATTERNS, WalletApiError, WalletOperation, WalletReadyState, assertDynamicLocationsHaveExternalKeyShareId, chain, chainEnumToVerifiedCredentialName, formatNamespacedVersion, getBitcoinChainConfig, getClientThreshold, getDynamicServerThreshold, getEnvironmentFromUrl, getMPCChainConfig, getRequiredExternalKeyShareId, getReshareConfig, getServerWalletReshareConfig, getTSSConfig, getVersionNamespace, getVersionWithoutNamespace, handleAxiosError, isEvmCompatibleChain, parseNamespacedVersion, serializeMessageForForwardMPC, verifiedCredentialNameToChainEnum };
2127
+ export { AuthMode, BITCOIN_ADDRESS_TYPE_CONFIG, BITCOIN_DERIVATION_PATHS, BROWSER_SDK_ROUTE_PREFIX, BackupLocation, BitcoinAddressType, BitcoinNetwork, BusinessAccountClient, BusinessAccountMemberRole, BusinessAccountSignerType, CreateRoomPartiesOptions, DELEGATED_SHARE_COUNT, DYNAMIC_AUTH_BASE_API_URL_MAP, DYNAMIC_AUTH_DEV_BASE_API_URL, DYNAMIC_AUTH_PREPROD_BASE_API_URL, DYNAMIC_AUTH_PROD_BASE_API_URL, DYNAMIC_CLIENT_RELAY_REDCOAST_API_KEY_MAP, DYNAMIC_CLIENT_RELAY_REDCOAST_APP_ID_MAP, DYNAMIC_FORWARD_MPC_DEV_ENCLAVE_URL, DYNAMIC_FORWARD_MPC_ENCLAVE_ATTESTATION_CONFIG_MAP, DYNAMIC_FORWARD_MPC_ENCLAVE_URL_MAP, DYNAMIC_FORWARD_MPC_PREPROD_ENCLAVE_URL, DYNAMIC_FORWARD_MPC_PROD_ENCLAVE_URL, DYNAMIC_KEYSHARES_RELAY_MAP, DYNAMIC_KEYSHARES_RELAY_PREPROD_BASE_API_URL, DYNAMIC_KEYSHARES_RELAY_PROD_BASE_API_URL, DynamicApiClient, DynamicClientSessionSignature, DynamicElevatedAccessTokenHeader, DynamicForwardMPCHeader, DynamicMfaTokenHeader, DynamicRequestIdHeader, DynamicTraceElapsedTimeHeader, DynamicTraceIdHeader, ENCRYPTED_SHARES_STORAGE_SUFFIX, ENVIRONMENT_ENUM, EVM_COMPATIBLE_CHAINS, FEATURE_FLAGS, IFRAME_DOMAIN_MAP, Logger, MIDNIGHT_DERIVATION_PATHS, MPC_CHAIN_CONFIG, MPC_CONFIG, MPC_RELAY_DEV_API_URL, MPC_RELAY_PREPROD_API_URL, MPC_RELAY_PROD_API_URL, MPC_RELAY_URL_MAP, NoopLogger, PREPROD_RELAY_API_KEY, PREPROD_RELAY_APP_ID, PROD_RELAY_API_KEY, PROD_RELAY_APP_ID, RELAY_API_KEY_HEADER, RELAY_APP_ID_HEADER, SDK_NAMESPACE, SERVER_SDK_ROUTE_PREFIX, SOLANA_RPC_URL, SuccessEventType, ThresholdSignatureScheme, URL_PATTERNS, WalletApiError, WalletOperation, WalletReadyState, assertDynamicLocationsHaveExternalKeyShareId, chain, chainEnumToVerifiedCredentialName, formatNamespacedVersion, getBitcoinChainConfig, getClientThreshold, getDynamicServerThreshold, getEnvironmentFromUrl, getMPCChainConfig, getRequiredExternalKeyShareId, getReshareConfig, getServerWalletReshareConfig, getTSSConfig, getVersionNamespace, getVersionWithoutNamespace, handleAxiosError, isEvmCompatibleChain, parseNamespacedVersion, serializeMessageForForwardMPC, verifiedCredentialNameToChainEnum };