@openzeppelin/relayer-plugin-channels 0.5.0 → 0.6.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.
@@ -39,8 +39,6 @@ export declare const POLLING: {
39
39
  readonly TIMEOUT_MS: 25000;
40
40
  };
41
41
  export declare const FEE: {
42
- readonly MIN_BASE_FEE: 205;
43
- readonly MAX_BASE_FEE: 605;
44
42
  readonly NON_SOROBAN_FEE: 100000;
45
43
  };
46
44
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/plugin/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,eAAO,MAAM,WAAW;;;;;;;;;;CAUd,CAAC;AAGX,eAAO,MAAM,MAAM;;;;CAIT,CAAC;AAGX,eAAO,MAAM,IAAI;;;;;CAOP,CAAC;AAGX,eAAO,MAAM,IAAI;;CAEP,CAAC;AAGX,eAAO,MAAM,UAAU;;;;;CAKb,CAAC;AAGX,eAAO,MAAM,OAAO;;;CAGV,CAAC;AAEX,eAAO,MAAM,GAAG;;;;CAKN,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/plugin/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,eAAO,MAAM,WAAW;;;;;;;;;;CAUd,CAAC;AAGX,eAAO,MAAM,MAAM;;;;CAIT,CAAC;AAGX,eAAO,MAAM,IAAI;;;;;CAOP,CAAC;AAGX,eAAO,MAAM,IAAI;;CAEP,CAAC;AAGX,eAAO,MAAM,UAAU;;;;;CAKb,CAAC;AAGX,eAAO,MAAM,OAAO;;;CAGV,CAAC;AAEX,eAAO,MAAM,GAAG;;CAGN,CAAC"}
@@ -50,8 +50,6 @@ exports.POLLING = {
50
50
  TIMEOUT_MS: 25000,
51
51
  };
52
52
  exports.FEE = {
53
- MIN_BASE_FEE: 205,
54
- MAX_BASE_FEE: 605,
55
53
  // For non-Soroban txs: 100,000 stroops (0.01 XLM) per Stellar best practice
56
54
  NON_SOROBAN_FEE: 100000,
57
55
  };
@@ -1,10 +1,14 @@
1
1
  /**
2
2
  * fee.ts
3
3
  *
4
- * Dynamic fee calculation for fee bump submissions.
5
- * - For Soroban transactions: use resourceFee + random inclusion fee
6
- * - For non-Soroban: use NON_SOROBAN_FEE
4
+ * Static fee calculation for fee bump submissions matching launchtube.
5
+ * - For Soroban transactions: use resourceFee + inclusion fee (BASE_FEE * 2 + 3)
6
+ * - For non-Soroban: use NON_SOROBAN_FEE + inclusion fee
7
+ * - KALE contract gets reduced fee (BASE_FEE * 2 + 1)
7
8
  */
8
9
  import { Transaction } from "@stellar/stellar-sdk";
10
+ export declare const KALE_CONTRACT = "CDL74RF5BLYR2YBLCCI7F5FB6TPSCLKEJUBSD2RSVWZ4YHF3VMFAIGWA";
11
+ export declare const INCLUSION_FEE_DEFAULT: number;
12
+ export declare const INCLUSION_FEE_KALE: number;
9
13
  export declare function calculateMaxFee(transaction: Transaction): number;
10
14
  //# sourceMappingURL=fee.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fee.d.ts","sourceRoot":"","sources":["../../src/plugin/fee.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAO,MAAM,sBAAsB,CAAC;AAGxD,wBAAgB,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,MAAM,CAsBhE"}
