@openzeppelin/relayer-plugin-channels 0.4.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.
- package/README.md +189 -2
- package/dist/client/channels-client.d.ts +50 -1
- package/dist/client/channels-client.d.ts.map +1 -1
- package/dist/client/channels-client.js +86 -0
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/types.d.ts +60 -0
- package/dist/client/types.d.ts.map +1 -1
- package/dist/plugin/config.d.ts +5 -8
- package/dist/plugin/config.d.ts.map +1 -1
- package/dist/plugin/config.js +46 -29
- package/dist/plugin/constants.d.ts +2 -3
- package/dist/plugin/constants.d.ts.map +1 -1
- package/dist/plugin/constants.js +2 -3
- package/dist/plugin/fee-tracking.d.ts +81 -0
- package/dist/plugin/fee-tracking.d.ts.map +1 -0
- package/dist/plugin/fee-tracking.js +146 -0
- package/dist/plugin/fee.d.ts +7 -3
- package/dist/plugin/fee.d.ts.map +1 -1
- package/dist/plugin/fee.js +34 -12
- package/dist/plugin/handler.d.ts +9 -0
- package/dist/plugin/handler.d.ts.map +1 -1
- package/dist/plugin/handler.js +99 -42
- package/dist/plugin/management.d.ts +5 -1
- package/dist/plugin/management.d.ts.map +1 -1
- package/dist/plugin/management.js +113 -18
- package/dist/plugin/pool.d.ts +1 -1
- package/dist/plugin/pool.d.ts.map +1 -1
- package/dist/plugin/pool.js +4 -6
- package/dist/plugin/simulation.d.ts +2 -0
- package/dist/plugin/simulation.d.ts.map +1 -1
- package/dist/plugin/simulation.js +32 -13
- package/dist/plugin/submit.d.ts +4 -1
- package/dist/plugin/submit.d.ts.map +1 -1
- package/dist/plugin/submit.js +25 -4
- package/package.json +13 -13
|
@@ -2,9 +2,13 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* management.ts
|
|
4
4
|
*
|
|
5
|
-
* Payload-based management API for channel relayerIds.
|
|
5
|
+
* Payload-based management API for channel relayerIds and fee limits.
|
|
6
6
|
* - listChannelAccounts: returns relayerIds from KV
|
|
7
7
|
* - setChannelAccounts: replaces relayerIds array in KV (checks lock conflicts)
|
|
8
|
+
* - getFeeUsage: returns fee consumption for an API key
|
|
9
|
+
* - getFeeLimit: returns custom limit for an API key (if set)
|
|
10
|
+
* - setFeeLimit: sets custom limit for an API key
|
|
11
|
+
* - deleteFeeLimit: removes custom limit for an API key
|
|
8
12
|
*/
|
|
9
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
14
|
exports.isManagementRequest = isManagementRequest;
|
|
@@ -12,16 +16,7 @@ exports.handleManagement = handleManagement;
|
|
|
12
16
|
const relayer_sdk_1 = require("@openzeppelin/relayer-sdk");
|
|
13
17
|
const config_1 = require("./config");
|
|
14
18
|
const constants_1 = require("./constants");
|
|
15
|
-
|
|
16
|
-
// Basic constant-time comparison without crypto dep
|
|
17
|
-
if (a.length !== b.length)
|
|
18
|
-
return false;
|
|
19
|
-
let result = 0;
|
|
20
|
-
for (let i = 0; i < a.length; i++) {
|
|
21
|
-
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
22
|
-
}
|
|
23
|
-
return result === 0;
|
|
24
|
-
}
|
|
19
|
+
const fee_tracking_1 = require("./fee-tracking");
|
|
25
20
|
function isManagementRequest(params) {
|
|
26
21
|
return Boolean(params &&
|
|
27
22
|
typeof params === "object" &&
|
|
@@ -30,8 +25,8 @@ function isManagementRequest(params) {
|
|
|
30
25
|
}
|
|
31
26
|
async function handleManagement(context) {
|
|
32
27
|
const { kv, params } = context;
|
|
33
|
-
const
|
|
34
|
-
if (!
|
|
28
|
+
const config = (0, config_1.loadConfig)();
|
|
29
|
+
if (!config.adminSecret) {
|
|
35
30
|
throw (0, relayer_sdk_1.pluginError)("Management API disabled", {
|
|
36
31
|
code: "MANAGEMENT_DISABLED",
|
|
37
32
|
status: constants_1.HTTP_STATUS.FORBIDDEN,
|
|
@@ -39,20 +34,26 @@ async function handleManagement(context) {
|
|
|
39
34
|
}
|
|
40
35
|
const m = params?.management || {};
|
|
41
36
|
const provided = (m.adminSecret ?? "").toString();
|
|
42
|
-
if (!provided ||
|
|
37
|
+
if (!provided || provided !== config.adminSecret) {
|
|
43
38
|
throw (0, relayer_sdk_1.pluginError)("Unauthorized", {
|
|
44
39
|
code: "UNAUTHORIZED",
|
|
45
40
|
status: constants_1.HTTP_STATUS.UNAUTHORIZED,
|
|
46
41
|
});
|
|
47
42
|
}
|
|
48
43
|
const action = String(m.action || "");
|
|
49
|
-
// Load config (requires env like STELLAR_NETWORK) after auth
|
|
50
|
-
const cfg = (0, config_1.loadConfig)();
|
|
51
44
|
switch (action) {
|
|
52
45
|
case "listChannelAccounts":
|
|
53
|
-
return await listChannelAccounts(kv,
|
|
46
|
+
return await listChannelAccounts(kv, config.network);
|
|
54
47
|
case "setChannelAccounts":
|
|
55
|
-
return await setChannelAccounts(kv,
|
|
48
|
+
return await setChannelAccounts(kv, config.network, m);
|
|
49
|
+
case "getFeeUsage":
|
|
50
|
+
return await getFeeUsage(kv, config.network, config.feeLimit, config.feeResetPeriodMs, m);
|
|
51
|
+
case "getFeeLimit":
|
|
52
|
+
return await getFeeLimit(kv, config.network, config.feeLimit, m);
|
|
53
|
+
case "setFeeLimit":
|
|
54
|
+
return await setFeeLimit(kv, config.network, m);
|
|
55
|
+
case "deleteFeeLimit":
|
|
56
|
+
return await deleteFeeLimit(kv, config.network, m);
|
|
56
57
|
default:
|
|
57
58
|
throw (0, relayer_sdk_1.pluginError)("Invalid management action", {
|
|
58
59
|
code: "INVALID_ACTION",
|
|
@@ -76,6 +77,100 @@ async function listChannelAccounts(kv, network) {
|
|
|
76
77
|
});
|
|
77
78
|
}
|
|
78
79
|
}
|
|
80
|
+
async function getFeeUsage(kv, network, defaultLimit, resetPeriodMs, payload) {
|
|
81
|
+
const apiKey = payload?.apiKey;
|
|
82
|
+
if (!apiKey || typeof apiKey !== "string") {
|
|
83
|
+
throw (0, relayer_sdk_1.pluginError)("Invalid payload: apiKey is required", {
|
|
84
|
+
code: "INVALID_PAYLOAD",
|
|
85
|
+
status: constants_1.HTTP_STATUS.BAD_REQUEST,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const tracker = new fee_tracking_1.FeeTracker({
|
|
90
|
+
kv,
|
|
91
|
+
network,
|
|
92
|
+
apiKey,
|
|
93
|
+
defaultLimit,
|
|
94
|
+
resetPeriodMs,
|
|
95
|
+
});
|
|
96
|
+
return await tracker.getUsageInfo();
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
throw (0, relayer_sdk_1.pluginError)("KV error while reading fee usage", {
|
|
100
|
+
code: "KV_ERROR",
|
|
101
|
+
status: constants_1.HTTP_STATUS.INTERNAL_SERVER_ERROR,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async function getFeeLimit(kv, network, defaultLimit, payload) {
|
|
106
|
+
const apiKey = payload?.apiKey;
|
|
107
|
+
if (!apiKey || typeof apiKey !== "string") {
|
|
108
|
+
throw (0, relayer_sdk_1.pluginError)("Invalid payload: apiKey is required", {
|
|
109
|
+
code: "INVALID_PAYLOAD",
|
|
110
|
+
status: constants_1.HTTP_STATUS.BAD_REQUEST,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
const tracker = new fee_tracking_1.FeeTracker({ kv, network, apiKey, defaultLimit });
|
|
115
|
+
const customLimit = await tracker.getCustomLimit();
|
|
116
|
+
return {
|
|
117
|
+
limit: customLimit ?? defaultLimit,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
catch (e) {
|
|
121
|
+
throw (0, relayer_sdk_1.pluginError)("KV error while reading fee limit", {
|
|
122
|
+
code: "KV_ERROR",
|
|
123
|
+
status: constants_1.HTTP_STATUS.INTERNAL_SERVER_ERROR,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async function setFeeLimit(kv, network, payload) {
|
|
128
|
+
const apiKey = payload?.apiKey;
|
|
129
|
+
const limit = payload?.limit;
|
|
130
|
+
if (!apiKey || typeof apiKey !== "string") {
|
|
131
|
+
throw (0, relayer_sdk_1.pluginError)("Invalid payload: apiKey is required", {
|
|
132
|
+
code: "INVALID_PAYLOAD",
|
|
133
|
+
status: constants_1.HTTP_STATUS.BAD_REQUEST,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
if (typeof limit !== "number" || !Number.isFinite(limit) || limit < 0) {
|
|
137
|
+
throw (0, relayer_sdk_1.pluginError)("Invalid payload: limit must be a non-negative number", {
|
|
138
|
+
code: "INVALID_PAYLOAD",
|
|
139
|
+
status: constants_1.HTTP_STATUS.BAD_REQUEST,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
const tracker = new fee_tracking_1.FeeTracker({ kv, network, apiKey });
|
|
144
|
+
await tracker.setCustomLimit(Math.floor(limit));
|
|
145
|
+
return { ok: true, limit: Math.floor(limit) };
|
|
146
|
+
}
|
|
147
|
+
catch (e) {
|
|
148
|
+
throw (0, relayer_sdk_1.pluginError)("KV error while setting fee limit", {
|
|
149
|
+
code: "KV_ERROR",
|
|
150
|
+
status: constants_1.HTTP_STATUS.INTERNAL_SERVER_ERROR,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async function deleteFeeLimit(kv, network, payload) {
|
|
155
|
+
const apiKey = payload?.apiKey;
|
|
156
|
+
if (!apiKey || typeof apiKey !== "string") {
|
|
157
|
+
throw (0, relayer_sdk_1.pluginError)("Invalid payload: apiKey is required", {
|
|
158
|
+
code: "INVALID_PAYLOAD",
|
|
159
|
+
status: constants_1.HTTP_STATUS.BAD_REQUEST,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
const tracker = new fee_tracking_1.FeeTracker({ kv, network, apiKey });
|
|
164
|
+
await tracker.deleteCustomLimit();
|
|
165
|
+
return { ok: true };
|
|
166
|
+
}
|
|
167
|
+
catch (e) {
|
|
168
|
+
throw (0, relayer_sdk_1.pluginError)("KV error while deleting fee limit", {
|
|
169
|
+
code: "KV_ERROR",
|
|
170
|
+
status: constants_1.HTTP_STATUS.INTERNAL_SERVER_ERROR,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
79
174
|
async function setChannelAccounts(kv, network, payload) {
|
|
80
175
|
const incoming = payload?.relayerIds;
|
|
81
176
|
if (!Array.isArray(incoming)) {
|
package/dist/plugin/pool.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export declare class ChannelPool {
|
|
|
17
17
|
private readonly channelLockTtlSec;
|
|
18
18
|
private readonly mutexTtlSec;
|
|
19
19
|
private readonly kv;
|
|
20
|
-
constructor(network: "testnet" | "mainnet", kv: PluginKVStore);
|
|
20
|
+
constructor(network: "testnet" | "mainnet", kv: PluginKVStore, lockTtlSeconds: number);
|
|
21
21
|
/** Acquire a relayerId with a token lock */
|
|
22
22
|
acquire(): Promise<PoolLock>;
|
|
23
23
|
private withGlobalMutex;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../../src/plugin/pool.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,EAAe,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../../src/plugin/pool.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,EAAe,MAAM,2BAA2B,CAAC;AAIvE,MAAM,MAAM,QAAQ,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAI5D,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAgB;gBAGjC,OAAO,EAAE,SAAS,GAAG,SAAS,EAC9B,EAAE,EAAE,aAAa,EACjB,cAAc,EAAE,MAAM;IASxB,4CAA4C;IACtC,OAAO,IAAI,OAAO,CAAC,QAAQ,CAAC;YAuBpB,eAAe;YAQf,iBAAiB;IAwB/B,oCAAoC;IAC9B,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAY5C,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,OAAO;YAID,mBAAmB;CAWlC"}
|
package/dist/plugin/pool.js
CHANGED
|
@@ -14,14 +14,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
14
14
|
exports.ChannelPool = void 0;
|
|
15
15
|
const relayer_sdk_1 = require("@openzeppelin/relayer-sdk");
|
|
16
16
|
const crypto_1 = __importDefault(require("crypto"));
|
|
17
|
-
const config_1 = require("./config");
|
|
18
17
|
const constants_1 = require("./constants");
|
|
19
18
|
class ChannelPool {
|
|
20
|
-
constructor(network, kv) {
|
|
19
|
+
constructor(network, kv, lockTtlSeconds) {
|
|
21
20
|
this.network = network;
|
|
22
21
|
this.kv = kv;
|
|
23
22
|
this.globalLockKey = `${this.network}:channel-pool-lock`;
|
|
24
|
-
this.channelLockTtlSec =
|
|
23
|
+
this.channelLockTtlSec = lockTtlSeconds;
|
|
25
24
|
this.mutexTtlSec = constants_1.POOL.MUTEX_TTL_SECONDS;
|
|
26
25
|
}
|
|
27
26
|
/** Acquire a relayerId with a token lock */
|
|
@@ -45,11 +44,10 @@ class ChannelPool {
|
|
|
45
44
|
}
|
|
46
45
|
// Run a function under the short-lived global mutex; returns null if busy
|
|
47
46
|
async withGlobalMutex(fn) {
|
|
48
|
-
|
|
47
|
+
return this.kv.withLock(this.globalLockKey, fn, {
|
|
49
48
|
ttlSec: this.mutexTtlSec,
|
|
50
49
|
onBusy: "skip",
|
|
51
|
-
})
|
|
52
|
-
return r;
|
|
50
|
+
});
|
|
53
51
|
}
|
|
54
52
|
// Inside the mutex: pick an available relayer and set its channel lock
|
|
55
53
|
async tryLockAnyRelayer() {
|
|
@@ -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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
}
|
package/dist/plugin/submit.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { Transaction } from "@stellar/stellar-sdk";
|
|
7
7
|
import { Relayer, PluginAPI } from "@openzeppelin/relayer-sdk";
|
|
8
8
|
import { ChannelAccountsResponse } from "./types";
|
|
9
|
+
import { FeeTracker } from "./fee-tracking";
|
|
9
10
|
/**
|
|
10
11
|
* Sign transaction with both channel and fund relayers
|
|
11
12
|
* - First sign with channel account
|
|
@@ -16,5 +17,7 @@ export declare function signWithChannelAndFund(transaction: Transaction, channel
|
|
|
16
17
|
/**
|
|
17
18
|
* Submit transaction with fee bump and wait for confirmation
|
|
18
19
|
*/
|
|
19
|
-
export declare function submitWithFeeBumpAndWait(fundRelayer: Relayer, signedXdr: string, network: "testnet" | "mainnet", maxFee: number, api: PluginAPI): Promise<ChannelAccountsResponse>;
|
|
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;
|
|
20
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;
|
|
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"}
|
package/dist/plugin/submit.js
CHANGED
|
@@ -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");
|
|
@@ -39,7 +40,7 @@ async function signWithChannelAndFund(transaction, channelRelayer, _fundRelayer,
|
|
|
39
40
|
/**
|
|
40
41
|
* Submit transaction with fee bump and wait for confirmation
|
|
41
42
|
*/
|
|
42
|
-
async function submitWithFeeBumpAndWait(fundRelayer, signedXdr, network, maxFee, api) {
|
|
43
|
+
async function submitWithFeeBumpAndWait(fundRelayer, signedXdr, network, maxFee, api, tracker) {
|
|
43
44
|
// Submit with fee bump
|
|
44
45
|
console.debug(`[channels] Sending fee bump tx: network=${network}, maxFee=${maxFee}, xdr_len=${signedXdr.length}`);
|
|
45
46
|
const payload = {
|
|
@@ -58,17 +59,28 @@ async function submitWithFeeBumpAndWait(fundRelayer, signedXdr, network, maxFee,
|
|
|
58
59
|
}));
|
|
59
60
|
// Check if transaction actually succeeded
|
|
60
61
|
if (final.status === "failed") {
|
|
61
|
-
|
|
62
|
+
// Record fee on on-chain failure (transaction was still submitted and consumed fees)
|
|
63
|
+
if (tracker) {
|
|
64
|
+
await tracker.recordUsage(maxFee);
|
|
65
|
+
}
|
|
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, {
|
|
62
70
|
code: "ONCHAIN_FAILED",
|
|
63
71
|
status: constants_1.HTTP_STATUS.BAD_REQUEST,
|
|
64
72
|
details: {
|
|
65
73
|
status: String(final.status),
|
|
66
|
-
reason
|
|
74
|
+
reason,
|
|
67
75
|
id: final.id,
|
|
68
76
|
hash: final.hash ?? null,
|
|
69
77
|
},
|
|
70
78
|
});
|
|
71
79
|
}
|
|
80
|
+
// Record fee on success
|
|
81
|
+
if (tracker) {
|
|
82
|
+
await tracker.recordUsage(maxFee);
|
|
83
|
+
}
|
|
72
84
|
return {
|
|
73
85
|
transactionId: final.id,
|
|
74
86
|
status: final.status,
|
|
@@ -80,7 +92,7 @@ async function submitWithFeeBumpAndWait(fundRelayer, signedXdr, network, maxFee,
|
|
|
80
92
|
if (error.code === "ONCHAIN_FAILED") {
|
|
81
93
|
throw error;
|
|
82
94
|
}
|
|
83
|
-
// Otherwise, it's a timeout
|
|
95
|
+
// Otherwise, it's a timeout - don't track fees (status unknown)
|
|
84
96
|
throw (0, relayer_sdk_1.pluginError)("Transaction wait timeout. It may still submit.", {
|
|
85
97
|
code: "WAIT_TIMEOUT",
|
|
86
98
|
status: constants_1.HTTP_STATUS.GATEWAY_TIMEOUT,
|
|
@@ -100,3 +112,12 @@ function isSignTransactionResponseStellar(data) {
|
|
|
100
112
|
"signature" in data &&
|
|
101
113
|
"signedXdr" in data);
|
|
102
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.
|
|
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",
|
|
@@ -19,23 +19,23 @@
|
|
|
19
19
|
"author": "OpenZeppelin <contact@openzeppelin.com>",
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@openzeppelin/relayer-sdk": "^1.7.0",
|
|
23
|
-
"@stellar/stellar-sdk": "^14.3.3",
|
|
24
22
|
"@actions/exec": "^1.1.1",
|
|
25
|
-
"
|
|
23
|
+
"@openzeppelin/relayer-sdk": "^1.8.0",
|
|
24
|
+
"@stellar/stellar-sdk": "^14.4.0",
|
|
25
|
+
"axios": "^1.13.2"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@changesets/cli": "^2.
|
|
29
|
-
"@types/node": "^22.
|
|
30
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
31
|
-
"@typescript-eslint/parser": "^8.
|
|
32
|
-
"eslint": "^9.
|
|
33
|
-
"eslint-config-prettier": "^9.
|
|
34
|
-
"eslint-plugin-prettier": "^5.
|
|
35
|
-
"prettier": "^3.
|
|
28
|
+
"@changesets/cli": "^2.29.8",
|
|
29
|
+
"@types/node": "^22.19.1",
|
|
30
|
+
"@typescript-eslint/eslint-plugin": "^8.48.1",
|
|
31
|
+
"@typescript-eslint/parser": "^8.48.1",
|
|
32
|
+
"eslint": "^9.39.1",
|
|
33
|
+
"eslint-config-prettier": "^9.1.2",
|
|
34
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
35
|
+
"prettier": "^3.7.4",
|
|
36
36
|
"typescript": "5.8.3",
|
|
37
37
|
"typescript-eslint": "8.27.0",
|
|
38
|
-
"vitest": "^4.0.
|
|
38
|
+
"vitest": "^4.0.15"
|
|
39
39
|
},
|
|
40
40
|
"engines": {
|
|
41
41
|
"node": ">=22.18.0",
|