@openzeppelin/miden-multisig-client 0.14.8 → 0.14.9-rc.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.
@@ -44,8 +44,9 @@ export class ProposalMetadataCodec {
44
44
  targetThreshold: metadata.targetThreshold,
45
45
  signerCommitments: metadata.targetSignerCommitments,
46
46
  };
47
- case 'unknown':
48
- return base;
47
+ case 'custom':
48
+ // Round-trip the original server label rather than the 'custom' bucket.
49
+ return { ...base, proposalType: metadata.rawProposalType };
49
50
  }
50
51
  }
51
52
  static fromGuardian(guardian) {
@@ -118,7 +119,13 @@ export class ProposalMetadataCodec {
118
119
  targetSignerCommitments: guardian.signerCommitments,
119
120
  };
120
121
  default:
121
- throw new Error(`Unsupported proposal type: ${guardian.proposalType}`);
122
+ // Any proposal type the SDK does not model collapses to the 'custom'
123
+ // bucket while preserving the original label (issue #266).
124
+ return {
125
+ ...base,
126
+ proposalType: 'custom',
127
+ rawProposalType: guardian.proposalType,
128
+ };
122
129
  }
123
130
  }
124
131
  static validate(metadata) {
@@ -152,8 +159,10 @@ export class ProposalMetadataCodec {
152
159
  throw new Error('p2id proposal metadata is incomplete');
153
160
  }
154
161
  return metadata;
155
- case 'unknown':
156
- throw new Error('unknown proposal type is not supported');
162
+ case 'custom':
163
+ // Custom proposals are opaque to the SDK; nothing to validate beyond
164
+ // the base fields. They can be listed/signed/exported, not built here.
165
+ return metadata;
157
166
  }
158
167
  }
159
168
  }
