-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[feat]: Implement an evaluator service and controller (#48)
* refactor: Extract evaluation logic into a standalone module/worker * fix: Port handling * fix: Evaluator response handling * fix: Remove chainId check * chore: Update config.example.yaml with the new evaluator config * feat: Implement evaluator controller * feat: Return errors on failed evaluation queries * refactor: Minor evaluator types refactor * feat: Add logging statments to the evaluator * chore: Misspelt variable correction * feat: Effectively disable evaluation when profitabilityFactor is 0 * fix: Evaluation calculation 0 division
- Loading branch information
1 parent
be45584
commit 03a2e38
Showing
15 changed files
with
1,251 additions
and
452 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import { BadRequestException, Controller, Get, OnModuleInit, Query } from "@nestjs/common"; | ||
import { EvaluatorInterface } from "./evaluator.interface"; | ||
import { EvaluatorService } from "./evaluator.service"; | ||
import { EvaluateAckQuery, EvaluateAckQueryResponse, EvaluateDeliveryQuery, EvaluteDeliveryQueryResponse } from "./evaluator.types"; | ||
|
||
@Controller() | ||
export class EvaluatorController implements OnModuleInit { | ||
private evaluator!: EvaluatorInterface; | ||
|
||
constructor( | ||
private readonly evaluatorService: EvaluatorService, | ||
) {} | ||
|
||
async onModuleInit() { | ||
await this.initializeEvaluatorInterface(); | ||
} | ||
|
||
private async initializeEvaluatorInterface(): Promise<void> { | ||
const port = await this.evaluatorService.attachToEvaluator(); | ||
this.evaluator = new EvaluatorInterface(port); | ||
} | ||
|
||
@Get('evaluateDelivery') | ||
async evaluateDelivery(@Query() query: EvaluateDeliveryQuery): Promise<any> { | ||
|
||
//TODO validate query format | ||
const result = await this.evaluator.evaluateDelivery( | ||
query.chainId, | ||
query.messageIdentifier, | ||
{ | ||
gasEstimate: BigInt(query.gasEstimate), | ||
observedGasEstimate: BigInt(query.observedGasEstimate), | ||
additionalFeeEstimate: BigInt(query.additionalFeeEstimate), | ||
}, | ||
BigInt(query.value) | ||
); | ||
|
||
if (result.evaluation == undefined) { | ||
throw new BadRequestException('Failed to generate an evaluation output for the given parameters.'); | ||
} | ||
|
||
const response: EvaluteDeliveryQueryResponse = { | ||
chainId: result.chainId, | ||
messageIdentifier: result.messageIdentifier, | ||
maxGasDelivery: result.evaluation.maxGasDelivery.toString(), | ||
maxGasAck: result.evaluation.maxGasAck.toString(), | ||
gasEstimate: result.evaluation.gasEstimate.toString(), | ||
observedGasEstimate: result.evaluation.observedGasEstimate.toString(), | ||
additionalFeeEstimate: result.evaluation.additionalFeeEstimate.toString(), | ||
destinationGasPrice: result.evaluation.destinationGasPrice.toString(), | ||
value: result.evaluation.value.toString(), | ||
sourceGasPrice: result.evaluation.sourceGasPrice.toString(), | ||
deliveryCost: result.evaluation.deliveryCost.toString(), | ||
deliveryReward: result.evaluation.deliveryReward.toString(), | ||
maxAckLoss: result.evaluation.maxAckLoss.toString(), | ||
deliveryFiatCost: result.evaluation.deliveryFiatCost, | ||
deliveryFiatReward: result.evaluation.deliveryFiatReward, | ||
securedDeliveryFiatReward: result.evaluation.securedDeliveryFiatReward, | ||
profitabilityFactor: result.evaluation.profitabilityFactor, | ||
securedDeliveryFiatProfit: result.evaluation.securedDeliveryFiatProfit, | ||
securedDeliveryRelativeProfit: result.evaluation.securedDeliveryRelativeProfit, | ||
minDeliveryReward: result.evaluation.minDeliveryReward, | ||
relativeMinDeliveryReward: result.evaluation.relativeMinDeliveryReward, | ||
relayDelivery: result.evaluation.relayDelivery, | ||
} | ||
|
||
return response; | ||
} | ||
|
||
@Get('evaluateAck') | ||
async evaluateAck(@Query() query: EvaluateAckQuery): Promise<any> { | ||
|
||
//TODO validate query format | ||
const result = await this.evaluator.evaluateAck( | ||
query.chainId, | ||
query.messageIdentifier, | ||
{ | ||
gasEstimate: BigInt(query.gasEstimate), | ||
observedGasEstimate: BigInt(query.observedGasEstimate), | ||
additionalFeeEstimate: BigInt(query.additionalFeeEstimate), | ||
}, | ||
BigInt(query.value) | ||
); | ||
|
||
if (result.evaluation == undefined) { | ||
throw new BadRequestException('Failed to generate an evaluation output for the given parameters.'); | ||
} | ||
|
||
const response: EvaluateAckQueryResponse = { | ||
chainId: result.chainId, | ||
messageIdentifier: result.messageIdentifier, | ||
maxGasDelivery: result.evaluation.maxGasDelivery.toString(), | ||
maxGasAck: result.evaluation.maxGasAck.toString(), | ||
gasEstimate: result.evaluation.gasEstimate.toString(), | ||
observedGasEstimate: result.evaluation.observedGasEstimate.toString(), | ||
additionalFeeEstimate: result.evaluation.additionalFeeEstimate.toString(), | ||
sourceGasPrice: result.evaluation.sourceGasPrice.toString(), | ||
ackCost: result.evaluation.ackCost.toString(), | ||
ackReward: result.evaluation.ackReward.toString(), | ||
profitabilityFactor: result.evaluation.profitabilityFactor, | ||
ackFiatProfit: result.evaluation.ackFiatProfit, | ||
ackRelativeProfit: result.evaluation.ackRelativeProfit, | ||
minAckReward: result.evaluation.minAckReward, | ||
relativeMinAckReward: result.evaluation.relativeMinAckReward, | ||
deliveryCost: result.evaluation.deliveryCost.toString(), | ||
deliveryReward: result.evaluation.deliveryReward.toString(), | ||
relayAckForDeliveryBounty: result.evaluation.relayAckForDeliveryBounty, | ||
relayAck: result.evaluation.relayAck, | ||
} | ||
|
||
return response; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { GasEstimateComponents } from "src/resolvers/resolver"; | ||
import { MessagePort } from "worker_threads"; | ||
import { EvaluateAckMessage, EvaluateAckResponseMessage, EvaluateDeliveryMessage, EvaluateDeliveryResponseMessage, EvaluatorMessage, EvaluatorMessageType, EvaluatorPortData } from "./evaluator.types"; | ||
|
||
|
||
export class EvaluatorInterface { | ||
private portMessageId = 0; | ||
|
||
constructor(private readonly port: MessagePort) {} | ||
|
||
private getNextPortMessageId(): number { | ||
return this.portMessageId++; | ||
} | ||
|
||
private async submitMessage(message: EvaluatorMessage): Promise<any> { | ||
|
||
const messageId = this.getNextPortMessageId(); | ||
|
||
const data: EvaluatorPortData = { | ||
messageId, | ||
message | ||
}; | ||
|
||
const resultPromise = new Promise<any>(resolve => { | ||
const listener = (responseData: EvaluatorPortData) => { | ||
if (responseData.messageId === messageId) { | ||
this.port.off("message", listener); | ||
resolve(responseData.message) | ||
} | ||
} | ||
this.port.on("message", listener); | ||
|
||
this.port.postMessage(data); | ||
}); | ||
|
||
return resultPromise; | ||
} | ||
|
||
async evaluateDelivery( | ||
chainId: string, | ||
messageIdentifier: string, | ||
gasEstimateComponents: GasEstimateComponents, | ||
value: bigint, | ||
): Promise<EvaluateDeliveryResponseMessage> { | ||
|
||
const message: EvaluateDeliveryMessage = { | ||
type: EvaluatorMessageType.EvaluateDelivery, | ||
chainId, | ||
messageIdentifier, | ||
gasEstimateComponents, | ||
value | ||
}; | ||
|
||
return this.submitMessage(message); | ||
} | ||
|
||
async evaluateAck( | ||
chainId: string, | ||
messageIdentifier: string, | ||
gasEstimateComponents: GasEstimateComponents, | ||
value: bigint, | ||
): Promise<EvaluateAckResponseMessage> { | ||
|
||
const message: EvaluateAckMessage = { | ||
type: EvaluatorMessageType.EvaluateAck, | ||
chainId, | ||
messageIdentifier, | ||
gasEstimateComponents, | ||
value | ||
}; | ||
|
||
return this.submitMessage(message); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { Global, Module } from '@nestjs/common'; | ||
import { PricingModule } from './../pricing/pricing.module'; | ||
import { WalletModule } from 'src/wallet/wallet.module'; | ||
import { EvaluatorService } from './evaluator.service'; | ||
import { EvaluatorController } from './evaluator.controller'; | ||
|
||
@Global() | ||
@Module({ | ||
controllers: [EvaluatorController], | ||
providers: [EvaluatorService], | ||
exports: [EvaluatorService], | ||
imports: [PricingModule, WalletModule], | ||
}) | ||
export class EvaluatorModule {} |
Oops, something went wrong.