1
+ {"version":3,"file":"fee.d.ts","sourceRoot":"","sources":["../../src/plugin/fee.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAEL,WAAW,EAIZ,MAAM,sBAAsB,CAAC;AAG9B,eAAO,MAAM,aAAa,6DACkC,CAAC;AAC7D,eAAO,MAAM,qBAAqB,QAA2B,CAAC;AAC9D,eAAO,MAAM,kBAAkB,QAA2B,CAAC;AA8B3D,wBAAgB,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,MAAM,CAsBhE"}
@@ -2,14 +2,41 @@
2
2
  /**
3
3
  * fee.ts
4
4
  *
5
- * Dynamic fee calculation for fee bump submissions.
6
- * - For Soroban transactions: use resourceFee + random inclusion fee
7
- * - For non-Soroban: use NON_SOROBAN_FEE
5
+ * Static fee calculation for fee bump submissions matching launchtube.
6
+ * - For Soroban transactions: use resourceFee + inclusion fee (BASE_FEE * 2 + 3)
7
+ * - For non-Soroban: use NON_SOROBAN_FEE + inclusion fee
8
+ * - KALE contract gets reduced fee (BASE_FEE * 2 + 1)
8
9
  */
9
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.INCLUSION_FEE_KALE = exports.INCLUSION_FEE_DEFAULT = exports.KALE_CONTRACT = void 0;
10
12
  exports.calculateMaxFee = calculateMaxFee;
11
13
  const stellar_sdk_1 = require("@stellar/stellar-sdk");
12
14
  const constants_1 = require("./constants");
15
+ exports.KALE_CONTRACT = "CDL74RF5BLYR2YBLCCI7F5FB6TPSCLKEJUBSD2RSVWZ4YHF3VMFAIGWA";
16
+ exports.INCLUSION_FEE_DEFAULT = Number(stellar_sdk_1.BASE_FEE) * 2 + 3;
17
+ exports.INCLUSION_FEE_KALE = Number(stellar_sdk_1.BASE_FEE) * 2 + 1;
18
+ function getInclusionFee(transaction) {
19
+ try {
20
+ if (transaction.operations.length !== 1)
21
+ return exports.INCLUSION_FEE_DEFAULT;
22
+ const op = transaction.operations[0];
23
+ if (op.type !== "invokeHostFunction")
24
+ return exports.INCLUSION_FEE_DEFAULT;
25
+ const invokeOp = op;
26
+ if (invokeOp.func.switch() !==
27
+ stellar_sdk_1.xdr.HostFunctionType.hostFunctionTypeInvokeContract()) {
28
+ return exports.INCLUSION_FEE_DEFAULT;
29
+ }
30
+ const invokeContract = invokeOp.func.invokeContract();
31
+ const contract = stellar_sdk_1.StrKey.encodeContract(invokeContract.contractAddress().contractId());
32
+ return contract === exports.KALE_CONTRACT
33
+ ? exports.INCLUSION_FEE_KALE
34
+ : exports.INCLUSION_FEE_DEFAULT;
35
+ }
36
+ catch {
37
+ return exports.INCLUSION_FEE_DEFAULT;
38
+ }
39
+ }
13
40
  function calculateMaxFee(transaction) {
14
41
  const envelope = transaction.toEnvelope();
15
42
  let resourceFee = 0n;
@@ -19,15 +46,10 @@ function calculateMaxFee(transaction) {
19
46
  resourceFee = sorobanData.resourceFee().toBigInt();
20
47
  }
21
48
  }
22
- const baseInclusion = getRandomInt(constants_1.FEE.MIN_BASE_FEE, constants_1.FEE.MAX_BASE_FEE);
49
+ const inclusionFee = getInclusionFee(transaction);
23
50
  const fee = resourceFee > 0n
24
- ? resourceFee + BigInt(baseInclusion)
25
- : BigInt(constants_1.FEE.NON_SOROBAN_FEE + baseInclusion);
26
- console.debug(`[channels] Calculated max_fee: ${Number(fee)} stroops (resourceFee: ${resourceFee}, baseInclusion: ${baseInclusion})`);
51
+ ? resourceFee + BigInt(inclusionFee)
52
+ : BigInt(constants_1.FEE.NON_SOROBAN_FEE + inclusionFee);
53
+ console.debug(`[channels] Calculated max_fee: ${Number(fee)} stroops (resourceFee: ${resourceFee}, inclusionFee: ${inclusionFee})`);
27
54
  return Number(fee);
28
55
  }
