@dynamic-labs-wallet/core 1.0.28 → 1.0.29

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