@ledgerhq/revault-sdk 0.24.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/bake.cjs CHANGED
@@ -299,6 +299,7 @@ async function bakeWorkspace(options, logger) {
299
299
  existingWhitelists,
300
300
  existingPolicies,
301
301
  existingAccounts,
302
+ existingEntities,
302
303
  tradelinkNetworkBlueprint
303
304
  ] = await Promise.all([
304
305
  _chunkLH4QQXUHcjs.fetchAll.call(void 0, admin1.client.users.list.query, { status: "ACTIVE" }),
@@ -306,6 +307,7 @@ async function bakeWorkspace(options, logger) {
306
307
  _chunkLH4QQXUHcjs.fetchAll.call(void 0, admin1.client.whitelists.list.query, { status: "ACTIVE" }),
307
308
  _chunkLH4QQXUHcjs.fetchAll.call(void 0, admin1.client.policies.list.query, { isPrivate: false }),
308
309
  _chunkLH4QQXUHcjs.fetchAll.call(void 0, admin1.client.accounts.list.query, { status: "ACTIVE" }),
310
+ _chunkLH4QQXUHcjs.fetchAll.call(void 0, admin1.client.entities.list.query, { status: "ACTIVE" }),
309
311
  admin1.client.tradelink.getNetworkBlueprint.query()
310
312
  ]);
311
313
  const tradelinkOrgExchangesByName = _lodash2.default.keyBy(_optionalChain([tradelinkNetworkBlueprint, 'optionalAccess', _7 => _7.exchanges]), "name");
@@ -364,19 +366,74 @@ async function bakeWorkspace(options, logger) {
364
366
  await createAccount(account, workspace);
365
367
  }
366
368
  }
369
+ if (workspace.entities) {
370
+ logTitle("Entities");
371
+ for (const entity of workspace.entities) {
372
+ await createEntity(entity);
373
+ }
374
+ }
375
+ async function editDomainObjectIfNeeded(params) {
376
+ const previousRequests = await admin1.client.requests.list.query({
377
+ type: params.requestTypes,
378
+ targetId: params.existingId,
379
+ state: "EXECUTED",
380
+ sortBy: "createdAt",
381
+ sortOrder: "desc",
382
+ pageSize: 1
383
+ });
384
+ if (_optionalChain([previousRequests, 'optionalAccess', _9 => _9.results]) && previousRequests.results.length > 0) {
385
+ const stateRequest = previousRequests.results[0];
386
+ _invariant2.default.call(void 0,
387
+ stateRequest && params.requestTypes.includes(stateRequest.type),
388
+ `State request should be a ${params.requestTypes[0]} or ${params.requestTypes[1]} request`
389
+ );
390
+ const lastData = params.extractLastData(stateRequest.data);
391
+ if (_lodash2.default.isEqual(stripUndefinedDeep(params.editData), stripUndefinedDeep(lastData))) {
392
+ logger.info(`[SKIPPED] ${params.entityType} "${params.entityName}" has not changed`);
393
+ return "skipped";
394
+ }
395
+ }
396
+ logStep(`Editing ${params.entityType.toLowerCase()} "${params.entityName}"...`);
397
+ await createAndApproveRequest(params.editInput, [admin1, admin2], logger);
398
+ logger.info(`[EDITED] ${params.entityType} "${params.entityName}" has been edited`);
399
+ return "edited";
400
+ }
367
401
  async function createWhitelists(whitelists) {
368
402
  for (const whitelist of whitelists) {
369
403
  await createWhitelist(whitelist);
370
404
  }
371
405
  async function createWhitelist(whitelist) {
372
406
  const existing = existingWhitelists.find((w) => w.name === whitelist.name);
407
+ const normalizeAddresses = (addresses) => addresses.map(({ destinationTags, ...rest }) => ({
408
+ ...rest,
409
+ ..._optionalChain([destinationTags, 'optionalAccess', _10 => _10.length]) ? { destinationTags } : {}
410
+ }));
411
+ const whitelistEditData = {
412
+ name: whitelist.name,
413
+ addresses: normalizeAddresses(whitelist.addresses)
414
+ };
373
415
  if (existing) {
374
- logger.info(`[SKIPPED] Whitelist "${whitelist.name}" already exists`);
416
+ await editDomainObjectIfNeeded({
417
+ entityType: "Whitelist",
418
+ entityName: whitelist.name,
419
+ existingId: existing.id,
420
+ editInput: {
421
+ type: "EDIT_WHITELIST",
422
+ targetId: existing.id,
423
+ data: { name: whitelist.name, addresses: whitelist.addresses }
424
+ },
425
+ editData: whitelistEditData,
426
+ requestTypes: ["CREATE_WHITELIST", "EDIT_WHITELIST"],
427
+ extractLastData: (data) => ({
428
+ name: data.name,
429
+ addresses: normalizeAddresses(data.addresses)
430
+ })
431
+ });
375
432
  return;
376
433
  }
377
434
  const input = {
378
435
  type: "CREATE_WHITELIST",
379
- data: whitelist
436
+ data: { ...whitelist, description: _nullishCoalesce(whitelist.description, () => ( null)) }
380
437
  };
381
438
  const req = await createAndApproveRequest(input, [admin1, admin2], logger);
382
439
  logSubStep(`Whitelist "${whitelist.name}" has been created`);
@@ -400,7 +457,7 @@ async function bakeWorkspace(options, logger) {
400
457
  };
401
458
  }),
