@epoch-protocol/epoch-commons-sdk 0.1.18 → 0.1.20

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.
@@ -70,7 +70,7 @@
70
70
  "contractAddress": {
71
71
  "Base": "0x2BB4FfD7E2c6D432b697554Efd77fA13bdbefd69",
72
72
  "Mainnet": "0x2BB4FfD7E2c6D432b697554Efd77fA13bdbefd69",
73
- "Miden": "0xfc90f0f4da30e51168453b60eafed7",
73
+ "Miden": "0x537c15a622074e91188aa894456c52",
74
74
  "Optimism": "0x2BB4FfD7E2c6D432b697554Efd77fA13bdbefd69"
75
75
  },
76
76
  "decimals": 18,
@@ -83,7 +83,7 @@
83
83
  "Base": "0xc30f1Ce05d1434d484E9A47283aA925fc8A8699a",
84
84
  "Mainnet": "0xc30f1Ce05d1434d484E9A47283aA925fc8A8699a",
85
85
  "Optimism": "0xc30f1Ce05d1434d484E9A47283aA925fc8A8699a",
86
- "Miden": "0x176275876f2fd41103257e341832b9"
86
+ "Miden": "0xd17976f0809a8191412f2a126625df"
87
87
  },
88
88
  "decimals": 18,
89
89
  "decimalsByChain": {
@@ -94,7 +94,7 @@
94
94
  "contractAddress": {
95
95
  "Base": "0xc04d2869665Be874881133943523723Be5782720",
96
96
  "Mainnet": "0xc04d2869665Be874881133943523723Be5782720",
97
- "Miden": "0x7725b0e9bb9406912d2ebeaeb05f4d",
97
+ "Miden": "0x6500ca8c2dd69e9147ab7eafad162c",
98
98
  "Optimism": "0xc04d2869665Be874881133943523723Be5782720"
99
99
  },
100
100
  "decimals": 18,
@@ -107,7 +107,7 @@
107
107
  "Base": "0x7946dd86eE310D0aC16804A37787289Fa5b88A8A",
108
108
  "Mainnet": "0x7946dd86eE310D0aC16804A37787289Fa5b88A8A",
109
109
  "Optimism": "0x7946dd86eE310D0aC16804A37787289Fa5b88A8A",
110
- "Miden": "0xa54717f6bd3210d128aeeaa8a2b7f3"
110
+ "Miden": "0x4a09f13153d9cd114c078bfb62a7ec"
111
111
  },
112
112
  "decimals": 18,
113
113
  "decimalsByChain": {
@@ -119,7 +119,7 @@
119
119
  "Base": "0x9b2a2754a9182fD65360E23afCDf3BeFF51796E9",
120
120
  "Mainnet": "0x9b2a2754a9182fD65360E23afCDf3BeFF51796E9",
121
121
  "Optimism": "0x9b2a2754a9182fD65360E23afCDf3BeFF51796E9",
122
- "Miden": "0x151823cde4b7bd91352617729d7614"
122
+ "Miden": "0x5fd2e6fd17712c51404d09c2b847f7"
123
123
  },
124
124
  "decimals": 18,