29
- function getRandomInt(min, max) {
30
- const mn = Math.ceil(min);
31
- const mx = Math.floor(max);
32
- return Math.floor(Math.random() * (mx - mn + 1)) + mn;
33
- }
@@ -5,6 +5,15 @@
5
5
  * Orchestrates the transaction processing pipeline using channel accounts with fee bumping.
6
6
  */
7
7
  import { PluginContext } from "@openzeppelin/relayer-sdk";
8
+ import { Transaction, xdr } from "@stellar/stellar-sdk";
9
+ /**
10
+ * Extracts func and auth from an unsigned Soroban transaction.
11
+ * Returns null if the transaction is not a single invokeHostFunction operation.
12
+ */
13
+ export declare function extractFuncAuthFromUnsignedXdr(tx: Transaction): {
14
+ func: xdr.HostFunction;
15
+ auth: xdr.SorobanAuthorizationEntry[];
16
+ } | null;
8
17
  /**
9
18
  * Main plugin handler exported for OpenZeppelin Relayer
10
19
  */
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/plugin/handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAe,MAAM,2BAA2B,CAAC;AAqNvE;;GAEG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,CAGlE"}
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/plugin/handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAe,MAAM,2BAA2B,CAAC;AASvE,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAcxD;;;GAGG;AACH,wBAAgB,8BAA8B,CAC5C,EAAE,EAAE,WAAW,GACd;IAAE,IAAI,EAAE,GAAG,CAAC,YAAY,CAAC;IAAC,IAAI,EAAE,GAAG,CAAC,yBAAyB,EAAE,CAAA;CAAE,GAAG,IAAI,CAkB1E;AAiOD;;GAEG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,CAElE"}
@@ -6,6 +6,7 @@
6
6
  * Orchestrates the transaction processing pipeline using channel accounts with fee bumping.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.extractFuncAuthFromUnsignedXdr = extractFuncAuthFromUnsignedXdr;
9
10
  exports.handler = handler;
10
11
  const relayer_sdk_1 = require("@openzeppelin/relayer-sdk");
11
12
  const pool_1 = require("./pool");
@@ -23,8 +24,44 @@ function getApiKey(headers, headerName) {
23
24
  const values = headers[headerName];
24
25
  return values?.[0]?.trim() || undefined;
25
26
  }
26
- async function handleXdrSubmit(xdrStr, fundRelayer, network, networkPassphrase, api, tracker) {
27
+ /**
28
+ * Extracts func and auth from an unsigned Soroban transaction.
29
+ * Returns null if the transaction is not a single invokeHostFunction operation.
30
+ */
31
+ function extractFuncAuthFromUnsignedXdr(tx) {
32
+ const ops = tx.operations;
33
+ if (ops.length !== 1) {
34
+ return null;
35
+ }
36
+ const envelope = tx.toEnvelope();
37
+ const rawOp = envelope.v1().tx().operations()[0].body();
38
+ if (rawOp.switch() !== stellar_sdk_1.xdr.OperationType.invokeHostFunction()) {
39
+ return null;
40
+ }
41
+ const invokeHostFn = rawOp.invokeHostFunctionOp();
42
+ return {
43
+ func: invokeHostFn.hostFunction(),
44
+ auth: invokeHostFn.auth(),
45
+ };
46
+ }
47
+ async function handleXdrSubmit(xdrStr, fundRelayer, fundAddress, network, networkPassphrase, api, pool, tracker) {
27
48
  const tx = new stellar_sdk_1.Transaction(xdrStr, networkPassphrase);
49
+ // Unsigned XDR: extract func+auth and route through channel path
50
+ if (tx.signatures.length === 0) {
51
+ const extracted = extractFuncAuthFromUnsignedXdr(tx);
52
+ if (!extracted) {
53
+ throw (0, relayer_sdk_1.pluginError)("Unsigned XDR must contain exactly one invokeHostFunction operation", {
54
+ code: "INVALID_UNSIGNED_XDR",
55
+ status: constants_1.HTTP_STATUS.BAD_REQUEST,
56
+ details: {
57
+ operationCount: tx.operations.length,
58
+ operationType: tx.operations[0]?.type,
59
+ },
60
+ });
61
+ }
62
+ console.log(`[channels] Unsigned XDR detected, extracting func+auth and routing through channel path`);
63
+ return handleFuncAuthSubmit(extracted.func, extracted.auth, api, pool, fundRelayer, fundAddress, network, networkPassphrase, tracker);
64
+ }
28
65
  const validated = (0, tx_1.validateExistingTransactionForSubmitOnly)(tx);
29
66
  const maxFee = (0, fee_1.calculateMaxFee)(validated);
30
67
  await tracker?.checkBudget(maxFee);
@@ -97,47 +134,40 @@ async function channelAccounts(context) {
97
134
  resetPeriodMs: config.feeResetPeriodMs,
98
135
  });
99
136
  }
