@ixo/editor 5.31.1 → 5.33.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.
@@ -98,6 +98,9 @@ var CAN_TO_TYPE = {
98
98
  "matrix/dm": "qi/matrix.dm",
99
99
  "proposal/create": "qi/proposal.create",
100
100
  "proposal/vote": "qi/proposal.vote",
101
+ "governance/member-proposal": "qi/governance.member-proposal",
102
+ "governance/settings-proposal": "qi/governance.settings-proposal",
103
+ "governance.transaction/send-funds": "qi/governance.transaction.send-funds",
101
104
  "domain/card-preview": "qi/domain.card-preview",
102
105
  "domain/sign": "qi/domain.sign",
103
106
  "credential/store": "qi/credential.store",
@@ -698,6 +701,234 @@ registerAction({
698
701
  }
699
702
  });
700
703
 
704
+ // src/core/lib/actionRegistry/actions/governance/memberProposal.ts
705
+ var VALID_OPERATIONS = ["add", "remove", "update-weight"];
706
+ function defaultTitle(operation, count) {
707
+ const noun = count === 1 ? "member" : "members";
708
+ switch (operation) {
709
+ case "add":
710
+ return `Add ${count} ${noun}`;
711
+ case "remove":
712
+ return `Remove ${count} ${noun}`;
713
+ case "update-weight":
714
+ return `Update voting power for ${count} ${noun}`;
715
+ }
716
+ }
717
+ registerAction({
718
+ type: "qi/governance.member-proposal",
719
+ can: "governance/member-proposal",
720
+ sideEffect: true,
721
+ defaultRequiresConfirmation: true,
722
+ requiredCapability: "flow/block/execute",
723
+ outputSchema: [
724
+ { path: "proposalId", displayName: "Proposal ID", type: "string", description: "The on-chain proposal identifier" },
725
+ { path: "status", displayName: "Proposal Status", type: "string", description: "Current proposal status (open, passed, rejected, executed, etc.)" },
726
+ { path: "proposalContractAddress", displayName: "Proposal Contract Address", type: "string", description: "The proposal module contract address" },
727
+ { path: "coreAddress", displayName: "Core Address", type: "string", description: "The DAO core contract address" },
728
+ { path: "operation", displayName: "Operation", type: "string", description: "add | remove | update-weight" },
729
+ { path: "createdAt", displayName: "Created At", type: "string", description: "ISO timestamp of proposal creation" }
730
+ ],
731
+ run: async (inputs, ctx) => {
732
+ const handlers = ctx.handlers;
733
+ if (!handlers) {
734
+ throw new Error("Handlers not available");
735
+ }
736
+ const coreAddress = String(inputs.coreAddress || "").trim();
737
+ if (!coreAddress) throw new Error("coreAddress is required");
738
+ const operation = String(inputs.operation || "").trim();
739
+ if (!VALID_OPERATIONS.includes(operation)) {
740
+ throw new Error(`operation must be one of: ${VALID_OPERATIONS.join(", ")}`);
741
+ }
742
+ const rawMembers = Array.isArray(inputs.members) ? inputs.members : [];
743
+ if (rawMembers.length === 0) throw new Error("At least one member is required");
744
+ const normalized = rawMembers.map((m) => ({
745
+ addr: String(m.address || m.addr || "").trim(),
746
+ weight: Number(m.weight ?? 0)
747
+ }));
748
+ if (normalized.some((m) => !m.addr)) throw new Error("Every member needs an address");
749
+ const addrs = normalized.map((m) => m.addr);
750
+ if (new Set(addrs).size !== addrs.length) throw new Error("Duplicate member addresses are not allowed");
751
+ let add = [];
752
+ let remove = [];
753
+ if (operation === "remove") {
754
+ remove = normalized.map((m) => ({ addr: m.addr }));
755
+ } else {
756
+ if (normalized.some((m) => !Number.isFinite(m.weight) || m.weight <= 0)) {
757
+ throw new Error("Voting power must be a positive number");
758
+ }
759
+ add = normalized.map((m) => ({ addr: m.addr, weight: Math.trunc(m.weight) }));
760
+ }
761
+ const title = String(inputs.title || "").trim() || defaultTitle(operation, normalized.length);
762
+ const description = String(inputs.description || "").trim() || title;
763
+ const updateMembersAction = { type: "UpdateMembers", data: { add, remove } };
764
+ const { preProposalContractAddress } = await handlers.getPreProposalContractAddress({ coreAddress });
765
+ const { groupContractAddress } = await handlers.getGroupContractAddress({ coreAddress });
766
+ const { proposalContractAddress } = await handlers.getProposalContractAddress({ coreAddress });
767
+ const proposalId = await handlers.createProposal({
768
+ preProposalContractAddress,
769
+ title,
770
+ description,
771
+ actions: [updateMembersAction],
772
+ coreAddress,
773
+ groupContractAddress
774
+ });
775
+ if (proposalId === void 0 || proposalId === null || String(proposalId).trim() === "") {
776
+ throw new Error("Proposal creation returned no proposal id. Check the handler logs.");
777
+ }
778
+ return {
779
+ output: {
780
+ proposalId: String(proposalId),
781
+ status: "open",
782
+ proposalContractAddress: proposalContractAddress || "",
783
+ coreAddress,
784
+ operation,
785
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
786
+ }
787
+ };
788
+ }
789
+ });
790
+
791
+ // src/core/lib/actionRegistry/actions/governance/settingsProposal.ts
792
+ function clampPercent(value, label, { allowZero = false } = {}) {
793
+ const n = Number(value);
794
+ if (!Number.isFinite(n)) throw new Error(`${label} must be a number`);
795
+ if (n < 0 || n > 100) throw new Error(`${label} must be between 0 and 100`);
796
+ if (!allowZero && n <= 0) throw new Error(`${label} must be greater than 0`);
797
+ return n;
798
+ }
799
+ registerAction({
800
+ type: "qi/governance.settings-proposal",
801
+ can: "governance/settings-proposal",
802
+ sideEffect: true,
803
+ defaultRequiresConfirmation: true,
804
+ requiredCapability: "flow/block/execute",
805
+ outputSchema: [
806
+ { path: "proposalId", displayName: "Proposal ID", type: "string", description: "The on-chain proposal identifier" },
807
+ { path: "status", displayName: "Proposal Status", type: "string", description: "Current proposal status (open, passed, rejected, executed, etc.)" },
808
+ { path: "proposalContractAddress", displayName: "Proposal Contract Address", type: "string", description: "The proposal module contract address" },
809
+ { path: "coreAddress", displayName: "Core Address", type: "string", description: "The DAO core contract address" },
810
+ { path: "createdAt", displayName: "Created At", type: "string", description: "ISO timestamp of proposal creation" }
811
+ ],
812
+ run: async (inputs, ctx) => {
813
+ const handlers = ctx.handlers;
814
+ if (!handlers) {
815
+ throw new Error("Handlers not available");
816
+ }
817
+ const coreAddress = String(inputs.coreAddress || "").trim();
818
+ if (!coreAddress) throw new Error("coreAddress is required");
819
+ const votingPeriodHours = Number(inputs.votingPeriodHours);
820
+ if (!Number.isFinite(votingPeriodHours) || votingPeriodHours <= 0) {
821
+ throw new Error("votingPeriodHours must be a positive number");
822
+ }
823
+ const quorumPercent = clampPercent(inputs.quorumPercent, "quorumPercent", { allowZero: true });
824
+ const thresholdPercent = clampPercent(inputs.thresholdPercent, "thresholdPercent");
825
+ const vetoThresholdPercent = clampPercent(inputs.vetoThresholdPercent, "vetoThresholdPercent", { allowZero: true });
826
+ if (thresholdPercent + vetoThresholdPercent > 100) {
827
+ throw new Error("thresholdPercent + vetoThresholdPercent cannot exceed 100");
828
+ }
829
+ const allowRevoting = Boolean(inputs.allowRevoting);
830
+ const data = {
831
+ onlyMembersExecute: false,
832
+ thresholdType: "%",
833
+ thresholdPercentage: thresholdPercent,
834
+ quorumEnabled: quorumPercent > 0,
835
+ quorumType: "%",
836
+ quorumPercentage: quorumPercent,
837
+ proposalDuration: Math.trunc(votingPeriodHours),
838
+ proposalDurationUnits: "hours",
839
+ allowRevoting,
840
+ // TODO(governance): the editor's UpdateProposalConfigData / the consumer's
841
+ // message builder don't yet carry a veto threshold for single-choice
842
+ // voting. Passed through so the consumer can apply it once supported.
843
+ vetoThresholdPercentage: vetoThresholdPercent
844
+ };
845
+ const updateVotingConfigAction = { type: "UpdateVotingConfig", data };
846
+ const title = String(inputs.title || "").trim() || "Update governance settings";
847
+ const description = String(inputs.description || "").trim() || "Updates the group's voting rules once the proposal passes.";
848
+ const { preProposalContractAddress } = await handlers.getPreProposalContractAddress({ coreAddress });
849
+ const { groupContractAddress } = await handlers.getGroupContractAddress({ coreAddress });
850
+ const { proposalContractAddress } = await handlers.getProposalContractAddress({ coreAddress });
851
+ const proposalId = await handlers.createProposal({
852
+ preProposalContractAddress,
853
+ title,
854
+ description,
855
+ actions: [updateVotingConfigAction],
856
+ coreAddress,
857
+ groupContractAddress
858
+ });
859
+ if (proposalId === void 0 || proposalId === null || String(proposalId).trim() === "") {
860
+ throw new Error("Proposal creation returned no proposal id. Check the handler logs.");
861
+ }
862
+ return {
863
+ output: {
864
+ proposalId: String(proposalId),
865
+ status: "open",
866
+ proposalContractAddress: proposalContractAddress || "",
867
+ coreAddress,
868
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
869
+ }
870
+ };
871
+ }
872
+ });
873
+
874
+ // src/core/lib/actionRegistry/actions/governance/transactionSendFunds.ts
875
+ registerAction({
876
+ type: "qi/governance.transaction.send-funds",
877
+ can: "governance.transaction/send-funds",
878
+ sideEffect: true,
879
+ defaultRequiresConfirmation: true,
880
+ requiredCapability: "flow/block/execute",
881
+ outputSchema: [
882
+ { path: "proposalId", displayName: "Proposal ID", type: "string", description: "The on-chain proposal identifier" },
883
+ { path: "status", displayName: "Proposal Status", type: "string", description: "Current proposal status (open, passed, rejected, executed, etc.)" },
884
+ { path: "proposalContractAddress", displayName: "Proposal Contract Address", type: "string", description: "The proposal module contract address" },
885
+ { path: "coreAddress", displayName: "Core Address", type: "string", description: "The DAO core contract address" },
886
+ { path: "recipient", displayName: "Recipient", type: "string", description: "Destination address of the spend" },
887
+ { path: "createdAt", displayName: "Created At", type: "string", description: "ISO timestamp of proposal creation" }
888
+ ],
889
+ run: async (inputs, ctx) => {
890
+ const handlers = ctx.handlers;
891
+ if (!handlers) {
892
+ throw new Error("Handlers not available");
893
+ }
894
+ const coreAddress = String(inputs.coreAddress || "").trim();
895
+ if (!coreAddress) throw new Error("coreAddress is required");
896
+ const recipient = String(inputs.recipient || "").trim();
897
+ if (!recipient) throw new Error("recipient is required");
898
+ const denom = String(inputs.denom || "").trim();
899
+ if (!denom) throw new Error("denom is required");
900
+ const amount = Number(inputs.amount);
901
+ if (!Number.isFinite(amount) || amount <= 0) throw new Error("amount must be greater than 0");
902
+ const spendAction = { type: "Spend", data: { to: recipient, denom, amount: String(amount) } };
903
+ const title = String(inputs.title || "").trim() || `Send ${amount} ${denom} to ${recipient}`;
904
+ const description = String(inputs.description || "").trim() || "Sends funds from the group treasury once the proposal passes.";
905
+ const { preProposalContractAddress } = await handlers.getPreProposalContractAddress({ coreAddress });
906
+ const { groupContractAddress } = await handlers.getGroupContractAddress({ coreAddress });
907
+ const { proposalContractAddress } = await handlers.getProposalContractAddress({ coreAddress });
908
+ const proposalId = await handlers.createProposal({
909
+ preProposalContractAddress,
910
+ title,
911
+ description,
912
+ actions: [spendAction],
913
+ coreAddress,
914
+ groupContractAddress
915
+ });
916
+ if (proposalId === void 0 || proposalId === null || String(proposalId).trim() === "") {
917
+ throw new Error("Proposal creation returned no proposal id. Check the handler logs.");
918
+ }
919
+ return {
920
+ output: {
921
+ proposalId: String(proposalId),
922
+ status: "open",
923
+ proposalContractAddress: proposalContractAddress || "",
924
+ coreAddress,
925
+ recipient,
926
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
927
+ }
928
+ };
929
+ }
930
+ });
931
+
701
932
  // src/core/lib/actionRegistry/actions/httpRequest.ts
