@towns-labs/relayer 2.1.0 → 2.1.1
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/dist/README.md +1 -1
- package/dist/index.js +112 -120
- package/dist/index.js.map +3 -3
- package/package.json +2 -2
- package/src/rpc/schema/prepareCalls.ts +2 -20
- package/src/rpc/schema/sendPreparedCalls.ts +1 -2
package/dist/README.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
This folder contains the built output assets for the worker "relayer-worker" generated at 2026-02-
|
|
1
|
+
This folder contains the built output assets for the worker "relayer-worker" generated at 2026-02-16T21:41:19.331Z.
|
package/dist/index.js
CHANGED
|
@@ -23663,6 +23663,7 @@ var ACCOUNT_NOT_DELEGATED = -32009;
|
|
|
23663
23663
|
var QUOTE_EXPIRED = -32010;
|
|
23664
23664
|
var INVALID_QUOTE_SIGNATURE = -32011;
|
|
23665
23665
|
var PAYMENT_EXCEEDS_MAX = -32012;
|
|
23666
|
+
var DRAFT_CONFLICT = -32013;
|
|
23666
23667
|
var ERROR_MESSAGES = {
|
|
23667
23668
|
[PARSE_ERROR]: "Parse error",
|
|
23668
23669
|
[INVALID_REQUEST]: "Invalid Request",
|
|
@@ -23681,7 +23682,8 @@ var ERROR_MESSAGES = {
|
|
|
23681
23682
|
[ACCOUNT_NOT_DELEGATED]: "Account not delegated",
|
|
23682
23683
|
[QUOTE_EXPIRED]: "Quote expired",
|
|
23683
23684
|
[INVALID_QUOTE_SIGNATURE]: "Invalid quote signature",
|
|
23684
|
-
[PAYMENT_EXCEEDS_MAX]: "Payment amount exceeds maximum"
|
|
23685
|
+
[PAYMENT_EXCEEDS_MAX]: "Payment amount exceeds maximum",
|
|
23686
|
+
[DRAFT_CONFLICT]: "Draft conflict"
|
|
23685
23687
|
};
|
|
23686
23688
|
var RpcError2 = class extends Error {
|
|
23687
23689
|
static {
|
|
@@ -23827,58 +23829,35 @@ var INTENT_TYPES = {
|
|
|
23827
23829
|
// src/services/relayer.ts
|
|
23828
23830
|
function createIntentNonceProvider(durableObject, chainId) {
|
|
23829
23831
|
const durableObjectNameFor = /* @__PURE__ */ __name((eoa) => `${chainId}:${eoa.toLowerCase()}`, "durableObjectNameFor");
|
|
23832
|
+
const getStubForEoa = /* @__PURE__ */ __name((eoa) => {
|
|
23833
|
+
const id = durableObject.idFromName(durableObjectNameFor(eoa));
|
|
23834
|
+
return durableObject.get(id);
|
|
23835
|
+
}, "getStubForEoa");
|
|
23836
|
+
const postToNonceDo = /* @__PURE__ */ __name(async (eoa, path, body) => {
|
|
23837
|
+
const stub = getStubForEoa(eoa);
|
|
23838
|
+
return stub.fetch(
|
|
23839
|
+
new Request(`http://do/${path}`, {
|
|
23840
|
+
method: "POST",
|
|
23841
|
+
headers: { "Content-Type": "application/json" },
|
|
23842
|
+
body: JSON.stringify(body)
|
|
23843
|
+
})
|
|
23844
|
+
);
|
|
23845
|
+
}, "postToNonceDo");
|
|
23830
23846
|
return {
|
|
23831
|
-
async acquireNonce(eoa, seqKey = 0n) {
|
|
23832
|
-
const id = durableObject.idFromName(durableObjectNameFor(eoa));
|
|
23833
|
-
const stub = durableObject.get(id);
|
|
23834
|
-
const response = await stub.fetch(
|
|
23835
|
-
new Request("http://do/acquire", {
|
|
23836
|
-
method: "POST",
|
|
23837
|
-
headers: { "Content-Type": "application/json" },
|
|
23838
|
-
body: JSON.stringify({ seqKey: seqKey.toString() })
|
|
23839
|
-
})
|
|
23840
|
-
);
|
|
23841
|
-
if (!response.ok) {
|
|
23842
|
-
const error48 = await response.json();
|
|
23843
|
-
throw new Error(`Failed to acquire intent nonce: ${error48.error}`);
|
|
23844
|
-
}
|
|
23845
|
-
const data = await response.json();
|
|
23846
|
-
return BigInt(data.nonce);
|
|
23847
|
-
},
|
|
23848
|
-
async acquireNonceSynced(eoa, seqKey, onChainSeq) {
|
|
23849
|
-
const id = durableObject.idFromName(durableObjectNameFor(eoa));
|
|
23850
|
-
const stub = durableObject.get(id);
|
|
23851
|
-
const response = await stub.fetch(
|
|
23852
|
-
new Request("http://do/acquire_synced", {
|
|
23853
|
-
method: "POST",
|
|
23854
|
-
headers: { "Content-Type": "application/json" },
|
|
23855
|
-
body: JSON.stringify({
|
|
23856
|
-
seqKey: seqKey.toString(),
|
|
23857
|
-
onChainSeq: onChainSeq.toString()
|
|
23858
|
-
})
|
|
23859
|
-
})
|
|
23860
|
-
);
|
|
23861
|
-
if (!response.ok) {
|
|
23862
|
-
const error48 = await response.json();
|
|
23863
|
-
throw new Error(`Failed to acquire synced intent nonce: ${error48.error}`);
|
|
23864
|
-
}
|
|
23865
|
-
const data = await response.json();
|
|
23866
|
-
return { nonce: BigInt(data.nonce), synced: data.synced };
|
|
23867
|
-
},
|
|
23868
23847
|
async acquireOrGetDraft(eoa, seqKey, onChainSeq, prepareKey) {
|
|
23869
|
-
const
|
|
23870
|
-
|
|
23871
|
-
|
|
23872
|
-
|
|
23873
|
-
|
|
23874
|
-
|
|
23875
|
-
|
|
23876
|
-
|
|
23877
|
-
|
|
23878
|
-
|
|
23879
|
-
|
|
23880
|
-
}
|
|
23881
|
-
|
|
23848
|
+
const response = await postToNonceDo(eoa, "acquire_or_get_draft", {
|
|
23849
|
+
seqKey: seqKey.toString(),
|
|
23850
|
+
onChainSeq: onChainSeq.toString(),
|
|
23851
|
+
draftKey: prepareKey
|
|
23852
|
+
});
|
|
23853
|
+
if (response.status === 409) {
|
|
23854
|
+
const body = await response.json();
|
|
23855
|
+
return {
|
|
23856
|
+
conflict: true,
|
|
23857
|
+
error: body.error,
|
|
23858
|
+
conflictDraftId: body.conflictDraftId
|
|
23859
|
+
};
|
|
23860
|
+
}
|
|
23882
23861
|
if (!response.ok) {
|
|
23883
23862
|
const error48 = await response.json();
|
|
23884
23863
|
throw new Error(`Failed to acquire intent draft: ${error48.error}`);
|
|
@@ -23893,55 +23872,16 @@ function createIntentNonceProvider(durableObject, chainId) {
|
|
|
23893
23872
|
};
|
|
23894
23873
|
},
|
|
23895
23874
|
async markSubmitted(eoa, seqKey, draftId) {
|
|
23896
|
-
const
|
|
23897
|
-
|
|
23898
|
-
|
|
23899
|
-
|
|
23900
|
-
method: "POST",
|
|
23901
|
-
headers: { "Content-Type": "application/json" },
|
|
23902
|
-
body: JSON.stringify({ seqKey: seqKey.toString(), draftId })
|
|
23903
|
-
})
|
|
23904
|
-
);
|
|
23875
|
+
const response = await postToNonceDo(eoa, "mark_submitted", {
|
|
23876
|
+
seqKey: seqKey.toString(),
|
|
23877
|
+
draftId
|
|
23878
|
+
});
|
|
23905
23879
|
if (!response.ok) {
|
|
23906
23880
|
const error48 = await response.json();
|
|
23907
23881
|
throw new Error(`Failed to mark intent draft submitted: ${error48.error}`);
|
|
23908
23882
|
}
|
|
23909
23883
|
const data = await response.json();
|
|
23910
23884
|
return data.status;
|
|
23911
|
-
},
|
|
23912
|
-
async cancelDraft(eoa, seqKey, draftId) {
|
|
23913
|
-
const id = durableObject.idFromName(durableObjectNameFor(eoa));
|
|
23914
|
-
const stub = durableObject.get(id);
|
|
23915
|
-
const response = await stub.fetch(
|
|
23916
|
-
new Request("http://do/cancel_draft", {
|
|
23917
|
-
method: "POST",
|
|
23918
|
-
headers: { "Content-Type": "application/json" },
|
|
23919
|
-
body: JSON.stringify({ seqKey: seqKey.toString(), draftId })
|
|
23920
|
-
})
|
|
23921
|
-
);
|
|
23922
|
-
if (!response.ok) {
|
|
23923
|
-
const error48 = await response.json();
|
|
23924
|
-
throw new Error(`Failed to cancel intent draft: ${error48.error}`);
|
|
23925
|
-
}
|
|
23926
|
-
const data = await response.json();
|
|
23927
|
-
return data.status;
|
|
23928
|
-
},
|
|
23929
|
-
async syncNonce(eoa, seqKey, confirmedSeq) {
|
|
23930
|
-
const id = durableObject.idFromName(durableObjectNameFor(eoa));
|
|
23931
|
-
const stub = durableObject.get(id);
|
|
23932
|
-
const response = await stub.fetch(
|
|
23933
|
-
new Request("http://do/sync", {
|
|
23934
|
-
method: "POST",
|
|
23935
|
-
headers: { "Content-Type": "application/json" },
|
|
23936
|
-
body: JSON.stringify({
|
|
23937
|
-
seqKey: seqKey.toString(),
|
|
23938
|
-
confirmedSeq: confirmedSeq.toString()
|
|
23939
|
-
})
|
|
23940
|
-
})
|
|
23941
|
-
);
|
|
23942
|
-
if (!response.ok) {
|
|
23943
|
-
console.warn("Failed to sync intent nonce for:", eoa);
|
|
23944
|
-
}
|
|
23945
23885
|
}
|
|
23946
23886
|
};
|
|
23947
23887
|
}
|
|
@@ -23983,6 +23923,14 @@ function calculateCombinedGas(simulationGas, gasConfig, paymentEnabled) {
|
|
|
23983
23923
|
return simulationGas + gasConfig.intentGasBuffer + (paymentEnabled ? gasConfig.paymentGasBuffer : 0n);
|
|
23984
23924
|
}
|
|
23985
23925
|
__name(calculateCombinedGas, "calculateCombinedGas");
|
|
23926
|
+
function normalizeCalls(calls) {
|
|
23927
|
+
return calls.map((call2) => ({
|
|
23928
|
+
to: call2.to,
|
|
23929
|
+
value: call2.value ? BigInt(call2.value) : 0n,
|
|
23930
|
+
data: call2.data ?? "0x"
|
|
23931
|
+
}));
|
|
23932
|
+
}
|
|
23933
|
+
__name(normalizeCalls, "normalizeCalls");
|
|
23986
23934
|
function extractRevertData(error48) {
|
|
23987
23935
|
if (error48 instanceof BaseError2) {
|
|
23988
23936
|
const rawError = error48.walk((e) => e instanceof RawContractError);
|
|
@@ -24069,12 +24017,20 @@ var RelayerService = class {
|
|
|
24069
24017
|
async acquireNonceFromProvider(eoa, seqKey, prepareKey) {
|
|
24070
24018
|
const onChainSeq = await this.fetchOnChainSeq(eoa, seqKey);
|
|
24071
24019
|
const safeOnChainSeq = onChainSeq ?? 0n;
|
|
24072
|
-
const
|
|
24020
|
+
const result = await this.intentNonceProvider.acquireOrGetDraft(
|
|
24073
24021
|
eoa,
|
|
24074
24022
|
seqKey,
|
|
24075
24023
|
safeOnChainSeq,
|
|
24076
24024
|
prepareKey
|
|
24077
24025
|
);
|
|
24026
|
+
if ("conflict" in result) {
|
|
24027
|
+
return {
|
|
24028
|
+
success: false,
|
|
24029
|
+
error: result.error,
|
|
24030
|
+
conflictDraftId: result.conflictDraftId
|
|
24031
|
+
};
|
|
24032
|
+
}
|
|
24033
|
+
const { nonce, draftId, expiresAtMs, fromCache } = result;
|
|
24078
24034
|
if (onChainSeq === null) {
|
|
24079
24035
|
this.logger.debug(
|
|
24080
24036
|
{ eoa, nonce: nonce.toString(), draftId },
|
|
@@ -24141,11 +24097,7 @@ var RelayerService = class {
|
|
|
24141
24097
|
errorCode: "DELEGATION_PENDING"
|
|
24142
24098
|
};
|
|
24143
24099
|
}
|
|
24144
|
-
const calls = request.calls
|
|
24145
|
-
to: c2.to,
|
|
24146
|
-
value: c2.value ? BigInt(c2.value) : 0n,
|
|
24147
|
-
data: c2.data ?? "0x"
|
|
24148
|
-
}));
|
|
24100
|
+
const calls = normalizeCalls(request.calls);
|
|
24149
24101
|
const executionData = encodeAbiParameters(
|
|
24150
24102
|
[
|
|
24151
24103
|
{
|
|
@@ -24161,6 +24113,17 @@ var RelayerService = class {
|
|
|
24161
24113
|
);
|
|
24162
24114
|
const nowSeconds = BigInt(Math.floor(Date.now() / 1e3));
|
|
24163
24115
|
const expiry = resolveIntentExpirySeconds(request.expiry, nowSeconds);
|
|
24116
|
+
let signature = request.signature ?? "0x";
|
|
24117
|
+
if (request.sessionKey && !request.signature) {
|
|
24118
|
+
const keyHash = keccak256(
|
|
24119
|
+
encodeAbiParameters(parseAbiParameters("uint8, bytes32"), [
|
|
24120
|
+
0,
|
|
24121
|
+
// secp256k1
|
|
24122
|
+
keccak256(request.sessionKey)
|
|
24123
|
+
])
|
|
24124
|
+
);
|
|
24125
|
+
signature = concat([`0x${"00".repeat(65)}`, keyHash, "0x00"]);
|
|
24126
|
+
}
|
|
24164
24127
|
const intent = {
|
|
24165
24128
|
eoa: request.eoa,
|
|
24166
24129
|
executionData,
|
|
@@ -24180,7 +24143,7 @@ var RelayerService = class {
|
|
|
24180
24143
|
settlerContext: request.settlerContext ?? "0x",
|
|
24181
24144
|
paymentAmount: BigInt(request.paymentAmount ?? "0"),
|
|
24182
24145
|
paymentRecipient: request.paymentRecipient ?? zeroAddress,
|
|
24183
|
-
signature
|
|
24146
|
+
signature,
|
|
24184
24147
|
paymentSignature: request.paymentSignature ?? "0x",
|
|
24185
24148
|
supportedAccountImplementation: request.supportedAccountImplementation ?? zeroAddress
|
|
24186
24149
|
};
|
|
@@ -24294,7 +24257,11 @@ var RelayerService = class {
|
|
|
24294
24257
|
request.prepareKey
|
|
24295
24258
|
);
|
|
24296
24259
|
if (!nonceResult.success) {
|
|
24297
|
-
return {
|
|
24260
|
+
return {
|
|
24261
|
+
success: false,
|
|
24262
|
+
error: nonceResult.error,
|
|
24263
|
+
conflictDraftId: nonceResult.conflictDraftId
|
|
24264
|
+
};
|
|
24298
24265
|
}
|
|
24299
24266
|
const nonce = nonceResult.nonce;
|
|
24300
24267
|
const nowSeconds = BigInt(Math.floor(Date.now() / 1e3));
|
|
@@ -24306,11 +24273,7 @@ var RelayerService = class {
|
|
|
24306
24273
|
const paymentToken = request.paymentToken ?? zeroAddress;
|
|
24307
24274
|
const paymentMaxAmount = BigInt(request.paymentMaxAmount ?? "0");
|
|
24308
24275
|
const paymentEnabled = isPaymentEnabled(payer, paymentToken);
|
|
24309
|
-
const calls = request.calls
|
|
24310
|
-
to: c2.to,
|
|
24311
|
-
value: c2.value ? BigInt(c2.value) : 0n,
|
|
24312
|
-
data: c2.data ?? "0x"
|
|
24313
|
-
}));
|
|
24276
|
+
const calls = normalizeCalls(request.calls);
|
|
24314
24277
|
const simulateResult = await this.simulateIntent({
|
|
24315
24278
|
eoa: request.eoa,
|
|
24316
24279
|
calls: request.calls,
|
|
@@ -24321,7 +24284,8 @@ var RelayerService = class {
|
|
|
24321
24284
|
settler: request.settler,
|
|
24322
24285
|
payer: request.payer,
|
|
24323
24286
|
paymentToken: request.paymentToken,
|
|
24324
|
-
paymentMaxAmount: request.paymentMaxAmount
|
|
24287
|
+
paymentMaxAmount: request.paymentMaxAmount,
|
|
24288
|
+
sessionKey: request.sessionKey
|
|
24325
24289
|
});
|
|
24326
24290
|
const simulationGas = simulateResult.gasUsed ? BigInt(simulateResult.gasUsed) : 0n;
|
|
24327
24291
|
const simulationSucceeded = simulateResult.success && simulationGas > 0n;
|
|
@@ -40081,9 +40045,22 @@ async function handlePrepareCalls(params, ctx) {
|
|
|
40081
40045
|
payer,
|
|
40082
40046
|
paymentToken,
|
|
40083
40047
|
paymentMaxAmount,
|
|
40084
|
-
prepareKey
|
|
40048
|
+
prepareKey,
|
|
40049
|
+
sessionKey: typedParams.session_key
|
|
40085
40050
|
});
|
|
40086
40051
|
if (!result.success || !result.typedData || !result.digest) {
|
|
40052
|
+
if (result.conflictDraftId) {
|
|
40053
|
+
throw new RpcError2(DRAFT_CONFLICT, result.error ?? "Draft conflict", {
|
|
40054
|
+
conflictDraftId: result.conflictDraftId
|
|
40055
|
+
});
|
|
40056
|
+
}
|
|
40057
|
+
const rawError = result.error ?? "Failed to prepare calls";
|
|
40058
|
+
if (rawError.toLowerCase().startsWith("simulation failed")) {
|
|
40059
|
+
const cause = rawError.replace(/^simulation failed:\s*/i, "").trim();
|
|
40060
|
+
throw new RpcError2(SIMULATION_FAILED, "Simulation failed", {
|
|
40061
|
+
cause: cause.length > 0 ? cause : void 0
|
|
40062
|
+
});
|
|
40063
|
+
}
|
|
40087
40064
|
throw new RpcError2(INTERNAL_ERROR, result.error ?? "Failed to prepare calls");
|
|
40088
40065
|
}
|
|
40089
40066
|
const feeConfig = getFeeConfig(env);
|
|
@@ -40218,14 +40195,6 @@ async function handlePrepareCalls(params, ctx) {
|
|
|
40218
40195
|
message[key] = value;
|
|
40219
40196
|
}
|
|
40220
40197
|
}
|
|
40221
|
-
let callKey;
|
|
40222
|
-
if (typedParams.key) {
|
|
40223
|
-
callKey = {
|
|
40224
|
-
type: typedParams.key.type,
|
|
40225
|
-
publicKey: typedParams.key.public_key,
|
|
40226
|
-
prehash: typedParams.key.prehash ?? false
|
|
40227
|
-
};
|
|
40228
|
-
}
|
|
40229
40198
|
return {
|
|
40230
40199
|
context: preparedContext,
|
|
40231
40200
|
digest: result.digest,
|
|
@@ -40241,7 +40210,6 @@ async function handlePrepareCalls(params, ctx) {
|
|
|
40241
40210
|
feeTotals: {},
|
|
40242
40211
|
assetDiffs: {}
|
|
40243
40212
|
},
|
|
40244
|
-
key: callKey,
|
|
40245
40213
|
signature: signedQuotes.signature
|
|
40246
40214
|
};
|
|
40247
40215
|
}
|
|
@@ -45445,11 +45413,17 @@ __name(isPendingTransactionIdUniqueConstraintError, "isPendingTransactionIdUniqu
|
|
|
45445
45413
|
|
|
45446
45414
|
// src/durable-objects/signer.do.ts
|
|
45447
45415
|
var DEFAULT_MAX_PENDING = 16;
|
|
45448
|
-
var BALANCE_CHECK_INTERVAL_MS =
|
|
45416
|
+
var BALANCE_CHECK_INTERVAL_MS = 3e5;
|
|
45449
45417
|
var STALE_TX_THRESHOLD_MS = 5 * 60 * 1e3;
|
|
45450
45418
|
var DEFAULT_INTENT_EXPIRY_BUFFER_SECONDS = 30;
|
|
45451
45419
|
var DUPLICATE_TX_WAIT_MS = 2e3;
|
|
45452
45420
|
var DUPLICATE_TX_WAIT_POLL_MS = 100;
|
|
45421
|
+
function mapStoredTxStatusToPublicStatus(storedStatus) {
|
|
45422
|
+
if (storedStatus === "confirmed") return "confirmed";
|
|
45423
|
+
if (storedStatus === "failed" || storedStatus === "stuck") return "failed";
|
|
45424
|
+
return "pending";
|
|
45425
|
+
}
|
|
45426
|
+
__name(mapStoredTxStatusToPublicStatus, "mapStoredTxStatusToPublicStatus");
|
|
45453
45427
|
function isIntentExpired(expiryTimestamp, bufferSeconds = DEFAULT_INTENT_EXPIRY_BUFFER_SECONDS) {
|
|
45454
45428
|
const expiry = typeof expiryTimestamp === "string" ? BigInt(expiryTimestamp) : expiryTimestamp;
|
|
45455
45429
|
const currentTime = BigInt(Math.floor(Date.now() / 1e3));
|
|
@@ -45884,11 +45858,12 @@ var SignerDO = class extends DurableObject {
|
|
|
45884
45858
|
}
|
|
45885
45859
|
} catch {
|
|
45886
45860
|
}
|
|
45861
|
+
const fallbackStatus = mapStoredTxStatusToPublicStatus(status);
|
|
45887
45862
|
return {
|
|
45888
45863
|
txId,
|
|
45889
45864
|
txHash,
|
|
45890
45865
|
chainId,
|
|
45891
|
-
status:
|
|
45866
|
+
status: fallbackStatus,
|
|
45892
45867
|
submittedAt: sentAt
|
|
45893
45868
|
};
|
|
45894
45869
|
}
|
|
@@ -47760,6 +47735,15 @@ var IntentNonceDO = class extends DurableObject4 {
|
|
|
47760
47735
|
draftKey,
|
|
47761
47736
|
draftTtlMs: coerceDraftTtlMs(draftTtlMs)
|
|
47762
47737
|
});
|
|
47738
|
+
if ("error" in result) {
|
|
47739
|
+
return Response.json(
|
|
47740
|
+
{
|
|
47741
|
+
error: result.error,
|
|
47742
|
+
conflictDraftId: result.conflictDraftId
|
|
47743
|
+
},
|
|
47744
|
+
{ status: 409 }
|
|
47745
|
+
);
|
|
47746
|
+
}
|
|
47763
47747
|
return Response.json({
|
|
47764
47748
|
nonce: result.nonce.toString(),
|
|
47765
47749
|
draftId: result.draftId,
|
|
@@ -47941,6 +47925,14 @@ var IntentNonceDO = class extends DurableObject4 {
|
|
|
47941
47925
|
this.deleteExpiredDraftForSeqKey(key, nowMs);
|
|
47942
47926
|
const existingDraft = this.getDraftForSeqKey(key);
|
|
47943
47927
|
if (existingDraft !== null) {
|
|
47928
|
+
const incomingDraftKey = options.draftKey ?? null;
|
|
47929
|
+
const existingDraftKey = existingDraft.draft_key;
|
|
47930
|
+
if (incomingDraftKey !== existingDraftKey) {
|
|
47931
|
+
return {
|
|
47932
|
+
error: "draft already exists for seqKey with a different draftKey; complete or cancel the active request first",
|
|
47933
|
+
conflictDraftId: existingDraft.draft_id
|
|
47934
|
+
};
|
|
47935
|
+
}
|
|
47944
47936
|
return {
|
|
47945
47937
|
nonce: BigInt(existingDraft.nonce),
|
|
47946
47938
|
draftId: existingDraft.draft_id,
|