@openzeppelin/relayer-plugin-channels 0.6.0 → 0.7.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 +79 -43
- package/dist/client/channels-client.d.ts +1 -1
- package/dist/client/channels-client.d.ts.map +1 -1
- package/dist/client/channels-client.js +20 -21
- package/dist/client/errors.d.ts +1 -1
- package/dist/client/errors.js +12 -9
- package/dist/client/index.d.ts +3 -3
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/types.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/plugin/build.d.ts +1 -1
- package/dist/plugin/build.d.ts.map +1 -1
- package/dist/plugin/build.js +11 -17
- package/dist/plugin/config.d.ts +4 -2
- package/dist/plugin/config.d.ts.map +1 -1
- package/dist/plugin/config.js +43 -13
- package/dist/plugin/constants.d.ts +1 -0
- package/dist/plugin/constants.d.ts.map +1 -1
- package/dist/plugin/constants.js +2 -1
- package/dist/plugin/fee-tracking.d.ts +2 -2
- package/dist/plugin/fee-tracking.d.ts.map +1 -1
- package/dist/plugin/fee-tracking.js +4 -9
- package/dist/plugin/fee.d.ts +12 -5
- package/dist/plugin/fee.d.ts.map +1 -1
- package/dist/plugin/fee.js +39 -23
- package/dist/plugin/handler.d.ts +2 -2
- package/dist/plugin/handler.d.ts.map +1 -1
- package/dist/plugin/handler.js +38 -33
- package/dist/plugin/index.d.ts +1 -1
- package/dist/plugin/management.d.ts +1 -1
- package/dist/plugin/management.d.ts.map +1 -1
- package/dist/plugin/management.js +53 -79
- package/dist/plugin/pool.d.ts +8 -3
- package/dist/plugin/pool.d.ts.map +1 -1
- package/dist/plugin/pool.js +45 -17
- package/dist/plugin/simulation.d.ts +2 -2
- package/dist/plugin/simulation.d.ts.map +1 -1
- package/dist/plugin/simulation.js +17 -28
- package/dist/plugin/submit.d.ts +5 -5
- package/dist/plugin/submit.d.ts.map +1 -1
- package/dist/plugin/submit.js +11 -14
- package/dist/plugin/tx.d.ts +1 -1
- package/dist/plugin/tx.d.ts.map +1 -1
- package/dist/plugin/tx.js +6 -8
- package/dist/plugin/types.d.ts +4 -4
- package/dist/plugin/types.d.ts.map +1 -1
- package/dist/plugin/validation.d.ts +1 -1
- package/dist/plugin/validation.d.ts.map +1 -1
- package/dist/plugin/validation.js +26 -27
- package/package.json +1 -1
package/dist/plugin/config.js
CHANGED
|
@@ -12,9 +12,9 @@ const relayer_sdk_1 = require("@openzeppelin/relayer-sdk");
|
|
|
12
12
|
const constants_1 = require("./constants");
|
|
13
13
|
function requireEnv(name) {
|
|
14
14
|
const v = process.env[name];
|
|
15
|
-
if (!v || v.trim() ===
|
|
15
|
+
if (!v || v.trim() === '') {
|
|
16
16
|
throw (0, relayer_sdk_1.pluginError)(`Missing required environment variable: ${name}`, {
|
|
17
|
-
code:
|
|
17
|
+
code: 'CONFIG_MISSING',
|
|
18
18
|
status: constants_1.HTTP_STATUS.INTERNAL_SERVER_ERROR,
|
|
19
19
|
details: { name },
|
|
20
20
|
});
|
|
@@ -33,9 +33,7 @@ function parseLockTtl() {
|
|
|
33
33
|
if (!raw)
|
|
34
34
|
return constants_1.CONFIG.DEFAULT_LOCK_TTL_SECONDS;
|
|
35
35
|
const n = Number(raw);
|
|
36
|
-
if (!Number.isFinite(n) ||
|
|
37
|
-
n < constants_1.CONFIG.MIN_LOCK_TTL_SECONDS ||
|
|
38
|
-
n > constants_1.CONFIG.MAX_LOCK_TTL_SECONDS) {
|
|
36
|
+
if (!Number.isFinite(n) || n < constants_1.CONFIG.MIN_LOCK_TTL_SECONDS || n > constants_1.CONFIG.MAX_LOCK_TTL_SECONDS) {
|
|
39
37
|
return constants_1.CONFIG.DEFAULT_LOCK_TTL_SECONDS;
|
|
40
38
|
}
|
|
41
39
|
return Math.floor(n);
|
|
@@ -57,34 +55,66 @@ function parseFeeResetPeriod() {
|
|
|
57
55
|
function parseApiKeyHeader() {
|
|
58
56
|
const raw = process.env.API_KEY_HEADER;
|
|
59
57
|
if (!raw)
|
|
60
|
-
return
|
|
58
|
+
return 'x-api-key';
|
|
61
59
|
const trimmed = raw.trim().toLowerCase();
|
|
62
|
-
return trimmed.length > 0 ? trimmed :
|
|
60
|
+
return trimmed.length > 0 ? trimmed : 'x-api-key';
|
|
61
|
+
}
|
|
62
|
+
function parseLimitedContracts() {
|
|
63
|
+
const raw = process.env.LIMITED_CONTRACTS;
|
|
64
|
+
if (!raw)
|
|
65
|
+
return new Set();
|
|
66
|
+
const contracts = raw
|
|
67
|
+
.split(',')
|
|
68
|
+
.map((s) => s.trim().toUpperCase())
|
|
69
|
+
.filter((s) => s.length > 0);
|
|
70
|
+
// Validate each contract address
|
|
71
|
+
for (const contract of contracts) {
|
|
72
|
+
if (!stellar_sdk_1.StrKey.isValidContract(contract)) {
|
|
73
|
+
throw (0, relayer_sdk_1.pluginError)(`Invalid contract address in LIMITED_CONTRACTS: ${contract}`, {
|
|
74
|
+
code: 'CONFIG_INVALID',
|
|
75
|
+
status: constants_1.HTTP_STATUS.INTERNAL_SERVER_ERROR,
|
|
76
|
+
details: { contract },
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return new Set(contracts);
|
|
81
|
+
}
|
|
82
|
+
function parseContractCapacityRatio() {
|
|
83
|
+
const raw = process.env.CONTRACT_CAPACITY_RATIO;
|
|
84
|
+
if (!raw)
|
|
85
|
+
return constants_1.CONFIG.DEFAULT_CONTRACT_CAPACITY_RATIO;
|
|
86
|
+
const n = Number(raw);
|
|
87
|
+
if (!Number.isFinite(n) || n < 0 || n > 1) {
|
|
88
|
+
return constants_1.CONFIG.DEFAULT_CONTRACT_CAPACITY_RATIO;
|
|
89
|
+
}
|
|
90
|
+
return n;
|
|
63
91
|
}
|
|
64
92
|
/**
|
|
65
93
|
* Load configuration from environment variables
|
|
66
94
|
*/
|
|
67
95
|
function loadConfig() {
|
|
68
|
-
const networkRaw = requireEnv(
|
|
69
|
-
if (networkRaw !==
|
|
96
|
+
const networkRaw = requireEnv('STELLAR_NETWORK').toLowerCase();
|
|
97
|
+
if (networkRaw !== 'testnet' && networkRaw !== 'mainnet') {
|
|
70
98
|
throw (0, relayer_sdk_1.pluginError)('STELLAR_NETWORK must be "testnet" or "mainnet"', {
|
|
71
|
-
code:
|
|
99
|
+
code: 'UNSUPPORTED_NETWORK',
|
|
72
100
|
status: constants_1.HTTP_STATUS.BAD_REQUEST,
|
|
73
101
|
});
|
|
74
102
|
}
|
|
75
103
|
return {
|
|
76
|
-
fundRelayerId: requireEnv(
|
|
104
|
+
fundRelayerId: requireEnv('FUND_RELAYER_ID'),
|
|
77
105
|
network: networkRaw,
|
|
78
106
|
lockTtlSeconds: parseLockTtl(),
|
|
79
|
-
adminSecret: parseOptionalString(
|
|
107
|
+
adminSecret: parseOptionalString('PLUGIN_ADMIN_SECRET'),
|
|
80
108
|
feeLimit: parseFeeLimit(),
|
|
81
109
|
feeResetPeriodMs: parseFeeResetPeriod(),
|
|
82
110
|
apiKeyHeader: parseApiKeyHeader(),
|
|
111
|
+
limitedContracts: parseLimitedContracts(),
|
|
112
|
+
contractCapacityRatio: parseContractCapacityRatio(),
|
|
83
113
|
};
|
|
84
114
|
}
|
|
85
115
|
/**
|
|
86
116
|
* Get the network passphrase based on the configuration
|
|
87
117
|
*/
|
|
88
118
|
function getNetworkPassphrase(network) {
|
|
89
|
-
return network ===
|
|
119
|
+
return network === 'mainnet' ? stellar_sdk_1.Networks.PUBLIC : stellar_sdk_1.Networks.TESTNET;
|
|
90
120
|
}
|
|
@@ -18,6 +18,7 @@ export declare const CONFIG: {
|
|
|
18
18
|
readonly DEFAULT_LOCK_TTL_SECONDS: 30;
|
|
19
19
|
readonly MIN_LOCK_TTL_SECONDS: 3;
|
|
20
20
|
readonly MAX_LOCK_TTL_SECONDS: 30;
|
|
21
|
+
readonly DEFAULT_CONTRACT_CAPACITY_RATIO: 0.8;
|
|
21
22
|
};
|
|
22
23
|
export declare const POOL: {
|
|
23
24
|
readonly MUTEX_TTL_SECONDS: 1;
|
|
@@ -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
|
|
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;;;;;CAKT,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"}
|
package/dist/plugin/constants.js
CHANGED
|
@@ -23,6 +23,7 @@ exports.CONFIG = {
|
|
|
23
23
|
DEFAULT_LOCK_TTL_SECONDS: 30,
|
|
24
24
|
MIN_LOCK_TTL_SECONDS: 3,
|
|
25
25
|
MAX_LOCK_TTL_SECONDS: 30,
|
|
26
|
+
DEFAULT_CONTRACT_CAPACITY_RATIO: 0.8,
|
|
26
27
|
};
|
|
27
28
|
// Pool Constants
|
|
28
29
|
exports.POOL = {
|
|
@@ -39,7 +40,7 @@ exports.TIME = {
|
|
|
39
40
|
};
|
|
40
41
|
// Simulation-related defaults
|
|
41
42
|
exports.SIMULATION = {
|
|
42
|
-
DEFAULT_FEE:
|
|
43
|
+
DEFAULT_FEE: '100',
|
|
43
44
|
MIN_TIME_BOUND: 0,
|
|
44
45
|
MAX_TIME_BOUND_OFFSET_SECONDS: 30,
|
|
45
46
|
MAX_FUTURE_TIME_BOUND_SECONDS: 30,
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Supports custom per-key limits with fallback to default.
|
|
6
6
|
* Supports periodic reset of consumption.
|
|
7
7
|
*/
|
|
8
|
-
import { PluginKVStore } from
|
|
8
|
+
import { PluginKVStore } from '@openzeppelin/relayer-sdk';
|
|
9
9
|
/** KV data structure for fee consumption */
|
|
10
10
|
export interface FeeData {
|
|
11
11
|
consumed: number;
|
|
@@ -22,7 +22,7 @@ export interface UsageInfo {
|
|
|
22
22
|
/** Configuration for FeeTracker */
|
|
23
23
|
export interface FeeTrackerConfig {
|
|
24
24
|
kv: PluginKVStore;
|
|
25
|
-
network:
|
|
25
|
+
network: 'testnet' | 'mainnet';
|
|
26
26
|
apiKey: string;
|
|
27
27
|
defaultLimit?: number;
|
|
28
28
|
resetPeriodMs?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fee-tracking.d.ts","sourceRoot":"","sources":["../../src/plugin/fee-tracking.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,aAAa,EAAe,MAAM,2BAA2B,CAAC;AAGvE,4CAA4C;AAC5C,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAQD,0CAA0C;AAC1C,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,mCAAmC;AACnC,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,aAAa,CAAC;IAClB,OAAO,EAAE,SAAS,GAAG,SAAS,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAgB;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAS;gBAE5B,MAAM,EAAE,gBAAgB;IAUpC;;;OAGG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB7C;;;;OAIG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"fee-tracking.d.ts","sourceRoot":"","sources":["../../src/plugin/fee-tracking.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,aAAa,EAAe,MAAM,2BAA2B,CAAC;AAGvE,4CAA4C;AAC5C,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAQD,0CAA0C;AAC1C,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,mCAAmC;AACnC,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,aAAa,CAAC;IAClB,OAAO,EAAE,SAAS,GAAG,SAAS,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAgB;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAS;gBAE5B,MAAM,EAAE,gBAAgB;IAUpC;;;OAGG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB7C;;;;OAIG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4B7C;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,SAAS,CAAC;IAexC;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAKnD;;OAEG;IACG,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD;;OAEG;IACG,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAMxC;;;OAGG;YACW,WAAW;IASzB;;OAEG;YACW,iBAAiB;IAK/B;;OAEG;IACH,OAAO,CAAC,eAAe;IAKvB,OAAO,KAAK,WAAW,GAEtB;IAED,OAAO,KAAK,QAAQ,GAEnB;IAED,OAAO,KAAK,OAAO,GAElB;CACF"}
|
|
@@ -31,7 +31,7 @@ class FeeTracker {
|
|
|
31
31
|
if (consumed + fee > limit) {
|
|
32
32
|
const remaining = limit - consumed;
|
|
33
33
|
throw (0, relayer_sdk_1.pluginError)(`Transaction fee (${fee} stroops) exceeds remaining budget (${remaining} stroops). Consumed: ${consumed}/${limit} stroops.`, {
|
|
34
|
-
code:
|
|
34
|
+
code: 'FEE_LIMIT_EXCEEDED',
|
|
35
35
|
status: constants_1.HTTP_STATUS.TOO_MANY_REQUESTS,
|
|
36
36
|
details: { consumed, fee, remaining, limit },
|
|
37
37
|
});
|
|
@@ -54,7 +54,7 @@ class FeeTracker {
|
|
|
54
54
|
periodStart: state.periodStart ?? now,
|
|
55
55
|
});
|
|
56
56
|
return true;
|
|
57
|
-
}, { ttlSec: 5, onBusy:
|
|
57
|
+
}, { ttlSec: 5, onBusy: 'skip' });
|
|
58
58
|
if (result !== null)
|
|
59
59
|
return;
|
|
60
60
|
// Lock busy, retry with jitter
|
|
@@ -71,17 +71,12 @@ class FeeTracker {
|
|
|
71
71
|
* Get complete usage info for management API.
|
|
72
72
|
*/
|
|
73
73
|
async getUsageInfo() {
|
|
74
|
-
const [state, limit] = await Promise.all([
|
|
75
|
-
this.getFeeState(),
|
|
76
|
-
this.getEffectiveLimit(),
|
|
77
|
-
]);
|
|
74
|
+
const [state, limit] = await Promise.all([this.getFeeState(), this.getEffectiveLimit()]);
|
|
78
75
|
return {
|
|
79
76
|
consumed: state.consumed,
|
|
80
77
|
limit,
|
|
81
78
|
remaining: limit !== undefined ? Math.max(0, limit - state.consumed) : undefined,
|
|
82
|
-
periodStartAt: state.periodStart
|
|
83
|
-
? new Date(state.periodStart).toISOString()
|
|
84
|
-
: undefined,
|
|
79
|
+
periodStartAt: state.periodStart ? new Date(state.periodStart).toISOString() : undefined,
|
|
85
80
|
periodEndsAt: state.periodStart && this.resetPeriodMs
|
|
86
81
|
? new Date(state.periodStart + this.resetPeriodMs).toISOString()
|
|
87
82
|
: undefined,
|
package/dist/plugin/fee.d.ts
CHANGED
|
@@ -4,11 +4,18 @@
|
|
|
4
4
|
* Static fee calculation for fee bump submissions matching launchtube.
|
|
5
5
|
* - For Soroban transactions: use resourceFee + inclusion fee (BASE_FEE * 2 + 3)
|
|
6
6
|
* - For non-Soroban: use NON_SOROBAN_FEE + inclusion fee
|
|
7
|
-
* -
|
|
7
|
+
* - Limited contracts (from LIMITED_CONTRACTS env) get reduced fee (BASE_FEE * 2 + 1)
|
|
8
8
|
*/
|
|
9
|
-
import { Transaction } from
|
|
10
|
-
export declare const KALE_CONTRACT = "CDL74RF5BLYR2YBLCCI7F5FB6TPSCLKEJUBSD2RSVWZ4YHF3VMFAIGWA";
|
|
9
|
+
import { Transaction, xdr } from '@stellar/stellar-sdk';
|
|
11
10
|
export declare const INCLUSION_FEE_DEFAULT: number;
|
|
12
|
-
export declare const
|
|
13
|
-
|
|
11
|
+
export declare const INCLUSION_FEE_LIMITED: number;
|
|
12
|
+
/**
|
|
13
|
+
* Extract contract ID from a HostFunction (for func+auth flow)
|
|
14
|
+
*/
|
|
15
|
+
export declare function getContractIdFromFunc(func: xdr.HostFunction): string | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Extract contract ID from a Transaction (for XDR flow)
|
|
18
|
+
*/
|
|
19
|
+
export declare function getContractIdFromTransaction(transaction: Transaction): string | undefined;
|
|
20
|
+
export declare function calculateMaxFee(transaction: Transaction, limitedContracts?: Set<string>): number;
|
|
14
21
|
//# sourceMappingURL=fee.d.ts.map
|
package/dist/plugin/fee.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fee.d.ts","sourceRoot":"","sources":["../../src/plugin/fee.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"fee.d.ts","sourceRoot":"","sources":["../../src/plugin/fee.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAY,WAAW,EAAE,GAAG,EAAqB,MAAM,sBAAsB,CAAC;AAGrF,eAAO,MAAM,qBAAqB,QAA2B,CAAC;AAC9D,eAAO,MAAM,qBAAqB,QAA2B,CAAC;AAE9D;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,GAAG,CAAC,YAAY,GAAG,MAAM,GAAG,SAAS,CAUhF;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,WAAW,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAYzF;AASD,wBAAgB,eAAe,CAAC,WAAW,EAAE,WAAW,EAAE,gBAAgB,GAAE,GAAG,CAAC,MAAM,CAAa,GAAG,MAAM,CAoB3G"}
|
package/dist/plugin/fee.js
CHANGED
|
@@ -5,39 +5,56 @@
|
|
|
5
5
|
* Static fee calculation for fee bump submissions matching launchtube.
|
|
6
6
|
* - For Soroban transactions: use resourceFee + inclusion fee (BASE_FEE * 2 + 3)
|
|
7
7
|
* - For non-Soroban: use NON_SOROBAN_FEE + inclusion fee
|
|
8
|
-
* -
|
|
8
|
+
* - Limited contracts (from LIMITED_CONTRACTS env) get reduced fee (BASE_FEE * 2 + 1)
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
-
exports.
|
|
11
|
+
exports.INCLUSION_FEE_LIMITED = exports.INCLUSION_FEE_DEFAULT = void 0;
|
|
12
|
+
exports.getContractIdFromFunc = getContractIdFromFunc;
|
|
13
|
+
exports.getContractIdFromTransaction = getContractIdFromTransaction;
|
|
12
14
|
exports.calculateMaxFee = calculateMaxFee;
|
|
13
15
|
const stellar_sdk_1 = require("@stellar/stellar-sdk");
|
|
14
16
|
const constants_1 = require("./constants");
|
|
15
|
-
exports.KALE_CONTRACT = "CDL74RF5BLYR2YBLCCI7F5FB6TPSCLKEJUBSD2RSVWZ4YHF3VMFAIGWA";
|
|
16
17
|
exports.INCLUSION_FEE_DEFAULT = Number(stellar_sdk_1.BASE_FEE) * 2 + 3;
|
|
17
|
-
exports.
|
|
18
|
-
|
|
18
|
+
exports.INCLUSION_FEE_LIMITED = Number(stellar_sdk_1.BASE_FEE) * 2 + 1;
|
|
19
|
+
/**
|
|
20
|
+
* Extract contract ID from a HostFunction (for func+auth flow)
|
|
21
|
+
*/
|
|
22
|
+
function getContractIdFromFunc(func) {
|
|
23
|
+
try {
|
|
24
|
+
if (func.switch() !== stellar_sdk_1.xdr.HostFunctionType.hostFunctionTypeInvokeContract()) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
const invokeContract = func.invokeContract();
|
|
28
|
+
return stellar_sdk_1.StrKey.encodeContract(invokeContract.contractAddress().contractId());
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Extract contract ID from a Transaction (for XDR flow)
|
|
36
|
+
*/
|
|
37
|
+
function getContractIdFromTransaction(transaction) {
|
|
19
38
|
try {
|
|
20
39
|
if (transaction.operations.length !== 1)
|
|
21
|
-
return
|
|
40
|
+
return undefined;
|
|
22
41
|
const op = transaction.operations[0];
|
|
23
|
-
if (op.type !==
|
|
24
|
-
return
|
|
42
|
+
if (op.type !== 'invokeHostFunction')
|
|
43
|
+
return undefined;
|
|
25
44
|
const invokeOp = op;
|
|
26
|
-
|
|
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;
|
|
45
|
+
return getContractIdFromFunc(invokeOp.func);
|
|
35
46
|
}
|
|
36
47
|
catch {
|
|
37
|
-
return
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function getInclusionFee(contractId, limitedContracts) {
|
|
52
|
+
if (contractId && limitedContracts.has(contractId)) {
|
|
53
|
+
return exports.INCLUSION_FEE_LIMITED;
|
|
38
54
|
}
|
|
55
|
+
return exports.INCLUSION_FEE_DEFAULT;
|
|
39
56
|
}
|
|
40
|
-
function calculateMaxFee(transaction) {
|
|
57
|
+
function calculateMaxFee(transaction, limitedContracts = new Set()) {
|
|
41
58
|
const envelope = transaction.toEnvelope();
|
|
42
59
|
let resourceFee = 0n;
|
|
43
60
|
if (envelope.switch() === stellar_sdk_1.xdr.EnvelopeType.envelopeTypeTx()) {
|
|
@@ -46,10 +63,9 @@ function calculateMaxFee(transaction) {
|
|
|
46
63
|
resourceFee = sorobanData.resourceFee().toBigInt();
|
|
47
64
|
}
|
|
48
65
|
}
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
: BigInt(constants_1.FEE.NON_SOROBAN_FEE + inclusionFee);
|
|
66
|
+
const contractId = getContractIdFromTransaction(transaction);
|
|
67
|
+
const inclusionFee = getInclusionFee(contractId, limitedContracts);
|
|
68
|
+
const fee = resourceFee > 0n ? resourceFee + BigInt(inclusionFee) : BigInt(constants_1.FEE.NON_SOROBAN_FEE + inclusionFee);
|
|
53
69
|
console.debug(`[channels] Calculated max_fee: ${Number(fee)} stroops (resourceFee: ${resourceFee}, inclusionFee: ${inclusionFee})`);
|
|
54
70
|
return Number(fee);
|
|
55
71
|
}
|
package/dist/plugin/handler.d.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Main handler for the channel accounts plugin.
|
|
5
5
|
* Orchestrates the transaction processing pipeline using channel accounts with fee bumping.
|
|
6
6
|
*/
|
|
7
|
-
import { PluginContext } from
|
|
8
|
-
import { Transaction, xdr } from
|
|
7
|
+
import { PluginContext } from '@openzeppelin/relayer-sdk';
|
|
8
|
+
import { Transaction, xdr } from '@stellar/stellar-sdk';
|
|
9
9
|
/**
|
|
10
10
|
* Extracts func and auth from an unsigned Soroban transaction.
|
|
11
11
|
* Returns null if the transaction is not a single invokeHostFunction operation.
|
|
@@ -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;AASvE,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAC;
|
|
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;AAWxD;;;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;AAwND;;GAEG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,CAElE"}
|
package/dist/plugin/handler.js
CHANGED
|
@@ -44,14 +44,14 @@ function extractFuncAuthFromUnsignedXdr(tx) {
|
|
|
44
44
|
auth: invokeHostFn.auth(),
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
|
-
async function handleXdrSubmit(xdrStr, fundRelayer, fundAddress, network, networkPassphrase, api, pool, tracker) {
|
|
47
|
+
async function handleXdrSubmit(xdrStr, fundRelayer, fundAddress, network, networkPassphrase, api, pool, acquireOptions, tracker) {
|
|
48
48
|
const tx = new stellar_sdk_1.Transaction(xdrStr, networkPassphrase);
|
|
49
49
|
// Unsigned XDR: extract func+auth and route through channel path
|
|
50
50
|
if (tx.signatures.length === 0) {
|
|
51
51
|
const extracted = extractFuncAuthFromUnsignedXdr(tx);
|
|
52
52
|
if (!extracted) {
|
|
53
|
-
throw (0, relayer_sdk_1.pluginError)(
|
|
54
|
-
code:
|
|
53
|
+
throw (0, relayer_sdk_1.pluginError)('Unsigned XDR must contain exactly one invokeHostFunction operation', {
|
|
54
|
+
code: 'INVALID_UNSIGNED_XDR',
|
|
55
55
|
status: constants_1.HTTP_STATUS.BAD_REQUEST,
|
|
56
56
|
details: {
|
|
57
57
|
operationCount: tx.operations.length,
|
|
@@ -60,41 +60,41 @@ async function handleXdrSubmit(xdrStr, fundRelayer, fundAddress, network, networ
|
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
console.log(`[channels] Unsigned XDR detected, extracting func+auth and routing through channel path`);
|
|
63
|
-
|
|
63
|
+
// Update acquireOptions with contractId from extracted func
|
|
64
|
+
const contractId = (0, fee_1.getContractIdFromFunc)(extracted.func);
|
|
65
|
+
const updatedOptions = { ...acquireOptions, contractId };
|
|
66
|
+
return handleFuncAuthSubmit(extracted.func, extracted.auth, api, pool, fundRelayer, fundAddress, network, networkPassphrase, updatedOptions, tracker);
|
|
64
67
|
}
|
|
65
68
|
const validated = (0, tx_1.validateExistingTransactionForSubmitOnly)(tx);
|
|
66
|
-
const maxFee = (0, fee_1.calculateMaxFee)(validated);
|
|
69
|
+
const maxFee = (0, fee_1.calculateMaxFee)(validated, acquireOptions.limitedContracts);
|
|
67
70
|
await tracker?.checkBudget(maxFee);
|
|
68
71
|
return (0, submit_1.submitWithFeeBumpAndWait)(fundRelayer, validated.toXDR(), network, maxFee, api, tracker);
|
|
69
72
|
}
|
|
70
|
-
async function handleFuncAuthSubmit(func, auth, api, pool, fundRelayer, fundAddress, network, networkPassphrase, tracker) {
|
|
73
|
+
async function handleFuncAuthSubmit(func, auth, api, pool, fundRelayer, fundAddress, network, networkPassphrase, acquireOptions, tracker) {
|
|
71
74
|
let poolLock;
|
|
72
75
|
try {
|
|
73
|
-
poolLock = await pool.acquire();
|
|
76
|
+
poolLock = await pool.acquire(acquireOptions);
|
|
74
77
|
const channelRelayer = api.useRelayer(poolLock.relayerId);
|
|
75
78
|
const channelInfo = await channelRelayer.getRelayer();
|
|
76
79
|
console.log(`[channels] Acquired channel: ${poolLock.relayerId}`);
|
|
77
80
|
if (!channelInfo || !channelInfo.address) {
|
|
78
|
-
throw (0, relayer_sdk_1.pluginError)(
|
|
79
|
-
code:
|
|
81
|
+
throw (0, relayer_sdk_1.pluginError)('Channel relayer not found', {
|
|
82
|
+
code: 'RELAYER_UNAVAILABLE',
|
|
80
83
|
status: constants_1.HTTP_STATUS.BAD_GATEWAY,
|
|
81
84
|
details: { relayerId: poolLock.relayerId },
|
|
82
85
|
});
|
|
83
86
|
}
|
|
84
87
|
const channelStatus = await channelRelayer.getRelayerStatus();
|
|
85
|
-
if (channelStatus.network_type !==
|
|
86
|
-
throw (0, relayer_sdk_1.pluginError)(
|
|
87
|
-
code:
|
|
88
|
+
if (channelStatus.network_type !== 'stellar') {
|
|
89
|
+
throw (0, relayer_sdk_1.pluginError)('Channel relayer network type must be stellar', {
|
|
90
|
+
code: 'UNSUPPORTED_NETWORK',
|
|
88
91
|
status: constants_1.HTTP_STATUS.BAD_REQUEST,
|
|
89
|
-
details: {
|
|
90
|
-
network_type: channelStatus.network_type,
|
|
91
|
-
relayerId: poolLock.relayerId,
|
|
92
|
-
},
|
|
92
|
+
details: { network_type: channelStatus.network_type, relayerId: poolLock.relayerId },
|
|
93
93
|
});
|
|
94
94
|
}
|
|
95
95
|
const built = await (0, simulation_1.simulateAndBuildWithChannel)(func, auth, { address: channelInfo.address, sequence: channelStatus.sequence_number }, fundAddress, fundRelayer, networkPassphrase);
|
|
96
96
|
const signedTx = await (0, submit_1.signWithChannelAndFund)(built, channelRelayer, fundRelayer, channelInfo.address, fundAddress, networkPassphrase);
|
|
97
|
-
const maxFee = (0, fee_1.calculateMaxFee)(signedTx);
|
|
97
|
+
const maxFee = (0, fee_1.calculateMaxFee)(signedTx, acquireOptions.limitedContracts);
|
|
98
98
|
await tracker?.checkBudget(maxFee);
|
|
99
99
|
return await (0, submit_1.submitWithFeeBumpAndWait)(fundRelayer, signedTx.toXDR(), network, maxFee, api, tracker);
|
|
100
100
|
}
|
|
@@ -119,8 +119,8 @@ async function channelAccounts(context) {
|
|
|
119
119
|
const apiKey = getApiKey(headers, config.apiKeyHeader);
|
|
120
120
|
// If default limit is set, require API key
|
|
121
121
|
if (config.feeLimit !== undefined && !apiKey) {
|
|
122
|
-
throw (0, relayer_sdk_1.pluginError)(
|
|
123
|
-
code:
|
|
122
|
+
throw (0, relayer_sdk_1.pluginError)('API key required', {
|
|
123
|
+
code: 'API_KEY_REQUIRED',
|
|
124
124
|
status: constants_1.HTTP_STATUS.BAD_REQUEST,
|
|
125
125
|
});
|
|
126
126
|
}
|
|
@@ -136,34 +136,39 @@ async function channelAccounts(context) {
|
|
|
136
136
|
}
|
|
137
137
|
// 1. Validate and parse request (xdr OR func+auth)
|
|
138
138
|
const request = (0, validation_1.validateAndParseRequest)(params);
|
|
139
|
-
console.debug(`[channels] Request type: ${request.type}, auth entries: ${request.type ===
|
|
139
|
+
console.debug(`[channels] Request type: ${request.type}, auth entries: ${request.type === 'func-auth' ? request.auth.length : 'N/A'}`);
|
|
140
140
|
// 2. Get fund relayer
|
|
141
141
|
const fundRelayer = api.useRelayer(config.fundRelayerId);
|
|
142
142
|
const fundInfo = await fundRelayer.getRelayer();
|
|
143
143
|
if (!fundInfo || !fundInfo.address) {
|
|
144
|
-
throw (0, relayer_sdk_1.pluginError)(
|
|
145
|
-
code:
|
|
144
|
+
throw (0, relayer_sdk_1.pluginError)('Fund relayer not found', {
|
|
145
|
+
code: 'RELAYER_UNAVAILABLE',
|
|
146
146
|
status: constants_1.HTTP_STATUS.BAD_GATEWAY,
|
|
147
147
|
details: { relayerId: config.fundRelayerId },
|
|
148
148
|
});
|
|
149
149
|
}
|
|
150
|
-
if (fundInfo.network_type !==
|
|
151
|
-
throw (0, relayer_sdk_1.pluginError)(
|
|
152
|
-
code:
|
|
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
153
|
status: constants_1.HTTP_STATUS.BAD_REQUEST,
|
|
154
|
-
details: {
|
|
155
|
-
network_type: fundInfo.network_type,
|
|
156
|
-
relayerId: config.fundRelayerId,
|
|
157
|
-
},
|
|
154
|
+
details: { network_type: fundInfo.network_type, relayerId: config.fundRelayerId },
|
|
158
155
|
});
|
|
159
156
|
}
|
|
160
|
-
// 3.
|
|
161
|
-
|
|
157
|
+
// 3. Build acquire options for contract capacity limits
|
|
158
|
+
const acquireOptions = {
|
|
159
|
+
limitedContracts: config.limitedContracts,
|
|
160
|
+
capacityRatio: config.contractCapacityRatio,
|
|
161
|
+
};
|
|
162
|
+
// 4. Branch by request type
|
|
163
|
+
if (request.type === 'xdr') {
|
|
162
164
|
console.log(`[channels] Flow: XDR submit-only`);
|
|
163
|
-
return await handleXdrSubmit(request.xdr, fundRelayer, fundInfo.address, config.network, networkPassphrase, api, pool, tracker);
|
|
165
|
+
return await handleXdrSubmit(request.xdr, fundRelayer, fundInfo.address, config.network, networkPassphrase, api, pool, acquireOptions, tracker);
|
|
164
166
|
}
|
|
167
|
+
// Extract contractId for func+auth flow
|
|
168
|
+
const contractId = (0, fee_1.getContractIdFromFunc)(request.func);
|
|
169
|
+
const funcAcquireOptions = { ...acquireOptions, contractId };
|
|
165
170
|
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);
|
|
171
|
+
return await handleFuncAuthSubmit(request.func, request.auth, api, pool, fundRelayer, fundInfo.address, config.network, networkPassphrase, funcAcquireOptions, tracker);
|
|
167
172
|
}
|
|
168
173
|
/**
|
|
169
174
|
* Main plugin handler exported for OpenZeppelin Relayer
|
package/dist/plugin/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* - setFeeLimit: sets custom limit for an API key
|
|
10
10
|
* - deleteFeeLimit: removes custom limit for an API key
|
|
11
11
|
*/
|
|
12
|
-
import type { PluginContext } from
|
|
12
|
+
import type { PluginContext } from '@openzeppelin/relayer-sdk';
|
|
13
13
|
export declare function isManagementRequest(params: any): boolean;
|
|
14
14
|
export declare function handleManagement(context: PluginContext): Promise<any>;
|
|
15
15
|
//# sourceMappingURL=management.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"management.d.ts","sourceRoot":"","sources":["../../src/plugin/management.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,2BAA2B,CAAC;AAM9E,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"management.d.ts","sourceRoot":"","sources":["../../src/plugin/management.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,2BAA2B,CAAC;AAM9E,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAIxD;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,CAkC3E"}
|