diff --git a/.env.example b/.env.example
index c6429ef43f0772af18279a0e30d3b509822cb830..123ff3c4cee7b585a17cc058c8a818f8a91b867e 100644
--- a/.env.example
+++ b/.env.example
@@ -1,5 +1,6 @@
-# Private key used for publishing
-RPC_URL=
+# RPC that supports eth_getLogs
+RPC_URL=https://rpc.mevblocker.io/
+# Private key
 # If private key is filled, it will be used for both deposits and publishing -- be careful if you are doing both
 PRIVATE_KEY=
 THREADS=0 # Use all available threads
@@ -23,4 +24,7 @@ RELAYS=
 
 # Nostr Relay APIs ("Seed" nodes) -- expects an array returned
 MAX_RELAYS=8
-SEEDS=https://api.nostr.watch/v1/online
\ No newline at end of file
+SEEDS=https://api.nostr.watch/v1/online
+
+# Your public ip in case you are using a tunneling service
+PUBLIC_IP=
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index bfccd05f9a9599b603b1a0006d2c11df4d734a80..23b5b2c4591c90b34da174ce14fb4e53910139b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,4 @@ yarn-error.log*
 # Ignore OS generated files
 .DS_Store
 Thumbs.db
+package-lock.json
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000000000000000000000000000000000000..81eda3e9dcf22720de44d8abac5996001e4a0277
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "momiji-helpers"]
+	path = momiji-helpers
+	url = http://open.offshift.io/greybeard/momiji-helpers.git
diff --git a/Dockerfile b/Dockerfile
index ef40147b276e315bbf7481c752d0da340df5e769..cedb60bf7b560db63ce3b64e82ae72c85ba94ba2 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM node:20.0
+FROM node:20.2
 RUN mkdir /code && mkdir /code/node 
 WORKDIR /code/node
 RUN wget https://github.com/noir-lang/noir/releases/download/v0.28.0/nargo-x86_64-unknown-linux-gnu.tar.gz
@@ -9,5 +9,9 @@ COPY . .
 RUN npm install
 RUN cd ./momiji-helpers/ && npm install
 WORKDIR /code/node
-
-ENTRYPOINT [ "npm", "start"]
+RUN apt update && apt install -y tor libc++-dev
+RUN echo "HiddenServiceDir /var/lib/tor/hidden_service/" >> /etc/tor/torrc
+RUN echo "HiddenServicePort 80 127.0.0.1:5150" >> /etc/tor/torrc
+RUN service tor start
+RUN update-rc.d tor enable
+ENTRYPOINT [ "npm", "start" ]
diff --git a/README.md b/README.md
index 51603d8d5883886237d08575f152d925f187735d..8af1377eef75a4e021e50522fbf7a791d5733be7 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ sudo docker build -t xftnode .
 
 for running
 ```sh
-sudo docker run -it xftnode
+sudo docker run -p 5150:5150 -it xftnode
 ```
 ### npm usage
 - Install noirup, the Nargo package manager
diff --git a/momiji-helpers/utils/peering.ts b/momiji-helpers/utils/peering.ts
index c1445f288cf4a7287f8a06169121fea4bd8c0b21..8a33b4133b8fe953143175c8ff606d6d8cc95e85 100644
--- a/momiji-helpers/utils/peering.ts
+++ b/momiji-helpers/utils/peering.ts
@@ -1,199 +1,115 @@
 // @ts-ignore
 import * as types from './types';
 import { TransactionBuilder } from "./transactionBuilder";
-import { createLibp2p } from 'libp2p'
-import { webSockets } from '@libp2p/websockets'
-import * as filters from '@libp2p/websockets/filters'
+// import { createLibp2p } from 'libp2p'
+// import * as filters from '@libp2p/websockets/filters'
 import { finalizeEvent, verifyEvent, setNostrWasm, VerifiedEvent, Event } from 'nostr-tools/wasm'
 import { SimplePool } from 'nostr-tools/pool'