702
933
  registerAction({
703
934
  type: "qi/http.request",
@@ -6089,9 +6320,12 @@ registerDiffResolver("bid", {
6089
6320
 
6090
6321
  // src/core/lib/actionRegistry/actions/evaluateBid/evaluateBid.diff.ts
6091
6322
  var USDC_DENOM = "ibc/6BBE9BD4246F8E04948D5A4EEE7164B2630263B9EBB5E7DC5F0A46C62A2FF97B";
6323
+ var DECIMALS = 6;
6092
6324
  function formatCoin(coin) {
6093
6325
  const denom = coin.denom === USDC_DENOM ? "USDC" : coin.denom === "uixo" ? "IXO" : coin.denom;
6094
- return `${coin.amount} ${denom}`;
6326
+ const rawAmount = Number(coin.amount);
6327
+ const displayAmount = Number.isFinite(rawAmount) ? String(rawAmount / Math.pow(10, DECIMALS)) : String(coin.amount);
6328
+ return `${displayAmount} ${denom}`;
6095
6329
  }
6096
6330
  registerDiffResolver("evaluateBid", {
6097
6331
  resolver: async (inputs, _ctx) => {
@@ -6254,10 +6488,10 @@ function renderNumber(value) {
6254
6488
  }
6255
6489
  var formatCoin2 = (coin) => {
6256
6490
  const USDC_DENOM3 = "ibc/6BBE9BD4246F8E04948D5A4EEE7164B2630263B9EBB5E7DC5F0A46C62A2FF97B";
6257
- const DECIMALS = 6;
6491
+ const DECIMALS2 = 6;
6258
6492
  const denom = coin.denom === USDC_DENOM3 ? "USDC" : coin.denom === "uixo" ? "IXO" : coin.denom;
6259
6493
  const raw = Number(coin.amount);
6260
- const display = raw >= Math.pow(10, DECIMALS) ? raw / Math.pow(10, DECIMALS) : raw;
6494
+ const display = raw >= Math.pow(10, DECIMALS2) ? raw / Math.pow(10, DECIMALS2) : raw;
6261
6495
  return `${display} ${denom}`;
6262
6496
  };
6263
6497
  var formatCoinAmount = (coin, assetsList) => {
@@ -7035,11 +7269,14 @@ function roleLabel(value) {
7035
7269
  }
7036
7270
  var IXO_DENOM = "uixo";
7037
7271
  var USDC_DENOM2 = "ibc/6BBE9BD4246F8E04948D5A4EEE7164B2630263B9EBB5E7DC5F0A46C62A2FF97B";
7272
+ var DENOM_DECIMALS = 6;
7038
7273
  function formatCoin4(value) {
7039
7274
  const coin = value;
7040
7275
  const denomValue = String(coin?.denom || "");
7041
7276
  const denom = denomValue === IXO_DENOM ? "IXO" : denomValue === USDC_DENOM2 ? "USDC" : denomValue;
7042
- return `${String(coin?.amount || "")} ${denom}`.trim();
7277
+ const rawAmount = Number(coin?.amount);
7278
+ const displayAmount = Number.isFinite(rawAmount) ? String(rawAmount / Math.pow(10, DENOM_DECIMALS)) : String(coin?.amount || "");
7279
+ return `${displayAmount} ${denom}`.trim();
7043
7280
  }
7044
7281
  function diffAdd(inputs) {
7045
7282
  const results = [];
@@ -11832,4 +12069,4 @@ export {
11832
12069
  executeQueuedFlowAgentCoreCommands,
11833
12070
  FlowAgentService
11834
12071
  };
11835
- //# sourceMappingURL=chunk-OP7EY4CE.mjs.map
12072
+ //# sourceMappingURL=chunk-NAR2R3YM.mjs.map