@@ -1 +1 @@
1
- {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/proposal/metadata.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/proposal/metadata.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,OAAO,qBAAqB;IAChC,MAAM,CAAC,UAAU,CAAC,QAA0B;QAC1C,MAAM,IAAI,GAA6B;YACrC,YAAY,EAAE,QAAQ,CAAC,YAAY;YACnC,WAAW,EAAE,QAAQ,CAAC,WAAW;YACjC,IAAI,EAAE,QAAQ,CAAC,OAAO;YACtB,kBAAkB,EAAE,QAAQ,CAAC,kBAAkB;SAChD,CAAC;QAEF,QAAQ,QAAQ,CAAC,YAAY,EAAE,CAAC;YAC9B,KAAK,eAAe;gBAClB,OAAO;oBACL,GAAG,IAAI;oBACP,OAAO,EAAE,QAAQ,CAAC,OAAO;oBACzB,2BAA2B,EAAE,QAAQ,CAAC,eAAe;oBACrD,iBAAiB,EAAE,QAAQ,CAAC,KAAK;iBAClC,CAAC;YACJ,KAAK,MAAM;gBACT,OAAO;oBACL,GAAG,IAAI;oBACP,WAAW,EAAE,QAAQ,CAAC,WAAW;oBACjC,QAAQ,EAAE,QAAQ,CAAC,QAAQ;oBAC3B,MAAM,EAAE,QAAQ,CAAC,MAAM;iBACxB,CAAC;YACJ,KAAK,iBAAiB;gBACpB,OAAO;oBACL,GAAG,IAAI;oBACP,eAAe,EAAE,QAAQ,CAAC,eAAe;oBACzC,iBAAiB,EAAE,QAAQ,CAAC,uBAAuB;oBACnD,iBAAiB,EAAE,QAAQ,CAAC,iBAAiB;oBAC7C,mBAAmB,EAAE,QAAQ,CAAC,mBAAmB;iBAClD,CAAC;YACJ,KAAK,4BAA4B;gBAC/B,OAAO;oBACL,GAAG,IAAI;oBACP,eAAe,EAAE,QAAQ,CAAC,eAAe;oBACzC,eAAe,EAAE,QAAQ,CAAC,eAAe;iBAC1C,CAAC;YACJ,KAAK,YAAY,CAAC;YAClB,KAAK,eAAe,CAAC;YACrB,KAAK,kBAAkB;gBACrB,OAAO;oBACL,GAAG,IAAI;oBACP,eAAe,EAAE,QAAQ,CAAC,eAAe;oBACzC,iBAAiB,EAAE,QAAQ,CAAC,uBAAuB;iBACpD,CAAC;YACJ,KAAK,QAAQ;gBACX,wEAAwE;gBACxE,OAAO,EAAE,GAAG,IAAI,EAAE,YAAY,EAAE,QAAQ,CAAC,eAAe,EAAE,CAAC;QAC/D,CAAC;IACH,CAAC;IAED,MAAM,CAAC,YAAY,CAAC,QAAmC;QACrD,IAAI,CAAC,QAAQ,EAAE,YAAY,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAC5D,CAAC;QAED,MAAM,IAAI,GAAG;YACX,WAAW,EAAE,QAAQ,CAAC,WAAW,IAAI,EAAE;YACvC,OAAO,EAAE,QAAQ,CAAC,IAAI;YACtB,kBAAkB,EAAE,QAAQ,CAAC,kBAAkB;SAChD,CAAC;QAEF,QAAQ,QAAQ,CAAC,YAAY,EAAE,CAAC;YAC9B,KAAK,MAAM;gBACT,IAAI,CAAC,QAAQ,CAAC,WAAW,IAAI,CAAC,QAAQ,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;oBACpE,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;gBACvE,CAAC;gBACD,OAAO;oBACL,GAAG,IAAI;oBACP,YAAY,EAAE,MAAM;oBACpB,WAAW,EAAE,QAAQ,CAAC,WAAW;oBACjC,QAAQ,EAAE,QAAQ,CAAC,QAAQ;oBAC3B,MAAM,EAAE,QAAQ,CAAC,MAAM;iBACxB,CAAC;YACJ,KAAK,eAAe;gBAClB,IAAI,CAAC,QAAQ,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACvD,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;gBAC/D,CAAC;gBACD,OAAO;oBACL,GAAG,IAAI;oBACP,YAAY,EAAE,eAAe;oBAC7B,OAAO,EAAE,QAAQ,CAAC,OAAO;oBACzB,eAAe,EAAE,QAAQ,CAAC,2BAAgD;oBAC1E,KAAK,EAAE,QAAQ,CAAC,iBAAiB;iBAClC,CAAC;YACJ,KAAK,iBAAiB;gBACpB,IAAI,CAAC,QAAQ,CAAC,iBAAiB,IAAI,CAAC,QAAQ,CAAC,mBAAmB,EAAE,CAAC;oBACjE,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;gBAClF,CAAC;gBACD,OAAO;oBACL,GAAG,IAAI;oBACP,YAAY,EAAE,iBAAiB;oBAC/B,iBAAiB,EAAE,QAAQ,CAAC,iBAAiB;oBAC7C,mBAAmB,EAAE,QAAQ,CAAC,mBAAmB;oBACjD,eAAe,EAAE,QAAQ,CAAC,eAAe;oBACzC,uBAAuB,EAAE,QAAQ,CAAC,iBAAiB;iBACpD,CAAC;YACJ,KAAK,4BAA4B;gBAC/B,IAAI,QAAQ,CAAC,eAAe,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,eAAe,EAAE,CAAC;oBACxE,MAAM,IAAI,KAAK,CAAC,yEAAyE,CAAC,CAAC;gBAC7F,CAAC;gBACD,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;oBAC/C,MAAM,IAAI,KAAK,CAAC,6BAA6B,QAAQ,CAAC,eAAe,EAAE,CAAC,CAAC;gBAC3E,CAAC;gBACD,OAAO;oBACL,GAAG,IAAI;oBACP,YAAY,EAAE,4BAA4B;oBAC1C,eAAe,EAAE,QAAQ,CAAC,eAAe;oBACzC,eAAe,EAAE,QAAQ,CAAC,eAAe;iBAC1C,CAAC;YACJ,KAAK,YAAY,CAAC;YAClB,KAAK,eAAe,CAAC;YACrB,KAAK,kBAAkB;gBACrB,IAAI,QAAQ,CAAC,eAAe,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,iBAAiB,IAAI,QAAQ,CAAC,iBAAiB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACrH,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,CAAC,YAAY,+CAA+C,CAAC,CAAC;gBAC3F,CAAC;gBACD,OAAO;oBACL,GAAG,IAAI;oBACP,YAAY,EAAE,QAAQ,CAAC,YAAmE;oBAC1F,eAAe,EAAE,QAAQ,CAAC,eAAe;oBACzC,uBAAuB,EAAE,QAAQ,CAAC,iBAAiB;iBACpD,CAAC;YACJ;gBACE,qEAAqE;gBACrE,2DAA2D;gBAC3D,OAAO;oBACL,GAAG,IAAI;oBACP,YAAY,EAAE,QAAQ;oBACtB,eAAe,EAAE,QAAQ,CAAC,YAAY;iBACvC,CAAC;QACN,CAAC;IACH,CAAC;IAED,MAAM,CAAC,QAAQ,CAAC,QAA0B;QACxC,QAAQ,QAAQ,CAAC,YAAY,EAAE,CAAC;YAC9B,KAAK,YAAY,CAAC;YAClB,KAAK,eAAe,CAAC;YACrB,KAAK,kBAAkB;gBACrB,IACE,QAAQ,CAAC,eAAe,KAAK,SAAS;oBACtC,CAAC,QAAQ,CAAC,uBAAuB;oBACjC,QAAQ,CAAC,uBAAuB,CAAC,MAAM,KAAK,CAAC,EAC7C,CAAC;oBACD,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,CAAC,YAAY,kCAAkC,CAAC,CAAC;gBAC9E,CAAC;gBACD,OAAO,QAAQ,CAAC;YAClB,KAAK,iBAAiB;gBACpB,IAAI,CAAC,QAAQ,CAAC,iBAAiB,IAAI,CAAC,QAAQ,CAAC,mBAAmB,EAAE,CAAC;oBACjE,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;gBACrE,CAAC;gBACD,OAAO,QAAQ,CAAC;YAClB,KAAK,4BAA4B;gBAC/B,IAAI,CAAC,QAAQ,CAAC,eAAe,IAAI,QAAQ,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;oBACxE,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;gBAChF,CAAC;gBACD,OAAO,QAAQ,CAAC;YAClB,KAAK,eAAe;gBAClB,IAAI,CAAC,QAAQ,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACvD,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;gBACnE,CAAC;gBACD,OAAO,QAAQ,CAAC;YAClB,KAAK,MAAM;gBACT,IAAI,CAAC,QAAQ,CAAC,WAAW,IAAI,CAAC,QAAQ,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;oBACpE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;gBAC1D,CAAC;gBACD,OAAO,QAAQ,CAAC;YAClB,KAAK,QAAQ;gBACX,qEAAqE;gBACrE,uEAAuE;gBACvE,OAAO,QAAQ,CAAC;QACpB,CAAC;IACH,CAAC;CACF"}
@@ -40,4 +40,48 @@ describe('ProposalMetadataCodec consume_notes v2 round-trip (issue #229)', () =>
40
40
  expect(back.notes).toBeUndefined();
41
41
  });
