From 9acada528b3250becbb3073a4b036b5bd17ed058 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C5=82=20Szynwelski?= <szynek@warp.cc>
Date: Fri, 17 Nov 2023 13:20:59 +0100
Subject: [PATCH] Transaction confirmation in Warp gateway

---
 .../decentralized-sequencer/interactions.ts   | 123 +++++++++++-------
 .../decentralized-sequencer/send-data-item.ts |  38 +++---
 src/__tests__/unit/evaluation-options.test.ts |  24 ++--
 src/contract/Contract.ts                      |   2 -
 src/contract/HandlerBasedContract.ts          |  16 +--
 src/contract/Signature.ts                     |   1 -
 .../sequencer/CentralizedSequencerClient.ts   |  15 ++-
 .../sequencer/DecentralizedSequencerClient.ts |  86 ++++++------
 src/contract/sequencer/SequencerClient.ts     |  21 ++-
 src/core/modules/StateEvaluator.ts            |  10 +-
 10 files changed, 185 insertions(+), 151 deletions(-)

diff --git a/src/__tests__/integration/decentralized-sequencer/interactions.ts b/src/__tests__/integration/decentralized-sequencer/interactions.ts
index 655eedc4..72bca4cc 100644
--- a/src/__tests__/integration/decentralized-sequencer/interactions.ts
+++ b/src/__tests__/integration/decentralized-sequencer/interactions.ts
@@ -3,7 +3,7 @@ import Arweave from 'arweave';
 import { JWKInterface } from 'arweave/node/lib/wallet';
 import fs from 'fs';
 import path from 'path';
-import { createServer, Server } from 'http';
+import { createServer, request, Server } from 'http';
 import { DeployPlugin, ArweaveSigner } from 'warp-contracts-plugin-deploy';
 import { Contract, WriteInteractionResponse } from '../../../contract/Contract';
 import { Warp } from '../../../core/Warp';
@@ -11,6 +11,7 @@ import { WarpFactory, defaultCacheOptions, defaultWarpGwOptions } from '../../..
 import { SourceType } from '../../../core/modules/impl/WarpGatewayInteractionsLoader';
 import { AddressInfo } from 'net';
 import { WARP_TAGS } from '../../../core/KnownTags';