-import { gossipsub } from '@chainsafe/libp2p-gossipsub'
-import { noise } from '@chainsafe/libp2p-noise'
-import { yamux } from '@chainsafe/libp2p-yamux'
-import { mplex } from '@libp2p/mplex'
-import { identify } from "@libp2p/identify"
-import { dcutr } from "@libp2p/dcutr"
+// import { gossipsub } from '@chainsafe/libp2p-gossipsub'
+// import { noise } from '@chainsafe/libp2p-noise'
+// import { yamux } from '@chainsafe/libp2p-yamux'
+// import { mplex } from '@libp2p/mplex'
+// import { identify } from "@libp2p/identify"
+// import { dcutr } from "@libp2p/dcutr"
 import { getPublicKey } from 'nostr-tools/pure';
 import { initNostrWasm } from 'nostr-wasm'
-import { multiaddr } from '@multiformats/multiaddr'
+// import { multiaddr } from '@multiformats/multiaddr'
 import { useWebSocketImplementation } from 'nostr-tools/pool'
-import { pubsubPeerDiscovery } from '@libp2p/pubsub-peer-discovery'
 import WebSocket from 'ws'
+import { Block } from 'ethers';
 
 if (typeof global === "object") useWebSocketImplementation(WebSocket);
 
 
 export class Peering extends TransactionBuilder {
 
-    p2p: any | undefined; 
-    pool: SimplePool = new SimplePool(); 
+    // p2p: any | undefined; 
+    pool: SimplePool = new SimplePool();
     relays: string[];
     seeds: string[];
     rebroadcast: any | undefined;
-    callback: Function;
+    // callback: Function;
 
     constructor(_config: types.GlobalConfig) {
         super(_config);
-        if (!this.config.gossip) throw new Error("Gossip config not found");
+        // if (!this.config.gossip) throw new Error("Gossip config not found");
         this.pool = new SimplePool();
         this.relays = this.config.gossip!.relays;
         this.seeds = this.config.gossip!.seeds;
-        this.callback = () => true;
+        // this.callback = () => true;
     }
 
-    postEvent = async (_sk: Uint8Array, _timestamp: number) => Promise.any(this.pool.publish(this.relays, finalizeEvent({
+    postEvent = async (_sk: Uint8Array, _timestamp: number, ips: string) => Promise.any(this.pool.publish(this.relays, finalizeEvent({
         kind: 1,
         created_at: _timestamp,
         tags: [],
-        content: this.p2p.getMultiaddrs().map((addr: any) => multiaddr(addr).toString()).join(',')
+        content: ips
     }, _sk)))
-    postTransaction = async (_tx: types.Transaction) => {
-        const hexString = (await this.contracts.state.getAddress()).slice(2).padStart(64, '0');
-        const matches = hexString.match(/.{1,2}/g);
-        const skUint8Array = matches ? new Uint8Array(matches.map(byte => parseInt(byte, 16))) : new Uint8Array();
-        const _pk: string = getPublicKey(skUint8Array);
 
-        if (this.p2p.services.pubsub.getSubscribers(_pk).length > 0) {
-            await this.p2p.services.pubsub.publish(_pk, new TextEncoder().encode(JSON.stringify(_tx)))
-        } else {
-            setTimeout(async () => await this.postTransaction(_tx), 10_000);
-        }
-    };
+    // postTransaction = async (_tx: types.Transaction) => {
+    //     const hexString = (await this.contracts.state.getAddress()).slice(2).padStart(64, '0');
+    //     const matches = hexString.match(/.{1,2}/g);
+    //     const skUint8Array = matches ? new Uint8Array(matches.map(byte => parseInt(byte, 16))) : new Uint8Array();
+    //     const _pk: string = getPublicKey(skUint8Array);
+
+    //     if (this.p2p.services.pubsub.getSubscribers(_pk).length > 0) {
+    //         await this.p2p.services.pubsub.publish(_pk, new TextEncoder().encode(JSON.stringify(_tx)))
+    //     } else {
+    //         setTimeout(async () => await this.postTransaction(_tx), 10_000);
+    //     }
+    // };
     initializePeering = async (callback?: Function): Promise<void> => {
         await this.initializeTransactionBuilder();
-        const hexString = (await this.contracts.state.getAddress()).slice(2).padStart(64, '0');
+        const hexString = "6835ccbeddc1c1c91fed2116099592a309f98d0df34d8dc72e770df83cc3b065".padStart(64, '0');
         const matches = hexString.match(/.{1,2}/g);
-        const skUint8Array = matches ? new Uint8Array(matches.map(byte => parseInt(byte, 16))) : new Uint8Array();
+        const skUint8Array = matches ? new Uint8Array(matches.map(byte => parseInt(byte, 16))) : new Uint8Array()
         let _pk = getPublicKey(skUint8Array);
-
         await this.fetchRelays();
         await initNostrWasm().then(setNostrWasm);
-
         const latestBlock = await this.config.provider.getBlock("latest");
-
-        let multiAddrs: string[] = [];
-
-        this.p2p = await createLibp2p({
-            addresses: {
-                listen: (typeof global === "object") ? ['/ip4/0.0.0.0/tcp/5150/ws'] : []
-            },
-            transports: [webSockets({
-                filter: filters.all
-            })],
-            connectionEncryption: [noise()],
-            streamMuxers: [yamux(), mplex()],
-            services: {
-                pubsub: gossipsub(),
-                identify: identify(),
-                dcutr: dcutr()
-            },
-            connectionGater: {
-                denyDialMultiaddr: () => {
-                    return false
-                }
-            },
-            peerDiscovery: [
-                pubsubPeerDiscovery({
-                    interval: 10_000 
-                })
-            ]
-        });
-
-        await this.p2p.start()
-        this.p2p.services.pubsub.subscribe(_pk)
-        await this.p2p.services.pubsub.start()
-
-        this.p2p.addEventListener('connection:open', async (event: CustomEvent) => {
-            console.log("peering.ts - connection open", event.detail.id.toString())
-        })
-        this.p2p.addEventListener('connection:close', () => {
-            console.log("peering.ts - connection closed")
-        })
-        this.p2p.addEventListener("peer:connect", async (event: CustomEvent) => {
-            console.log("peering.ts - peer connect")
-        })
-        this.p2p.addEventListener("peer:discovery", (event: CustomEvent) => {
-            console.log("peering.ts - peer discovered:", event.detail.id.toString())
-        })
-
-        if (typeof global === "object") {
-            try {
-                this.pool.subscribeMany(
-                    this.relays.slice(0, this.config.gossip!.maxRelays),
-                    [
-                        {
-                            kinds: [1],
-                            authors: [_pk],
-                            since: latestBlock!.timestamp
-                        },
-                    ],
-                    {
-                        onevent: async (event) => event.content.split(',')
-                            .filter(addr => {
-                                try {
-                                    multiaddr(addr);
-                                    return true;
-                                } catch (error) {
-                                    return false;
-                                }
-                            })
-                            .forEach(async (addr) => this.addPeer(addr)),
-                        oneose: async () => true,
-                        onclose: async () => true,
-                    }
-                )
-            } catch (error) {
+        let ip: string
+        if (this.config.ip == "") {
+            let ips: string[] = []
+            let ipServices = ["https://api.ipify.org", "https://icanhazip.com", "https://ifconfig.me/ip", "https://checkip.amazonaws.com/"]
+            for (let i = 0; i < ipServices.length; i++) {
+                let res = await fetch(ipServices[i])
+                ips.push((await res.text()).trim())
             }
-        } else {
-            try {
-                const events: Event[] = await this.pool.querySync(this.relays.slice(0, this.config.gossip!.maxRelays),
-                    {
-                        kinds: [1],
-                        authors: [_pk],
-                        since: latestBlock!.timestamp - (typeof global === 'object' ? 0 : 86400)
-                    }
-                )
-                console.log("peering.ts - nostr events", events)
-                multiAddrs = events.map(event => event.content.split(","))
-                    .flat(1)
-                    .filter(addr => {
-                        try {
-                            multiaddr(addr);
-                            return true;
-                        } catch (error) {
-                            return false;
-                        }
-                    })
-
-                for (let addr in multiAddrs) await this.addPeer(multiAddrs[addr]);
-
-            } catch (error) {
-                console.log("error, peering.ts - initializePeering(), pool.querySync()", error)
+            let last = ips[0]
+            for (let i = 0; i < ips.length; i++) {
+                if (last === ips[i]) {
+                    last = ips[i]
+                } else {
+                    console.log("Please set your public ip in the config file")
+                    return
+                }
             }
+            ip = last
+        }else {
+            ip = this.config.ip as string
         }
-
         if (typeof global === 'object') {
-            this.rebroadcast = setInterval(async () => await this.postEvent(skUint8Array, latestBlock!.timestamp), 60 * 60 * 1000);
-            await this.postEvent(skUint8Array, latestBlock!.timestamp);
-            this.p2p.services.pubsub.addEventListener('message', (message: CustomEvent) => {
-                if (!(message.detail.topic == _pk) || !callback) return;
-                callback(JSON.parse(new TextDecoder().decode(message.detail.data)));
-            })
+            this.rebroadcast = setInterval(async () => {
+                await this.postEvent(skUint8Array, latestBlock!.timestamp, ip)
+            }, 60 * 60 * 1000);
 
+            await this.postEvent(skUint8Array, latestBlock!.timestamp, ip);
         }
 
     }
 
-    addPeer = async (peerMultiaddr: string): Promise<void> => {
-        if (!this.p2p) {
-            throw new Error('P2P is not initialized');
-        }
-        try {
-            await this.p2p.dial(multiaddr(peerMultiaddr));
-            console.log(`Successfully connected to peer: ${peerMultiaddr}`);
-        } catch (error) {
-        }
-    };
+    queryEvents = async (latestBlock: number) => {
+        const hexString = "6835ccbeddc1c1c91fed2116099592a309f98d0df34d8dc72e770df83cc3b065".padStart(64, '0');
+        const matches = hexString.match(/.{1,2}/g);
+        const skUint8Array = matches ? new Uint8Array(matches.map(byte => parseInt(byte, 16))) : new Uint8Array()
+        let _pk = getPublicKey(skUint8Array);
+        let event = await this.pool.querySync(this.relays, {
+            kinds: [1],
+            authors: [_pk],
+            since: latestBlock - (typeof global === 'object' ? 0 : 86400)
 
+        })
+    }
     fetchRelays = async (refresh: boolean = false): Promise<string[]> => {
         if (this.seeds.length == 0 || refresh) return this.relays;
         for (const seed of this.seeds) {
diff --git a/momiji-helpers/utils/types.ts b/momiji-helpers/utils/types.ts
index 07d834de1ab2c8ecbc2cb622433d8df254c2a65c..0ec00191a166510ac71ae11da3abca26d57a4c83 100644
--- a/momiji-helpers/utils/types.ts
+++ b/momiji-helpers/utils/types.ts
@@ -88,6 +88,7 @@ export type GlobalConfig = {
   withdrawal?: string, 
   verbose?: boolean,
   threads?: number,
+  ip?: string,
   provider: Provider
 }
 
diff --git a/package.json b/package.json
index ec843a6fc7b9fbc4897ffcc64c60c901ed2b8410..4a73936830cfacaf8e6ee8066293a8bd34493cae 100644
--- a/package.json
+++ b/package.json
@@ -17,27 +17,27 @@
   "devDependencies": {
     "@types/node": "^20.10.4",
     "@types/validator": "^13.11.7",
+    "@types/ws": "^8.5.12",
     "dotenv": "^16.4.5",
     "hardhat": "^2.22.3",
     "nodemon": "^3.0.2",
     "ts-node": "^10.9.2",
-    "typescript": "^5.3.2"
+    "typescript": "^5.3.2",
+    "ws": "^8.18.0"
   },
   "dependencies": {
     "@chainlink/contracts": "^0.8.0",
-    "@libp2p/bootstrap": "^10.1.0",
-    "@libp2p/identify": "^2.1.2",
     "@noir-lang/noir_js": "^0.28.0",
     "@nomicfoundation/hardhat-toolbox": "^5.0.0",
     "@openzeppelin/contracts": "^5.0.2",
     "@openzeppelin/contracts-upgradeable": "^5.0.2",
     "@openzeppelin/hardhat-upgrades": "^3.2.0",
-    "@types/pubsub-js": "^1.8.6",
+    "@types/express": "^4.17.21",
     "@uniswap/v3-periphery": "^1.4.4",
+    "cors": "^2.8.5",
     "ethers": "^6.13.0",
-    "fonstr": "^0.0.11",
+    "express": "^4.19.2",
     "indexeddbshim": "^15.0.0",
-    "memorelay": "^2.0.4",
     "reflect-metadata": "^0.1.14",
     "sequelize": "^6.35.1",
     "sequelize-typescript": "^2.1.6"
diff --git a/src/index.ts b/src/index.ts
index 77c8106a71aa07a934044328253396f4a3addaf3..454c9d3e2310a4e69c65aaeda5ea1376a34e63a6 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -2,6 +2,7 @@ import { Publisher } from "./modules/momiji";
 import * as types from "../momiji-helpers/utils/types";
 import { ethers } from "ethers";
 import 'dotenv/config';
+import { WebSocketServer } from "ws";
 
 const main = async () => {
   let _globalConfig: types.GlobalConfig = {
@@ -18,10 +19,50 @@ const main = async () => {
     verbose: (process.env.VERBOSE) ? (process.env.VERBOSE.toLowerCase() === "true") : false,
     profit: (process.env.PROFIT) ? parseInt(process.env.PROFIT) : 0
   };
-  
+
   let publisher = new Publisher(_globalConfig);
 
   await publisher.initializePublisher();
+  const wss = new WebSocketServer({
+    host: '0.0.0.0',
+    port: 5150
+  });
+  wss.on('connection', function connection(ws) {
+    ws.on('error', console.error);
+    ws.on('message', async function message(data) {
+      if (publisher.queue.length >= 14){
+        ws.send('{status:"error", msg:"Prover queue is full"}')
+        return
+      }
+      const valid = await publisher.newTransactionReceivedMultiple(JSON.parse(data.toString()));
+      if (valid){
+        ws.send('{status:"ok", msg:"Proof added to queue"}');  
+      } else {
+        ws.send('{status:"error", msg:"Invalid proof"}');
+      }
+    });
+  });
+  let queuing = false
+  const wenQueue = async() => {
+    if (!queuing && publisher.queue.length > 0) {
+      queuing = true;
+      await publisher.proveMultiple()
+      queuing = false;
+    }
+    
+  }
+
+  let publishing = false
+  const wenPublish = async () => {
+    if (!publishing && !queuing && publisher.batch.length > 0) {
+      publishing = true
+      await publisher.publishSingle()
+      publishing = false
+    }
+  }
+
+  setInterval(wenQueue, 1e4)
+  setInterval(wenPublish, 1e5)
 }
 
 console.log(`💫 Starting publisher...`)
diff --git a/src/modules/momiji.ts b/src/modules/momiji.ts
index cff67d97cc06007a785f2498796dfa8314ce69aa..654c9ad55901a48806785c8be68bbabb640b9e58 100644
--- a/src/modules/momiji.ts
+++ b/src/modules/momiji.ts
@@ -4,19 +4,22 @@ import { keccak_tx } from '../../momiji-helpers/circuits/helpers/codegen/keccak_
 import { tx_as_hash } from '../../momiji-helpers/circuits/helpers/codegen/tx_as_hash';
 
 export class Publisher extends BatchBuilder {
-  private publishTimeout: NodeJS.Timeout;
+  // private publishTimeout: NodeJS.Timeout;
   private signer: types.EthersSigner | undefined;
   private mempool: types.Mempool = {};
   private provingQueue: Promise<types.RecursionInputs[]> = Promise.resolve([]);
-  private batch: types.RecursionInputs[] = [];
+  public batch: types.RecursionInputs[] = [];
   private confirmed: types.Confirmed = {};
   private publishing: boolean = false;
   private proving: boolean = false;
   private sweeping: boolean = false;
+  public queue: types.Transaction[] = [];
+  public accumulator: string = types.ZERO_VALUE;
+  private contractPublish: types.ContractPublish = {} as types.ContractPublish;
 
   constructor(config: types.GlobalConfig) {
     super(config);
-    this.publishTimeout = setInterval(this.publish, 60 * 1000);
+    // this.publishTimeout = setInterval(this.publish, 60 * 1000);
     this.mempool = {};
     this.confirmed = {};
   }
@@ -60,12 +63,46 @@ export class Publisher extends BatchBuilder {
     }
 
     let txid = await keccak_tx(tx.public_inputs);
-    
     let _verified = await this.addToMempool(tx, txid)
     if (_verified) this.queueToProve(tx)
     else console.log(`🔴 Transaction rejected -- failed to verify: ${txid}`);
   }
 
+  async newTransactionReceivedSingle(tx: types.Transaction): Promise<void> {
+    let txid = await keccak_tx(tx.public_inputs);
+    let _verified = await this.addToMempool(tx, txid)
+    if (_verified) {
+      await this.proveSingle(tx);
+    }
+    else console.log(`🔴 Transaction rejected -- failed to verify: ${txid}`);
+  }
+
+  async newTransactionReceivedMultiple(tx: types.Transaction): Promise<boolean> {
+    let txid = await keccak_tx(tx.public_inputs);
+    let _verified = await this.addToMempool(tx, txid)
+    if (_verified) {
+      this.queue.push(tx)
+      return true
+    }
+    else {
+      console.log(`🔴 Transaction rejected -- failed to verify: ${txid}`);
+      return false
+    };
+  }
+
+  async proveMultiple(): Promise<void> {
+    for (let i = 0; i < this.queue.length; i ++) {
+      if (this.batch.length >= 15) {
+        console.log(`⚠️ Batch is full. Cannot prove additional transactions.`)
+        this.queue = this.queue.slice(i)
+        return
+      }
+      this.batch = await this.proveSingle(this.queue[i])
+    }
+    this.queue = []
+    return
+  }
+
   async setupListeners(): Promise<void> {
     if (!this.contracts) await this.initializePublisher();
     if (!this.contracts) return;
@@ -104,7 +141,8 @@ export class Publisher extends BatchBuilder {
 
     this.contracts.state.on(this.contracts.state.filters.BatchPublish(undefined, undefined, undefined, undefined, undefined), async (event: any) => {
       console.log(`🎯 Batch published: ${event}.`);
-      this.provingQueue = Promise.resolve([]);
+      // this.provingQueue = Promise.resolve([]);
+      this.queue = []
       this.batch = [];
       this.printRoot();
       return;
@@ -131,7 +169,7 @@ export class Publisher extends BatchBuilder {
     const withdrawals: types.WithdrawalSwap[] = await this._generateWithdrawals([{
       amount: (new types.NoirFr(withdrawalAmount)).toString(),
       recipient: types.toFixedHex(1, true),
-      swap_percentage: 100 
+      swap_percentage: 100
     }], 1)
     const proverTx: types.Transaction = await this._generateTransactionProof(utxo_commitments, utxo_encrypted, withdrawals);
     return proverTx;
@@ -149,7 +187,13 @@ export class Publisher extends BatchBuilder {
     })
     const withdrawalAmount: bigint = utxo_commitments.map(utxo => BigInt(utxo.amount)).reduce((prev, curr) => prev + curr)
     const withdrawalAmountEther: bigint = await this._getEtherFromXFT(withdrawalAmount)
-    const gasEstimate: bigint = await this.contracts.state.publish.estimateGas(contractPublish.proof, contractPublish.batch)
+    let gasEstimate: bigint
+    try {
+      gasEstimate = await this.contracts.state.publish.estimateGas(contractPublish.proof, contractPublish.batch)
+    } catch {
+      this.batch.pop()
+      return false
+    }
     const maxFeePerGas: bigint = await this.config.provider.getFeeData().then(feeData => feeData.maxFeePerGas as bigint)
     const maxFeePerGasAdjusted: bigint = (this.config.profit) ? maxFeePerGas + BigInt(this.config.profit * 1e9) : maxFeePerGas
     const txFeeEstimate: bigint = gasEstimate * maxFeePerGasAdjusted
@@ -189,6 +233,15 @@ export class Publisher extends BatchBuilder {
     })
   }
 
+  async proveSingle(tx: types.Transaction): Promise<types.RecursionInputs[]> {
+    this.proving = true;
+    console.log(`⏳ Proving ${await keccak_tx(tx.public_inputs)}.`)
+    const proof = await this.rollupTransaction(this.batch, tx);
+    console.log(`✔️ Proved ${await keccak_tx(tx.public_inputs)}.`)
+    this.proving = false
+    return proof
+  }
+
   publish = async () => {
 
     if (this.publishing) return;
@@ -196,10 +249,9 @@ export class Publisher extends BatchBuilder {
     if (this.sweeping) return;
     if (this.batch.length === 0) return;
 
-    await this.provingQueue;
-    const contractPublish: types.ContractPublish = await this.preparePublish(this.batch)
-
-    if (!(await this.publishReady(this.batch, contractPublish))) return;
+    // await this.provingQueue;
+    // const contractPublish: types.ContractPublish = await this.preparePublish(this.batch)
+    // if (!(await this.publishReady(this.batch, contractPublish))) return;
 
     console.log(`🧹 Sweeping prover fees...`)
     this.sweeping = true
@@ -213,7 +265,7 @@ export class Publisher extends BatchBuilder {
 
     console.log(`🗞️ Publishing batch...`)
     const contractPublishProver: types.ContractPublish = await this.preparePublish(this.batch)
-    clearInterval(this.publishTimeout);
+    // clearInterval(this.publishTimeout);
 
     await this.contracts.state.publish(contractPublishProver.proof, contractPublishProver.batch)
       .then((tx: any) => console.log(`📡 Batch published: ${tx.hash}.`), (error: any) => console.log(error.message));
@@ -221,7 +273,48 @@ export class Publisher extends BatchBuilder {
     this.provingQueue = Promise.resolve([]);
     this.batch = [];
 
-    this.publishTimeout = setInterval(this.publish, 60 * 1000);
+    // this.publishTimeout = setInterval(this.publish, 60 * 1000);
+    this.publishing = false;
+
+    return;
+  }
+
+  async publishSingle() {
+
+    if (this.publishing) return;
+    if (this.proving) return;
+    if (this.batch.length === 0) return;
+
+    // If the accumulator has changed since the last pass
+    // ie. a new transaction has been added to the batch
+    if (this.accumulator !== this.batch[this.batch.length - 1].accumulator) {
+      console.log(`📝 Preparing batch publish`)
+      this.proving = true
+      this.contractPublish = await this.preparePublish(this.batch)
+      this.accumulator = this.batch[this.batch.length - 1].accumulator
+      this.proving = false
+      return;
+    }
+    if (!(await this.publishReady(this.batch, this.contractPublish))) return;
+
+    this.publishing = true
+    console.log(`🧹 Sweeping prover fees...`)
+    const transactions: types.Transaction[] = this.batch.filter(tx => tx.transaction !== undefined).map(tx => tx.transaction as types.Transaction)
+    const proverTx: types.Transaction = await this.sweepProfit(transactions)
+    const proverBatch: types.RecursionInputs[] = await this.proveSingle(proverTx)
+    const contractPublishProver: types.ContractPublish = await this.preparePublish(proverBatch)
+
+    console.log(`🗞️ Publishing batch...`)
+    await this.contracts.state.publish(contractPublishProver.proof, contractPublishProver.batch)
+      .then((tx: any) => console.log(`📡 Batch published: ${tx.hash}.`), (error: any) => console.log(error.message));
+
+    this.queue = [];
+    this.batch = [];
+    this.accumulator = types.ZERO_VALUE;
+    this.contractPublish = {} as types.ContractPublish
+
+    if (Object.keys(this.mempool).length >= 1000) this.mempool = {}
+    if (Object.keys(this.confirmed).length >= 1000) this.confirmed = {}
     this.publishing = false;
 
     return;