@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 CHANGED
@@ -1 +1 @@
1
- This folder contains the built output assets for the worker "relayer-worker" generated at 2026-02-13T08:35:58.863Z.
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 id = durableObject.idFromName(durableObjectNameFor(eoa));
23870
- const stub = durableObject.get(id);
23871
- const response = await stub.fetch(
23872
- new Request("http://do/acquire_or_get_draft", {
23873
- method: "POST",
23874
- headers: { "Content-Type": "application/json" },
23875
- body: JSON.stringify({
23876
- seqKey: seqKey.toString(),
23877
- onChainSeq: onChainSeq.toString(),
23878
- draftKey: prepareKey
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 id = durableObject.idFromName(durableObjectNameFor(eoa));
23897
- const stub = durableObject.get(id);
23898
- const response = await stub.fetch(
23899
- new Request("http://do/mark_submitted", {
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 { nonce, draftId, expiresAtMs, fromCache } = await this.intentNonceProvider.acquireOrGetDraft(
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.map((c2) => ({
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: request.signature ?? "0x",
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 { success: false, error: nonceResult.error };
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.map((c2) => ({
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 = 6e4;
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: "pending",
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,