402
459
  threshold: rule.threshold,
403
- whitelistIds: _optionalChain([rule, 'access', _9 => _9.whitelistNames, 'optionalAccess', _10 => _10.map, 'call', _11 => _11(whitelistIdFromName)])
460
+ whitelistIds: _optionalChain([rule, 'access', _11 => _11.whitelistNames, 'optionalAccess', _12 => _12.map, 'call', _13 => _13(whitelistIdFromName)])
404
461
  };
405
462
  }
406
463
  function rawSigningRuleFromManifest(rule) {
@@ -414,7 +471,6 @@ async function bakeWorkspace(options, logger) {
414
471
  };
415
472
  }
416
473
  async function createAccount(account, workspace2) {
417
- let req = null;
418
474
  const existing = existingAccounts.find(
419
475
  (a) => a.currency === account.currency && a.index === account.index && (_nullishCoalesce(a.contractAddress, () => ( null))) === (_nullishCoalesce(account.contractAddress, () => ( null)))
420
476
  );
@@ -444,58 +500,41 @@ async function bakeWorkspace(options, logger) {
444
500
  index: account.index,
445
501
  governance
446
502
  };
447
- const createInput = {
448
- type: "CREATE_ACCOUNT",
449
- data: accountData
450
- };
451
503
  if (existing) {
452
- const editInput = {
453
- type: "EDIT_ACCOUNT",
454
- targetId: existing.id,
455
- data: { name: accountData.name, governance }
456
- };
457
- const previousAccountRequests = await admin1.client.requests.list.query({
458
- type: ["CREATE_ACCOUNT", "EDIT_ACCOUNT"],
459
- targetId: existing.id,
460
- state: "EXECUTED",
461
- sortBy: "createdAt",
462
- sortOrder: "desc"
504
+ const editData = { name: accountData.name, governance };
505
+ const result = await editDomainObjectIfNeeded({
506
+ entityType: "Account",
507
+ entityName: account.name,
508
+ existingId: existing.id,
509
+ editInput: { type: "EDIT_ACCOUNT", targetId: existing.id, data: editData },
510
+ editData,
511
+ requestTypes: ["CREATE_ACCOUNT", "EDIT_ACCOUNT"],
512
+ extractLastData: (data) => ({ name: data.name, governance: data.governance })
463
513
  });
464
- if (_optionalChain([previousAccountRequests, 'optionalAccess', _12 => _12.results]) && previousAccountRequests.results.length > 0) {
465
- const stateRequest = _optionalChain([previousAccountRequests, 'access', _13 => _13.results, 'optionalAccess', _14 => _14[0]]);
466
- _invariant2.default.call(void 0,
467
- stateRequest && (stateRequest.type === "EDIT_ACCOUNT" || stateRequest.type === "CREATE_ACCOUNT"),
468
- "State request should be an edit account request"
469
- );
470
- if (_lodash2.default.isEqual(
471
- stripUndefinedDeep(accountData.governance),
472
- stripUndefinedDeep(stateRequest.data.governance)
473
- ) && accountData.name === stateRequest.data.name) {
474
- logger.info(`[SKIPPED] Account "${account.name}" has not changed`);
475
- await activateHederaAccount(account, workspace2, existing.id);
476
- return;
477
- }
478
- req = await createAndApproveRequest(editInput, [admin1, admin2], logger);
479
- logger.info(`[EDITED] Account "${account.name}" has been edited`);
480
- return;
514
+ if (result === "skipped") {
515
+ await activateHederaAccount(account, workspace2, existing.id);
481
516
  }
482
- } else {
483
- logStep(`Creating account "${account.name}"`);
484
- req = await createAndApproveRequest(createInput, [admin1, admin2], logger);
485
- const createdAccount = await admin1.client.accounts.findById.query({ id: req.targetId });
486
- existingAccounts.push(createdAccount);
517
+ return;
487
518
  }
488
- if (account.governance.type === "TRADELINK" && req) {
519
+ logStep(`Creating account "${account.name}"`);
520
+ const createInput = {
521
+ type: "CREATE_ACCOUNT",
522
+ data: accountData
523
+ };
524
+ const req = await createAndApproveRequest(createInput, [admin1, admin2], logger);
525
+ const createdAccount = await admin1.client.accounts.findById.query({ id: req.targetId });
526
+ existingAccounts.push(createdAccount);
527
+ if (account.governance.type === "TRADELINK") {
489
528
  logStep(`Activating tradelink account "${account.name}"`);
490
529
  const assetManagerId = assetManagerIdFromName(account.governance.assetManager);
491
- const assetManager = _optionalChain([existingTradelinkNetwork, 'optionalAccess', _15 => _15.assetManagers, 'access', _16 => _16.find, 'call', _17 => _17(
530
+ const assetManager = _optionalChain([existingTradelinkNetwork, 'optionalAccess', _14 => _14.assetManagers, 'access', _15 => _15.find, 'call', _16 => _16(
492
531
  (assetManager2) => assetManager2.id === assetManagerId
493
532
  )]);
494
533
  _invariant2.default.call(void 0, assetManager, `Asset manager for account exists`);
495
534
  const loggedAssetManagerOperators = await Promise.all(
496
535
  assetManager.groups.flatMap(
497
536
  ({ quorum, userIds }) => userIds.slice(0, quorum).map((userId) => {
498
- const deviceIndex = _optionalChain([usersWithDeviceIndex, 'access', _18 => _18.find, 'call', _19 => _19((o) => o.user.id === userId), 'optionalAccess', _20 => _20.deviceIndex]);
537
+ const deviceIndex = _optionalChain([usersWithDeviceIndex, 'access', _17 => _17.find, 'call', _18 => _18((o) => o.user.id === userId), 'optionalAccess', _19 => _19.deviceIndex]);
499
538
  return workspaceEngine.withDeviceIndex(deviceIndex).login();
500
539
  })
501
540
  )
@@ -506,7 +545,7 @@ async function bakeWorkspace(options, logger) {
506
545
  logger
507
546
  );
508
547
  }
509
- if (_optionalChain([req, 'optionalAccess', _21 => _21.targetId])) {
548
+ if (req.targetId) {
510
549
  await activateHederaAccount(account, workspace2, req.targetId);
511
550
  }
512
551
  logSubStep(`Account ${account.name} has been created`);
@@ -515,7 +554,7 @@ async function bakeWorkspace(options, logger) {
515
554
  if (account.currency === "hedera" && account.contractAddress === void 0 && workspace2.hederaData && accountId) {
516
555
  const salt = engine.__options.salt;
517
556
  const hederaAccountsForSalt = workspace2.hederaData[salt];
518
- const hederaAccountData = _optionalChain([hederaAccountsForSalt, 'optionalAccess', _22 => _22.find, 'call', _23 => _23(
557
+ const hederaAccountData = _optionalChain([hederaAccountsForSalt, 'optionalAccess', _20 => _20.find, 'call', _21 => _21(
519
558
  (h) => h.index === account.index && h.activate
520
559
  )]);
521
560
  if (hederaAccountData) {
@@ -601,21 +640,30 @@ async function bakeWorkspace(options, logger) {
601
640
  }
602
641
  async function createGroup(group) {
603
642
  const existing = existingGroups.find((g) => g.name === group.name);
643
+ const members = group.members.map(userIdFromDeviceIndexOrName);
644
+ const groupData = {
645
+ name: group.name,
646
+ description: "",
647
+ members
648
+ };
604
649
  if (existing) {
605
- logger.info(`[SKIPPED] Group "${group.name}" already exists`);
650
+ await editDomainObjectIfNeeded({
651
+ entityType: "Group",
652
+ entityName: group.name,
653
+ existingId: existing.id,
654
+ editInput: { type: "EDIT_GROUP", targetId: existing.id, data: groupData },
655
+ editData: groupData,
656
+ requestTypes: ["CREATE_GROUP", "EDIT_GROUP"],
657
+ extractLastData: (data) => ({ ...data, description: _nullishCoalesce(data.description, () => ( "")) })
658
+ });
606
659
  return;
607
660
  }
608
661
  logStep(`Creating group ${group.name}...`);
609
- const members = group.members.map(userIdFromDeviceIndexOrName);
610
662
  logSubStep("Creating request...");
611
663
  let request = await admin1.client.requests.create.mutate({
612
664
  request: {
613
665
  type: "CREATE_GROUP",
614
- data: {
615
- name: group.name,
616
- description: "",
617
- members
618
- }
666
+ data: groupData
619
667
  }
620
668
  });
621
669
  request = await approveRequest(request, [admin1, admin2], logger);
@@ -625,8 +673,25 @@ async function bakeWorkspace(options, logger) {
625
673
  }
626
674
  async function createPolicy(policy) {
627
675
  const existing = existingPolicies.find((p) => p.name === policy.name);
676
+ const policyEditData = {
677
+ name: policy.name,
678
+ description: "",
679
+ govRules: policy.rules.map(govRuleFromManifest)
680
+ };
628
681
  if (existing) {
629
- logger.info(`[SKIPPED] Policy "${policy.name}" already exists`);
682
+ await editDomainObjectIfNeeded({
683
+ entityType: "Policy",
684
+ entityName: policy.name,
685
+ existingId: existing.id,
686
+ editInput: { type: "EDIT_POLICY", targetId: existing.id, data: policyEditData },
687
+ editData: policyEditData,
688
+ requestTypes: ["CREATE_POLICY", "EDIT_POLICY"],
689
+ extractLastData: (data) => ({
690
+ name: data.name,
691
+ description: _nullishCoalesce(data.description, () => ( "")),
692
+ govRules: data.govRules
693
+ })
694
+ });
630
695
  return;
631
696
  }
632
697
  logStep(`Creating policy ${policy.name}...`);
@@ -635,10 +700,8 @@ async function bakeWorkspace(options, logger) {
635
700
  request: {
636
701
  type: "CREATE_POLICY",
637
702
  data: {
638
- name: policy.name,
639
- currency: policy.currency,
640
- description: "",
641
- govRules: policy.rules.map(govRuleFromManifest)
703
+ ...policyEditData,
704
+ currency: policy.currency
642
705
  }
643
706
  }
644
707
  });
@@ -662,6 +725,37 @@ async function bakeWorkspace(options, logger) {
662
725
  await createAndApproveRequest(input, [admin1, admin2], logger);
663
726
  logSubStep(`Workspace rule for "${permission}" has been edited`);
664
727
  }
728
+ async function createEntity(entity) {
729
+ const existing = existingEntities.find((e) => e.name === entity.name);
730
+ const accountIds = (_nullishCoalesce(entity.accounts, () => ( []))).map(accountIdFromName);
731
+ const memberIds = (_nullishCoalesce(entity.members, () => ( []))).map(userIdFromUserName);
732
+ const entityEditData = { name: entity.name, accountIds, memberIds };
733
+ if (existing) {
734
+ await editDomainObjectIfNeeded({
735
+ entityType: "Entity",
736
+ entityName: entity.name,
737
+ existingId: existing.id,
738
+ editInput: { type: "EDIT_ENTITY", targetId: existing.id, data: entityEditData },
739
+ editData: entityEditData,
740
+ requestTypes: ["CREATE_ENTITY", "EDIT_ENTITY"],
741
+ extractLastData: (data) => ({
742
+ name: data.name,
743
+ accountIds: data.accountIds,
744
+ memberIds: data.memberIds
745
+ })
746
+ });
747
+ return;
748
+ }
749
+ logStep(`Creating entity "${entity.name}"...`);
750
+ const input = {
751
+ type: "CREATE_ENTITY",
752
+ data: entityEditData
753
+ };
754
+ const request = await createAndApproveRequest(input, [admin1, admin2], logger);
755
+ logSubStep(`Entity "${entity.name}" has been created`);
756
+ const createdEntity = await admin1.client.entities.findById.query({ id: request.targetId });
757
+ existingEntities.push(createdEntity);
758
+ }
665
759
  async function createTradelink(tradelink) {
666
760
  if (tradelink.custodian) {
667
761
  if (existingTradelinkNetwork === null) {
@@ -822,20 +916,25 @@ async function bakeWorkspace(options, logger) {
822
916
  _invariant2.default.call(void 0, whitelist, `Whitelist ${whitelistName} not found`);
823
917
  return whitelist.id;
824
918
  }
919
+ function accountIdFromName(accountName) {
920
+ const account = existingAccounts.find((a) => a.name === accountName);
921
+ _invariant2.default.call(void 0, account, `Account "${accountName}" not found`);
922
+ return account.id;
923
+ }
825
924
  function assetManagerIdFromName(assetManagerName) {
826
925
  const orgAssetManager = tradelinkOrgAssetManagersByName[assetManagerName];
827
926
  _invariant2.default.call(void 0,
828
927
  orgAssetManager,
829
928
  `Can't find Tradelink organization asset manager ${assetManagerName}`
830
929
  );
831
- const assetManager = _optionalChain([existingTradelinkNetwork, 'optionalAccess', _24 => _24.assetManagers, 'access', _25 => _25.find, 'call', _26 => _26(
930
+ const assetManager = _optionalChain([existingTradelinkNetwork, 'optionalAccess', _22 => _22.assetManagers, 'access', _23 => _23.find, 'call', _24 => _24(
832
931
  (assetManager2) => assetManager2.tradelinkId === orgAssetManager.tradelinkId
833
932
  )]);
834
933
  _invariant2.default.call(void 0, assetManager, `Can't find Tradelink assetManager ${assetManagerName}`);
835
934
  return assetManager.id;
836
935
  }
837
936
  function exchangeIdFromName(exchangeName) {
838
- const exchange = _optionalChain([existingTradelinkNetwork, 'optionalAccess', _27 => _27.exchanges, 'access', _28 => _28.find, 'call', _29 => _29((exchange2) => {
937
+ const exchange = _optionalChain([existingTradelinkNetwork, 'optionalAccess', _25 => _25.exchanges, 'access', _26 => _26.find, 'call', _27 => _27((exchange2) => {
839
938
  const orgExchange = tradelinkOrgExchangesByName[exchangeName];
840
939
  _invariant2.default.call(void 0, orgExchange, `Can't find Tradelink organization exchange ${exchange2.tradelinkId}`);
841
940
  return orgExchange.name === exchangeName;
@@ -854,6 +953,239 @@ async function bakeWorkspace(options, logger) {
854
953
  }
855
954
  }
856
955
 
956
+ // src/bake/recipe-workspace.ts
957
+
958
+
959
+ async function recipeWorkspace(options, logger) {
960
+ const { engine, workspaceName } = options;
961
+ const workspaceEngine = engine.withWorkspace(workspaceName);
962
+ const findDeviceIndex = _chunkLH4QQXUHcjs.createDeviceIndexResolver.call(void 0, workspaceEngine);
963
+ logger.info("Authenticating with admin...");
964
+ const admin = await workspaceEngine.withDeviceIndex(4).login();
965
+ logger.info("Fetching existing data...");
966
+ const [
967
+ existingUsers,
968
+ existingGroups,
969
+ existingWhitelists,
970
+ publicPolicies,
971
+ privatePolicies,
972
+ existingAccounts,
973
+ existingEntities,
974
+ existingWorkspaceRules
975
+ ] = await Promise.all([
976
+ _chunkLH4QQXUHcjs.fetchAll.call(void 0, admin.client.users.list.query, { status: "ACTIVE" }),
977
+ _chunkLH4QQXUHcjs.fetchAll.call(void 0, admin.client.groups.list.query, { status: "ACTIVE" }),
978
+ _chunkLH4QQXUHcjs.fetchAll.call(void 0, admin.client.whitelists.list.query, { status: "ACTIVE" }),
979
+ _chunkLH4QQXUHcjs.fetchAll.call(void 0, admin.client.policies.list.query, { isPrivate: false }),
980
+ _chunkLH4QQXUHcjs.fetchAll.call(void 0, admin.client.policies.list.query, { isPrivate: true }),
981
+ _chunkLH4QQXUHcjs.fetchAll.call(void 0, admin.client.accounts.list.query, { status: "ACTIVE" }),
982
+ _chunkLH4QQXUHcjs.fetchAll.call(void 0, admin.client.entities.list.query, { status: "ACTIVE" }),
983
+ admin.client.governance.listWorkspaceRules.query()
984
+ ]);
985
+ const existingPolicies = [...publicPolicies, ...privatePolicies];
986
+ const existingPsdUsers = existingUsers.filter((user) => user.deviceType === "PSD");
987
+ const existingApiUsers = existingUsers.filter((user) => user.deviceType === "API");
988
+ logger.info("Resolving device indexes...");
989
+ const psdUsersWithIndex = await resolveDevicesIndexes(existingPsdUsers);
990
+ logger.info("Reading users...");
991
+ const adminIndexes = psdUsersWithIndex.filter((u) => u.user.role === "ADMIN").map((u) => u.deviceIndex);
992
+ const operatorIndexes = psdUsersWithIndex.filter((u) => u.user.role === "OPERATOR").map((u) => u.deviceIndex);
993
+ const apiUsers = existingApiUsers.map((user) => {
994
+ const role = _tspattern.match.call(void 0, user.role).with("ADMIN", "OPERATOR", (role2) => role2).with("READ_ONLY_API_KEY", (role2) => {
995
+ throw new Error(`Unexpected API user role "${role2}" for user ${user.name}`);
996
+ }).exhaustive();
997
+ return {
998
+ name: user.name,
999
+ role,
1000
+ pubKey: user.pubKey,
1001
+ viewAll: user.legacyViewAll
1002
+ };
1003
+ });
1004
+ logger.info("Reading groups...");
1005
+ const groups = existingGroups.map((group) => ({
1006
+ name: group.name,
1007
+ members: group.members.map((member) => resolveUserIdentifier(member.id))
1008
+ }));
1009
+ logger.info("Reading whitelists...");
1010
+ const whitelists = existingWhitelists.map((whitelist) => ({
1011
+ name: whitelist.name,
1012
+ description: _nullishCoalesce(whitelist.description, () => ( void 0)),
1013
+ type: "TRANSACTION",
1014
+ addresses: whitelist.addresses.map((addr) => ({
1015
+ name: addr.name,
1016
+ currency: addr.currency,
1017
+ address: addr.address,
1018
+ ..._optionalChain([addr, 'access', _28 => _28.destinationTags, 'optionalAccess', _29 => _29.length]) ? { destinationTags: addr.destinationTags } : {}
1019
+ }))
1020
+ }));
1021
+ logger.info("Reading policies...");
1022
+ const policies = publicPolicies.map((policy) => ({
1023
+ name: policy.name,
1024
+ currency: policy.currency,
1025
+ rules: policy.rules.map(govRuleToManifest)
1026
+ }));
1027
+ logger.info("Reading accounts...");
1028
+ const accounts = existingAccounts.map((account) => {
1029
+ const governance = _tspattern.match.call(void 0, account.governance).with({ type: "CLASSIC" }, (gov) => {
1030
+ const policy = existingPolicies.find((p) => p.id === gov.policyId);
1031
+ _invariant2.default.call(void 0, policy, `Policy ${gov.policyId} not found for account ${account.name}`);
1032
+ return policy.isPrivate ? {
1033
+ type: "CLASSIC",
1034
+ rules: policy.rules.map(govRuleToManifest)
1035
+ } : {
1036
+ type: "POLICY",
1037
+ policy: policy.name
1038
+ };
1039
+ }).with({ type: "RAW_SIGNING" }, (gov) => {
1040
+ const policy = existingPolicies.find((p) => p.id === gov.policyId);
1041
+ _invariant2.default.call(void 0, policy, `Policy ${gov.policyId} not found for account ${account.name}`);
1042
+ return {
1043
+ type: "RAW_SIGNING",
1044
+ rules: policy.rules.map((rule) => ({
1045
+ type: "SIGN_DIGESTS",
1046
+ steps: rule.reviewSteps.map((step) => {
1047
+ _invariant2.default.call(void 0, "userIds" in step, "RAW_SIGNING rules only support user steps");
1048
+ return {
1049
+ quorum: step.quorum,
1050
+ members: step.userIds.map(resolveUserToDeviceIndexOrName)
1051
+ };
1052
+ })
1053
+ }))
1054
+ };
1055
+ }).with({ type: "TRADELINK" }, (gov) => ({
1056
+ type: "TRADELINK",
1057
+ assetManager: resolveAssetManagerName(gov.assetManagerId),
1058
+ exchanges: gov.exchangeConfigs.map((exchangeConfig) => ({
1059
+ name: resolveExchangeName(exchangeConfig.exchangeId),
1060
+ ...exchangeConfig.repledgeAuthorized ? { repledgeAuthorized: true } : {},
1061
+ ...exchangeConfig.needsAssetManagerApprovalForSettlements ? { needsAssetManagerApprovalForSettlements: true } : {}
1062
+ }))
1063
+ })).exhaustive();
1064
+ return {
1065
+ name: account.name,
1066
+ currency: account.currency,
1067
+ index: account.index,
1068
+ ...account.contractAddress ? { contractAddress: account.contractAddress } : {},
1069
+ ...account.tokenIdentifier ? { tokenIdentifier: account.tokenIdentifier } : {},
1070
+ governance
1071
+ };
1072
+ });
1073
+ logger.info("Reading entities...");
1074
+ const entities = existingEntities.map((entity) => ({
1075
+ name: entity.name,
1076
+ ...entity.accountIds.length > 0 ? { accounts: entity.accountIds.map(accountNameFromId) } : {},
1077
+ ...entity.memberIds.length > 0 ? { members: entity.memberIds.map(userNameFromId) } : {}
1078
+ }));
1079
+ logger.info("Reading workspace rules...");
1080
+ const workspaceRules = existingWorkspaceRules.results.filter((rule) => rule.permission !== "ROOT").map((rule) => {
1081
+ _invariant2.default.call(void 0,
1082
+ rule.reviewSteps.length === 1,
1083
+ "Only one review step supported for workspace rules in manifest"
1084
+ );
1085
+ const step = rule.reviewSteps[0];
1086
+ const quorum = step.quorum;
1087
+ _invariant2.default.call(void 0, rule.permission !== "ROOT", "filtered out");
1088
+ return {
1089
+ permission: rule.permission,
1090
+ quorum,
1091
+ members: step.userIds.map(resolveUserToDeviceIndexOrName)
1092
+ };
1093
+ });
1094
+ const workspace = {
1095
+ users: {
1096
+ ...adminIndexes.length > 0 ? { admins: adminIndexes } : {},
1097
+ ...operatorIndexes.length > 0 ? { operators: operatorIndexes } : {},
1098
+ ...apiUsers.length > 0 ? { api: apiUsers } : {}
1099
+ },
1100
+ ...groups.length > 0 ? { groups } : {},
1101
+ ...whitelists.length > 0 ? { whitelists } : {},
1102
+ ...policies.length > 0 ? { policies } : {},
1103
+ ...accounts.length > 0 ? { accounts } : {},
1104
+ ...entities.length > 0 ? { entities } : {},
1105
+ ...workspaceRules.length > 0 ? { workspaceRules } : {}
1106
+ };
1107
+ logger.info("Recipe completed");
1108
+ return workspace;
1109
+ async function resolveDevicesIndexes(users) {
1110
+ const result = [];
1111
+ for (const user of users) {
1112
+ _invariant2.default.call(void 0, user.deviceUserId, `User ${user.id} does not have any device user id`);
1113
+ const deviceIndex = await findDeviceIndex(user.deviceUserId);
1114
+ result.push({ user, deviceIndex });
1115
+ }
1116
+ return result;
1117
+ }
1118
+ function resolveUserIdentifier(userId) {
1119
+ const psdUser = psdUsersWithIndex.find((u) => u.user.id === userId);
1120
+ if (psdUser) return psdUser.deviceIndex;
1121
+ const apiUser = existingApiUsers.find((u) => u.id === userId);
1122
+ if (apiUser) return apiUser.name;
1123
+ throw new Error(`User ${userId} not found`);
1124
+ }
1125
+ function resolveUserToDeviceIndexOrName(userId) {
1126
+ return resolveUserIdentifier(userId);
1127
+ }
1128
+ function validateGovernanceRuleType(value) {
1129
+ _invariant2.default.call(void 0,
1130
+ ["SEND", "STAKE", "DEPLOY_CONTRACT", "EXECUTE_CONTRACT", "SIGN_MESSAGE", "RECEIVE"].includes(
1131
+ value
1132
+ ),
1133
+ "Unexpected governance rule type: " + value
1134
+ );
1135
+ return value;
1136
+ }
1137
+ function govRuleToManifest(rule) {
1138
+ const operationType = validateGovernanceRuleType(rule.operationType);
1139
+ const threshold = rule.threshold !== void 0 && (rule.threshold.min !== null || rule.threshold.max !== null) ? { threshold: { min: rule.threshold.min, max: rule.threshold.max } } : {};
1140
+ return {
1141
+ type: operationType,
1142
+ steps: rule.reviewSteps.map((step) => {
1143
+ if ("groupId" in step) {
1144
+ const group = existingGroups.find((g) => g.id === step.groupId);
1145
+ _invariant2.default.call(void 0, group, `Group ${step.groupId} not found`);
1146
+ return { quorum: step.quorum, group: group.name };
1147
+ }
1148
+ return {
1149
+ quorum: step.quorum,
1150
+ members: step.userIds.map(resolveUserToDeviceIndexOrName)
1151
+ };
1152
+ }),
1153
+ ...threshold,
1154
+ ...rule.whitelistIds && rule.whitelistIds.length > 0 ? { whitelistNames: rule.whitelistIds.map(whitelistNameFromId) } : {}
1155
+ };
1156
+ }
1157
+ function whitelistNameFromId(whitelistId) {
1158
+ const whitelist = existingWhitelists.find((w) => w.id === whitelistId);
1159
+ _invariant2.default.call(void 0, whitelist, `Whitelist ${whitelistId} not found`);
1160
+ return whitelist.name;
1161
+ }
1162
+ function accountNameFromId(accountId) {
1163
+ const account = existingAccounts.find((a) => a.id === accountId);
1164
+ _invariant2.default.call(void 0, account, `Account ${accountId} not found`);
1165
+ return account.name;
1166
+ }
1167
+ function userNameFromId(userId) {
1168
+ const user = existingApiUsers.find((u) => u.id === userId);
1169
+ _invariant2.default.call(void 0, user, `API user ${userId} not found`);
1170
+ return user.name;
1171
+ }
1172
+ function resolveAssetManagerName(assetManagerId) {
1173
+ return assetManagerId;
1174
+ }
1175
+ function resolveExchangeName(exchangeId) {
1176
+ return exchangeId;
1177
+ }
1178
+ }
1179
+
1180
+ // src/bake/recipe.ts
1181
+ async function recipeManifest(options, logger) {
1182
+ const workspace = await recipeWorkspace(options, logger);
1183
+ return {
1184
+ manifestVersion: 2,
1185
+ workspace
1186
+ };
1187
+ }
1188
+
857
1189
  // src/bake/types.ts
858
1190
  var _zod = require('zod'); var _zod2 = _interopRequireDefault(_zod);
859
1191
  var zManifestUser = _zod2.default.number().meta({ description: "User device index" });
@@ -878,7 +1210,7 @@ var zManifestWhitelistAddress = _zod2.default.object({
878
1210
  }).strict();
879
1211
  var zManifestWhitelist = _zod2.default.object({
880
1212
  name: _zod2.default.string().meta({ description: "Whitelist name" }),
881
- description: _zod2.default.string().optional().default("").meta({ description: "Whitelist description" }),
1213
+ description: _zod2.default.string().optional().meta({ description: "Whitelist description" }),
882
1214
  type: _zod2.default.enum(["TRANSACTION", "SMART_CONTRACT"]).optional().default("TRANSACTION").meta({ description: "Whitelist type" }),
883
1215
  addresses: _zod2.default.array(zManifestWhitelistAddress).meta({ description: "Whitelist addresses" })
884
1216
  }).strict().meta({ description: "Whitelist" });
@@ -907,8 +1239,8 @@ var zManifestGovernanceRule = _zod2.default.object({
907
1239
  ]),
908
1240
  steps: _zod2.default.array(zManifestGovernanceStep).min(1),
909
1241
  threshold: _zod2.default.object({
910
- min: _zod2.default.string(),
911
- max: _zod2.default.string()
1242
+ min: _zod2.default.string().nullable(),
1243
+ max: _zod2.default.string().nullable()
912
1244
  }).optional(),
913
1245
  whitelistNames: _zod2.default.array(_zod2.default.string()).optional()
914
1246
  }).strict();
@@ -957,6 +1289,11 @@ var zManifestAccount = _zod2.default.object({
957
1289
  tokenIdentifier: _zod2.default.string().optional(),
958
1290
  governance: zManifestAccountGovernance
959
1291
  }).strict();
1292
+ var zManifestEntity = _zod2.default.object({
1293
+ name: _zod2.default.string().min(1).max(19).meta({ description: "Entity name" }),
1294
+ accounts: _zod2.default.array(_zod2.default.string()).optional().meta({ description: "Account names to include in this entity" }),
1295
+ members: _zod2.default.array(_zod2.default.string()).optional().meta({ description: "API operator names to include in this entity" })
1296
+ }).strict().meta({ description: "Entity" });
960
1297
  var zManifestHederaData = _zod2.default.object({
961
1298
  index: _zod2.default.number(),
962
1299
  onChainId: _zod2.default.string(),
@@ -1015,6 +1352,7 @@ var zManifestWorkspace = _zod2.default.object({
1015
1352
  whitelists: _zod2.default.array(zManifestWhitelist).optional(),
1016
1353
  policies: _zod2.default.array(zManifestPolicy).optional(),
1017
1354
  accounts: _zod2.default.array(zManifestAccount).optional(),
1355
+ entities: _zod2.default.array(zManifestEntity).optional(),
1018
1356
  hederaData: _zod2.default.record(_zod2.default.string(), _zod2.default.array(zManifestHederaData)).optional(),
1019
1357
  pledges: _zod2.default.array(zManifestPledgeAdjustment).optional(),
1020
1358
  workspaceRules: _zod2.default.array(zManifestWorkspaceRule).optional()
@@ -1073,7 +1411,8 @@ async function bakeManifest(options, logger) {
1073
1411
 
1074
1412
 
1075
1413
 
1076
- exports.bakeManifest = bakeManifest; exports.bakePledgeAdjustment = bakePledgeAdjustment; exports.createAndApproveRequest = createAndApproveRequest; exports.zManifest = zManifest; exports.zManifestPolicy = zManifestPolicy;
1414
+
1415
+ exports.bakeManifest = bakeManifest; exports.bakePledgeAdjustment = bakePledgeAdjustment; exports.createAndApproveRequest = createAndApproveRequest; exports.recipeManifest = recipeManifest; exports.zManifest = zManifest; exports.zManifestPolicy = zManifestPolicy;
1077
1416
  /* v8 ignore else - TODO bake empty tradelink org -- @preserve */
1078
1417
  /* v8 ignore else -- @preserve */
1079
1418
  //# sourceMappingURL=bake.cjs.map