125
125
  "decimalsByChain": {
@@ -279,6 +279,11 @@ export type QueuedTransactionForIntent = {
279
279
  transactionHash: string | null;
280
280
  /** EIP-7702 authorization list for relay txs (enable delegation). */
281
281
  authorizationList?: Record<string, unknown>[];
282
+ /**
283
+ * Compact / Miden reservation keys so the queue worker can finalize those
284
+ * reservations on SUCCESS or FAILED.
285
+ */
286
+ processedCompactKeys?: string[];
282
287
  };
283
288
  export type CompactDataForQueue = {
284
289
  intentNonce: string;
@@ -1,3 +1,4 @@
1
1
  export * from "./compact.js";
2
+ export * from "./miden-intent.js";
2
3
  export * from "./path.js";
3
4
  export * from "./witness-typestring.js";
@@ -1,3 +1,4 @@
1
1
  export * from "./compact.js";
2
+ export * from "./miden-intent.js";
2
3
  export * from "./path.js";
3
4
  export * from "./witness-typestring.js";
@@ -0,0 +1,29 @@
1
+ import { EVM_TO_MIDEN_EXTRA_TYPESTRING, EVM_TO_MIDEN_FIELD_NAMES, MIDEN_MARKER_FIELD_NAMES, MIDEN_TO_EVM_EXTRA_TYPESTRING, MIDEN_TO_EVM_FIELD_NAMES } from "./witness-typestring.js";
2
+ export type MidenBridgeDirection = "miden-to-evm" | "evm-to-miden";
3
+ export interface MidenExtraData {
4
+ midenSourceAccount?: string;
5
+ midenRecipientAccount?: string;
6
+ midenFaucetId?: string;
7
+ midenNoteType?: string;
8
+ midenNoteId?: string;
9
+ }
10
+ /** True when mandate/extra data carries any Miden bridge marker field. */
11
+ export declare function hasMidenMarkersInData(data?: Record<string, unknown>): boolean;
12
+ /** True when a witness/extra typestring declares any Miden bridge marker field. */
13
+ export declare function hasMidenMarkersInTypestring(typestring: string): boolean;
14
+ /**
15
+ * True iff the intent is a Miden bridge intent (markers in typestring and/or data).
16
+ * At least one of `typestring` or `data` must be provided.
17
+ */
18
+ export declare function isMidenIntent(typestring?: string, data?: Record<string, unknown>): boolean;
19
+ /** EVM→Miden: recipient set, source account absent. */
20
+ export declare function isEVMToMidenIntent(data?: Record<string, unknown>): boolean;
21
+ /** Miden→EVM: source account set. */
22
+ export declare function isMidenToEvmIntent(data?: Record<string, unknown>): boolean;
23
+ export declare function hasMidenToEvmTypestringMarker(typestring: string): boolean;
24
+ export declare function hasEvmToMidenTypestringMarker(typestring: string): boolean;
25
+ export declare function resolveMidenBridgeDirection(typestring: string, data?: Record<string, unknown>): MidenBridgeDirection | "conflict" | null;
26
+ export declare function getMidenMetadata(data?: Record<string, unknown>, typestring?: string): MidenExtraData | null;
27
+ /** @deprecated Use {@link isEVMToMidenIntent}. */
28
+ export declare function isEVMToMidenMandate(mandate?: Record<string, unknown>): boolean;
29
+ export { EVM_TO_MIDEN_EXTRA_TYPESTRING, EVM_TO_MIDEN_FIELD_NAMES, MIDEN_TO_EVM_EXTRA_TYPESTRING, MIDEN_TO_EVM_FIELD_NAMES, MIDEN_MARKER_FIELD_NAMES, };
@@ -0,0 +1,79 @@
1
+ import { EVM_TO_MIDEN_EXTRA_TYPESTRING, EVM_TO_MIDEN_FIELD_NAMES, MIDEN_MARKER_FIELD_NAMES, MIDEN_RECIPIENT_ACCOUNT_FIELD_NAME, MIDEN_SOURCE_ACCOUNT_FIELD_NAME, MIDEN_TO_EVM_EXTRA_TYPESTRING, MIDEN_TO_EVM_FIELD_NAMES, } from "./witness-typestring.js";
2
+ function hasTruthyField(data, fieldName) {
3
+ return data[fieldName] != null && data[fieldName] !== "";
4
+ }
5
+ /** True when mandate/extra data carries any Miden bridge marker field. */
6
+ export function hasMidenMarkersInData(data) {
7
+ if (!data)
8
+ return false;
9
+ return MIDEN_MARKER_FIELD_NAMES.some((name) => hasTruthyField(data, name));
10
+ }
11
+ /** True when a witness/extra typestring declares any Miden bridge marker field. */
12
+ export function hasMidenMarkersInTypestring(typestring) {
13
+ if (!typestring.trim())
14
+ return false;
15
+ return MIDEN_MARKER_FIELD_NAMES.some((name) => typestring.includes(name));
16
+ }
17
+ /**
18
+ * True iff the intent is a Miden bridge intent (markers in typestring and/or data).
19
+ * At least one of `typestring` or `data` must be provided.
20
+ */
21
+ export function isMidenIntent(typestring, data) {
22
+ const hasTypestring = typestring !== undefined && typestring.trim() !== "";
23
+ const hasData = data !== undefined;
24
+ if (!hasTypestring && !hasData) {
25
+ throw new Error("isMidenIntent requires typestring and/or data");
26
+ }
27
+ return ((hasTypestring && hasMidenMarkersInTypestring(typestring)) ||
28
+ hasMidenMarkersInData(data));
29
+ }
30
+ /** EVM→Miden: recipient set, source account absent. */
31
+ export function isEVMToMidenIntent(data) {
32
+ if (!data)
33
+ return false;
34
+ return (hasTruthyField(data, MIDEN_RECIPIENT_ACCOUNT_FIELD_NAME) &&
35
+ !hasTruthyField(data, MIDEN_SOURCE_ACCOUNT_FIELD_NAME));
36
+ }
37
+ /** Miden→EVM: source account set. */
38
+ export function isMidenToEvmIntent(data) {
39
+ if (!data)
40
+ return false;
41
+ return hasTruthyField(data, MIDEN_SOURCE_ACCOUNT_FIELD_NAME);
42
+ }
43
+ export function hasMidenToEvmTypestringMarker(typestring) {
44
+ return typestring.includes(MIDEN_SOURCE_ACCOUNT_FIELD_NAME);
45
+ }
46
+ export function hasEvmToMidenTypestringMarker(typestring) {
47
+ return typestring.includes(MIDEN_RECIPIENT_ACCOUNT_FIELD_NAME);
48
+ }
49
+ export function resolveMidenBridgeDirection(typestring, data) {
50
+ const toEvm = isMidenToEvmIntent(data) || hasMidenToEvmTypestringMarker(typestring);
51
+ const fromEvm = isEVMToMidenIntent(data) || hasEvmToMidenTypestringMarker(typestring);
52
+ if (toEvm && fromEvm)
53
+ return "conflict";
54
+ if (toEvm)
55
+ return "miden-to-evm";
56
+ if (fromEvm)
57
+ return "evm-to-miden";
58
+ // Match server routing: other Miden markers without a recipient use collateral path.
59
+ if (isMidenIntent(typestring, data))
60
+ return "miden-to-evm";
61
+ return null;
62
+ }
63
+ export function getMidenMetadata(data, typestring) {
64
+ if (!data || !isMidenIntent(typestring, data))
65
+ return null;
66
+ return {
67
+ midenSourceAccount: data?.midenSourceAccount,
68
+ midenRecipientAccount: data?.midenRecipientAccount,
69
+ midenFaucetId: data?.midenFaucetId,
70
+ midenNoteType: data?.midenNoteType,
71
+ midenNoteId: data?.midenNoteId,
72
+ };
73
+ }
74
+ /** @deprecated Use {@link isEVMToMidenIntent}. */
75
+ export function isEVMToMidenMandate(mandate) {
76
+ return isEVMToMidenIntent(mandate);
77
+ }
78
+ // Re-export canonical typestrings for callers that import direction helpers here.
79
+ export { EVM_TO_MIDEN_EXTRA_TYPESTRING, EVM_TO_MIDEN_FIELD_NAMES, MIDEN_TO_EVM_EXTRA_TYPESTRING, MIDEN_TO_EVM_FIELD_NAMES, MIDEN_MARKER_FIELD_NAMES, };
@@ -1,35 +1,53 @@
1
+ export declare function parseWitnessTypestringFields(witnessTypeString: string): Array<{
2
+ type: string;
3
+ name: string;
4
+ }>;
5
+ export declare function fieldNamesFromExtraTypestring(typestring: string): string[];
1
6
  /** Base Mandate fields shared by all Epoch intents (matches `getTaskData`). */
2
7
  export declare const BASE_MANDATE_TYPESTRING = "address tokenIn,uint256 tokenInAmount,address tokenOut,uint256 minTokenOut,uint256 destinationChainId,bytes4 taskType,bytes32 protocolHashIdentifier,address recipient";
3
8
  /** Miden → EVM cross-chain bridge witness suffix (canonical). */
4
9
  export declare const MIDEN_TO_EVM_EXTRA_TYPESTRING = "string midenSourceAccount,string midenFaucetId,string midenNoteType,string midenNoteId";
5
10
  /** EVM → Miden cross-chain bridge witness suffix (canonical). */
6
11
  export declare const EVM_TO_MIDEN_EXTRA_TYPESTRING = "string midenRecipientAccount,string midenFaucetId";
7
- export declare const MIDEN_TO_EVM_WITNESS_TYPESTRING = "address tokenIn,uint256 tokenInAmount,address tokenOut,uint256 minTokenOut,uint256 destinationChainId,bytes4 taskType,bytes32 protocolHashIdentifier,address recipient,string midenSourceAccount,string midenFaucetId,string midenNoteType,string midenNoteId";
8
- export declare const EVM_TO_MIDEN_WITNESS_TYPESTRING = "address tokenIn,uint256 tokenInAmount,address tokenOut,uint256 minTokenOut,uint256 destinationChainId,bytes4 taskType,bytes32 protocolHashIdentifier,address recipient,string midenRecipientAccount,string midenFaucetId";
12
+ /** Lending DEPOSIT extraData suffix (canonical). */
13
+ export declare const DEPOSIT_EXTRADATA_TYPESTRING = "string marketUid,string action,string payAsset,bool isAll";
14
+ /** Lending WITHDRAW extraData suffix (canonical). */
15
+ export declare const WITHDRAW_EXTRADATA_TYPESTRING = "string marketUid,string action,string payAsset,bool isAll";
16
+ export declare const MIDEN_TO_EVM_FIELD_NAMES: string[];
17
+ export declare const EVM_TO_MIDEN_FIELD_NAMES: string[];
18
+ /** Union of all Miden bridge field names from the canonical extra typestrings. */
19
+ export declare const MIDEN_MARKER_FIELD_NAMES: string[];
20
+ /** Fields declared only on the Miden→EVM extra typestring. */
21
+ export declare const MIDEN_TO_EVM_ONLY_FIELD_NAMES: string[];
22
+ /** Fields declared only on the EVM→Miden extra typestring. */
23
+ export declare const EVM_TO_MIDEN_ONLY_FIELD_NAMES: string[];
24
+ export declare const MIDEN_SOURCE_ACCOUNT_FIELD_NAME: "midenSourceAccount";
25
+ export declare const MIDEN_RECIPIENT_ACCOUNT_FIELD_NAME: "midenRecipientAccount";
9
26
  export type WitnessTypestringValidationResult = {
10
27
  valid: true;
11
28
  } | {
12
29
  valid: false;
13
30
  error: string;
14
31
  };
15
- export declare function parseWitnessTypestringFields(witnessTypeString: string): Array<{
16
- type: string;
17
- name: string;
18
- }>;
19
32
  /**
20
- * When a mandate includes Miden bridge metadata, the witness typestring must end
21
- * with the canonical Miden suffix. Direction (Miden→EVM vs EVM→Miden) is inferred
22
- * from the mandate and from the typestring's own suffix.
33
+ * Validate Miden `extraDataTypestring` / `extraData` for task assembly.
34
+ * Required Miden fields must be declared and present; additional protocol fields
35
+ * are allowed when declared in the typestring.
36
+ */
37
+ export declare function validateMidenExtraFields(extraDataTypestring: string, extraData?: Record<string, unknown>): WitnessTypestringValidationResult;
38
+ /**
39
+ * When a mandate includes Miden bridge metadata, the witness typestring must
40
+ * declare every required Miden field for the detected direction. Additional
41
+ * non-Miden fields may appear before or after the Miden fields.
23
42
  *
24
43
  * No-op for pure EVM intents — Miden checks run only when {@link isMidenIntent}.
25
44
  */
26
- export declare function assertMidenWitnessSuffix(witnessTypeString: string, mandate?: Record<string, unknown>): WitnessTypestringValidationResult;
27
- export declare function isEVMToMidenMandate(mandate?: Record<string, unknown>): boolean;
45
+ export declare function assertMidenWitnessFields(witnessTypeString: string, mandate?: Record<string, unknown>): WitnessTypestringValidationResult;
28
46
  /**
29
47
  * Validates a witness typestring against its data. Handles both the full witness
30
48
  * (base mandate + extra suffix) and a bare extra suffix — the same rules apply to
31
49
  * either: comma spacing, "type name" format, key alignment with the data object,
32
- * and the canonical Miden bridge suffix. Pass the assembled typestring and the
50
+ * and required Miden bridge fields when applicable. Pass the assembled typestring and the
33
51
  * merged data; there is no separate extra-suffix validator.
34
52
  */
35
53
  export declare function validateWitnessTypeString(witnessTypeString: string, mandate?: Record<string, unknown>): WitnessTypestringValidationResult;
@@ -1,11 +1,4 @@
1
- /** Base Mandate fields shared by all Epoch intents (matches `getTaskData`). */
2
- export const BASE_MANDATE_TYPESTRING = "address tokenIn,uint256 tokenInAmount,address tokenOut,uint256 minTokenOut,uint256 destinationChainId,bytes4 taskType,bytes32 protocolHashIdentifier,address recipient";
3
- /** Miden → EVM cross-chain bridge witness suffix (canonical). */
4
- export const MIDEN_TO_EVM_EXTRA_TYPESTRING = "string midenSourceAccount,string midenFaucetId,string midenNoteType,string midenNoteId";
5
- /** EVM → Miden cross-chain bridge witness suffix (canonical). */
6
- export const EVM_TO_MIDEN_EXTRA_TYPESTRING = "string midenRecipientAccount,string midenFaucetId";
7
- export const MIDEN_TO_EVM_WITNESS_TYPESTRING = `${BASE_MANDATE_TYPESTRING},${MIDEN_TO_EVM_EXTRA_TYPESTRING}`;
8
- export const EVM_TO_MIDEN_WITNESS_TYPESTRING = `${BASE_MANDATE_TYPESTRING},${EVM_TO_MIDEN_EXTRA_TYPESTRING}`;
1
+ import { isMidenIntent, resolveMidenBridgeDirection } from "./miden-intent.js";
9
2
  export function parseWitnessTypestringFields(witnessTypeString) {
10
3
  return witnessTypeString
11
4
  .split(",")
@@ -21,6 +14,41 @@ export function parseWitnessTypestringFields(witnessTypeString) {
21
14
  return { type, name };
22
15
  });
23
16
  }
17
+ export function fieldNamesFromExtraTypestring(typestring) {
18
+ return parseWitnessTypestringFields(typestring).map((field) => field.name);
19
+ }
20
+ function uniqueFieldNames(typestrings) {
21
+ return [
22
+ ...new Set(typestrings.flatMap((typestring) => fieldNamesFromExtraTypestring(typestring))),
23
+ ];
24
+ }
25
+ function onlyInLeft(left, right) {
26
+ const rightSet = new Set(right);
27
+ return left.filter((name) => !rightSet.has(name));
28
+ }
29
+ /** Base Mandate fields shared by all Epoch intents (matches `getTaskData`). */
30
+ export const BASE_MANDATE_TYPESTRING = "address tokenIn,uint256 tokenInAmount,address tokenOut,uint256 minTokenOut,uint256 destinationChainId,bytes4 taskType,bytes32 protocolHashIdentifier,address recipient";
31
+ /** Miden → EVM cross-chain bridge witness suffix (canonical). */
32
+ export const MIDEN_TO_EVM_EXTRA_TYPESTRING = "string midenSourceAccount,string midenFaucetId,string midenNoteType,string midenNoteId";
33
+ /** EVM → Miden cross-chain bridge witness suffix (canonical). */
34
+ export const EVM_TO_MIDEN_EXTRA_TYPESTRING = "string midenRecipientAccount,string midenFaucetId";
35
+ /** Lending DEPOSIT extraData suffix (canonical). */
36
+ export const DEPOSIT_EXTRADATA_TYPESTRING = "string marketUid,string action,string payAsset,bool isAll";
37
+ /** Lending WITHDRAW extraData suffix (canonical). */
38
+ export const WITHDRAW_EXTRADATA_TYPESTRING = "string marketUid,string action,string payAsset,bool isAll";
39
+ export const MIDEN_TO_EVM_FIELD_NAMES = fieldNamesFromExtraTypestring(MIDEN_TO_EVM_EXTRA_TYPESTRING);
40
+ export const EVM_TO_MIDEN_FIELD_NAMES = fieldNamesFromExtraTypestring(EVM_TO_MIDEN_EXTRA_TYPESTRING);
41
+ /** Union of all Miden bridge field names from the canonical extra typestrings. */
42
+ export const MIDEN_MARKER_FIELD_NAMES = uniqueFieldNames([
43
+ MIDEN_TO_EVM_EXTRA_TYPESTRING,
44
+ EVM_TO_MIDEN_EXTRA_TYPESTRING,
45
+ ]);
46
+ /** Fields declared only on the Miden→EVM extra typestring. */
47
+ export const MIDEN_TO_EVM_ONLY_FIELD_NAMES = onlyInLeft(MIDEN_TO_EVM_FIELD_NAMES, EVM_TO_MIDEN_FIELD_NAMES);
48
+ /** Fields declared only on the EVM→Miden extra typestring. */
49
+ export const EVM_TO_MIDEN_ONLY_FIELD_NAMES = onlyInLeft(EVM_TO_MIDEN_FIELD_NAMES, MIDEN_TO_EVM_FIELD_NAMES);
50
+ export const MIDEN_SOURCE_ACCOUNT_FIELD_NAME = MIDEN_TO_EVM_ONLY_FIELD_NAMES.find((name) => name === "midenSourceAccount");
51
+ export const MIDEN_RECIPIENT_ACCOUNT_FIELD_NAME = EVM_TO_MIDEN_ONLY_FIELD_NAMES.find((name) => name === "midenRecipientAccount");
24
52
  function assertNoSpacesAroundCommas(typestring) {
25
53
  if (/,(\s)|(\s),/.test(typestring)) {
26
54
  return {
@@ -63,70 +91,113 @@ function assertKeysMatchTypestring(typestring, data, dataLabel = "extraData", ty
63
91
  }
64
92
  return { valid: true };
65
93
  }
66
- function isEvmToMidenMandate(mandate) {
67
- return (mandate?.midenRecipientAccount != null &&
68
- mandate?.midenSourceAccount == null);
94
+ function requiredMidenExtraTypestring(direction) {
95
+ return direction === "miden-to-evm"
96
+ ? MIDEN_TO_EVM_EXTRA_TYPESTRING
97
+ : EVM_TO_MIDEN_EXTRA_TYPESTRING;
69
98
  }
70
- function isMidenToEvmMandate(mandate) {
71
- return mandate?.midenSourceAccount != null;
99
+ function assertMidenFieldsDeclared(typestring, requiredExtraTypestring, typestringLabel) {
100
+ const requiredFields = parseWitnessTypestringFields(requiredExtraTypestring);
101
+ let declaredFields;
102
+ try {
103
+ declaredFields = parseWitnessTypestringFields(typestring);
104
+ }
105
+ catch (error) {
106
+ return {
107
+ valid: false,
108
+ error: error instanceof Error
109
+ ? error.message
110
+ : "Invalid witnessTypeString format",
111
+ };
112
+ }
113
+ const declaredByName = new Map(declaredFields.map((field) => [field.name, field.type]));
114
+ for (const field of requiredFields) {
115
+ const declaredType = declaredByName.get(field.name);
116
+ if (declaredType === undefined) {
117
+ return {
118
+ valid: false,
119
+ error: `${typestringLabel} is missing required Miden field "${field.type} ${field.name}"`,
120
+ };
121
+ }
122
+ if (declaredType !== field.type) {
123
+ return {
124
+ valid: false,
125
+ error: `${typestringLabel} field "${field.name}" must be type "${field.type}", got "${declaredType}"`,
126
+ };
127
+ }
128
+ }
129
+ return { valid: true };
72
130
  }
73
- function hasMidenToEvmSuffix(witnessTypeString) {
74
- return witnessTypeString.includes("midenSourceAccount");
131
+ function assertMidenFieldsPresentInData(data, requiredExtraTypestring, dataLabel) {
132
+ const requiredNames = parseWitnessTypestringFields(requiredExtraTypestring).map((field) => field.name);
133
+ for (const name of requiredNames) {
134
+ if (!Object.prototype.hasOwnProperty.call(data, name)) {
135
+ return {
136
+ valid: false,
137
+ error: `${dataLabel} is missing required Miden field "${name}"`,
138
+ };
139
+ }
140
+ }
141
+ return { valid: true };
75
142
  }
76
- function hasEvmToMidenSuffix(witnessTypeString) {
77
- return (witnessTypeString.includes("midenRecipientAccount") &&
78
- !witnessTypeString.includes("midenSourceAccount"));
143
+ function validateRequiredMidenFields(typestring, data, typestringLabel, dataLabel) {
144
+ const direction = resolveMidenBridgeDirection(typestring, data);
145
+ if (direction === "conflict") {
146
+ return {
147
+ valid: false,
148
+ error: "Miden witness cannot include both midenSourceAccount and midenRecipientAccount",
149
+ };
150
+ }
151
+ if (!direction) {
152
+ return {
153
+ valid: false,
154
+ error: "Unable to determine Miden bridge direction",
155
+ };
156
+ }
157
+ const requiredExtraTypestring = requiredMidenExtraTypestring(direction);
158
+ const declarationCheck = assertMidenFieldsDeclared(typestring, requiredExtraTypestring, typestringLabel);
159
+ if (!declarationCheck.valid)
160
+ return declarationCheck;
161
+ if (!data) {
162
+ return {
163
+ valid: false,
164
+ error: `${dataLabel} is required for Miden intents`,
165
+ };
166
+ }
167
+ return assertMidenFieldsPresentInData(data, requiredExtraTypestring, dataLabel);
79
168
  }
80
169
  /**
81
- * True iff the intent is a Miden bridge intent — i.e. it carries Miden markers
82
- * either in the mandate (midenSourceAccount / midenRecipientAccount) or in the
83
- * witness typestring itself. Pure EVM intents carry none of these, so all Miden
84
- * validation is skipped for them.
170
+ * Validate Miden `extraDataTypestring` / `extraData` for task assembly.
171
+ * Required Miden fields must be declared and present; additional protocol fields
172
+ * are allowed when declared in the typestring.
85
173
  */
86
- function isMidenIntent(witnessTypeString, mandate) {
87
- return (isMidenToEvmMandate(mandate) ||
88
- isEvmToMidenMandate(mandate) ||
89
- hasMidenToEvmSuffix(witnessTypeString) ||
90
- hasEvmToMidenSuffix(witnessTypeString));
174
+ export function validateMidenExtraFields(extraDataTypestring, extraData) {
175
+ if (!extraDataTypestring.trim() && extraData === undefined) {
176
+ return { valid: true };
177
+ }
178
+ if (!isMidenIntent(extraDataTypestring, extraData)) {
179
+ return { valid: true };
180
+ }
181
+ return validateRequiredMidenFields(extraDataTypestring, extraData, "extraDataTypestring", "extraData");
91
182
  }
92
183
  /**
93
- * When a mandate includes Miden bridge metadata, the witness typestring must end
94
- * with the canonical Miden suffix. Direction (Miden→EVM vs EVM→Miden) is inferred
95
- * from the mandate and from the typestring's own suffix.
184
+ * When a mandate includes Miden bridge metadata, the witness typestring must
185
+ * declare every required Miden field for the detected direction. Additional
186
+ * non-Miden fields may appear before or after the Miden fields.
96
187
  *
97
188
  * No-op for pure EVM intents — Miden checks run only when {@link isMidenIntent}.
98
189
  */
99
- export function assertMidenWitnessSuffix(witnessTypeString, mandate) {
190
+ export function assertMidenWitnessFields(witnessTypeString, mandate) {
100
191
  if (!isMidenIntent(witnessTypeString, mandate)) {
101
192
  return { valid: true };
102
193
  }
103
- const toEvm = isMidenToEvmMandate(mandate) || hasMidenToEvmSuffix(witnessTypeString);
104
- const fromEvm = isEvmToMidenMandate(mandate) || hasEvmToMidenSuffix(witnessTypeString);
105
- if (toEvm && fromEvm) {
106
- return {
107
- valid: false,
108
- error: "Miden witness cannot include both midenSourceAccount and midenRecipientAccount",
109
- };
110
- }
111
- const expectedSuffix = toEvm
112
- ? MIDEN_TO_EVM_EXTRA_TYPESTRING
113
- : EVM_TO_MIDEN_EXTRA_TYPESTRING;
114
- if (!witnessTypeString.endsWith(expectedSuffix)) {
115
- return {
116
- valid: false,
117
- error: `Invalid Miden witness suffix. Expected typestring to end with "${expectedSuffix}".`,
118
- };
119
- }
120
- return { valid: true };
121
- }
122
- export function isEVMToMidenMandate(mandate) {
123
- return isEvmToMidenMandate(mandate);
194
+ return validateRequiredMidenFields(witnessTypeString, mandate, "witnessTypeString", "mandate");
124
195
  }
125
196
  /**
126
197
  * Validates a witness typestring against its data. Handles both the full witness
127
198
  * (base mandate + extra suffix) and a bare extra suffix — the same rules apply to
128
199
  * either: comma spacing, "type name" format, key alignment with the data object,
129
- * and the canonical Miden bridge suffix. Pass the assembled typestring and the
200
+ * and required Miden bridge fields when applicable. Pass the assembled typestring and the
130
201
  * merged data; there is no separate extra-suffix validator.
131
202
  */
132
203
  export function validateWitnessTypeString(witnessTypeString, mandate) {
@@ -159,7 +230,7 @@ export function validateWitnessTypeString(witnessTypeString, mandate) {
159
230
  if (!keyCheck.valid)
160
231
  return keyCheck;
161
232
  }
162
- const midenCheck = assertMidenWitnessSuffix(witnessTypeString, mandate);
233
+ const midenCheck = assertMidenWitnessFields(witnessTypeString, mandate);
163
234
  if (!midenCheck.valid)
164
235
  return midenCheck;
165
236
  return { valid: true };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epoch-protocol/epoch-commons-sdk",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "description": "SDK for interacting with Epoch Commons Protocol",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",