@zkp2p/cash 0.1.4 → 0.1.5

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.
@@ -272,6 +272,8 @@ interface RelayExecutionResult {
272
272
  interface RelayTransaction {
273
273
  hash: string;
274
274
  chainId: number;
275
+ /** Relay batch-call identifiers are not transaction hashes. */
276
+ isBatchTx?: boolean | undefined;
275
277
  }
276
278
  interface RelayStatus {
277
279
  requestId: string;
@@ -272,6 +272,8 @@ interface RelayExecutionResult {
272
272
  interface RelayTransaction {
273
273
  hash: string;
274
274
  chainId: number;
275
+ /** Relay batch-call identifiers are not transaction hashes. */
276
+ isBatchTx?: boolean | undefined;
275
277
  }
276
278
  interface RelayStatus {
277
279
  requestId: string;
package/dist/index.cjs CHANGED
@@ -980,10 +980,18 @@ function collectRelayTransactions(steps, sourceChainId) {
980
980
  for (const step of steps) {
981
981
  for (const item of step.items) {
982
982
  for (const tx of item.internalTxHashes ?? []) {
983
- record({ hash: tx.txHash, chainId: tx.chainId });
983
+ record({
984
+ hash: tx.txHash,
985
+ chainId: tx.chainId,
986
+ ...tx.isBatchTx ? { isBatchTx: true } : {}
987
+ });
984
988
  }
985
989
  for (const tx of item.txHashes ?? []) {
986
- record({ hash: tx.txHash, chainId: tx.chainId });
990
+ record({
991
+ hash: tx.txHash,
992
+ chainId: tx.chainId,
993
+ ...tx.isBatchTx ? { isBatchTx: true } : {}
994
+ });
987
995
  }
988
996
  }
989
997
  }
@@ -1703,6 +1711,78 @@ function createCashClient(options) {
1703
1711
  }
1704
1712
  throw errors.allowanceNotVisible(amount, lastReadError);
1705
1713
  }
1714
+ async function waitForBaseSignerAfterRelay(client, cashoutSigner, sourceSigner, owner, sourceChainId, executed) {
1715
+ if (sourceChainId !== BASE_CHAIN_ID) return;
1716
+ const baseTransactions = (executed.transactions?.origin ?? []).filter(
1717
+ (transaction) => transaction.chainId === BASE_CHAIN_ID
1718
+ );
1719
+ const batchIds = baseTransactions.filter((transaction) => transaction.isBatchTx === true).map((transaction) => transaction.hash);
1720
+ let batchTransactionHashes = [];
1721
+ if (batchIds.length > 0) {
1722
+ let batchError;
1723
+ let batchesComplete = false;
1724
+ for (let attempt = 0; attempt < 20; attempt++) {
1725
+ try {
1726
+ const statuses = await Promise.all(
1727
+ batchIds.map((id) => sourceSigner.getCallsStatus({ id }))
1728
+ );
1729
+ if (statuses.some((status) => status.status === "failure")) {
1730
+ throw new Error("Relay wallet call bundle failed");
1731
+ }
1732
+ if (statuses.every((status) => status.status === "success")) {
1733
+ batchTransactionHashes = statuses.flatMap(
1734
+ (status) => (status.receipts ?? []).map((receipt) => receipt.transactionHash)
1735
+ );
1736
+ batchesComplete = true;
1737
+ break;
1738
+ }
1739
+ } catch (err) {
1740
+ batchError = err;
1741
+ }
1742
+ await sleep(250);
1743
+ }
1744
+ if (!batchesComplete) {
1745
+ throw batchError ?? new Error("Relay wallet call bundle did not complete");
1746
+ }
1747
+ }
1748
+ const hashes = [
1749
+ ...baseTransactions.filter((transaction) => transaction.isBatchTx !== true).map((transaction) => transaction.hash),
1750
+ ...batchTransactionHashes
1751
+ ];
1752
+ if (hashes.length === 0) return;
1753
+ let transactions;
1754
+ let lastLookupError;
1755
+ for (let attempt = 0; attempt < 20; attempt++) {
1756
+ try {
1757
+ transactions = await Promise.all(
1758
+ hashes.map((hash) => client.publicClient.getTransaction({ hash }))
1759
+ );
1760
+ break;
1761
+ } catch (err) {
1762
+ lastLookupError = err;
1763
+ await sleep(250);
1764
+ }
1765
+ }
1766
+ if (!transactions) throw lastLookupError;
1767
+ const ownerNonces = transactions.filter((transaction) => transaction.from.toLowerCase() === owner.toLowerCase()).map((transaction) => transaction.nonce);
1768
+ if (ownerNonces.length === 0) return;
1769
+ const afterRelay = Math.max(...ownerNonces) + 1;
1770
+ let lastNonceError;
1771
+ for (let attempt = 0; attempt < 20; attempt++) {
1772
+ try {
1773
+ const pendingHex = await cashoutSigner.transport.request({
1774
+ method: "eth_getTransactionCount",
1775
+ params: [owner, "pending"]
1776
+ });
1777
+ if (Number(BigInt(pendingHex)) >= afterRelay) return;
1778
+ } catch (err) {
1779
+ lastNonceError = err;
1780
+ }
1781
+ await sleep(250);
1782
+ }
1783
+ if (lastNonceError) throw lastNonceError;
1784
+ throw new Error(`Signer provider did not observe Relay nonce ${afterRelay - 1}`);
1785
+ }
1706
1786
  return {
1707
1787
  capabilities,
1708
1788
  async sourceCapabilities() {
@@ -1774,6 +1854,21 @@ function createCashClient(options) {
1774
1854
  ...executed.transactions ? { transactions: executed.transactions } : {}
1775
1855
  };
1776
1856
  sourceResult = routedSource;
1857
+ try {
1858
+ await waitForBaseSignerAfterRelay(
1859
+ client,
1860
+ opts.signer,
1861
+ sourceSigner,
1862
+ owner,
1863
+ input.source.chainId,
1864
+ executed
1865
+ );
1866
+ } catch (err) {
1867
+ throw errors.sourceRouteCompletedCashoutFailed(
1868
+ routedSource,
1869
+ mapChainError("resolve same-chain Relay nonce", err)
1870
+ );
1871
+ }
1777
1872
  const attributedParams2 = { ...params2, txOverrides: attribution };
1778
1873
  const send2 = async () => {
1779
1874
  try {
@@ -2146,7 +2241,8 @@ var bigintString = zod.z.string().regex(/^-?\d+$/, "expected a decimal bigint st
2146
2241
  var nonNegativeBigintString = zod.z.string().regex(/^\d+$/, "expected a non-negative decimal bigint string");
2147
2242
  var relayTransactionJsonSchema = zod.z.object({
2148
2243
  hash: zod.z.string(),
2149
- chainId: zod.z.number()
2244
+ chainId: zod.z.number(),
2245
+ isBatchTx: zod.z.boolean().optional()
2150
2246
  });
2151
2247
  var relayTransactionsJsonSchema = zod.z.object({
2152
2248
  origin: zod.z.array(relayTransactionJsonSchema),