From 13f2b080c2c42d516a04ea3a53d26dc9d8217f22 Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Thu, 18 Sep 2025 08:48:34 +0200 Subject: [PATCH 01/11] feat: dryrun fifo --- src/ao/messaging/getData.ts | 49 ++++++++++++++++++++++++++++++++++++- src/index.ts | 3 +++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/ao/messaging/getData.ts b/src/ao/messaging/getData.ts index 6874977..e248440 100644 --- a/src/ao/messaging/getData.ts +++ b/src/ao/messaging/getData.ts @@ -1,5 +1,7 @@ -import { DryRunResult } from "@permaweb/aoconnect/dist/lib/dryrun"; +import { DryRunResult, MessageInput } from "@permaweb/aoconnect/dist/lib/dryrun"; import { connectToAO, Services } from "../utils/connect"; +import { dryrun } from "@permaweb/aoconnect/browser"; +import { dryRunAwait } from "../utils/dryRunAwait"; interface MessageTags { Target: string; @@ -52,3 +54,48 @@ export async function getData( throw new Error(`Error sending ao dryrun: ${error}`); } } + +export class DryRunFIFO { + #queue: { + msg: MessageInput; + resolve: (result: DryRunResult) => void; + reject: (reason?: any) => void; + }[]; + #delay: number; + #running: boolean; + + constructor(delay = 1200) { + this.#queue = []; + this.#delay = delay; + this.#running = false; + } + + put(msg: MessageInput) { + return new Promise((resolve, reject) => { + this.#queue.push({ msg, resolve, reject }); + this.#run(); + }); + } + + async #run() { + if (this.#running) return; + this.#running = true; + + while (this.#queue.length > 0) { + const { msg, resolve, reject } = this.#queue.shift()!; + + try { + const res = await dryrun(msg); + resolve(res); + } catch (e) { + reject(e); + } + + if (this.#queue.length > 0) { + await dryRunAwait(this.#delay); + } + } + + this.#running = false; + } +} diff --git a/src/index.ts b/src/index.ts index ca02d40..94fb99a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -124,11 +124,14 @@ import { GetEarnings, GetEarningsRes, } from "./functions/lend/getEarnings"; +import { DryRunFIFO } from "./ao/messaging/getData"; class LiquidOps { private signer: any; private configs: Omit; + static dryRunFifo = new DryRunFIFO(); + constructor(signer: any, configs: Omit = {}) { if (!signer) { throw new Error("Please specify a ao createDataItemSigner signer"); From eaa95fa0e0fb95b5145d94e44d2019e8d391c8ac Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Thu, 18 Sep 2025 09:36:09 +0200 Subject: [PATCH 02/11] feat: improved dryrun fifo for multiple CUs --- src/ao/messaging/getData.ts | 57 ++++++++++++++++++++++++++++++------- src/index.ts | 2 +- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/src/ao/messaging/getData.ts b/src/ao/messaging/getData.ts index e248440..1787b90 100644 --- a/src/ao/messaging/getData.ts +++ b/src/ao/messaging/getData.ts @@ -1,7 +1,8 @@ -import { DryRunResult, MessageInput } from "@permaweb/aoconnect/dist/lib/dryrun"; +import { DryRun, DryRunResult, MessageInput } from "@permaweb/aoconnect/dist/lib/dryrun"; import { connectToAO, Services } from "../utils/connect"; -import { dryrun } from "@permaweb/aoconnect/browser"; import { dryRunAwait } from "../utils/dryRunAwait"; +import { connect } from "@permaweb/aoconnect"; +import LiquidOps from "../.."; interface MessageTags { Target: string; @@ -36,7 +37,11 @@ export async function getData( const targetProcessID = messageTags["Target"]; try { - const { dryrun } = connectToAO(config); + let { dryrun } = connectToAO(config); + if (LiquidOps.dryRunFifo) { + dryrun = LiquidOps.dryRunFifo.put; + } + const { Messages, Spawns, Output, Error } = await dryrun({ process: targetProcessID, data: "", @@ -55,19 +60,50 @@ export async function getData( } } +class DryRunList { + #list: DryRun[]; + #delay: number; + #resolves: Array<(val: DryRun) => void>; + + constructor(list: DryRun[] = [], delay: number) { + this.#list = list; + this.#delay = delay; + this.#resolves = []; + } + + push(item: DryRun) { + setTimeout(() => { + const nextRequest = this.#resolves.shift(); + if (nextRequest) nextRequest(item); + else this.#list.push(item); + }, this.#delay); + } + + async waitForOne() { + const next = this.#list.shift(); + if (next) return next; + + return new Promise((resolve) => { + this.#resolves.push(resolve); + }); + } +} + export class DryRunFIFO { - #queue: { + #queue: Array<{ msg: MessageInput; resolve: (result: DryRunResult) => void; reject: (reason?: any) => void; - }[]; - #delay: number; + }>; #running: boolean; + #availableDryRuns: DryRunList; - constructor(delay = 1200) { + constructor(delay = 1200, CUs: string[]) { this.#queue = []; - this.#delay = delay; this.#running = false; + this.#availableDryRuns = new DryRunList(CUs.map( + (CU_URL) => connect({ MODE: "legacy", CU_URL }).dryrun + ), delay); } put(msg: MessageInput) { @@ -82,6 +118,7 @@ export class DryRunFIFO { this.#running = true; while (this.#queue.length > 0) { + const dryrun = await this.#availableDryRuns.waitForOne(); const { msg, resolve, reject } = this.#queue.shift()!; try { @@ -91,9 +128,7 @@ export class DryRunFIFO { reject(e); } - if (this.#queue.length > 0) { - await dryRunAwait(this.#delay); - } + this.#availableDryRuns.push(dryrun) } this.#running = false; diff --git a/src/index.ts b/src/index.ts index 94fb99a..879a140 100644 --- a/src/index.ts +++ b/src/index.ts @@ -130,7 +130,7 @@ class LiquidOps { private signer: any; private configs: Omit; - static dryRunFifo = new DryRunFIFO(); + static dryRunFifo?: DryRunFIFO; constructor(signer: any, configs: Omit = {}) { if (!signer) { From 0bb22ba683e0e8e05b0acea6374c2efe30ad9ba2 Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Thu, 18 Sep 2025 09:36:45 +0200 Subject: [PATCH 03/11] feat: export fifo --- src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.ts b/src/index.ts index 879a140..f02fe97 100644 --- a/src/index.ts +++ b/src/index.ts @@ -355,4 +355,5 @@ export { tokenInput, tokenData, lqdTokenAddress, + DryRunFIFO, }; From 7a9a0480a59941ddc88b475ccc976b16bf41de99 Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Thu, 18 Sep 2025 09:41:02 +0200 Subject: [PATCH 04/11] feat: switch fifo params --- src/ao/messaging/getData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ao/messaging/getData.ts b/src/ao/messaging/getData.ts index 1787b90..32b7bf4 100644 --- a/src/ao/messaging/getData.ts +++ b/src/ao/messaging/getData.ts @@ -98,7 +98,7 @@ export class DryRunFIFO { #running: boolean; #availableDryRuns: DryRunList; - constructor(delay = 1200, CUs: string[]) { + constructor(CUs: string[], delay = 1200) { this.#queue = []; this.#running = false; this.#availableDryRuns = new DryRunList(CUs.map( From b4d0deb707271dee6731c8b7af4c1b9d49df0093 Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Thu, 18 Sep 2025 17:19:49 +0200 Subject: [PATCH 05/11] fix: private err --- src/ao/messaging/getData.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/ao/messaging/getData.ts b/src/ao/messaging/getData.ts index 32b7bf4..698dd1b 100644 --- a/src/ao/messaging/getData.ts +++ b/src/ao/messaging/getData.ts @@ -37,17 +37,16 @@ export async function getData( const targetProcessID = messageTags["Target"]; try { - let { dryrun } = connectToAO(config); - if (LiquidOps.dryRunFifo) { - dryrun = LiquidOps.dryRunFifo.put; - } - - const { Messages, Spawns, Output, Error } = await dryrun({ + const { dryrun } = connectToAO(config); + const msg = { process: targetProcessID, data: "", tags: convertedMessageTags, Owner: messageTags.Owner || "1234", - }); + }; + const { Messages, Spawns, Output, Error } = LiquidOps.dryRunFifo ? + await LiquidOps.dryRunFifo.put(msg) : + await dryrun(msg); return { Messages, From 4d612e6c7a74e137920a12899d3425ba8f827754 Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Fri, 19 Sep 2025 09:09:52 +0200 Subject: [PATCH 06/11] chore: move dryrunfifo --- src/ao/messaging/DryRunFIFO.ts | 77 ++++++++++++++++++++++++++++++++++ src/ao/messaging/getData.ts | 75 --------------------------------- src/index.ts | 2 +- 3 files changed, 78 insertions(+), 76 deletions(-) create mode 100644 src/ao/messaging/DryRunFIFO.ts diff --git a/src/ao/messaging/DryRunFIFO.ts b/src/ao/messaging/DryRunFIFO.ts new file mode 100644 index 0000000..4f2a0e2 --- /dev/null +++ b/src/ao/messaging/DryRunFIFO.ts @@ -0,0 +1,77 @@ +import { DryRun, DryRunResult, MessageInput } from "@permaweb/aoconnect/dist/lib/dryrun"; +import { connect } from "@permaweb/aoconnect"; + +export class DryRunFIFO { + #queue: Array<{ + msg: MessageInput; + resolve: (result: DryRunResult) => void; + reject: (reason?: any) => void; + }>; + #running: boolean; + #availableDryRuns: DryRunList; + + constructor(CUs: string[], delay = 1200) { + this.#queue = []; + this.#running = false; + this.#availableDryRuns = new DryRunList(CUs.map( + (CU_URL) => connect({ MODE: "legacy", CU_URL }).dryrun + ), delay); + } + + put(msg: MessageInput) { + return new Promise((resolve, reject) => { + this.#queue.push({ msg, resolve, reject }); + this.#run(); + }); + } + + async #run() { + if (this.#running) return; + this.#running = true; + + while (this.#queue.length > 0) { + const dryrun = await this.#availableDryRuns.waitForOne(); + const { msg, resolve, reject } = this.#queue.shift()!; + + try { + const res = await dryrun(msg); + resolve(res); + } catch (e) { + reject(e); + } + + this.#availableDryRuns.push(dryrun) + } + + this.#running = false; + } +} + +class DryRunList { + #list: DryRun[]; + #delay: number; + #resolves: Array<(val: DryRun) => void>; + + constructor(list: DryRun[] = [], delay: number) { + this.#list = list; + this.#delay = delay; + this.#resolves = []; + } + + push(item: DryRun) { + setTimeout(() => { + const nextRequest = this.#resolves.shift(); + if (nextRequest) nextRequest(item); + else this.#list.push(item); + }, this.#delay); + } + + async waitForOne() { + const next = this.#list.shift(); + if (next) return next; + + return new Promise((resolve) => { + this.#resolves.push(resolve); + }); + } +} diff --git a/src/ao/messaging/getData.ts b/src/ao/messaging/getData.ts index 698dd1b..46d54c3 100644 --- a/src/ao/messaging/getData.ts +++ b/src/ao/messaging/getData.ts @@ -58,78 +58,3 @@ export async function getData( throw new Error(`Error sending ao dryrun: ${error}`); } } - -class DryRunList { - #list: DryRun[]; - #delay: number; - #resolves: Array<(val: DryRun) => void>; - - constructor(list: DryRun[] = [], delay: number) { - this.#list = list; - this.#delay = delay; - this.#resolves = []; - } - - push(item: DryRun) { - setTimeout(() => { - const nextRequest = this.#resolves.shift(); - if (nextRequest) nextRequest(item); - else this.#list.push(item); - }, this.#delay); - } - - async waitForOne() { - const next = this.#list.shift(); - if (next) return next; - - return new Promise((resolve) => { - this.#resolves.push(resolve); - }); - } -} - -export class DryRunFIFO { - #queue: Array<{ - msg: MessageInput; - resolve: (result: DryRunResult) => void; - reject: (reason?: any) => void; - }>; - #running: boolean; - #availableDryRuns: DryRunList; - - constructor(CUs: string[], delay = 1200) { - this.#queue = []; - this.#running = false; - this.#availableDryRuns = new DryRunList(CUs.map( - (CU_URL) => connect({ MODE: "legacy", CU_URL }).dryrun - ), delay); - } - - put(msg: MessageInput) { - return new Promise((resolve, reject) => { - this.#queue.push({ msg, resolve, reject }); - this.#run(); - }); - } - - async #run() { - if (this.#running) return; - this.#running = true; - - while (this.#queue.length > 0) { - const dryrun = await this.#availableDryRuns.waitForOne(); - const { msg, resolve, reject } = this.#queue.shift()!; - - try { - const res = await dryrun(msg); - resolve(res); - } catch (e) { - reject(e); - } - - this.#availableDryRuns.push(dryrun) - } - - this.#running = false; - } -} diff --git a/src/index.ts b/src/index.ts index f02fe97..e8b60b5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -124,7 +124,7 @@ import { GetEarnings, GetEarningsRes, } from "./functions/lend/getEarnings"; -import { DryRunFIFO } from "./ao/messaging/getData"; +import { DryRunFIFO } from "./ao/messaging/DryRunFIFO"; class LiquidOps { private signer: any; From b5551687b462c357cd07fe5ae29524db4e336a24 Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Fri, 19 Sep 2025 09:10:32 +0200 Subject: [PATCH 07/11] feat: modify default delay --- src/ao/messaging/DryRunFIFO.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ao/messaging/DryRunFIFO.ts b/src/ao/messaging/DryRunFIFO.ts index 4f2a0e2..70d75bf 100644 --- a/src/ao/messaging/DryRunFIFO.ts +++ b/src/ao/messaging/DryRunFIFO.ts @@ -10,7 +10,7 @@ export class DryRunFIFO { #running: boolean; #availableDryRuns: DryRunList; - constructor(CUs: string[], delay = 1200) { + constructor(CUs: string[], delay = 500) { this.#queue = []; this.#running = false; this.#availableDryRuns = new DryRunList(CUs.map( From 936fb1bfe0756be96f416e3cfd713fa641411ea3 Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Fri, 19 Sep 2025 09:13:49 +0200 Subject: [PATCH 08/11] chore: fmt --- src/ao/messaging/DryRunFIFO.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ao/messaging/DryRunFIFO.ts b/src/ao/messaging/DryRunFIFO.ts index 70d75bf..cfd1797 100644 --- a/src/ao/messaging/DryRunFIFO.ts +++ b/src/ao/messaging/DryRunFIFO.ts @@ -40,7 +40,7 @@ export class DryRunFIFO { reject(e); } - this.#availableDryRuns.push(dryrun) + this.#availableDryRuns.push(dryrun); } this.#running = false; From 73db044cc5854e2dc13d5d9de89a38ab3419388f Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Fri, 19 Sep 2025 13:59:08 +0200 Subject: [PATCH 09/11] feat: improve await logic --- src/ao/messaging/DryRunFIFO.ts | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/ao/messaging/DryRunFIFO.ts b/src/ao/messaging/DryRunFIFO.ts index cfd1797..a576810 100644 --- a/src/ao/messaging/DryRunFIFO.ts +++ b/src/ao/messaging/DryRunFIFO.ts @@ -1,12 +1,14 @@ import { DryRun, DryRunResult, MessageInput } from "@permaweb/aoconnect/dist/lib/dryrun"; import { connect } from "@permaweb/aoconnect"; +interface DryRunQueueItem { + msg: MessageInput; + resolve: (result: DryRunResult) => void; + reject: (reason?: any) => void; +} + export class DryRunFIFO { - #queue: Array<{ - msg: MessageInput; - resolve: (result: DryRunResult) => void; - reject: (reason?: any) => void; - }>; + #queue: DryRunQueueItem[]; #running: boolean; #availableDryRuns: DryRunList; @@ -33,14 +35,10 @@ export class DryRunFIFO { const dryrun = await this.#availableDryRuns.waitForOne(); const { msg, resolve, reject } = this.#queue.shift()!; - try { - const res = await dryrun(msg); - resolve(res); - } catch (e) { - reject(e); - } - - this.#availableDryRuns.push(dryrun); + dryrun(msg) + .then(resolve) + .catch(reject) + .finally(() => this.#availableDryRuns.push(dryrun)) } this.#running = false; From 0b6f29a907c2ec549525d92261ef275f8c10b4bb Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Fri, 19 Sep 2025 18:59:15 +0200 Subject: [PATCH 10/11] feat: dry run parallell process --- src/ao/messaging/DryRunFIFO.ts | 59 +++++++++++++++++++++++++++++++++- src/index.ts | 3 +- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/ao/messaging/DryRunFIFO.ts b/src/ao/messaging/DryRunFIFO.ts index a576810..017b520 100644 --- a/src/ao/messaging/DryRunFIFO.ts +++ b/src/ao/messaging/DryRunFIFO.ts @@ -38,7 +38,7 @@ export class DryRunFIFO { dryrun(msg) .then(resolve) .catch(reject) - .finally(() => this.#availableDryRuns.push(dryrun)) + .finally(() => this.#availableDryRuns.push(dryrun)); } this.#running = false; @@ -73,3 +73,60 @@ class DryRunList { }); } } + +export class DryRunQueue { + #history: Record>; + #CUs: string[]; + #queue: DryRunQueueItem[]; + #delay: number; + + constructor(CUs: string[], delay = 500) { + this.#CUs = CUs; + this.#history = {}; + this.#queue = []; + this.#delay = delay; + } + + get(msg: MessageInput) { + const { process } = msg; + return new Promise((resolve, reject) => { + if (!this.#history[process]) { + this.#history[process] = new Set(); + } + + if (this.#history[process].size === this.#CUs.length) { + this.#queue.push({ msg, resolve, reject }); + return; + } + + const CU_URL = this.#CUs.find((u) => !this.#history[process].has(u))!; + this.#executeOnCU(resolve, reject, msg, CU_URL); + }); + } + + #executeOnCU( + resolve: (value: DryRunResult) => void, + reject: (reason?: any) => void, + msg: MessageInput, + CU_URL: string + ) { + connect({ MODE: "legacy", CU_URL }) + .dryrun(msg) + .then(resolve) + .catch(reject); + + this.#timeoutCUForProcess(msg.process, CU_URL); + } + + #timeoutCUForProcess(process: string, CU_URL: string) { + this.#history[process].add(CU_URL); + setTimeout(() => { + this.#history[process].delete(CU_URL); + + const next = this.#queue.find((item) => item.msg.process === process); + if (next) { + this.#executeOnCU(next.resolve, next.reject, next.msg, CU_URL); + } + }, this.#delay); + } +} diff --git a/src/index.ts b/src/index.ts index e8b60b5..ba612b7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -124,7 +124,7 @@ import { GetEarnings, GetEarningsRes, } from "./functions/lend/getEarnings"; -import { DryRunFIFO } from "./ao/messaging/DryRunFIFO"; +import { DryRunFIFO, DryRunQueue } from "./ao/messaging/DryRunFIFO"; class LiquidOps { private signer: any; @@ -356,4 +356,5 @@ export { tokenData, lqdTokenAddress, DryRunFIFO, + DryRunQueue, }; From b01484fcbcffc9975ccaf5f44762964ae106bcf4 Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Fri, 19 Sep 2025 19:00:04 +0200 Subject: [PATCH 11/11] chore: remove parallel alternative --- src/ao/messaging/DryRunFIFO.ts | 57 ---------------------------------- src/index.ts | 3 +- 2 files changed, 1 insertion(+), 59 deletions(-) diff --git a/src/ao/messaging/DryRunFIFO.ts b/src/ao/messaging/DryRunFIFO.ts index 017b520..ddffe2c 100644 --- a/src/ao/messaging/DryRunFIFO.ts +++ b/src/ao/messaging/DryRunFIFO.ts @@ -73,60 +73,3 @@ class DryRunList { }); } } - -export class DryRunQueue { - #history: Record>; - #CUs: string[]; - #queue: DryRunQueueItem[]; - #delay: number; - - constructor(CUs: string[], delay = 500) { - this.#CUs = CUs; - this.#history = {}; - this.#queue = []; - this.#delay = delay; - } - - get(msg: MessageInput) { - const { process } = msg; - return new Promise((resolve, reject) => { - if (!this.#history[process]) { - this.#history[process] = new Set(); - } - - if (this.#history[process].size === this.#CUs.length) { - this.#queue.push({ msg, resolve, reject }); - return; - } - - const CU_URL = this.#CUs.find((u) => !this.#history[process].has(u))!; - this.#executeOnCU(resolve, reject, msg, CU_URL); - }); - } - - #executeOnCU( - resolve: (value: DryRunResult) => void, - reject: (reason?: any) => void, - msg: MessageInput, - CU_URL: string - ) { - connect({ MODE: "legacy", CU_URL }) - .dryrun(msg) - .then(resolve) - .catch(reject); - - this.#timeoutCUForProcess(msg.process, CU_URL); - } - - #timeoutCUForProcess(process: string, CU_URL: string) { - this.#history[process].add(CU_URL); - setTimeout(() => { - this.#history[process].delete(CU_URL); - - const next = this.#queue.find((item) => item.msg.process === process); - if (next) { - this.#executeOnCU(next.resolve, next.reject, next.msg, CU_URL); - } - }, this.#delay); - } -} diff --git a/src/index.ts b/src/index.ts index ba612b7..e8b60b5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -124,7 +124,7 @@ import { GetEarnings, GetEarningsRes, } from "./functions/lend/getEarnings"; -import { DryRunFIFO, DryRunQueue } from "./ao/messaging/DryRunFIFO"; +import { DryRunFIFO } from "./ao/messaging/DryRunFIFO"; class LiquidOps { private signer: any; @@ -356,5 +356,4 @@ export { tokenData, lqdTokenAddress, DryRunFIFO, - DryRunQueue, };