42
42
  });
43
+ describe('ProposalMetadataCodec custom proposal types (issue #266)', () => {
44
+ it('fromGuardian collapses an unmodeled type to the custom bucket, keeping the raw label', () => {
45
+ const wire = {
46
+ proposalType: 'b2agg',
47
+ description: 'agglayer bridge note',
48
+ };
49
+ const md = ProposalMetadataCodec.fromGuardian(wire);
50
+ expect(md.proposalType).toBe('custom');
51
+ expect(md.rawProposalType).toBe('b2agg');
52
+ });
53
+ it('toGuardian round-trips the raw label, not the custom bucket', () => {
54
+ const md = {
55
+ proposalType: 'custom',
56
+ description: 'agglayer bridge note',
57
+ rawProposalType: 'b2agg',
58
+ };
59
+ const wire = ProposalMetadataCodec.toGuardian(md);
60
+ expect(wire.proposalType).toBe('b2agg');
61
+ const back = ProposalMetadataCodec.fromGuardian(wire);
62
+ expect(back.proposalType).toBe('custom');
63
+ expect(back.rawProposalType).toBe('b2agg');
64
+ });
65
+ it('validate accepts a custom proposal', () => {
66
+ const md = {
67
+ proposalType: 'custom',
68
+ description: 'opaque',
69
+ rawProposalType: 'b2agg',
70
+ };
71
+ expect(ProposalMetadataCodec.validate(md)).toBe(md);
72
+ });
73
+ it('round-trips update_procedure_threshold through the codec', () => {
74
+ const wire = {
75
+ proposalType: 'update_procedure_threshold',
76
+ targetProcedure: 'send_asset',
77
+ targetThreshold: 2,
78
+ };
79
+ const md = ProposalMetadataCodec.fromGuardian(wire);
80
+ expect(md.proposalType).toBe('update_procedure_threshold');
81
+ const back = ProposalMetadataCodec.toGuardian(md);
82
+ expect(back.proposalType).toBe('update_procedure_threshold');
83
+ expect(back.targetProcedure).toBe('send_asset');
84
+ expect(back.targetThreshold).toBe(2);
85
+ });
86
+ });
43
87
  //# sourceMappingURL=metadata.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"metadata.test.js","sourceRoot":"","sources":["../../src/proposal/metadata.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAGtD,QAAQ,CAAC,gEAAgE,EAAE,GAAG,EAAE;IAC9E,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,EAAE,GAAiC;YACvC,YAAY,EAAE,eAAe;YAC7B,WAAW,EAAE,kBAAkB;YAC/B,OAAO,EAAE,CAAC,OAAO,CAAC;YAClB,eAAe,EAAE,CAAC;YAClB,KAAK,EAAE,CAAC,kBAAkB,CAAC;SAC5B,CAAC;QACF,MAAM,IAAI,GAAG,qBAAqB,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QACxC,MAAM,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,IAAI,GAA6B;YACrC,YAAY,EAAE,eAAe;YAC7B,OAAO,EAAE,CAAC,OAAO,CAAC;YAClB,2BAA2B,EAAE,CAAC;YAC9B,iBAAiB,EAAE,CAAC,kBAAkB,CAAC;SACxC,CAAC;QACF,MAAM,EAAE,GAAG,qBAAqB,CAAC,YAAY,CAAC,IAAI,CAAiC,CAAC;QACpF,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC9C,MAAM,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,EAAE,GAAiC;YACvC,YAAY,EAAE,eAAe;YAC7B,WAAW,EAAE,QAAQ;YACrB,OAAO,EAAE,CAAC,OAAO,CAAC;SACnB,CAAC;QACF,MAAM,IAAI,GAAG,qBAAqB,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC,aAAa,EAAE,CAAC;QACzD,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,aAAa,EAAE,CAAC;QAC/C,MAAM,IAAI,GAAG,qBAAqB,CAAC,YAAY,CAAC,IAAI,CAAiC,CAAC;QACtF,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,aAAa,EAAE,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,aAAa,EAAE,CAAC;IACrC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"metadata.test.js","sourceRoot":"","sources":["../../src/proposal/metadata.test.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,12 @@
1
- import type { ProposalSignature, ProposalType as GuardianProposalType, SignatureScheme } from '@openzeppelin/guardian-client';
1
+ import type { ProposalSignature, SignatureScheme } from '@openzeppelin/guardian-client';
2
2
  import type { ProcedureName } from '../procedures.js';
