import yargs from "yargs"; import { hideBin } from "yargs/helpers"; import { checkHex, Client } from "../index"; import { privateKeyToAccount } from "viem/accounts"; import { isHex } from "viem"; import { BidStatusUpdate, Opportunity } from "../types"; const DAY_IN_SECONDS = 60 * 60 * 24; class SimpleSearcher { private client: Client; constructor( public endpoint: string, public chainId: string, public privateKey: string ) { this.client = new Client( { baseUrl: endpoint }, undefined, this.opportunityHandler.bind(this), this.bidStatusHandler.bind(this) ); } async bidStatusHandler(bidStatus: BidStatusUpdate) { console.log( `Bid status for bid ${bidStatus.id}: ${bidStatus.status} ${ bidStatus.status == "submitted" ? bidStatus.result : "" }` ); } async opportunityHandler(opportunity: Opportunity) { const bid = BigInt(argv.bid); // Bid info should be generated by evaluating the opportunity // here for simplicity we are using a constant bid and 24 hours of validity const bidParams = { amount: bid, validUntil: BigInt(Math.round(Date.now() / 1000 + DAY_IN_SECONDS)), }; const opportunityBid = await this.client.signOpportunityBid( opportunity, bidParams, checkHex(argv.privateKey) ); try { const bidId = await this.client.submitOpportunityBid(opportunityBid); console.log( `Successful bid. Opportunity id ${opportunityBid.opportunityId} Bid id ${bidId}` ); } catch (error) { console.error( `Failed to bid on opportunity ${opportunity.opportunityId}: ${error}` ); } } async start() { try { await this.client.subscribeChains([argv.chainId]); console.log( `Subscribed to chain ${argv.chainId}. Waiting for opportunities...` ); } catch (error) { console.error(error); this.client.websocket?.close(); } } } const argv = yargs(hideBin(process.argv)) .option("endpoint", { description: "Express relay endpoint. e.g: https://per-staging.dourolabs.app/", type: "string", demandOption: true, }) .option("chain-id", { description: "Chain id to fetch opportunities for. e.g: sepolia", type: "string", demandOption: true, }) .option("bid", { description: "Bid amount in wei", type: "string", default: "100", }) .option("private-key", { description: "Private key to sign the bid with in hex format with 0x prefix. e.g: 0xdeadbeef...", type: "string", demandOption: true, }) .help() .alias("help", "h") .parseSync(); async function run() { if (isHex(argv.privateKey)) { const account = privateKeyToAccount(argv.privateKey); console.log(`Using account: ${account.address}`); } else { throw new Error(`Invalid private key: ${argv.privateKey}`); } const searcher = new SimpleSearcher( argv.endpoint, argv.chainId, argv.privateKey ); await searcher.start(); } run();