Skip to content
2 changes: 2 additions & 0 deletions packages/sui-indexer/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ async function runRedeemSolver(storage: D1Storage, env: Env, activeNetworks: Sui
);

const results = await Promise.allSettled([
service.refillPresignPool(activeNetworks),
service.processPendingRedeems(), // propose a solution
service
.solveReadyRedeems() // trigger status change
Expand All @@ -117,6 +118,7 @@ async function runRedeemSolver(storage: D1Storage, env: Env, activeNetworks: Sui

// Check for any rejected promises and log errors
reportErrors(results, "runRedeemSolver", "Processing redeems error", [
"refillPresignPool",
"processPendingRedeems",
"solveReadyRedeems/processSolvedRedeems",
"broadcastReadyRedeems",
Expand Down
42 changes: 42 additions & 0 deletions packages/sui-indexer/src/redeem-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import type { BtcIndexerRpc } from "@gonative-cc/btcindexer/rpc-interface";
import { computeBtcSighash, DEFAULT_FEE_SATS, type UtxoInput, type TxOutput } from "./sighash";

const MAXIMUM_NUMBER_UTXO = 100;
const PRESIGN_POOL_TARGET = 50;
const PRESIGN_POOL_MIN = 20;
const MAX_CREATE_PER_RUN = 5;

export class RedeemService {
constructor(
Expand All @@ -25,6 +28,45 @@ export class RedeemService {
throw new Error("No SuiClients configured");
}
}
// Makes sure we have enough presigns in the queue
async refillPresignPool(activeNetworks: SuiNet[]) {
for (const network of activeNetworks) {
const count = await this.storage.getPresignCount(network);
if (count >= PRESIGN_POOL_MIN) {
continue;
}
const needed = PRESIGN_POOL_TARGET - count;
const toCreate = Math.min(needed, MAX_CREATE_PER_RUN);
logger.debug({
msg: "Filling presign pool",
network,
currentCount: count,
creating: toCreate,
});
const client = this.getSuiClient(network);
for (let i = 0; i < toCreate; i++) {
try {
const presignId = await client.requestIkaPresign();
await this.storage.insertPresignObject(presignId, network);
logger.debug({
msg: "Created presign object",
network,
presignId,
});
} catch (e) {
logError(
{
msg: "Failed to create presign object",
method: "maintainPresignPool",
network,
},
e,
);
break;
}
}
}
}

// Propose a solution for pending redeems.
async processPendingRedeems() {
Expand Down
16 changes: 16 additions & 0 deletions packages/sui-indexer/src/storage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,22 @@ describe("IndexerStorage", () => {
expect(popped3).toBeNull();
});

it("should count presign objects", async () => {
const net1 = "testnet";
await storage.insertPresignObject("presign1", net1);
await storage.insertPresignObject("presign2", net1);
await storage.insertPresignObject("presign3", "mainnet");

const count = await storage.getPresignCount(net1);
expect(count).toBe(2);

const countMain = await storage.getPresignCount("mainnet");
expect(countMain).toBe(1);

const countEmpty = await storage.getPresignCount("devnet");
expect(countEmpty).toBe(0);
});

it("getPendingRedeems should return pending redeems ordered by created_at", async () => {
await insertRedeemRequest(storage, 2, "redeemer1", recipientScript, 5000, 2000, "0xSuiTx2");
await insertRedeemRequest(storage, 1, "redeemer1", recipientScript, 3000, 1000, "0xSuiTx1");
Expand Down
8 changes: 8 additions & 0 deletions packages/sui-indexer/src/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,14 @@ export class D1Storage {
return results.map((r) => toSuiNet(r.sui_network));
}

async getPresignCount(network: SuiNet): Promise<number> {
const result = await this.db
.prepare("SELECT COUNT(*) as count FROM presign_objects WHERE sui_network = ?")
.bind(network)
.first<{ count: number }>();
return result?.count || 0;
}

async popPresignObject(network: SuiNet): Promise<string | null> {
const result = await this.db
.prepare(
Expand Down