+import { LoggerFactory } from '../../../logging/LoggerFactory';
 
 interface ExampleContractState {
     counter: number;
@@ -18,6 +19,7 @@ interface ExampleContractState {
 
 // FIXME: change to the address of the sequencer on dev
 const DECENTRALIZED_SEQUENCER_URL = 'http://sequencer-0.warp.cc:1317';
+const GW_URL = 'http://34.141.17.15:5666/';
 
 describe('Testing sending of interactions to a decentralized sequencer', () => {
     let contractSrc: string;
@@ -26,28 +28,21 @@ describe('Testing sending of interactions to a decentralized sequencer', () => {
     let arlocal: ArLocal;
     let warp: Warp;
     let contract: Contract<ExampleContractState>;
-    let sequencerServer: Server;
-    let centralizedSeqeuencerUrl: string;
+    let mockGwServer: Server;
+    let mockGwUrl: string;
     let centralizedSequencerType: boolean;
-
-    beforeAll(async () => {
-        const port = 1813;
-        arlocal = new ArLocal(port, false);
-        await arlocal.start();
-
-        const arweave = Arweave.init({
-            host: 'localhost',
-            port: port,
-            protocol: 'http'
-        });
-
-        // a mock server simulating a centralized sequencer
-        centralizedSequencerType = false;
-        sequencerServer = createServer((req, res) => {
+    let confirmAnyTx: boolean;
+
+    /**
+     * For testing purposes, operations returning the sequencer's address and registering/confirming interactions are mocked. 
+     * Other requests are forwarded to the real Gateway.
+     */
+    const mockGw = async () => {
+        mockGwServer = createServer((req, res) => {
             if (req.url === '/gateway/sequencer/address') {
                 res.writeHead(200, { 'Content-Type': 'application/json' });
                 res.end(JSON.stringify({
-                    url: centralizedSequencerType ? centralizedSeqeuencerUrl : DECENTRALIZED_SEQUENCER_URL,
+                    url: centralizedSequencerType ? mockGwUrl : DECENTRALIZED_SEQUENCER_URL,
                     type: centralizedSequencerType ? 'centralized' : 'decentralized'
                 }));
                 return;
@@ -56,16 +51,57 @@ describe('Testing sending of interactions to a decentralized sequencer', () => {
                 res.writeHead(301, { Location: DECENTRALIZED_SEQUENCER_URL });
                 res.end();
                 return;
+            } else if (req.url?.startsWith('/gateway/interactions/')) {
+                res.writeHead(confirmAnyTx ? 200 : 204);
+                res.end();
+                return;
             }
-            throw new Error("Unexpected sequencer path: " + req.url);
-        })
+
+            var options = {
+                hostname: new URL(GW_URL).hostname,
+                port: new URL(GW_URL).port,
+                path: req.url,
+                method: req.method,
+                headers: req.headers
+            };
+
+            var proxy = request(options, (gwRes) => {
+                if (gwRes.statusCode) {
+                    res.writeHead(gwRes.statusCode, gwRes.headers)
+                    gwRes.pipe(res, {
+                        end: true
+                    });
+                }
+            });
+
+            req.pipe(proxy, {
+                end: true
+            });
+        });
         await new Promise<void>(resolve => {
-            sequencerServer.listen(() => {
-                const address = sequencerServer.address() as AddressInfo
-                centralizedSeqeuencerUrl = `http://localhost:${address.port}`
+            mockGwServer.listen(() => {
+                const address = mockGwServer.address() as AddressInfo
+                mockGwUrl = `http://localhost:${address.port}`
                 resolve()
             })
-        })
+        });
+    }
+
+    beforeAll(async () => {
+        LoggerFactory.INST.logLevel('debug');
+        const port = 1813;
+        arlocal = new ArLocal(port, false);
+        await arlocal.start();
+
+        const arweave = Arweave.init({
+            host: 'localhost',
+            port: port,
+            protocol: 'http'
+        });
+
+        centralizedSequencerType = false;
+        confirmAnyTx = false;
+        await mockGw();
 
         const cacheOptions = {
             ...defaultCacheOptions,
@@ -77,6 +113,7 @@ describe('Testing sending of interactions to a decentralized sequencer', () => {
             .custom(arweave, cacheOptions, 'custom')
             .useWarpGateway(gatewayOptions, cacheOptions)
             .build()
+            .useGwUrl(mockGwUrl)
             .use(new DeployPlugin());
 
         ({ jwk: wallet } = await warp.generateWallet());
@@ -91,7 +128,7 @@ describe('Testing sending of interactions to a decentralized sequencer', () => {
         });
 
         contract = warp.contract<ExampleContractState>(contractTxId).setEvaluationOptions({
-            sequencerUrl: centralizedSeqeuencerUrl
+            sequencerUrl: mockGwUrl
         });
         contract.connect(wallet);
 
@@ -100,7 +137,7 @@ describe('Testing sending of interactions to a decentralized sequencer', () => {
     afterAll(async () => {
         await arlocal.stop();
         await new Promise(resolve => {
-            sequencerServer.close(resolve)
+            mockGwServer.close(resolve)
         })
     });
 
@@ -110,41 +147,37 @@ describe('Testing sending of interactions to a decentralized sequencer', () => {
                 if (tag.name === WARP_TAGS.SEQUENCER_NONCE) {
                     return Number(tag.value)
                 }
-            }    
+            }
         }
         return -1
     }
 
-    it('should add new interactions waiting for confirmation from the sequencer', async () => {
-        contract.setEvaluationOptions({ waitForConfirmation: true })
+    it('should follow the redirection returned by the centralized sequencer.', async () => {
+        confirmAnyTx = true;
+        centralizedSequencerType = true;
+        contract.setEvaluationOptions({
+            waitForConfirmation: true
+        });
 
         await contract.writeInteraction({ function: 'add' });
         const result = await contract.writeInteraction({ function: 'add' });
         expect(getNonceFromResult(result)).toEqual(1)
-        expect(result?.bundlrResponse).toBeUndefined();
-        expect(result?.sequencerTxHash).toBeDefined();
     });
 
-    it('should add new interactions without waiting for confirmation from the sequencer', async () => {
-        contract.setEvaluationOptions({ waitForConfirmation: false })
+    it('should add new interactions waiting for confirmation from the gateway', async () => {
+        contract.setEvaluationOptions({ waitForConfirmation: true })
+        setTimeout(() => confirmAnyTx = true, 2000);
 
         await contract.writeInteraction({ function: 'add' });
         const result = await contract.writeInteraction({ function: 'add' });
         expect(getNonceFromResult(result)).toEqual(3)
-        expect(result?.bundlrResponse).toBeUndefined();
-        expect(result?.sequencerTxHash).toBeUndefined();
     });
 
-    it('should follow the redirection returned by the centralized sequencer.', async () => {
-        centralizedSequencerType = true;
-        contract.setEvaluationOptions({
-            sequencerUrl: centralizedSeqeuencerUrl,
-            waitForConfirmation: true
-        });
+    it('should add new interactions without waiting for confirmation from the gateway', async () => {
+        contract.setEvaluationOptions({ waitForConfirmation: false })
 
+        await contract.writeInteraction({ function: 'add' });
         const result = await contract.writeInteraction({ function: 'add' });
-        expect(getNonceFromResult(result)).toEqual(4)
-        expect(result?.bundlrResponse).toBeUndefined();
-        expect(result?.sequencerTxHash).toBeDefined();
+        expect(getNonceFromResult(result)).toEqual(5)
     });
 });
diff --git a/src/__tests__/integration/decentralized-sequencer/send-data-item.ts b/src/__tests__/integration/decentralized-sequencer/send-data-item.ts
index 2b237b39..810ee60a 100644
--- a/src/__tests__/integration/decentralized-sequencer/send-data-item.ts
+++ b/src/__tests__/integration/decentralized-sequencer/send-data-item.ts
@@ -7,17 +7,18 @@ import { Tag } from '../../../utils/types/arweave-types';
 import { WarpFactory } from '../../../core/WarpFactory';
 import { WarpFetchWrapper } from '../../../core/WarpFetchWrapper';
 import { Signature } from '../../../contract/Signature';
+import { SequencerClient } from '../../../contract/sequencer/SequencerClient';
 
 // FIXME: change to the address of the sequencer on dev
 const SEQUENCER_URL = 'http://sequencer-0.warp.cc:1317';
+const GW_URL = 'http://34.141.17.15:5666/';
 
 describe('Testing a decentralized sequencer client', () => {
-  let client: DecentralizedSequencerClient;
 
-  beforeAll(async () => {
+  const createClient = (): SequencerClient => {
     const warpFetchWrapper = new WarpFetchWrapper(WarpFactory.forLocal())
-    client = new DecentralizedSequencerClient(SEQUENCER_URL, warpFetchWrapper);
-  });
+    return new DecentralizedSequencerClient(SEQUENCER_URL, GW_URL, warpFetchWrapper);
+  }
 
   const createSignature = async (): Promise<Signature> => {
     const wallet = await Arweave.crypto.generateJWK();
@@ -42,7 +43,8 @@ describe('Testing a decentralized sequencer client', () => {
   }
 
   it('should return consecutive nonces for a given signature', async () => {
-    const signature = await createSignature()
+    const client = createClient();
+    const signature = await createSignature();
     let nonce = await client.getNonce(signature);
     expect(nonce).toEqual(0);
 
@@ -51,7 +53,8 @@ describe('Testing a decentralized sequencer client', () => {
   });
 
   it('should reject a data item with an invalid nonce', async () => {
-    const signature = await createSignature()
+    const client = createClient();
+    const signature = await createSignature();
     const dataItem = await createDataItem(signature, 13);
 
     expect(client.sendDataItem(dataItem, false))
@@ -60,7 +63,8 @@ describe('Testing a decentralized sequencer client', () => {
   });
 
   it('should reject a data item without nonce', async () => {
-    const signature = await createSignature()
+    const client = createClient();
+    const signature = await createSignature();
     const dataItem = await createDataItem(signature, 0, false);
 
     expect(client.sendDataItem(dataItem, true))
@@ -69,7 +73,8 @@ describe('Testing a decentralized sequencer client', () => {
   });
 
   it('should reject a data item without contract', async () => {
-    const signature = await createSignature()
+    const client = createClient();
+    const signature = await createSignature();
     const dataItem = await createDataItem(signature, 0, true, false);
 
     expect(client.sendDataItem(dataItem, true))
@@ -78,7 +83,8 @@ describe('Testing a decentralized sequencer client', () => {
   });
 
   it('should reject an unsigned data item', async () => {
-    const signature = await createSignature()
+    const client = createClient();
+    const signature = await createSignature();
     const dataItem = await createDataItem(signature, 0, true, true, false);
 
     expect(client.sendDataItem(dataItem, true))
@@ -86,25 +92,13 @@ describe('Testing a decentralized sequencer client', () => {
       .toThrowError('data item verification error');
   });
 
-  it('should return a confirmed result', async () => {
-    const signature = await createSignature();
-    const nonce = await client.getNonce(signature);
-    const dataItem = await createDataItem(signature, nonce);
-    const result = await client.sendDataItem(dataItem, true);
-
-    expect(result.sequencerMoved).toEqual(false);
-    expect(result.bundlrResponse).toBeUndefined();
-    expect(result.sequencerTxHash).toBeDefined();
-  });
-
   it('should return an unconfirmed result', async () => {
+    const client = createClient();
     const signature = await createSignature();
     const nonce = await client.getNonce(signature);
     const dataItem = await createDataItem(signature, nonce);
     const result = await client.sendDataItem(dataItem, false);
   
     expect(result.sequencerMoved).toEqual(false);
-    expect(result.bundlrResponse).toBeUndefined();
-    expect(result.sequencerTxHash).toBeUndefined();
   });
 });
diff --git a/src/__tests__/unit/evaluation-options.test.ts b/src/__tests__/unit/evaluation-options.test.ts
index f610be7d..46acce63 100644
--- a/src/__tests__/unit/evaluation-options.test.ts
+++ b/src/__tests__/unit/evaluation-options.test.ts
@@ -21,7 +21,7 @@ describe('Evaluation options evaluator', () => {
       mineArLocalBlocks: true,
       remoteStateSyncEnabled: false,
       remoteStateSyncSource: 'https://dre-1.warp.cc/contract',
-      sequencerUrl: 'https://d1o5nlqr4okus2.cloudfront.net/',
+      sequencerUrl: undefined,
       sourceType: SourceType.BOTH,
       stackTrace: {
         saveState: false
@@ -30,7 +30,7 @@ describe('Evaluation options evaluator', () => {
       unsafeClient: 'throw',
       updateCacheForEachInteraction: false,
       useKVStorage: false,
-      waitForConfirmation: false,
+      waitForConfirmation: undefined,
       useConstructor: false,
       walletBalanceUrl: 'http://nyc-1.dev.arweave.net:1984/',
       whitelistSources: []
@@ -59,7 +59,7 @@ describe('Evaluation options evaluator', () => {
       mineArLocalBlocks: true,
       remoteStateSyncEnabled: false,
       remoteStateSyncSource: 'https://dre-1.warp.cc/contract',
-      sequencerUrl: 'https://d1o5nlqr4okus2.cloudfront.net/',
+      sequencerUrl: undefined,
       sourceType: SourceType.BOTH,
       stackTrace: {
         saveState: false
@@ -68,7 +68,7 @@ describe('Evaluation options evaluator', () => {
       unsafeClient: 'throw',
       updateCacheForEachInteraction: false,
       useKVStorage: false,
-      waitForConfirmation: false,
+      waitForConfirmation: undefined,
       useConstructor: false,
       walletBalanceUrl: 'http://nyc-1.dev.arweave.net:1984/',
       whitelistSources: []
@@ -92,7 +92,7 @@ describe('Evaluation options evaluator', () => {
       mineArLocalBlocks: true,
       remoteStateSyncEnabled: false,
       remoteStateSyncSource: 'https://dre-1.warp.cc/contract',
-      sequencerUrl: 'https://d1o5nlqr4okus2.cloudfront.net/',
+      sequencerUrl: undefined,
       sourceType: 'both',
       stackTrace: {
         saveState: false
@@ -101,7 +101,7 @@ describe('Evaluation options evaluator', () => {
       unsafeClient: 'allow',
       updateCacheForEachInteraction: false,
       useKVStorage: false,
-      waitForConfirmation: false,
+      waitForConfirmation: undefined,
       useConstructor: false,
       walletBalanceUrl: 'http://nyc-1.dev.arweave.net:1984/',
       whitelistSources: []
@@ -122,7 +122,7 @@ describe('Evaluation options evaluator', () => {
       mineArLocalBlocks: true,
       remoteStateSyncEnabled: false,
       remoteStateSyncSource: 'https://dre-1.warp.cc/contract',
-      sequencerUrl: 'https://d1o5nlqr4okus2.cloudfront.net/',
+      sequencerUrl: undefined,
       sourceType: 'both',
       stackTrace: {
         saveState: false
@@ -131,7 +131,7 @@ describe('Evaluation options evaluator', () => {
       unsafeClient: 'allow',
       updateCacheForEachInteraction: false,
       useKVStorage: false,
-      waitForConfirmation: false,
+      waitForConfirmation: undefined,
       useConstructor: false,
       walletBalanceUrl: 'http://nyc-1.dev.arweave.net:1984/',
       whitelistSources: []
@@ -152,7 +152,7 @@ describe('Evaluation options evaluator', () => {
       mineArLocalBlocks: true,
       remoteStateSyncEnabled: false,
       remoteStateSyncSource: 'https://dre-1.warp.cc/contract',
-      sequencerUrl: 'https://d1o5nlqr4okus2.cloudfront.net/',
+      sequencerUrl: undefined,
       sourceType: 'both',
       stackTrace: {
         saveState: false
@@ -161,7 +161,7 @@ describe('Evaluation options evaluator', () => {
       unsafeClient: 'throw',
       updateCacheForEachInteraction: false,
       useKVStorage: false,
-      waitForConfirmation: false,
+      waitForConfirmation: undefined,
       useConstructor: false,
       walletBalanceUrl: 'http://nyc-1.dev.arweave.net:1984/',
       whitelistSources: []
@@ -182,7 +182,7 @@ describe('Evaluation options evaluator', () => {
       mineArLocalBlocks: true,
       remoteStateSyncEnabled: false,
       remoteStateSyncSource: 'https://dre-1.warp.cc/contract',
-      sequencerUrl: 'https://d1o5nlqr4okus2.cloudfront.net/',
+      sequencerUrl: undefined,
       sourceType: 'both',
       stackTrace: {
         saveState: false
@@ -191,7 +191,7 @@ describe('Evaluation options evaluator', () => {
       unsafeClient: 'skip',
       updateCacheForEachInteraction: false,
       useKVStorage: false,
-      waitForConfirmation: false,
+      waitForConfirmation: undefined,
       useConstructor: false,
       walletBalanceUrl: 'http://nyc-1.dev.arweave.net:1984/',
       whitelistSources: []
diff --git a/src/contract/Contract.ts b/src/contract/Contract.ts
index e0d10282..2f97a8b9 100644
--- a/src/contract/Contract.ts
+++ b/src/contract/Contract.ts
@@ -20,10 +20,8 @@ export interface BundlrResponse {
 }
 
 export interface WriteInteractionResponse {
-  bundlrResponse?: BundlrResponse;
   originalTxId: string;
   interactionTx: Transaction | DataItem;
-  sequencerTxHash?: string;
 }
 
 export interface DREContractStatusResponse<State> {
diff --git a/src/contract/HandlerBasedContract.ts b/src/contract/HandlerBasedContract.ts
index 01a2f3a7..3883647a 100644
--- a/src/contract/HandlerBasedContract.ts
+++ b/src/contract/HandlerBasedContract.ts
@@ -341,14 +341,13 @@ export class HandlerBasedContract<State> implements Contract<State> {
     );
 
     const sequencerClient = await this.getSequencerClient();
+    const confirmInteraction = this._evaluationOptions.waitForConfirmation;
     const sendResponse = await sequencerClient.sendDataItem(
       interactionDataItem,
-      this._evaluationOptions.waitForConfirmation
+      confirmInteraction === undefined || confirmInteraction === true // by default, we wait for confirmation
     );
     if (sendResponse.sequencerMoved) {
-      this.logger.info(
-        `The sequencer at the given address (${this._evaluationOptions.sequencerUrl}) is redirecting to a new sequencer`
-      );
+      this.logger.info(`The sequencer is redirecting to a new sequencer`);
       if (sequencerRedirected) {
         throw new Error('Too many sequencer redirects');
       }
@@ -357,16 +356,14 @@ export class HandlerBasedContract<State> implements Contract<State> {
     }
 
     return {
-      bundlrResponse: sendResponse.bundlrResponse,
       originalTxId: await interactionDataItem.id,
-      interactionTx: interactionDataItem,
-      sequencerTxHash: sendResponse.sequencerTxHash
+      interactionTx: interactionDataItem
     };
   }
 
   private async getSequencerClient(): Promise<SequencerClient> {
     if (!this._sequencerClient) {
-      this._sequencerClient = await createSequencerClient(this._evaluationOptions.sequencerUrl, this._warpFetchWrapper);
+      this._sequencerClient = await createSequencerClient(this.warp.gwUrl(), this._warpFetchWrapper);
     }
     return this._sequencerClient;
   }
@@ -495,6 +492,9 @@ export class HandlerBasedContract<State> implements Contract<State> {
 
   connect(signature: ArWallet | CustomSignature | Signer): Contract<State> {
     this._signature = new Signature(this.warp, signature);
+    if (this._sequencerClient) {
+      this._sequencerClient.clearNonce();
+    }
     return this;
   }
 
diff --git a/src/contract/Signature.ts b/src/contract/Signature.ts
index e51eaec8..81297fb7 100644
--- a/src/contract/Signature.ts
+++ b/src/contract/Signature.ts
@@ -29,7 +29,6 @@ export class Signature {
   private readonly signatureProviderType: 'CustomSignature' | 'ArWallet' | 'BundlerSigner';
   private readonly wallet;
   private cachedAddress?: string;
-  sequencerNonce: number;
 
   constructor(warp: Warp, walletOrSignature: SignatureProvider) {
     this.warp = warp;
diff --git a/src/contract/sequencer/CentralizedSequencerClient.ts b/src/contract/sequencer/CentralizedSequencerClient.ts
index a61c6f98..6ea27f5b 100644
--- a/src/contract/sequencer/CentralizedSequencerClient.ts
+++ b/src/contract/sequencer/CentralizedSequencerClient.ts
@@ -1,19 +1,22 @@
-import { BundlrResponse } from 'contract/Contract';
 import { WarpFetchWrapper } from 'core/WarpFetchWrapper';
 import { NetworkCommunicationError } from '../../utils/utils';
 import { DataItem } from 'warp-arbundles';
 import { SendDataItemResponse, SequencerClient } from './SequencerClient';
+import { LoggerFactory } from '../../logging/LoggerFactory';
 
 /**
  * Client for a centralized sequencer.
  */
 export class CentralizedSequencerClient implements SequencerClient {
+  private readonly logger = LoggerFactory.INST.create('CentralizedSequencerClient');
+
   private registerUrl: string;
   private warpFetchWrapper: WarpFetchWrapper;
 
   constructor(sequencerUrl: string, warpFetchWrapper: WarpFetchWrapper) {
     this.registerUrl = `${sequencerUrl}/gateway/v2/sequencer/register`;
     this.warpFetchWrapper = warpFetchWrapper;
+    this.logger.info('The interactions will be sent to the centralized sequencer at the address', sequencerUrl);
   }
 
   /**
@@ -24,6 +27,13 @@ export class CentralizedSequencerClient implements SequencerClient {
     return Promise.resolve(undefined);
   }
 
+  /**
+   * The sequencer does not have a nonce mechanism.
+   */
+  clearNonce(): void {
+    // do nothing
+  }
+
   /**
    * It sends an interaction to the sequencer and checks if the response has a status of 301 (Moved Permanently).
    */
@@ -39,16 +49,13 @@ export class CentralizedSequencerClient implements SequencerClient {
     });
 
     if (response.ok) {
-      const bundlrResponse = (await response.json()) as BundlrResponse;
       return {
-        bundlrResponse,
         sequencerMoved: false
       };
     }
 
     if (response.status == 301) {
       return {
-        bundlrResponse: undefined,
         sequencerMoved: true
       };
     }
diff --git a/src/contract/sequencer/DecentralizedSequencerClient.ts b/src/contract/sequencer/DecentralizedSequencerClient.ts
index 51ebb36f..74b0209f 100644
--- a/src/contract/sequencer/DecentralizedSequencerClient.ts
+++ b/src/contract/sequencer/DecentralizedSequencerClient.ts
@@ -5,17 +5,13 @@ import { LoggerFactory } from '../../logging/LoggerFactory';
 import { WarpFetchWrapper } from '../../core/WarpFetchWrapper';
 import { SendDataItemResponse, SequencerClient } from './SequencerClient';
 import { Signature } from 'contract/Signature';
+import { Benchmark } from '../../logging/Benchmark';
 
 type NonceResponse = {
   address: string;
   nonce: number;
 };
 
-type CheckTxResponse = {
-  confirmed: boolean;
-  txHash?: string;
-};
-
 /**
  * Client for a decentralized sequencer.
  */
@@ -26,12 +22,15 @@ export class DecentralizedSequencerClient implements SequencerClient {
   private sendDataItemUrl: string;
   private getTxUrl: string;
   private warpFetchWrapper: WarpFetchWrapper;
+  private nonce: number | undefined;
 
-  constructor(sequencerUrl: string, warpFetchWrapper: WarpFetchWrapper) {
+  constructor(sequencerUrl: string, gatewayUrl: string, warpFetchWrapper: WarpFetchWrapper) {
     this.nonceUrl = `${sequencerUrl}/api/v1/nonce`;
     this.sendDataItemUrl = `${sequencerUrl}/api/v1/dataitem`;
-    this.getTxUrl = `${sequencerUrl}/api/v1/tx-data-item-id`;
+    this.getTxUrl = `${gatewayUrl}/gateway/interactions/`;
     this.warpFetchWrapper = warpFetchWrapper;
+    this.nonce = undefined;
+    this.logger.info('The interactions will be sent to the decentralized sequencer at the address', sequencerUrl);
   }
 
   /**
@@ -42,13 +41,12 @@ export class DecentralizedSequencerClient implements SequencerClient {
    * @returns nonce
    */
   async getNonce(signature: Signature): Promise<number> {
-    const nonce = signature.sequencerNonce;
-    if (nonce !== undefined) {
-      signature.sequencerNonce = nonce + 1;
-      return nonce;
+    if (this.nonce === undefined) {
+      this.nonce = await this.fetchNonce(signature);
+    } else {
+      this.nonce = this.nonce + 1;
     }
-
-    return this.fetchNonce(signature);
+    return this.nonce;
   }
 
   /**
@@ -75,10 +73,17 @@ export class DecentralizedSequencerClient implements SequencerClient {
 
     const nonceResponse = await getJsonResponse<NonceResponse>(response);
     this.logger.info('Nonce for owner', { owner, nonceResponse });
-    signature.sequencerNonce = nonceResponse.nonce + 1;
+    this.nonce = nonceResponse.nonce + 1;
     return nonceResponse.nonce;
   }
 
+  /**
+   * Clears the stored nonce value. The next call to {@link getNonce} will request a new value from the sequencer.
+   */
+  clearNonce(): void {
+    this.nonce = undefined;
+  }
+
   /**
    * Broadcasts a data item to the sequencer network and optionally monitoring its inclusion in the blockchain.
    * If the broadcasting is rejected by the node (e.g., during the CheckTx method), an error is thrown.
@@ -90,13 +95,19 @@ export class DecentralizedSequencerClient implements SequencerClient {
    * @returns hash of the sequencer transaction if wait for confirmation is selected
    */
   async sendDataItem(dataItem: DataItem, waitForConfirmation: boolean): Promise<SendDataItemResponse> {
-    const response = await this.sendDataItemWithRetry(dataItem);
+    await this.sendDataItemWithRetry(dataItem);
 
     if (waitForConfirmation) {
-      response.sequencerTxHash = await this.confirmTx(await dataItem.id);
+      const dataItemId = await dataItem.id;
+      this.logger.info('Waiting for confirmation of', dataItemId);
+      const benchmark = Benchmark.measure();
+      await this.confirmTx(dataItemId);
+      this.logger.info('Transaction confirmed after', benchmark.elapsed());
     }
 
-    return response;
+    return {
+      sequencerMoved: false
+    };
   }
 
   /**
@@ -106,16 +117,15 @@ export class DecentralizedSequencerClient implements SequencerClient {
    * @param dataItem data item to be sent
    * @param numberOfTries the number of retries
    */
-  private async sendDataItemWithRetry(dataItem: DataItem, numberOfTries = 20): Promise<SendDataItemResponse> {
+  private async sendDataItemWithRetry(dataItem: DataItem, numberOfTries = 20): Promise<void> {
     if (numberOfTries <= 0) {
       throw new Error(
         `Failed to send the interaction (id = ${await dataItem.id}) to the sequencer despite multiple retries`
       );
     }
 
-    if (await this.tryToSendDataItem(dataItem)) {
-      return { sequencerMoved: false };
-    } else {
+    const dataItemSent = await this.tryToSendDataItem(dataItem);
+    if (!dataItemSent) {
       await sleep(1000);
       return this.sendDataItemWithRetry(dataItem, numberOfTries - 1);
     }
@@ -166,38 +176,30 @@ export class DecentralizedSequencerClient implements SequencerClient {
    * @param dataItem data item to be sent
    * @param numberOfTries the number of retries
    */
-  private async confirmTx(dataItemId: string, numberOfTries = 20): Promise<string> {
+  private async confirmTx(dataItemId: string, numberOfTries = 20): Promise<void> {
     if (numberOfTries <= 0) {
-      throw new Error(`Failed to confirm of the interaction with id = ${dataItemId} in the sequencer network`);
+      throw new Error(`Failed to confirm of the interaction with id = ${dataItemId}`);
     }
 
-    await sleep(1000);
-
-    const result = await this.checkTx(dataItemId);
-    if (!result.confirmed) {
+    await sleep(500);
+    const confirmed = await this.checkTx(dataItemId);
+    if (!confirmed) {
       return this.confirmTx(dataItemId, numberOfTries - 1);
     }
-    return result.txHash;
   }
 
-  private async checkTx(dataItemId: string): Promise<CheckTxResponse> {
-    const response = await this.warpFetchWrapper.fetch(this.getTxUrl, {
-      method: 'POST',
-      headers: {
-        'Content-Type': 'application/json'
-      },
-      body: JSON.stringify({ data_item_id: dataItemId })
-    });
+  private async checkTx(dataItemId: string): Promise<boolean> {
+    const response = await this.warpFetchWrapper.fetch(this.getTxUrl + dataItemId);
 
-    if (response.ok) {
-      const result = await response.json();
-      this.logger.info(`The transaction with hash ${result.tx_hash} confirmed.`);
-      return { confirmed: true, txHash: result.tx_hash };
+    if (response.status == 200) {
+      const result = await response.text();
+      this.logger.info(`The interaction confirmed: ${result}!`);
+      return true;
     }
 
-    if (response.status == 404) {
+    if (response.status == 204) {
       this.logger.debug(`The transaction with data item id (${dataItemId}) not confirmed yet.`);
-      return { confirmed: false };
+      return false;
     }
 
     const text = await response.text();
diff --git a/src/contract/sequencer/SequencerClient.ts b/src/contract/sequencer/SequencerClient.ts
index fe5be510..da9a45ba 100644
--- a/src/contract/sequencer/SequencerClient.ts
+++ b/src/contract/sequencer/SequencerClient.ts
@@ -1,4 +1,3 @@
-import { BundlrResponse } from 'contract/Contract';
 import { Signature } from 'contract/Signature';
 import { getJsonResponse, stripTrailingSlash } from '../../utils/utils';
 import { DataItem } from 'warp-arbundles';
@@ -14,14 +13,6 @@ export type SendDataItemResponse = {
    * Whether the sequencer returned a "Moved Permanently" status with the address of the new sequencer
    */
   sequencerMoved: boolean;
-  /**
-   * The response from the bundler if the sequencer sends an interaction there
-   */
-  bundlrResponse?: BundlrResponse;
-  /**
-   * The transaction hash in the decentralized sequencer blockchain containing the data item
-   */
-  sequencerTxHash?: string;
 };
 
 /**
@@ -34,6 +25,11 @@ export interface SequencerClient {
    */
   getNonce(signature: Signature): Promise<number>;
 
+  /**
+   * Clears the stored nonce value.
+   */
+  clearNonce(): void;
+
   /**
    * It sends an interaction in the form of a data item to the sequencer.
    * Potentially waits for confirmation that the interaction has been included in the sequencer chain.
@@ -62,14 +58,15 @@ type SequencerAddress = {
  * It queries an endpoint with an address and sequencer type, and returns a client for that sequencer.
  *
  * @param sequencerUrl URL address with an endpoint that returns the sequencer's address
+ * @param gatewayUrl Warp gateway URL
  * @param warpFetchWrapper wrapper for fetch operation
  * @returns client for the sequencer
  */
 export const createSequencerClient = async (
-  sequencerUrl: string,
+  gatewayUrl: string,
   warpFetchWrapper: WarpFetchWrapper
 ): Promise<SequencerClient> => {
-  const response = warpFetchWrapper.fetch(`${stripTrailingSlash(sequencerUrl)}/gateway/sequencer/address`);
+  const response = warpFetchWrapper.fetch(`${stripTrailingSlash(gatewayUrl)}/gateway/sequencer/address`);
   const address = await getJsonResponse<SequencerAddress>(response);
 
   if (address.type == 'centralized') {
@@ -77,7 +74,7 @@ export const createSequencerClient = async (
   }
 
   if (address.type == 'decentralized') {
-    return new DecentralizedSequencerClient(address.url, warpFetchWrapper);
+    return new DecentralizedSequencerClient(address.url, gatewayUrl, warpFetchWrapper);
   }
 
   throw new Error('Unknown sequencer type: ' + address.type);
diff --git a/src/core/modules/StateEvaluator.ts b/src/core/modules/StateEvaluator.ts
index b4781058..2b2933ff 100644
--- a/src/core/modules/StateEvaluator.ts
+++ b/src/core/modules/StateEvaluator.ts
@@ -111,7 +111,8 @@ export class DefaultEvaluationOptions implements EvaluationOptions {
   // (e.g. when using unsafe client and Arweave does not respond properly for a while)
   ignoreExceptions = true;
 
-  waitForConfirmation = false;
+  // by default, the option is not set, which will result in confirming L2 interactions but not L1
+  waitForConfirmation = undefined;
 
   updateCacheForEachInteraction = false;
 
@@ -125,7 +126,7 @@ export class DefaultEvaluationOptions implements EvaluationOptions {
     saveState: false
   };
 
-  sequencerUrl = `https://d1o5nlqr4okus2.cloudfront.net/`;
+  sequencerUrl = undefined;
 
   gasLimit = Number.MAX_SAFE_INTEGER;
 
@@ -161,7 +162,7 @@ export interface EvaluationOptions {
 
   // Allows waiting for confirmation of the interaction.
   // In the case of the 'disableBundling' option, the confirmation comes from the Arweave network,
-  // otherwise from the decentralized Warp Sequencer.
+  // otherwise from the Warp Gateway.
   waitForConfirmation: boolean;
 
   // whether the state cache should be updated after evaluating each interaction transaction.
@@ -197,6 +198,9 @@ export interface EvaluationOptions {
     saveState: boolean;
   };
 
+  // Sequencer URL.
+  // During the transitional stage between the centralized and decentralized sequencers,
+  // this value will not be used, and the SDK will query the Warp Gateway for the address.
   sequencerUrl: string;
 
   gasLimit: number;