From 1baf798186395c1c4d7b584bb1e89d07369d2907 Mon Sep 17 00:00:00 2001 From: artemkolodko Date: Fri, 8 Nov 2024 21:43:24 +0000 Subject: [PATCH] call setWinner --- src/abi/TokenFactory.json | 113 +++++++++++++++++++++++++++++++-- src/config/index.ts | 7 +- src/indexer/indexer.service.ts | 60 ++++++++++++++--- 3 files changed, 162 insertions(+), 18 deletions(-) diff --git a/src/abi/TokenFactory.json b/src/abi/TokenFactory.json index 10f779d..8237e63 100644 --- a/src/abi/TokenFactory.json +++ b/src/abi/TokenFactory.json @@ -431,6 +431,30 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "collateralByDay", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -460,6 +484,25 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "creationDate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "fee", @@ -486,6 +529,38 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "day", + "type": "uint256" + } + ], + "name": "getWinnerByDay", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxFundingRateInterval", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "owner", @@ -553,16 +628,36 @@ { "inputs": [ { - "internalType": "address", - "name": "winnerAddress", - "type": "address" + "internalType": "uint256", + "name": "interval", + "type": "uint256" } ], + "name": "setMaxFundingRateInterval", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], "name": "setWinner", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "startOfCurrentDay", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "tokenImplementation", @@ -635,15 +730,21 @@ "type": "function" }, { - "inputs": [], - "name": "winnerToken", - "outputs": [ + "inputs": [ { "internalType": "address", "name": "", "type": "address" } ], + "name": "winners", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], "stateMutability": "view", "type": "function" } diff --git a/src/config/index.ts b/src/config/index.ts index ebc78a4..7a4dc42 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -27,7 +27,8 @@ export default () => ({ RPC_URL: process.env.RPC_URL || 'https://a.api.s0.t.hmny.io', RATE_LIMITER_TTL: parseInt(process.env.RATE_LIMITER_TTL) || 10000, RATE_LIMITER_LIMIT: parseInt(process.env.RATE_LIMITER_LIMIT) || 20, - PUMP_FUN_CONTRACT_ADDRESS: process.env.PUMP_FUN_CONTRACT_ADDRESS || '', - PUMP_FUN_INITIAL_BLOCK_NUMBER: parseInt(process.env.PUMP_FUN_INITIAL_BLOCK_NUMBER || '0'), - GOOGLE_CLOUD_CONFIG: getGoogleCloudConfig() + TOKEN_FACTORY_ADDRESS: process.env.TOKEN_FACTORY_ADDRESS || '', + INDEXER_INITIAL_BLOCK_NUMBER: parseInt(process.env.INDEXER_INITIAL_BLOCK_NUMBER || '0'), + GOOGLE_CLOUD_CONFIG: getGoogleCloudConfig(), + SERVICE_PRIVATE_KEY: process.env.SERVICE_PRIVATE_KEY || '', }); diff --git a/src/indexer/indexer.service.ts b/src/indexer/indexer.service.ts index 670f193..771f73f 100644 --- a/src/indexer/indexer.service.ts +++ b/src/indexer/indexer.service.ts @@ -9,13 +9,14 @@ import {UserService} from "../user/user.service"; import {DataSource} from "typeorm"; import * as TokenFactoryABI from "../abi/TokenFactory.json"; import {AppService} from "../app.service"; +import {Cron, CronExpression} from "@nestjs/schedule"; @Injectable() export class IndexerService { private readonly logger = new Logger(IndexerService.name); private readonly web3: Web3 + private readonly accountAddress: string private readonly tokenFactoryContract: Contract - private readonly tokenContract: Contract private readonly blocksIndexingRange = 1000 constructor( @@ -25,28 +26,32 @@ export class IndexerService { private dataSource: DataSource, ) { const rpcUrl = configService.get('RPC_URL') - const contractAddress = configService.get('PUMP_FUN_CONTRACT_ADDRESS') - const initialBlockNumber = configService.get('PUMP_FUN_INITIAL_BLOCK_NUMBER') + const contractAddress = configService.get('TOKEN_FACTORY_ADDRESS') + const initialBlockNumber = configService.get('INDEXER_INITIAL_BLOCK_NUMBER') if(!contractAddress) { - this.logger.error(`[PUMP_FUN_CONTRACT_ADDRESS] is missing but required, exit`) + this.logger.error(`[TOKEN_FACTORY_ADDRESS] is missing but required, exit`) process.exit(1) } if(!initialBlockNumber) { - this.logger.error(`[PUMP_FUN_INITIAL_BLOCK_NUMBER] is missing but required, exit`) + this.logger.error(`[INDEXER_INITIAL_BLOCK_NUMBER] is missing but required, exit`) process.exit(1) } this.logger.log(`Starting app service, RPC_URL=${ rpcUrl - }, PUMP_FUN_CONTRACT_ADDRESS=${ + }, TOKEN_FACTORY_ADDRESS=${ contractAddress - }, PUMP_FUN_INITIAL_BLOCK_NUMBER=${ + }, INDEXER_INITIAL_BLOCK_NUMBER=${ initialBlockNumber }`) this.web3 = new Web3(rpcUrl); + const account = this.web3.eth.accounts.privateKeyToAccount(configService.get('SERVICE_PRIVATE_KEY')) + this.accountAddress = account.address + this.web3.eth.accounts.wallet.add(account); + this.logger.log(`Service account address=${account.address}`) this.tokenFactoryContract = new this.web3.eth.Contract(TokenFactoryABI, contractAddress); this.bootstrap().then( () => { @@ -62,9 +67,9 @@ export class IndexerService { where: {} }) if(!indexerState) { - const blockNumber = +this.configService.get('PUMP_FUN_INITIAL_BLOCK_NUMBER') + const blockNumber = +this.configService.get('INDEXER_INITIAL_BLOCK_NUMBER') if(!blockNumber) { - this.logger.error('[PUMP_FUN_INITIAL_BLOCK_NUMBER] is empty but required, exit') + this.logger.error('[INDEXER_INITIAL_BLOCK_NUMBER] is empty but required, exit') process.exit(1) } await this.dataSource.manager.insert(IndexerState, { @@ -307,4 +312,41 @@ export class IndexerService { this.eventsTrackingLoop() } + + @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT) + async callSetWinner() { + let txnHash = '' + let gasFees = 0n + + for(let i = 0; i < 3; i++) { + try { + gasFees = await this.tokenFactoryContract.methods + .setWinner() + .estimateGas({ from: this.accountAddress }); + const gasPrice = await this.web3.eth.getGasPrice(); + const tx = { + from: this.accountAddress, + to: this.configService.get('TOKEN_FACTORY_ADDRESS'), + gas: gasFees, + gasPrice, + data: this.tokenFactoryContract.methods.setWinner().encodeABI(), + }; + const signPromise = await this.web3.eth.accounts.signTransaction(tx, this.configService.get('SERVICE_PRIVATE_KEY')); + const sendTxn = + await this.web3.eth.sendSignedTransaction( + signPromise.rawTransaction, + ); + txnHash = sendTxn.transactionHash.toString() + break; + } catch (e) { + this.logger.warn(`Failed to send setWinner transaction, attempt: ${(i + 1)} / 3:`, e) + } + } + + if(txnHash) { + this.logger.log(`[setWinner] successfully called, transaction hash=${txnHash}, gasFees=${gasFees}`) + } else { + this.logger.error('Failed to call setWinner!') + } + } }