@openzeppelin/relayer-plugin-channels 0.20.0 → 0.21.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.
@@ -20,5 +20,10 @@ export declare function getContractIdFromFunc(func: xdr.HostFunction): string |
20
20
  * Extract contract ID from a Transaction (for XDR flow)
21
21
  */
22
22
  export declare function getContractIdFromTransaction(transaction: Transaction): string | undefined;
23
+ /**
24
+ * Compute the maximum fee (in stroops) for a transaction: the Soroban resource
25
+ * fee declared in the envelope's sorobanData (0 for classic transactions) plus
26
+ * the inclusion fee, which is higher when the invoked contract is rate-limited.
27
+ */
23
28
  export declare function calculateMaxFee(transaction: Transaction, limitedContracts: Set<string>, fees: InclusionFees): number;
24
29
  //# sourceMappingURL=fee.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fee.d.ts","sourceRoot":"","sources":["../../src/plugin/fee.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,WAAW,EAAE,GAAG,EAAqB,MAAM,sBAAsB,CAAC;AAG3E,MAAM,WAAW,aAAa;IAC5B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED;;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,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,aAAa,GAAG,MAAM,CAgCpH"}
1
+ {"version":3,"file":"fee.d.ts","sourceRoot":"","sources":["../../src/plugin/fee.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,WAAW,EAAE,GAAG,EAAqB,MAAM,sBAAsB,CAAC;AAG3E,MAAM,WAAW,aAAa;IAC5B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,GAAG,CAAC,YAAY,GAAG,MAAM,GAAG,SAAS,CAahF;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,WAAW,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAYzF;AASD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,aAAa,GAAG,MAAM,CAgCpH"}
@@ -19,11 +19,14 @@ const constants_1 = require("./constants");
19
19
  */
20
20
  function getContractIdFromFunc(func) {
21
21
  try {
22
- if (func.switch() !== stellar_sdk_1.xdr.HostFunctionType.hostFunctionTypeInvokeContract()) {
22
+ if (func.type !== 'hostFunctionTypeInvokeContract') {
23
23
  return undefined;
24
24
  }
25
- const invokeContract = func.invokeContract();
26
- return stellar_sdk_1.StrKey.encodeContract(invokeContract.contractAddress().contractId());
25
+ const contractAddress = func.invokeContract.contractAddress;
26
+ if (contractAddress.type !== 'scAddressTypeContract') {
27
+ return undefined;
28
+ }
29
+ return stellar_sdk_1.StrKey.encodeContract(contractAddress.contractId.toBytes());
27
30
  }
28
31
  catch {
29
32
  return undefined;
@@ -52,13 +55,18 @@ function getInclusionFee(contractId, limitedContracts, fees) {
52
55
  }
53
56
  return fees.inclusionFeeDefault;
54
57
  }
58
+ /**
59
+ * Compute the maximum fee (in stroops) for a transaction: the Soroban resource
60
+ * fee declared in the envelope's sorobanData (0 for classic transactions) plus
61
+ * the inclusion fee, which is higher when the invoked contract is rate-limited.
62
+ */
55
63
  function calculateMaxFee(transaction, limitedContracts, fees) {
56
64
  const envelope = transaction.toEnvelope();
57
65
  let resourceFee = 0n;
58
- if (envelope.switch() === stellar_sdk_1.xdr.EnvelopeType.envelopeTypeTx()) {
59
- const sorobanData = envelope.v1().tx().ext().sorobanData();
60
- if (sorobanData) {
61
- resourceFee = sorobanData.resourceFee().toBigInt();
66
+ if (envelope.type === 'envelopeTypeTx') {
67
+ const ext = envelope.v1.tx.ext;
68
+ if (ext.type === 'sorobanData') {
69
+ resourceFee = ext.sorobanData.resourceFee;
62
70
  }
63
71
  }
64
72
  const contractId = getContractIdFromTransaction(transaction);
@@ -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;AACvE,OAAO,KAAK,EAA4B,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAQnF,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAaxD,8EAA8E;AAC9E,KAAK,iBAAiB,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;AAUnE,kDAAkD;AAClD,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAanC;AAiCD;;;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;AA6UD;;GAEG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,CAElE"}
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;AACvE,OAAO,KAAK,EAA4B,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAQnF,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAaxD,8EAA8E;AAC9E,KAAK,iBAAiB,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;AAUnE,kDAAkD;AAClD,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAanC;AAiCD;;;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,CAqB1E;AA6UD;;GAEG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,CAElE"}
@@ -77,14 +77,17 @@ function extractFuncAuthFromUnsignedXdr(tx) {
77
77
  return null;
78
78
  }
79
79
  const envelope = tx.toEnvelope();
80
- const rawOp = envelope.v1().tx().operations()[0].body();
81
- if (rawOp.switch() !== stellar_sdk_1.xdr.OperationType.invokeHostFunction()) {
80
+ if (envelope.type !== 'envelopeTypeTx') {
82
81
  return null;
83
82
  }
84
- const invokeHostFn = rawOp.invokeHostFunctionOp();
83
+ const rawOp = envelope.v1.tx.operations[0].body;
84
+ if (rawOp.type !== 'invokeHostFunction') {
85
+ return null;
86
+ }
87
+ const invokeHostFn = rawOp.invokeHostFunctionOp;
85
88
  return {
86
- func: invokeHostFn.hostFunction(),
87
- auth: invokeHostFn.auth(),
89
+ func: invokeHostFn.hostFunction,
90
+ auth: [...invokeHostFn.auth],
88
91
  };
89
92
  }
90
93
  async function handleXdrSubmit(xdrStr, ctx, skipWait) {
@@ -116,7 +119,7 @@ async function handleXdrSubmit(xdrStr, ctx, skipWait) {
116
119
  contractId,
117
120
  isLimited: contractId ? ctx.acquireOptions.limitedContracts.has(contractId) : false,
118
121
  };
119
- return (0, submit_1.submitWithFeeBumpAndWait)(ctx.fundRelayer, validated.toXDR(), ctx.network, maxFee, ctx.api, ctx.startTime, ctx.tracker, submitContext, skipWait, ctx.config);
122
+ return (0, submit_1.submitWithFeeBumpAndWait)(ctx.fundRelayer, validated.toXdr(), ctx.network, maxFee, ctx.api, ctx.startTime, ctx.tracker, submitContext, skipWait, ctx.config);
120
123
  }
121
124
  async function handleFuncAuthSubmit(func, auth, ctx, skipWait) {
122
125
  // Simulate once — used for both read-only detection and transaction assembly
@@ -167,7 +170,7 @@ async function handleFuncAuthSubmit(func, auth, ctx, skipWait) {
167
170
  isLimited: contractId ? ctx.acquireOptions.limitedContracts.has(contractId) : false,
168
171
  };
169
172
  try {
170
- const result = await (0, submit_1.submitWithFeeBumpAndWait)(ctx.fundRelayer, signedTx.toXDR(), ctx.network, maxFee, ctx.api, ctx.startTime, ctx.tracker, submitContext, skipWait, ctx.config);
173
+ const result = await (0, submit_1.submitWithFeeBumpAndWait)(ctx.fundRelayer, signedTx.toXdr(), ctx.network, maxFee, ctx.api, ctx.startTime, ctx.tracker, submitContext, skipWait, ctx.config);
171
174
  if (result.status === 'pending' || result.status === 'sent' || result.status === 'submitted') {
172
175
  const extendSec = computeExtendTtlSec(txBuildTime, ctx.config.maxTimeBoundOffsetSeconds);
173
176
  console.log(`[channels]: extending lock (${extendSec}s) and clearing sequence`);
@@ -1 +1 @@
1
- {"version":3,"file":"sequence.d.ts","sourceRoot":"","sources":["../../src/plugin/sequence.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAwBxE;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAsH3F;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,EAAE,EAAE,aAAa,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,MAAM,EACf,2BAA2B,EAAE,MAAM,GAClC,OAAO,CAAC,MAAM,CAAC,CAwBjB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,EAAE,EAAE,aAAa,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAaf;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWtG"}
1
+ {"version":3,"file":"sequence.d.ts","sourceRoot":"","sources":["../../src/plugin/sequence.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAwBxE;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAyH3F;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,EAAE,EAAE,aAAa,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,MAAM,EACf,2BAA2B,EAAE,MAAM,GAClC,OAAO,CAAC,MAAM,CAAC,CAwBjB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,EAAE,EAAE,aAAa,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAaf;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWtG"}
@@ -56,7 +56,7 @@ async function getAccountSequence(relayer, address) {
56
56
  id: Math.floor(Math.random() * 1e8).toString(),
57
57
  method: 'getLedgerEntries',
58
58
  params: {
59
- keys: [accountKey.toXDR('base64')],
59
+ keys: [accountKey.toXdr('base64')],
60
60
  },
61
61
  });
62
62
  }
@@ -126,8 +126,11 @@ async function getAccountSequence(relayer, address) {
126
126
  });
127
127
  }
128
128
  try {
129
- const accountEntry = stellar_sdk_1.xdr.LedgerEntryData.fromXDR(firstEntryXdr, 'base64');
130
- return accountEntry.account().seqNum().toString();
129
+ const accountEntry = stellar_sdk_1.xdr.LedgerEntryData.fromXdr(firstEntryXdr, 'base64');
130
+ if (accountEntry.type !== 'account') {
131
+ throw new Error(`Unexpected ledger entry type: ${accountEntry.type}`);
132
+ }
133
+ return accountEntry.account.seqNum.toString();
131
134
  }
132
135
  catch (error) {
133
136
  console.error('[channels] Sequence fetch failed', {
@@ -40,6 +40,16 @@ export declare function simulateTransaction(func: xdr.HostFunction, auth: xdr.So
40
40
  * obtained simulation result. No additional network calls are made.
41
41
  */
42
42
  export declare function buildWithChannel(func: xdr.HostFunction, auth: xdr.SorobanAuthorizationEntry[] | undefined, channel: ChannelAccount, networkPassphrase: string, simResult: rpc.Api.RawSimulateTransactionResponse, minSignatureExpirationLedgerBuffer?: number, maxTimeBoundOffsetSeconds?: number): Transaction;
43
+ /**
44
+ * Return the signatureExpirationLedger of an address-credentialed auth entry,
45
+ * or undefined for source-account credentials (which carry no expiry).
46
+ *
47
+ * Reads the expiry directly off the credential arm so every variant is covered
48
+ * with an exhaustive switch: legacy `sorobanCredentialsAddress`, CAP-71
49
+ * `sorobanCredentialsAddressV2` (introduced in Protocol 27, the stellar-sdk v17
50
+ * default) and `sorobanCredentialsAddressWithDelegates`.
51
+ */
52
+ export declare function getAddressCredentialExpiry(entry: xdr.SorobanAuthorizationEntry): number | undefined;
43
53
  /** Extract human-readable message + error type from simulation error diagnostic events */
44
54
  export declare function parseSimulationError(error: string): string;
45
55
  //# sourceMappingURL=simulation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"simulation.d.ts","sourceRoot":"","sources":["../../src/plugin/simulation.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAsB,GAAG,EAAE,WAAW,EAAsB,GAAG,EAAE,MAAM,sBAAsB,CAAC;AACrG,OAAO,EAAgD,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAGlG,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,UAAU,EAAE,OAAO,CAAC;IACpB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,YAAY,EAAE,GAAG,CAAC,GAAG,CAAC,8BAA8B,CAAC;CACtD;AAOD;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,GAAG,CAAC,YAAY,EACtB,IAAI,EAAE,GAAG,CAAC,yBAAyB,EAAE,GAAG,SAAS,EACjD,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,OAAO,EAChB,iBAAiB,EAAE,MAAM,EACzB,yBAAyB,GAAE,MAA2C,GACrE,OAAO,CAAC,gBAAgB,CAAC,CAiF3B;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,GAAG,CAAC,YAAY,EACtB,IAAI,EAAE,GAAG,CAAC,yBAAyB,EAAE,GAAG,SAAS,EACjD,OAAO,EAAE,cAAc,EACvB,iBAAiB,EAAE,MAAM,EACzB,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,8BAA8B,EACjD,kCAAkC,GAAE,MAA0D,EAC9F,yBAAyB,GAAE,MAA2C,GACrE,WAAW,CA+Fb;AA4CD,0FAA0F;AAC1F,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAe1D"}
1
+ {"version":3,"file":"simulation.d.ts","sourceRoot":"","sources":["../../src/plugin/simulation.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAsB,GAAG,EAAE,WAAW,EAAsB,GAAG,EAAE,MAAM,sBAAsB,CAAC;AACrG,OAAO,EAAgD,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAGlG,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,UAAU,EAAE,OAAO,CAAC;IACpB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,YAAY,EAAE,GAAG,CAAC,GAAG,CAAC,8BAA8B,CAAC;CACtD;AAOD;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,GAAG,CAAC,YAAY,EACtB,IAAI,EAAE,GAAG,CAAC,yBAAyB,EAAE,GAAG,SAAS,EACjD,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,OAAO,EAChB,iBAAiB,EAAE,MAAM,EACzB,yBAAyB,GAAE,MAA2C,GACrE,OAAO,CAAC,gBAAgB,CAAC,CAiF3B;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,GAAG,CAAC,YAAY,EACtB,IAAI,EAAE,GAAG,CAAC,yBAAyB,EAAE,GAAG,SAAS,EACjD,OAAO,EAAE,cAAc,EACvB,iBAAiB,EAAE,MAAM,EACzB,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,8BAA8B,EACjD,kCAAkC,GAAE,MAA0D,EAC9F,yBAAyB,GAAE,MAA2C,GACrE,WAAW,CAiGb;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,GAAG,CAAC,yBAAyB,GAAG,MAAM,GAAG,SAAS,CAYnG;AA2CD,0FAA0F;AAC1F,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAe1D"}
@@ -10,6 +10,7 @@
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
11
  exports.simulateTransaction = simulateTransaction;
12
12
  exports.buildWithChannel = buildWithChannel;
13
+ exports.getAddressCredentialExpiry = getAddressCredentialExpiry;
13
14
  exports.parseSimulationError = parseSimulationError;
14
15
  const stellar_sdk_1 = require("@stellar/stellar-sdk");
15
16
  const relayer_sdk_1 = require("@openzeppelin/relayer-sdk");
@@ -42,7 +43,7 @@ async function simulateTransaction(func, auth, sourceAddress, relayer, networkPa
42
43
  id: Math.floor(Math.random() * 1e8).toString(),
43
44
  method: 'simulateTransaction',
44
45
  // Enforce mode validates auth entry signatures during simulation.
45
- params: { transaction: transaction.toXDR(), authMode: constants_1.SIMULATION.SIMULATION_AUTH_MODE },
46
+ params: { transaction: transaction.toXdr(), authMode: constants_1.SIMULATION.SIMULATION_AUTH_MODE },
46
47
  });
47
48
  }
48
49
  catch (err) {
@@ -81,8 +82,8 @@ async function simulateTransaction(func, auth, sourceAddress, relayer, networkPa
81
82
  let hasReadWrite = false;
82
83
  if (simResult.transactionData) {
83
84
  try {
84
- const sorobanData = stellar_sdk_1.xdr.SorobanTransactionData.fromXDR(simResult.transactionData, 'base64');
85
- hasReadWrite = sorobanData.resources().footprint().readWrite().length > 0;
85
+ const sorobanData = stellar_sdk_1.xdr.SorobanTransactionData.fromXdr(simResult.transactionData, 'base64');
86
+ hasReadWrite = sorobanData.resources.footprint.readWrite.length > 0;
86
87
  }
87
88
  catch {
88
89
  // If we can't parse transactionData, treat as not read-only (safe fallback)
@@ -114,7 +115,7 @@ function buildWithChannel(func, auth, channel, networkPassphrase, simResult, min
114
115
  // Parse sorobanData from the simulation result
115
116
  let sorobanData;
116
117
  try {
117
- sorobanData = stellar_sdk_1.xdr.SorobanTransactionData.fromXDR(simResult.transactionData, 'base64');
118
+ sorobanData = stellar_sdk_1.xdr.SorobanTransactionData.fromXdr(simResult.transactionData, 'base64');
118
119
  }
119
120
  catch (err) {
120
121
  throw (0, relayer_sdk_1.pluginError)('Failed to parse simulation transactionData', {
@@ -130,7 +131,7 @@ function buildWithChannel(func, auth, channel, networkPassphrase, simResult, min
130
131
  }
131
132
  else {
132
133
  try {
133
- resolvedAuth = (simResult.results?.[0]?.auth ?? []).map((a) => stellar_sdk_1.xdr.SorobanAuthorizationEntry.fromXDR(a, 'base64'));
134
+ resolvedAuth = (simResult.results?.[0]?.auth ?? []).map((a) => stellar_sdk_1.xdr.SorobanAuthorizationEntry.fromXdr(a, 'base64'));
134
135
  }
135
136
  catch (err) {
136
137
  throw (0, relayer_sdk_1.pluginError)('Failed to parse simulation auth entries', {
@@ -177,7 +178,9 @@ function buildWithChannel(func, auth, channel, networkPassphrase, simResult, min
177
178
  }))
178
179
  .build();
179
180
  try {
180
- const resourceFee = transaction.toEnvelope().v1().tx().ext().sorobanData()?.resourceFee();
181
+ const envelope = transaction.toEnvelope();
182
+ const ext = envelope.type === 'envelopeTypeTx' ? envelope.v1.tx.ext : undefined;
183
+ const resourceFee = ext?.type === 'sorobanData' ? ext.sorobanData.resourceFee : undefined;
181
184
  console.debug(`[channels] Assembly complete: fee=${transaction.fee}, resourceFee=${resourceFee}`);
182
185
  return transaction;
183
186
  }
@@ -192,6 +195,28 @@ function buildWithChannel(func, auth, channel, networkPassphrase, simResult, min
192
195
  });
193
196
  }
194
197
  }
198
+ /**
199
+ * Return the signatureExpirationLedger of an address-credentialed auth entry,
200
+ * or undefined for source-account credentials (which carry no expiry).
201
+ *
202
+ * Reads the expiry directly off the credential arm so every variant is covered
203
+ * with an exhaustive switch: legacy `sorobanCredentialsAddress`, CAP-71
204
+ * `sorobanCredentialsAddressV2` (introduced in Protocol 27, the stellar-sdk v17
205
+ * default) and `sorobanCredentialsAddressWithDelegates`.
206
+ */
207
+ function getAddressCredentialExpiry(entry) {
208
+ const creds = entry.credentials;
209
+ switch (creds.type) {
210
+ case 'sorobanCredentialsSourceAccount':
211
+ return undefined;
212
+ case 'sorobanCredentialsAddress':
213
+ return creds.address.signatureExpirationLedger;
214
+ case 'sorobanCredentialsAddressV2':
215
+ return creds.addressV2.signatureExpirationLedger;
216
+ case 'sorobanCredentialsAddressWithDelegates':
217
+ return creds.addressWithDelegates.addressCredentials.signatureExpirationLedger;
218
+ }
219
+ }
195
220
  /**
196
221
  * Reject transactions where any address-credentialed auth entry has a
197
222
  * signatureExpirationLedger too close to the current ledger. This catches
@@ -201,11 +226,10 @@ function validateAuthExpiry(authEntries, latestLedger, minBuffer) {
201
226
  if (!authEntries?.length)
202
227
  return;
203
228
  for (const entry of authEntries) {
204
- const creds = entry.credentials();
205
- if (creds.switch() !== stellar_sdk_1.xdr.SorobanCredentialsType.sorobanCredentialsAddress()) {
229
+ const expiry = getAddressCredentialExpiry(entry);
230
+ if (expiry === undefined) {
206
231
  continue;
207
232
  }
208
- const expiry = creds.address().signatureExpirationLedger();
209
233
  const margin = expiry - latestLedger;
210
234
  if (margin < minBuffer) {
211
235
  console.error(`[channels] Auth entry signatureExpirationLedger too close to current ledger (expires in ${margin} ledgers, minimum ${minBuffer} required)`);
@@ -1 +1 @@
1
- {"version":3,"file":"submit.d.ts","sourceRoot":"","sources":["../../src/plugin/submit.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAO,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAEL,OAAO,EAGP,SAAS,EACV,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5C,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,UAAU,wBAAwB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;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,CAuBtB;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,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,UAAU,EACpB,OAAO,CAAC,EAAE,aAAa,EACvB,QAAQ,CAAC,EAAE,OAAO,EAClB,MAAM,CAAC,EAAE,IAAI,CAAC,qBAAqB,EAAE,iBAAiB,GAAG,kBAAkB,CAAC,GAC3E,OAAO,CAAC,uBAAuB,CAAC,CAmHlC;AASD,oEAAoE;AACpE,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,wBAAwB,GAAG,IAAI,CAkCvF;AAeD,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAepG;AAED,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOrD"}
1
+ {"version":3,"file":"submit.d.ts","sourceRoot":"","sources":["../../src/plugin/submit.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAO,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAEL,OAAO,EAGP,SAAS,EACV,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5C,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,UAAU,wBAAwB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;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,CAuBtB;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,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,UAAU,EACpB,OAAO,CAAC,EAAE,aAAa,EACvB,QAAQ,CAAC,EAAE,OAAO,EAClB,MAAM,CAAC,EAAE,IAAI,CAAC,qBAAqB,EAAE,iBAAiB,GAAG,kBAAkB,CAAC,GAC3E,OAAO,CAAC,uBAAuB,CAAC,CAmHlC;AASD,oEAAoE;AACpE,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,wBAAwB,GAAG,IAAI,CAmCvF;AAeD,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAepG;AAED,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOrD"}
@@ -20,7 +20,7 @@ const constants_1 = require("./constants");
20
20
  * - Both signatures are added to the transaction
21
21
  */
22
22
  async function signWithChannelAndFund(transaction, channelRelayer, _fundRelayer, channelAddress, _fundAddress, networkPassphrase) {
23
- const txXdr = transaction.toXDR();
23
+ const txXdr = transaction.toXdr();
24
24
  console.debug(`[channels] Signing transaction with channel (${channelAddress})`);
25
25
  // Get signatures from both accounts sequentially
26
26
  // Channel signs first
@@ -172,14 +172,16 @@ function decodeTransactionResult(reason) {
172
172
  const match = reason.match(/([A-Za-z0-9+/=]{20,})$/);
173
173
  if (!match)
174
174
  return null;
175
- const result = stellar_sdk_1.xdr.TransactionResult.fromXDR(match[1], 'base64');
176
- const outerResultCode = String(result.result().switch().name);
175
+ const result = stellar_sdk_1.xdr.TransactionResult.fromXdr(match[1], 'base64');
176
+ const outerResultCode = result.result.type;
177
177
  let resultCode = outerResultCode;
178
178
  // Unwrap fee bump inner failure to get the actual result code
179
179
  if (outerResultCode === 'txFeeBumpInnerFailed') {
180
180
  try {
181
- const innerResult = result.result().innerResultPair().result();
182
- const innerResultCode = String(innerResult.result().switch().name);
181
+ const outer = result.result;
182
+ if (outer.type !== 'txFeeBumpInnerFailed')
183
+ throw new Error('unreachable');
184
+ const innerResultCode = outer.innerResultPair.result.result.type;
183
185
  resultCode = `${outerResultCode}:${innerResultCode}`;
184
186
  }
185
187
  catch {
@@ -187,7 +189,7 @@ function decodeTransactionResult(reason) {
187
189
  }
188
190
  }
189
191
  return {
190
- feeCharged: Number(result.feeCharged().toBigInt()),
192
+ feeCharged: Number(result.feeCharged),
191
193
  resultCode,
192
194
  };
193
195
  }
@@ -5,5 +5,12 @@
5
5
  */
6
6
  import { Transaction } from '@stellar/stellar-sdk';
7
7
  import type { ChannelAccountsConfig } from './config';
8
+ /**
9
+ * Validate a client-built transaction for the XDR submit-only path.
10
+ *
11
+ * Rejects fee-bump envelopes, Soroban transactions whose fee exceeds the
12
+ * declared resource fee plus the base inclusion fee, and a `timeBounds.maxTime`
13
+ * that is already in the past or further out than the configured maximum offset.
14
+ */
8
15
  export declare function validateExistingTransactionForSubmitOnly(tx: Transaction, config: Pick<ChannelAccountsConfig, 'maxTimeBoundOffsetSeconds'>): Transaction;
9
16
  //# sourceMappingURL=tx.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tx.d.ts","sourceRoot":"","sources":["../../src/plugin/tx.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAO,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAGtD,wBAAgB,wCAAwC,CACtD,EAAE,EAAE,WAAW,EACf,MAAM,EAAE,IAAI,CAAC,qBAAqB,EAAE,2BAA2B,CAAC,GAC/D,WAAW,CAmDb"}
1
+ {"version":3,"file":"tx.d.ts","sourceRoot":"","sources":["../../src/plugin/tx.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEnD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAGtD;;;;;;GAMG;AACH,wBAAgB,wCAAwC,CACtD,EAAE,EAAE,WAAW,EACf,MAAM,EAAE,IAAI,CAAC,qBAAqB,EAAE,2BAA2B,CAAC,GAC/D,WAAW,CAkDb"}
package/dist/plugin/tx.js CHANGED
@@ -6,25 +6,30 @@
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.validateExistingTransactionForSubmitOnly = validateExistingTransactionForSubmitOnly;
9
- const stellar_sdk_1 = require("@stellar/stellar-sdk");
10
9
  const relayer_sdk_1 = require("@openzeppelin/relayer-sdk");
11
10
  const constants_1 = require("./constants");
11
+ /**
12
+ * Validate a client-built transaction for the XDR submit-only path.
13
+ *
14
+ * Rejects fee-bump envelopes, Soroban transactions whose fee exceeds the
15
+ * declared resource fee plus the base inclusion fee, and a `timeBounds.maxTime`
16
+ * that is already in the past or further out than the configured maximum offset.
17
+ */
12
18
  function validateExistingTransactionForSubmitOnly(tx, config) {
13
19
  const { maxTimeBoundOffsetSeconds } = config;
14
20
  const now = Math.floor(Date.now() / 1000);
15
21
  // Reject fee-bump envelopes
16
22
  const envelope = tx.toEnvelope();
17
- const kind = envelope.switch();
18
- if (kind !== stellar_sdk_1.xdr.EnvelopeType.envelopeTypeTx()) {
23
+ if (envelope.type !== 'envelopeTypeTx') {
19
24
  throw (0, relayer_sdk_1.pluginError)('Input must be a regular transaction envelope (fee-bump not allowed)', {
20
25
  code: 'INVALID_ENVELOPE_TYPE',
21
26
  status: constants_1.HTTP_STATUS.BAD_REQUEST,
22
27
  });
23
28
  }
24
29
  // Soroban sanity checks
25
- const sorobanData = envelope.v1().tx().ext().sorobanData();
26
- if (sorobanData) {
27
- const resourceFee = sorobanData.resourceFee().toBigInt();
30
+ const ext = envelope.v1.tx.ext;
31
+ if (ext.type === 'sorobanData') {
32
+ const resourceFee = ext.sorobanData.resourceFee;
28
33
  if (BigInt(tx.fee) > resourceFee + 201n) {
29
34
  throw (0, relayer_sdk_1.pluginError)('Transaction fee must be equal to the resource fee', {
30
35
  code: 'FEE_MISMATCH',
@@ -5,5 +5,12 @@
5
5
  * Supports either a signed XDR or func+auth.
6
6
  */
7
7
  import { ChannelAccountsRequest } from './types';
8
+ /**
9
+ * Validate and normalize an incoming plugin request into one of the two
10
+ * supported shapes: a signed transaction `xdr` (submit-only) or a `func` +
11
+ * `auth` pair (channel flow). Base64 XDR fields are decoded here; malformed
12
+ * encodings and source-account auth credentials are rejected with
13
+ * `INVALID_PARAMS`.
14
+ */
8
15
  export declare function validateAndParseRequest(params: any): ChannelAccountsRequest;
9
16
  //# sourceMappingURL=validation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/plugin/validation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAqBjD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,GAAG,GAAG,sBAAsB,CAmJ3E"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/plugin/validation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAqBjD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,GAAG,GAAG,sBAAsB,CAkJ3E"}
@@ -25,6 +25,13 @@ function parseFundRelayerId(params) {
25
25
  }
26
26
  return undefined;
27
27
  }
28
+ /**
29
+ * Validate and normalize an incoming plugin request into one of the two
30
+ * supported shapes: a signed transaction `xdr` (submit-only) or a `func` +
31
+ * `auth` pair (channel flow). Base64 XDR fields are decoded here; malformed
32
+ * encodings and source-account auth credentials are rejected with
33
+ * `INVALID_PARAMS`.
34
+ */
28
35
  function validateAndParseRequest(params) {
29
36
  if (!params || typeof params !== 'object') {
30
37
  throw (0, relayer_sdk_1.pluginError)('Invalid request: params must be an object', {
@@ -109,11 +116,11 @@ function validateAndParseRequest(params) {
109
116
  let func;
110
117
  let auth = [];
111
118
  try {
112
- func = stellar_sdk_1.xdr.HostFunction.fromXDR(params.func, 'base64');
119
+ func = stellar_sdk_1.xdr.HostFunction.fromXdr(params.func, 'base64');
113
120
  if (!Array.isArray(params.auth)) {
114
121
  throw new Error('auth must be an array of base64 strings');
115
122
  }
116
- auth = params.auth.map((a) => stellar_sdk_1.xdr.SorobanAuthorizationEntry.fromXDR(a, 'base64'));
123
+ auth = params.auth.map((a) => stellar_sdk_1.xdr.SorobanAuthorizationEntry.fromXdr(a, 'base64'));
117
124
  }
118
125
  catch (e) {
119
126
  throw (0, relayer_sdk_1.pluginError)('Invalid `func` or `auth` encoding', {
@@ -124,8 +131,7 @@ function validateAndParseRequest(params) {
124
131
  }
125
132
  // Reject SourceAccount credentials: incompatible with relayer-managed channel source
126
133
  for (const entry of auth) {
127
- const credType = entry.credentials().switch();
128
- if (credType === stellar_sdk_1.xdr.SorobanCredentialsType.sorobanCredentialsSourceAccount()) {
134
+ if (entry.credentials.type === 'sorobanCredentialsSourceAccount') {
129
135
  throw (0, relayer_sdk_1.pluginError)('Detached address credentials required: source-account credentials are incompatible with relayer-managed channel accounts', {
130
136
  code: 'INVALID_PARAMS',
131
137
  status: constants_1.HTTP_STATUS.BAD_REQUEST,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openzeppelin/relayer-plugin-channels",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "description": "OpenZeppelin Relayer Plugin for Stellar Channel Accounts",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -21,7 +21,7 @@
21
21
  "dependencies": {
22
22
  "@actions/exec": "^1.1.1",
23
23
  "@openzeppelin/relayer-sdk": "^1.10.0",
24
- "@stellar/stellar-sdk": "^14.6.0",
24
+ "@stellar/stellar-sdk": "^17.0.1",
25
25
  "axios": "^1.13.5"
26
26
  },
27
27
  "devDependencies": {
@@ -1,21 +0,0 @@
1
- /**
2
- * build.ts
3
- *
4
- * Transaction rebuild logic to use channel account as source.
5
- */
6
- import { Transaction } from '@stellar/stellar-sdk';
7
- export interface RebuildParams {
8
- inputXdr: string;
9
- channelAddress: string;
10
- channelSequence: string;
11
- fundAddress: string;
12
- networkPassphrase: string;
13
- }
14
- /**
15
- * Rebuild transaction with channel account as source
16
- * - Set transaction source to channel account with current sequence
17
- * - Preserve memo, timeBounds, etc.
18
- * - Copy operations ensuring their source equals the fund address
19
- */
20
- export declare function rebuildWithChannel(params: RebuildParams): Transaction;
21
- //# sourceMappingURL=build.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/plugin/build.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAe,MAAM,sBAAsB,CAAC;AAIhE,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,GAAG,WAAW,CAmGrE"}
@@ -1,101 +0,0 @@
1
- "use strict";
2
- /**
3
- * build.ts
4
- *
5
- * Transaction rebuild logic to use channel account as source.
6
- */
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.rebuildWithChannel = rebuildWithChannel;
9
- const stellar_sdk_1 = require("@stellar/stellar-sdk");
10
- const relayer_sdk_1 = require("@openzeppelin/relayer-sdk");
11
- const constants_1 = require("./constants");
12
- /**
13
- * Rebuild transaction with channel account as source
14
- * - Set transaction source to channel account with current sequence
15
- * - Preserve memo, timeBounds, etc.
16
- * - Copy operations ensuring their source equals the fund address
17
- */
18
- function rebuildWithChannel(params) {
19
- const { inputXdr, channelAddress, channelSequence, fundAddress, networkPassphrase } = params;
20
- // Parse input transaction
21
- let inputTx;
22
- try {
23
- const envelope = stellar_sdk_1.xdr.TransactionEnvelope.fromXDR(inputXdr, 'base64');
24
- // Ensure it's a regular transaction envelope (not fee bump)
25
- if (envelope.switch() !== stellar_sdk_1.xdr.EnvelopeType.envelopeTypeTx()) {
26
- throw (0, relayer_sdk_1.pluginError)('Input must be a regular transaction envelope (not fee bump)', {
27
- code: 'INVALID_ENVELOPE_TYPE',
28
- status: constants_1.HTTP_STATUS.BAD_REQUEST,
29
- });
30
- }
31
- inputTx = new stellar_sdk_1.Transaction(envelope, networkPassphrase);
32
- }
33
- catch (error) {
34
- if (error.code === 'INVALID_ENVELOPE_TYPE') {
35
- throw error;
36
- }
37
- throw (0, relayer_sdk_1.pluginError)('Failed to parse input transaction XDR', {
38
- code: 'INVALID_XDR',
39
- status: constants_1.HTTP_STATUS.BAD_REQUEST,
40
- details: { message: error instanceof Error ? error.message : String(error) },
41
- });
42
- }
43
- // Validate timeBounds
44
- if (inputTx.timeBounds) {
45
- const now = Math.floor(Date.now() / 1000);
46
- const maxTime = Number(inputTx.timeBounds.maxTime);
47
- if (maxTime > 0) {
48
- if (maxTime < now) {
49
- throw (0, relayer_sdk_1.pluginError)('Transaction has expired: maxTime is in the past', {
50
- code: 'TIMEBOUNDS_EXPIRED',
51
- status: constants_1.HTTP_STATUS.BAD_REQUEST,
52
- details: { maxTime, now },
53
- });
54
- }
55
- const maxAllowedTime = now + constants_1.TIME.MAX_TIME_BOUND_OFFSET_SECONDS;
56
- if (maxTime > maxAllowedTime) {
57
- throw (0, relayer_sdk_1.pluginError)(`Transaction maxTime is too far in the future. Max allowed: ${constants_1.TIME.MAX_TIME_BOUND_OFFSET_SECONDS} seconds from now`, {
58
- code: 'INVALID_TIME_BOUNDS',
59
- status: constants_1.HTTP_STATUS.BAD_REQUEST,
60
- details: { maxTime, maxAllowedTime },
61
- });
62
- }
63
- }
64
- }
65
- // Validate operations: all operation sources must be fund address or missing
66
- for (let i = 0; i < inputTx.operations.length; i++) {
67
- const op = inputTx.operations[i];
68
- if (op.source && op.source !== fundAddress) {
69
- throw (0, relayer_sdk_1.pluginError)(`Operation ${i} has source ${op.source} but must be ${fundAddress} or omitted`, {
70
- code: 'INVALID_OPERATION_SOURCE',
71
- status: constants_1.HTTP_STATUS.BAD_REQUEST,
72
- details: { operationIndex: i, operationSource: op.source, expectedSource: fundAddress },
73
- });
74
- }
75
- }
76
- // Manipulate the transaction envelope XDR directly to change source and sequence
77
- // This is the most reliable approach that preserves all transaction details
78
- const envelope = inputTx.toEnvelope();
79
- const txBody = envelope.v1().tx();
80
- // Update source account to channel address
81
- const channelAccountId = stellar_sdk_1.StrKey.decodeEd25519PublicKey(channelAddress);
82
- const channelMuxed = stellar_sdk_1.xdr.MuxedAccount.keyTypeEd25519(channelAccountId);
83
- txBody.sourceAccount(channelMuxed);
84
- // Update sequence number
85
- // Sequence numbers in Stellar are represented as Int64 in XDR
86
- const seqNum = stellar_sdk_1.xdr.Int64.fromString(channelSequence);
87
- txBody.seqNum(seqNum);
88
- // Update operation sources to fund address if not set
89
- const fundAccountId = stellar_sdk_1.StrKey.decodeEd25519PublicKey(fundAddress);
90
- const fundMuxed = stellar_sdk_1.xdr.MuxedAccount.keyTypeEd25519(fundAccountId);
91
- const operations = txBody.operations();
92
- for (let i = 0; i < operations.length; i++) {
93
- const op = operations[i];
94
- // If operation doesn't have a source, set it to fund address
95
- if (!op.sourceAccount()) {
96
- op.sourceAccount(fundMuxed);
97
- }
98
- }
99
- // Create new transaction from modified envelope
100
- return new stellar_sdk_1.Transaction(envelope, networkPassphrase);
101
- }