simpleSearcherEvm.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. "use strict";
  2. var __importDefault = (this && this.__importDefault) || function (mod) {
  3. return (mod && mod.__esModule) ? mod : { "default": mod };
  4. };
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. const yargs_1 = __importDefault(require("yargs"));
  7. const helpers_1 = require("yargs/helpers");
  8. const index_1 = require("../index");
  9. const accounts_1 = require("viem/accounts");
  10. const viem_1 = require("viem");
  11. const const_1 = require("../const");
  12. const DAY_IN_SECONDS = 60 * 60 * 24;
  13. class SimpleSearcherEvm {
  14. endpoint;
  15. chainId;
  16. privateKey;
  17. apiKey;
  18. client;
  19. constructor(endpoint, chainId, privateKey, apiKey) {
  20. this.endpoint = endpoint;
  21. this.chainId = chainId;
  22. this.privateKey = privateKey;
  23. this.apiKey = apiKey;
  24. this.client = new index_1.Client({
  25. baseUrl: endpoint,
  26. apiKey,
  27. }, undefined, this.opportunityHandler.bind(this), this.bidStatusHandler.bind(this));
  28. }
  29. async bidStatusHandler(_bidStatus) {
  30. const bidStatus = _bidStatus;
  31. let resultDetails = "";
  32. if (bidStatus.type == "submitted" || bidStatus.type == "won") {
  33. resultDetails = `, transaction ${bidStatus.result}, index ${bidStatus.index} of multicall`;
  34. }
  35. else if (bidStatus.type == "lost") {
  36. if (bidStatus.result) {
  37. resultDetails = `, transaction ${bidStatus.result}`;
  38. }
  39. if (bidStatus.index) {
  40. resultDetails += `, index ${bidStatus.index} of multicall`;
  41. }
  42. }
  43. console.log(`Bid status for bid ${bidStatus.id}: ${bidStatus.type}${resultDetails}`);
  44. }
  45. async opportunityHandler(opportunity) {
  46. if (!("targetContract" in opportunity))
  47. throw new Error("Not a valid EVM opportunity");
  48. const bidAmount = BigInt(argv.bid);
  49. // Bid info should be generated by evaluating the opportunity
  50. // here for simplicity we are using a constant bid and 24 hours of validity
  51. // TODO: generate nonce more intelligently, to reduce gas costs
  52. const nonce = BigInt(Math.floor(Math.random() * 2 ** 50));
  53. const bidParams = {
  54. amount: bidAmount,
  55. nonce: nonce,
  56. deadline: BigInt(Math.round(Date.now() / 1000 + DAY_IN_SECONDS)),
  57. };
  58. const bid = await this.client.signBid(opportunity, bidParams, (0, index_1.checkHex)(argv.privateKey));
  59. try {
  60. const bidId = await this.client.submitBid(bid);
  61. console.log(`Successful bid. Opportunity id ${opportunity.opportunityId} Bid id ${bidId}`);
  62. }
  63. catch (error) {
  64. console.error(`Failed to bid on opportunity ${opportunity.opportunityId}: ${error}`);
  65. }
  66. }
  67. async start() {
  68. try {
  69. await this.client.subscribeChains([argv.chainId]);
  70. console.log(`Subscribed to chain ${argv.chainId}. Waiting for opportunities...`);
  71. }
  72. catch (error) {
  73. console.error(error);
  74. this.client.websocket?.close();
  75. }
  76. }
  77. }
  78. const argv = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
  79. .option("endpoint", {
  80. description: "Express relay endpoint. e.g: https://per-staging.dourolabs.app/",
  81. type: "string",
  82. demandOption: true,
  83. })
  84. .option("chain-id", {
  85. description: "Chain id to fetch opportunities for. e.g: sepolia",
  86. type: "string",
  87. demandOption: true,
  88. })
  89. .option("bid", {
  90. description: "Bid amount in wei",
  91. type: "string",
  92. default: "10000000000000000",
  93. })
  94. .option("private-key", {
  95. description: "Private key to sign the bid with in hex format with 0x prefix. e.g: 0xdeadbeef...",
  96. type: "string",
  97. demandOption: true,
  98. })
  99. .option("api-key", {
  100. description: "The API key of the searcher to authenticate with the server for fetching and submitting bids",
  101. type: "string",
  102. demandOption: false,
  103. })
  104. .help()
  105. .alias("help", "h")
  106. .parseSync();
  107. async function run() {
  108. if ((0, viem_1.isHex)(argv.privateKey)) {
  109. const account = (0, accounts_1.privateKeyToAccount)(argv.privateKey);
  110. console.log(`Using account: ${account.address}`);
  111. }
  112. else {
  113. throw new Error(`Invalid private key: ${argv.privateKey}`);
  114. }
  115. const searcher = new SimpleSearcherEvm(argv.endpoint, argv.chainId, argv.privateKey, argv.apiKey);
  116. if (const_1.OPPORTUNITY_ADAPTER_CONFIGS[argv.chainId] === undefined) {
  117. throw new Error(`Opportunity adapter config not found for chain ${argv.chainId}`);
  118. }
  119. await searcher.start();
  120. }
  121. run();