100
- try {
101
- // 1. Validate and parse request (xdr OR func+auth)
102
- const request = (0, validation_1.validateAndParseRequest)(params);
103
- console.debug(`[channels] Request type: ${request.type}, auth entries: ${request.type === "func-auth" ? request.auth.length : "N/A"}`);
104
- // 2. Get fund relayer
105
- const fundRelayer = api.useRelayer(config.fundRelayerId);
106
- const fundInfo = await fundRelayer.getRelayer();
107
- if (!fundInfo || !fundInfo.address) {
108
- throw (0, relayer_sdk_1.pluginError)("Fund relayer not found", {
109
- code: "RELAYER_UNAVAILABLE",
110
- status: constants_1.HTTP_STATUS.BAD_GATEWAY,
111
- details: { relayerId: config.fundRelayerId },
112
- });
113
- }
114
- const fundStatus = await fundRelayer.getRelayerStatus();
115
- if (fundStatus.network_type !== "stellar") {
116
- throw (0, relayer_sdk_1.pluginError)("Fund relayer network type must be stellar", {
117
- code: "UNSUPPORTED_NETWORK",
118
- status: constants_1.HTTP_STATUS.BAD_REQUEST,
119
- details: {
120
- network_type: fundStatus.network_type,
121
- relayerId: config.fundRelayerId,
122
- },
123
- });
124
- }
125
- // 3. Branch by request type
126
- if (request.type === "xdr") {
127
- console.log(`[channels] Flow: XDR submit-only`);
128
- return await handleXdrSubmit(request.xdr, fundRelayer, config.network, networkPassphrase, api, tracker);
129
- }
130
- console.log(`[channels] Flow: func+auth with channel account`);
131
- return await handleFuncAuthSubmit(request.func, request.auth, api, pool, fundRelayer, fundInfo.address, config.network, networkPassphrase, tracker);
137
+ // 1. Validate and parse request (xdr OR func+auth)
138
+ const request = (0, validation_1.validateAndParseRequest)(params);
139
+ console.debug(`[channels] Request type: ${request.type}, auth entries: ${request.type === "func-auth" ? request.auth.length : "N/A"}`);
140
+ // 2. Get fund relayer
141
+ const fundRelayer = api.useRelayer(config.fundRelayerId);
142
+ const fundInfo = await fundRelayer.getRelayer();
143
+ if (!fundInfo || !fundInfo.address) {
144
+ throw (0, relayer_sdk_1.pluginError)("Fund relayer not found", {
145
+ code: "RELAYER_UNAVAILABLE",
146
+ status: constants_1.HTTP_STATUS.BAD_GATEWAY,
147
+ details: { relayerId: config.fundRelayerId },
148
+ });
132
149
  }
133
- finally {
134
- // Nothing to cleanup here; func-auth path releases locks internally
150
+ if (fundInfo.network_type !== "stellar") {
151
+ throw (0, relayer_sdk_1.pluginError)("Fund relayer network type must be stellar", {
152
+ code: "UNSUPPORTED_NETWORK",
153
+ status: constants_1.HTTP_STATUS.BAD_REQUEST,
154
+ details: {
155
+ network_type: fundInfo.network_type,
156
+ relayerId: config.fundRelayerId,
157
+ },
158
+ });
159
+ }
160
+ // 3. Branch by request type
161
+ if (request.type === "xdr") {
162
+ console.log(`[channels] Flow: XDR submit-only`);
163
+ return await handleXdrSubmit(request.xdr, fundRelayer, fundInfo.address, config.network, networkPassphrase, api, pool, tracker);
135
164
  }
165
+ console.log(`[channels] Flow: func+auth with channel account`);
166
+ return await handleFuncAuthSubmit(request.func, request.auth, api, pool, fundRelayer, fundInfo.address, config.network, networkPassphrase, tracker);
136
167
  }
137
168
  /**
138
169
  * Main plugin handler exported for OpenZeppelin Relayer
139
170
  */
140
171
  async function handler(context) {
141
- const result = await channelAccounts(context);
142
- return result;
172
+ return channelAccounts(context);
143
173
  }
@@ -12,4 +12,6 @@ export interface ChannelAccount {
12
12
  sequence: string;
13
13
  }
14
14
  export declare function simulateAndBuildWithChannel(func: xdr.HostFunction, auth: xdr.SorobanAuthorizationEntry[] | undefined, channel: ChannelAccount, _fundAddress: string, relayer: Relayer, networkPassphrase: string): Promise<Transaction>;
15
+ /** Extract human-readable message + error type from simulation error diagnostic events */
16
+ export declare function parseSimulationError(error: string): string;
15
17
  //# sourceMappingURL=simulation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"simulation.d.ts","sourceRoot":"","sources":["../../src/plugin/simulation.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAIL,WAAW,EAEX,GAAG,EACJ,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAGL,OAAO,EACR,MAAM,2BAA2B,CAAC;AAGnC,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAsB,2BAA2B,CAC/C,IAAI,EAAE,GAAG,CAAC,YAAY,EACtB,IAAI,EAAE,GAAG,CAAC,yBAAyB,EAAE,GAAG,SAAS,EACjD,OAAO,EAAE,cAAc,EACvB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,OAAO,EAChB,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAAC,WAAW,CAAC,CA8FtB"}
1
+ {"version":3,"file":"simulation.d.ts","sourceRoot":"","sources":["../../src/plugin/simulation.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAIL,WAAW,EAEX,GAAG,EACJ,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAGL,OAAO,EACR,MAAM,2BAA2B,CAAC;AAGnC,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAsB,2BAA2B,CAC/C,IAAI,EAAE,GAAG,CAAC,YAAY,EACtB,IAAI,EAAE,GAAG,CAAC,yBAAyB,EAAE,GAAG,SAAS,EACjD,OAAO,EAAE,cAAc,EACvB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,OAAO,EAChB,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAAC,WAAW,CAAC,CAoGtB;AAED,0FAA0F;AAC1F,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAe1D"}
@@ -8,6 +8,7 @@
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.simulateAndBuildWithChannel = simulateAndBuildWithChannel;
11
+ exports.parseSimulationError = parseSimulationError;
11
12
  const stellar_sdk_1 = require("@stellar/stellar-sdk");
12
13
  const relayer_sdk_1 = require("@openzeppelin/relayer-sdk");
13
14
  const constants_1 = require("./constants");
@@ -51,23 +52,26 @@ async function simulateAndBuildWithChannel(func, auth, channel, _fundAddress, re
51
52
  }
52
53
  if (rpcResponse.error) {
53
54
  const { code, message, description, data } = rpcResponse.error;
54
- throw (0, relayer_sdk_1.pluginError)("Simulation RPC execution failed", {
55
+ console.error(`[channels] RPC error: code=${code}, message=${message}, detail=${description || data}`);
56
+ throw (0, relayer_sdk_1.pluginError)("Simulation RPC failed", {
55
57
  code: "SIMULATION_RPC_FAILURE",
58
+ status: constants_1.HTTP_STATUS.BAD_GATEWAY,
59
+ details: { message: "RPC provider error" },
60
+ });
61
+ }
62
+ const simResult = {
63
+ id: String(rpcResponse.id ?? "1"),
64
+ ...rpcResponse.result,
65
+ };
66
+ if ("error" in simResult && simResult.error) {
67
+ console.error(`[channels] Simulation error: ${simResult.error}`);
68
+ throw (0, relayer_sdk_1.pluginError)("Simulation failed", {
69
+ code: "SIMULATION_FAILED",
56
70
  status: constants_1.HTTP_STATUS.BAD_REQUEST,
57
- details: {
58
- rpcCode: code,
59
- message,
60
- description: description || data,
61
- },
71
+ details: { error: parseSimulationError(simResult.error) },
62
72
  });
63
73
  }
64
74
  try {
65
- // Format simulation result for SDK's assembleTransaction
66
- const simResult = {
67
- id: String(rpcResponse.id ?? "1"),
68
- ...rpcResponse.result,
69
- };
70
- console.debug(`[channels] Simulation result:`, JSON.stringify(simResult, null, 2));
71
75
  // Use SDK's assembleTransaction to apply simulation results
72
76
  const prepared = stellar_sdk_1.rpc
73
77
  .assembleTransaction(transaction, simResult)
@@ -83,7 +87,8 @@ async function simulateAndBuildWithChannel(func, auth, channel, _fundAddress, re
83
87
  return prepared;
84
88
  }
85
89
  catch (err) {
86
- throw (0, relayer_sdk_1.pluginError)("Simulation result processing failed", {
90
+ console.error(`[channels] Assembly error: ${err instanceof Error ? err.message : String(err)}`);
91
+ throw (0, relayer_sdk_1.pluginError)("Simulation failed", {
87
92
  code: "SIMULATION_FAILED",
88
93
  status: constants_1.HTTP_STATUS.INTERNAL_SERVER_ERROR,
89
94
  details: {
@@ -92,3 +97,17 @@ async function simulateAndBuildWithChannel(func, auth, channel, _fundAddress, re
92
97
  });
93
98
  }
94
99
  }
100
+ /** Extract human-readable message + error type from simulation error diagnostic events */
101
+ function parseSimulationError(error) {
102
+ const firstLine = error.split("\n")[0]?.trim() || "Simulation failed";
103
+ const errorType = firstLine.match(/Error\(([^)]+)\)/)?.[1];
104
+ const arrayMatch = error.match(/data:\s*\["((?:[^"\\]|\\.)*)"/);
105
+ if (arrayMatch?.[1] && arrayMatch[1].length > 3) {
106
+ return errorType ? `${arrayMatch[1]} (${errorType})` : arrayMatch[1];
107
+ }
108
+ const stringMatch = error.match(/data:\s*"((?:[^"\\]|\\.)*)"/);
109
+ if (stringMatch?.[1] && stringMatch[1].length > 3) {
110
+ return errorType ? `${stringMatch[1]} (${errorType})` : stringMatch[1];
111
+ }
112
+ return firstLine;
113
+ }
@@ -18,4 +18,6 @@ export declare function signWithChannelAndFund(transaction: Transaction, channel
18
18
  * Submit transaction with fee bump and wait for confirmation
19
19
  */
20
20
  export declare function submitWithFeeBumpAndWait(fundRelayer: Relayer, signedXdr: string, network: "testnet" | "mainnet", maxFee: number, api: PluginAPI, tracker?: FeeTracker): Promise<ChannelAccountsResponse>;
21
+ /** Strip provider wrapper text, extract last segment (e.g., "TxInsufficientBalance") */
22
+ export declare function sanitizeReason(reason: string): string;
21
23
  //# sourceMappingURL=submit.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"submit.d.ts","sourceRoot":"","sources":["../../src/plugin/submit.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAEL,OAAO,EAGP,SAAS,EACV,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5C;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,WAAW,EACxB,cAAc,EAAE,OAAO,EACvB,YAAY,EAAE,OAAO,EACrB,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAAC,WAAW,CAAC,CA2BtB;AAED;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,WAAW,EAAE,OAAO,EACpB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,SAAS,GAAG,SAAS,EAC9B,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,SAAS,EACd,OAAO,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,uBAAuB,CAAC,CAiElC"}
1
+ {"version":3,"file":"submit.d.ts","sourceRoot":"","sources":["../../src/plugin/submit.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAEL,OAAO,EAGP,SAAS,EACV,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5C;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,WAAW,EACxB,cAAc,EAAE,OAAO,EACvB,YAAY,EAAE,OAAO,EACrB,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAAC,WAAW,CAAC,CA2BtB;AAED;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,WAAW,EAAE,OAAO,EACpB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,SAAS,GAAG,SAAS,EAC9B,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,SAAS,EACd,OAAO,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,uBAAuB,CAAC,CAoElC;AAgBD,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOrD"}
@@ -7,6 +7,7 @@
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.signWithChannelAndFund = signWithChannelAndFund;
9
9
  exports.submitWithFeeBumpAndWait = submitWithFeeBumpAndWait;
10
+ exports.sanitizeReason = sanitizeReason;
10
11
  const stellar_sdk_1 = require("@stellar/stellar-sdk");
11
12
  const relayer_sdk_1 = require("@openzeppelin/relayer-sdk");
12
13
  const constants_1 = require("./constants");
@@ -62,12 +63,15 @@ async function submitWithFeeBumpAndWait(fundRelayer, signedXdr, network, maxFee,
62
63
  if (tracker) {
63
64
  await tracker.recordUsage(maxFee);
64
65
  }
65
- throw (0, relayer_sdk_1.pluginError)(final.status_reason || "Transaction failed", {
66
+ const rawReason = final.status_reason || "Transaction failed";
67
+ console.error(`[channels] Transaction failed: ${rawReason}`);
68
+ const reason = sanitizeReason(rawReason);
69
+ throw (0, relayer_sdk_1.pluginError)(reason, {
66
70
  code: "ONCHAIN_FAILED",
67
71
  status: constants_1.HTTP_STATUS.BAD_REQUEST,
68
72
  details: {
69
73
  status: String(final.status),
70
- reason: final.status_reason ?? null,
74
+ reason,
71
75
  id: final.id,
72
76
  hash: final.hash ?? null,
73
77
  },
@@ -108,3 +112,12 @@ function isSignTransactionResponseStellar(data) {
108
112
  "signature" in data &&
109
113
  "signedXdr" in data);
110
114
  }
115
+ /** Strip provider wrapper text, extract last segment (e.g., "TxInsufficientBalance") */
116
+ function sanitizeReason(reason) {
117
+ const segments = reason.split(/:\s*/);
118
+ const last = segments[segments.length - 1]?.trim();
119
+ if (last && last.length > 2 && !last.toLowerCase().includes("provider")) {
120
+ return last;
121
+ }
122
+ return reason.length > 100 ? reason.slice(0, 100) + "..." : reason;
123
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openzeppelin/relayer-plugin-channels",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "OpenZeppelin Relayer Plugin for Stellar Channel Accounts",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",