3
- export type ProposalType = Exclude<GuardianProposalType, 'custom'>;
3
+ /**
4
+ * Closed set of proposal types the multisig SDK models behaviorally, plus the
5
+ * `'custom'` bucket for any server-defined type the SDK does not model (issue
6
+ * #266). Defined explicitly (not derived from the now-arbitrary guardian-client
7
+ * wire union) so the exhaustive switches in the metadata codec stay sound.
8
+ */
9
+ export type ProposalType = 'add_signer' | 'remove_signer' | 'change_threshold' | 'update_procedure_threshold' | 'switch_guardian' | 'consume_notes' | 'p2id' | 'custom';
4
10
  export type ProposalStatus = 'pending' | 'ready' | 'finalized';
5
11
  export type TransactionProposalStatus = {
6
12
  type: 'pending';
@@ -61,10 +67,15 @@ export interface P2IdProposalMetadata extends BaseProposalMetadata {
61
67
  faucetId: string;
62
68
  amount: string;
63
69
  }
64
- export interface UnknownProposalMetadata extends BaseProposalMetadata {
65
- proposalType: 'unknown';
70
+ export interface CustomProposalMetadata extends BaseProposalMetadata {
71
+ proposalType: 'custom';
72
+ /** Original server-defined proposal label, e.g. "b2agg" (issue #266). Mirrors
73
+ * Rust `ProposalMetadata.proposal_type`; it is what lets a custom proposal
74
+ * round-trip back to GUARDIAN/export, so it is required in the domain model.
75
+ * Any wire-level optionality is resolved in the parser/codec boundary. */
76
+ rawProposalType: string;
66
77
  }
67
- export type ProposalMetadata = UpdateSignersProposalMetadata | SwitchGuardianProposalMetadata | UpdateProcedureThresholdProposalMetadata | ConsumeNotesProposalMetadata | P2IdProposalMetadata | UnknownProposalMetadata;
78
+ export type ProposalMetadata = UpdateSignersProposalMetadata | SwitchGuardianProposalMetadata | UpdateProcedureThresholdProposalMetadata | ConsumeNotesProposalMetadata | P2IdProposalMetadata | CustomProposalMetadata;
68
79
  export interface Proposal {
69
80
  id: string;
70
81
  accountId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"proposal.d.ts","sourceRoot":"","sources":["../../src/types/proposal.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"proposal.d.ts","sourceRoot":"","sources":["../../src/types/proposal.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"proposal.js","sourceRoot":"","sources":["../../src/types/proposal.ts"],"names":[],"mappings":"AAmDA,gFAAgF;AAChF,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAU,CAAC;AAE5D,iEAAiE;AACjE,MAAM,CAAC,MAAM,gCAAgC,GAAG,GAAG,GAAG,IAAI,CAAC;AAW3D,MAAM,UAAU,gBAAgB,CAAC,EAAgC;IAC/D,OAAO,EAAE,CAAC,eAAe,KAAK,iCAAiC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,EAAgC;IAC/D,OAAO,EAAE,CAAC,eAAe,KAAK,SAAS,IAAI,EAAE,CAAC,eAAe,KAAK,CAAC,CAAC;AACtE,CAAC"}
1
+ {"version":3,"file":"proposal.js","sourceRoot":"","sources":["../../src/types/proposal.ts"],"names":[],"mappings":"AA6DA,gFAAgF;AAChF,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAU,CAAC;AAE5D,iEAAiE;AACjE,MAAM,CAAC,MAAM,gCAAgC,GAAG,GAAG,GAAG,IAAI,CAAC;AAW3D,MAAM,UAAU,gBAAgB,CAAC,EAAgC;IAC/D,OAAO,EAAE,CAAC,eAAe,KAAK,iCAAiC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,EAAgC;IAC/D,OAAO,EAAE,CAAC,eAAe,KAAK,SAAS,IAAI,EAAE,CAAC,eAAe,KAAK,CAAC,CAAC;AACtE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openzeppelin/miden-multisig-client",
3
- "version": "0.14.8",
3
+ "version": "0.14.9-rc.0",
4
4
  "description": "TypeScript SDK for Miden multisig accounts with GUARDIAN integration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -30,7 +30,7 @@
30
30
  "@noble/curves": "^1.9.7",
31
31
  "@miden-sdk/miden-sdk": "0.14.5",
32
32
  "@noble/hashes": "^2.0.1",
33
- "@openzeppelin/guardian-client": "^0.14.8"
33
+ "@openzeppelin/guardian-client": "^0.14.9-rc.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "typescript": "^5.7.2",
package/src/multisig.ts CHANGED
@@ -13,13 +13,13 @@ import type {
13
13
  NoteAsset,
14
14
  Proposal,
15
15
  ProposalMetadata,
16
+ ProposalSignatureEntry,
16
17
  ProposalType,
17
18
  } from './types.js';
18
19
  import type { ProcedureName } from './procedures.js';
19
20
  import type {
20
21
  MidenClient,
21
22
  TransactionProver,
22
- TransactionRequest,
23
23
  WasmWebClient,
24
24
  } from '@miden-sdk/miden-sdk';
25
25
  import {
@@ -31,6 +31,7 @@ import {
31
31
  Note,
32
32
  RpcClient,
33
33
  Signature,
34
+ TransactionRequest,
34
35
  TransactionSummary,
35
36
  Word,
36
37
  } from '@miden-sdk/miden-sdk';
@@ -97,6 +98,32 @@ export interface AccountStateVerificationResult {
97
98
  /**
98
99
  * Represents a multisig account with GUARDIAN integration.
99
100
  */
101
+ const BUILTIN_PROPOSAL_TYPES = new Set<string>([
102
+ 'add_signer',
103
+ 'remove_signer',
104
+ 'change_threshold',
105
+ 'update_procedure_threshold',
106
+ 'switch_guardian',
107
+ 'consume_notes',
108
+ 'p2id',
109
+ // Reserved: the SDK's internal bucket name for unmodeled types. A producer
110
+ // must not use it as a custom label, or it would collide with the bucket.
111
+ 'custom',
112
+ ]);
113
+
114
+ /**
115
+ * Deserialize producer-supplied transaction request bytes, wrapping any failure
116
+ * in a stable message that mirrors the Rust SDK's `deserialize_transaction_request`.
117
+ */
118
+ function deserializeTransactionRequest(bytes: Uint8Array): TransactionRequest {
119
+ try {
120
+ return TransactionRequest.deserialize(bytes);
121
+ } catch (err) {
122
+ const detail = err instanceof Error ? err.message : String(err);
123
+ throw new Error(`failed to decode transaction request: ${detail}`);
124
+ }
125
+ }
126
+
100
127
  export class Multisig {
101
128
  account: Account;
102
129
  threshold: number;
@@ -977,6 +1004,220 @@ export class Multisig {
977
1004
  proposal.status = 'finalized';
978
1005
  }
979
1006
 
1007
+ /**
1008
+ * Submit an integration-built transaction (advice already injected). Mirrors
1009
+ * the Rust `submit_transaction`; used by the custom proposal producer flow
1010
+ * after `prepareCustomExecution` rebuilds its request with the returned advice.
1011
+ */
1012
+ async submitTransaction(request: TransactionRequest): Promise<void> {
1013
+ await this.midenClient.transactions.submit(AccountId.fromHex(this._accountId), request);
1014
+ }
1015
+
1016
+ /**
1017
+ * Create a proposal from a producer-built transaction the SDK does not model
1018
+ * (issue #266 producer API). `transactionRequestBytes` is a serialized TransactionRequest;
1019
+ * `proposalType` is a free-form, non-empty label that must not collide with a
1020
+ * built-in type. The integration keeps its own recipe to execute later via
1021
+ * `prepareCustomExecution`.
1022
+ */
1023
+ async createCustomProposal(
1024
+ transactionRequestBytes: Uint8Array,
1025
+ proposalType: string,
1026
+ nonce?: number,
1027
+ ): Promise<Proposal> {
1028
+ const label = proposalType.trim().toLowerCase();
1029
+ if (label.length === 0) {
1030
+ throw new Error('proposalType must not be empty');
1031
+ }
1032
+ if (!/^[a-z0-9_]+$/.test(label)) {
1033
+ throw new Error(
1034
+ `proposalType '${label}' must be lowercase snake_case ([a-z0-9_]): no spaces, hyphens, or other characters`,
1035
+ );
1036
+ }
1037
+ if (BUILTIN_PROPOSAL_TYPES.has(label)) {
1038
+ throw new Error(
1039
+ `'${label}' is a built-in proposal type; use the typed proposal API instead`,
1040
+ );
1041
+ }
1042
+
1043
+ const webClient = await this.getRawClient();
1044
+ const request = deserializeTransactionRequest(transactionRequestBytes);
1045
+ const summary = await executeForSummary(webClient, this._accountId, request);
1046
+ const summaryBase64 = uint8ArrayToBase64(summary.serialize());
1047
+ const proposalNonce = nonce ?? Date.now();
1048
+
1049
+ const metadata: ProposalMetadata = {
1050
+ proposalType: 'custom',
1051
+ description: '',
1052
+ rawProposalType: label,
1053
+ requiredSignatures: this.getEffectiveThreshold('custom'),
1054
+ };
1055
+
1056
+ return this.createProposal(proposalNonce, summaryBase64, metadata);
1057
+ }
1058
+
1059
+ /**
1060
+ * Assemble the validated execution advice (cosigner signatures + GUARDIAN
1061
+ * acknowledgment) for a ready custom proposal, so an integration can rebuild
1062
+ * its transaction with its own recipe and submit (issue #266 producer API).
1063
+ *
1064
+ * `transactionRequestBytes` is the serialized transaction request; it is used only to verify
1065
+ * (binding check) that it reproduces the signed commitment, before the
1066
+ * acknowledgment is requested. Returns the advice the integration folds into
1067
+ * its rebuilt transaction (`builder.extendAdviceMap(advice)`).
1068
+ */
1069
+ async prepareCustomExecution(
1070
+ proposalId: string,
1071
+ transactionRequestBytes: Uint8Array,
1072
+ ): Promise<AdviceMap> {
1073
+ const normalizedProposalId = normalizeHexWord(proposalId);
1074
+ const delta = await this.guardian.getDeltaProposal(this._accountId, normalizedProposalId);
1075
+ const existing = this.getLocalProposal(proposalId);
1076
+ const proposal = this.proposalFactory().fromDelta(
1077
+ delta,
1078
+ normalizedProposalId,
1079
+ existing?.metadata,
1080
+ existing?.signatures ?? [],
1081
+ );
1082
+
1083
+ if (proposal.metadata.proposalType !== 'custom') {
1084
+ throw new Error(
1085
+ 'prepareCustomExecution is only for custom proposals; use executeProposal for built-in types',
1086
+ );
1087
+ }
1088
+
1089
+ const effectiveThreshold = this.getEffectiveThreshold('custom');
1090
+ const signaturesForExecution = new ProposalSignatures(
1091
+ proposal.signatures,
1092
+ this.signerCommitments,
1093
+ `Invalid proposal signatures for ${proposalId}`,
1094
+ ).entries();
1095
+ if (signaturesForExecution.length < effectiveThreshold) {
1096
+ throw new Error(
1097
+ `Proposal is not ready for execution: have ${signaturesForExecution.length} of ${effectiveThreshold} required signatures.`,
1098
+ );
1099
+ }
1100
+
1101
+ const txSummary = TransactionSummary.deserialize(
1102
+ base64ToUint8Array(delta.deltaPayload.txSummary.data),
1103
+ );
1104
+ const signedCommitmentHex = normalizeHexWord(txSummary.toCommitment().toHex());
1105
+
1106
+ const bindingRequest = deserializeTransactionRequest(transactionRequestBytes);
1107
+
1108
+ const webClient = await this.getRawClient();
1109
+ const derived = await executeForSummary(webClient, this._accountId, bindingRequest);
1110
+ const derivedCommitmentHex = normalizeHexWord(derived.toCommitment().toHex());
1111
+ if (derivedCommitmentHex !== signedCommitmentHex) {
1112
+ throw new Error(
1113
+ `Custom proposal binding mismatch: expected ${signedCommitmentHex}, got ${derivedCommitmentHex}`,
1114
+ );
1115
+ }
1116
+
1117
+ return this.assembleCustomAdvice(
1118
+ proposalId,
1119
+ signaturesForExecution,
1120
+ signedCommitmentHex,
1121
+ delta,
1122
+ );
1123
+ }
1124
+
1125
+ private async assembleCustomAdvice(
1126
+ proposalId: string,
1127
+ signaturesForExecution: ProposalSignatureEntry[],
1128
+ normalizedTxCommitmentHex: string,
1129
+ delta: DeltaObject,
1130
+ ): Promise<AdviceMap> {
1131
+ const normalizedSignerCommitments = new Set(
1132
+ this.signerCommitments.map((commitment) => normalizeHexWord(commitment)),
1133
+ );
1134
+ const adviceMap = new AdviceMap();
1135
+ const adviceMapKeys = new Set<string>();
1136
+ const createTxCommitmentWord = (): Word => Word.fromHex(normalizedTxCommitmentHex);
1137
+
1138
+ for (const cosignerSig of signaturesForExecution) {
1139
+ let signerCommitmentHex = normalizeHexWord(cosignerSig.signerId);
1140
+ const ecdsaPublicKey =
1141
+ cosignerSig.signature.scheme === 'ecdsa' ? cosignerSig.signature.publicKey : undefined;
1142
+
1143
+ if (cosignerSig.signature.scheme === 'ecdsa') {
1144
+ if (!ecdsaPublicKey) {
1145
+ throw new Error(
1146
+ `ECDSA proposal signature for ${signerCommitmentHex} is missing publicKey`,
1147
+ );
1148
+ }
1149
+ const derivedCommitment = tryComputeEcdsaCommitmentHex(ecdsaPublicKey);
1150
+ if (derivedCommitment && derivedCommitment !== signerCommitmentHex) {
1151
+ if (!normalizedSignerCommitments.has(derivedCommitment)) {
1152
+ throw new Error(
1153
+ `ECDSA public key commitment mismatch: derived commitment ${derivedCommitment} is not in signerCommitments.`,
1154
+ );
1155
+ }
1156
+ signerCommitmentHex = derivedCommitment;
1157
+ }
1158
+ }
1159
+
1160
+ const signerCommitment = Word.fromHex(signerCommitmentHex);
1161
+ const sigBytes = signatureHexToBytes(
1162
+ cosignerSig.signature.signature,
1163
+ cosignerSig.signature.scheme,
1164
+ );
1165
+ const signature = Signature.deserialize(sigBytes);
1166
+ const { key, values } = buildSignatureAdviceEntry(
1167
+ signerCommitment,
1168
+ createTxCommitmentWord(),
1169
+ signature,
1170
+ ecdsaPublicKey,
1171
+ cosignerSig.signature.scheme === 'ecdsa' ? cosignerSig.signature.signature : undefined,
1172
+ );
1173
+ const keyHex = normalizeHexWord(key.toHex());
1174
+ if (adviceMapKeys.has(keyHex)) {
1175
+ throw new Error(`Duplicate advice-map key detected for proposal ${proposalId}`);
1176
+ }
1177
+ adviceMapKeys.add(keyHex);
1178
+ adviceMap.insert(key, new FeltArray(values));
1179
+ }
1180
+
1181
+ const executionDelta = { ...delta, deltaPayload: delta.deltaPayload.txSummary };
1182
+ const pushResult = await this.guardian.pushDelta(executionDelta);
1183
+ const ackSigHex = pushResult.ackSig;
1184
+ if (!ackSigHex) {
1185
+ throw new Error('GUARDIAN did not return acknowledgment signature');
1186
+ }
1187
+
1188
+ const guardianCommitment = Word.fromHex(normalizeHexWord(this.guardianCommitment));
1189
+ const ackScheme = (pushResult.ackScheme as 'ecdsa' | 'falcon') || this.signer.scheme;
1190
+ const ackPubkey = pushResult.ackPubkey || this.guardianPublicKey;
1191
+ if (ackScheme === 'ecdsa' && !ackPubkey) {
1192
+ throw new Error('GUARDIAN acknowledgment is missing ECDSA public key');
1193
+ }
1194
+ if (ackScheme === 'ecdsa' && ackPubkey) {
1195
+ const derivedCommitment = tryComputeEcdsaCommitmentHex(ackPubkey);
1196
+ if (derivedCommitment && derivedCommitment !== normalizeHexWord(this.guardianCommitment)) {
1197
+ throw new Error('GUARDIAN public key commitment mismatch');
1198
+ }
1199
+ }
1200
+ const ackSigBytes = signatureHexToBytes(ackSigHex, ackScheme);
1201
+ const ackSignature = Signature.deserialize(ackSigBytes);
1202
+ const { key: ackKey, values: ackValues } = buildSignatureAdviceEntry(
1203
+ guardianCommitment,
1204
+ createTxCommitmentWord(),
1205
+ ackSignature,
1206
+ ackScheme === 'ecdsa' ? ackPubkey : undefined,
1207
+ ackScheme === 'ecdsa' ? ackSigHex : undefined,
1208
+ );
1209
+ const ackKeyHex = normalizeHexWord(ackKey.toHex());
1210
+ if (adviceMapKeys.has(ackKeyHex)) {
1211
+ throw new Error(
1212
+ `Duplicate advice-map key detected for GUARDIAN acknowledgment in proposal ${proposalId}`,
1213
+ );
1214
+ }
1215
+ adviceMapKeys.add(ackKeyHex);
1216
+ adviceMap.insert(ackKey, new FeltArray(ackValues));
1217
+
1218
+ return adviceMap;
1219
+ }
1220
+
980
1221
  private getLocalProposal(proposalId: string): Proposal | undefined {
981
1222
  const normalizedProposalId = normalizeHexWord(proposalId);
982
1223
  return this.proposals.get(proposalId) ?? this.proposals.get(normalizedProposalId);
@@ -994,6 +1235,15 @@ export class Multisig {
994
1235
  await this.verifyProposalMetadataBinding(proposal);
995
1236
 
996
1237
  const metadata = proposal.metadata;
1238
+ // Reject custom proposals before any advice assembly or GUARDIAN ack push:
1239
+ // the SDK cannot rebuild an opaque custom transaction, and the rejection
1240
+ // must stay side-effect free (mirrors the Rust early guard in execute_proposal).
1241
+ if (metadata.proposalType === 'custom') {
1242
+ throw new Error(
1243
+ 'Cannot execute a custom proposal via executeProposal; use prepareCustomExecution to ' +
1244
+ 'get the cosigner + GUARDIAN advice, then submitTransaction with your rebuilt request (issue #266).',
1245
+ );
1246
+ }
997
1247
  const effectiveThreshold = this.getEffectiveThreshold(metadata.proposalType);
998
1248
  const signatureContext = `Invalid proposal signatures for ${proposalId}`;
999
1249
  const signaturesForExecution = new ProposalSignatures(
@@ -1299,8 +1549,11 @@ export class Multisig {
1299
1549
 
1300
1550
  private async verifyProposalMetadataBinding(proposal: Proposal): Promise<string> {
1301
1551
  const txSummaryCommitment = this.ensureProposalCommitmentMatchesSummary(proposal);
1302
- if (proposal.metadata.proposalType === 'unknown') {
1303
- throw new Error(`Cannot verify proposal metadata for unknown proposal type: ${proposal.id}`);
1552
+ if (proposal.metadata.proposalType === 'custom') {
1553
+ // Custom proposals (issue #266) have no per-type reconstruction recipe;
1554
+ // the id ↔ tx_summary commitment match above is the only available
1555
+ // integrity guarantee for an opaque proposal.
1556
+ return txSummaryCommitment;
1304
1557
  }
1305
1558
 
1306
1559
  const summary = TransactionSummary.deserialize(base64ToUint8Array(proposal.txSummary));
@@ -1410,8 +1663,10 @@ export class Multisig {
1410
1663
  );
1411
1664
  return request;
1412
1665
  }
1413
- case 'unknown':
1414
- throw new Error('Unsupported proposal type: unknown');
1666
+ case 'custom':
1667
+ throw new Error(
1668
+ `Cannot build a transaction for a custom proposal type: ${metadata.rawProposalType ?? 'custom'}`,
1669
+ );
1415
1670
  }
1416
1671
  }
1417
1672
 
@@ -1,7 +1,10 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
  import type { ProposalMetadata as GuardianProposalMetadata } from '@openzeppelin/guardian-client';
3
3
  import { ProposalMetadataCodec } from './metadata.js';
4
- import type { ConsumeNotesProposalMetadata } from '../types/proposal.js';
4
+ import type {
5
+ ConsumeNotesProposalMetadata,
6
+ CustomProposalMetadata,
7
+ } from '../types/proposal.js';
5
8
 
6
9
  describe('ProposalMetadataCodec consume_notes v2 round-trip (issue #229)', () => {
7
10
  it('toGuardian threads metadataVersion and notes to the wire', () => {
@@ -45,3 +48,52 @@ describe('ProposalMetadataCodec consume_notes v2 round-trip (issue #229)', () =>
45
48
  expect(back.notes).toBeUndefined();
46
49
  });
47
50
  });
51
+
52
+ describe('ProposalMetadataCodec custom proposal types (issue #266)', () => {
53
+ it('fromGuardian collapses an unmodeled type to the custom bucket, keeping the raw label', () => {
54
+ const wire: GuardianProposalMetadata = {
55
+ proposalType: 'b2agg',
56
+ description: 'agglayer bridge note',
57
+ };
58
+ const md = ProposalMetadataCodec.fromGuardian(wire) as CustomProposalMetadata;
59
+ expect(md.proposalType).toBe('custom');
60
+ expect(md.rawProposalType).toBe('b2agg');
61
+ });
62
+
63
+ it('toGuardian round-trips the raw label, not the custom bucket', () => {
64
+ const md: CustomProposalMetadata = {
65
+ proposalType: 'custom',
66
+ description: 'agglayer bridge note',
67
+ rawProposalType: 'b2agg',
68
+ };
69
+ const wire = ProposalMetadataCodec.toGuardian(md);
70
+ expect(wire.proposalType).toBe('b2agg');
71
+
72
+ const back = ProposalMetadataCodec.fromGuardian(wire) as CustomProposalMetadata;
73
+ expect(back.proposalType).toBe('custom');
74
+ expect(back.rawProposalType).toBe('b2agg');
75
+ });
76
+
77
+ it('validate accepts a custom proposal', () => {
78
+ const md: CustomProposalMetadata = {
79
+ proposalType: 'custom',
80
+ description: 'opaque',
81
+ rawProposalType: 'b2agg',
82
+ };
83
+ expect(ProposalMetadataCodec.validate(md)).toBe(md);
84
+ });
85
+
86
+ it('round-trips update_procedure_threshold through the codec', () => {
87
+ const wire: GuardianProposalMetadata = {
88
+ proposalType: 'update_procedure_threshold',
89
+ targetProcedure: 'send_asset',
90
+ targetThreshold: 2,
91
+ };
92
+ const md = ProposalMetadataCodec.fromGuardian(wire);
93
+ expect(md.proposalType).toBe('update_procedure_threshold');
94
+ const back = ProposalMetadataCodec.toGuardian(md);
95
+ expect(back.proposalType).toBe('update_procedure_threshold');
96
+ expect(back.targetProcedure).toBe('send_asset');
97
+ expect(back.targetThreshold).toBe(2);
98
+ });
99
+ });
@@ -48,8 +48,9 @@ export class ProposalMetadataCodec {
48
48
  targetThreshold: metadata.targetThreshold,
49
49
  signerCommitments: metadata.targetSignerCommitments,
50
50
  };
51
- case 'unknown':
52
- return base;
51
+ case 'custom':
52
+ // Round-trip the original server label rather than the 'custom' bucket.
53
+ return { ...base, proposalType: metadata.rawProposalType };
53
54
  }
54
55
  }
55
56
 
@@ -120,12 +121,18 @@ export class ProposalMetadataCodec {
120
121
  }
121
122
  return {
122
123
  ...base,
123
- proposalType: guardian.proposalType,
124
+ proposalType: guardian.proposalType as 'add_signer' | 'remove_signer' | 'change_threshold',
124
125
  targetThreshold: guardian.targetThreshold,
125
126
  targetSignerCommitments: guardian.signerCommitments,
126
127
  };
127
128
  default:
128
- throw new Error(`Unsupported proposal type: ${guardian.proposalType as string}`);
129
+ // Any proposal type the SDK does not model collapses to the 'custom'
130
+ // bucket while preserving the original label (issue #266).
131
+ return {
132
+ ...base,
133
+ proposalType: 'custom',
134
+ rawProposalType: guardian.proposalType,
135
+ };
129
136
  }
130
137
  }
131
138
 
@@ -162,8 +169,10 @@ export class ProposalMetadataCodec {
162
169
  throw new Error('p2id proposal metadata is incomplete');
163
170
  }
164
171
  return metadata;
165
- case 'unknown':
166
- throw new Error('unknown proposal type is not supported');
172
+ case 'custom':
173
+ // Custom proposals are opaque to the SDK; nothing to validate beyond
174
+ // the base fields. They can be listed/signed/exported, not built here.
175
+ return metadata;
167
176
  }
168
177
  }
169
178
  }