diff --git a/packages/api/src/keymanager/index.ts b/packages/api/src/keymanager/index.ts index 1ffcaef897e..44252acce49 100644 --- a/packages/api/src/keymanager/index.ts +++ b/packages/api/src/keymanager/index.ts @@ -18,6 +18,7 @@ export type { GraffitiData, GasLimitData, BuilderBoostFactorData, + ProposerConfigResponse, } from "./routes.js"; export type {ApiClient}; diff --git a/packages/api/src/keymanager/routes.ts b/packages/api/src/keymanager/routes.ts index 0db096d14e8..c31ae5a0445 100644 --- a/packages/api/src/keymanager/routes.ts +++ b/packages/api/src/keymanager/routes.ts @@ -109,6 +109,17 @@ export type SignerDefinition = { export type RemoteSignerDefinition = Pick; +export type ProposerConfigResponse = { + graffiti?: string; + strictFeeRecipientCheck?: boolean; + feeRecipient?: string; + builder?: { + gasLimit?: number; + selection?: string; + boostFactor?: string; + }; +}; + /** * JSON serialized representation of a single keystore in EIP-2335: BLS12-381 Keystore format. * ``` @@ -356,6 +367,15 @@ export type Endpoints = { EmptyMeta >; + getProposerConfig: Endpoint< + // ⏎ + "GET", + {pubkey: PubkeyHex}, + {params: {pubkey: string}}, + ProposerConfigResponse, + EmptyMeta + >; + /** * Create a signed voluntary exit message for an active validator, identified by a public key known to the validator * client. This endpoint returns a `SignedVoluntaryExit` object, which can be used to initiate voluntary exit via the @@ -635,6 +655,19 @@ export function getDefinitions(_config: ChainForkConfig): RouteDefinitions ({params: {pubkey}}), + parseReq: ({params: {pubkey}}) => ({pubkey}), + schema: { + params: {pubkey: Schema.StringRequired}, + }, + }, + resp: JsonOnlyResponseCodec, + }, + signVoluntaryExit: { url: "/eth/v1/validator/{pubkey}/voluntary_exit", method: "POST", diff --git a/packages/api/test/unit/keymanager/testData.ts b/packages/api/test/unit/keymanager/testData.ts index bd37798c468..6b17611d1e0 100644 --- a/packages/api/test/unit/keymanager/testData.ts +++ b/packages/api/test/unit/keymanager/testData.ts @@ -112,4 +112,19 @@ export const testData: GenericServerTestCases = { args: {pubkey: pubkeyRand}, res: undefined, }, + getProposerConfig: { + args: {pubkey: pubkeyRand}, + res: { + data: { + graffiti: graffitiRandUtf8, + strictFeeRecipientCheck: false, + feeRecipient: ethaddressRand, + builder: { + gasLimit: gasLimitRand, + selection: "maxprofit", + boostFactor: builderBoostFactorRand.toString(), + }, + }, + }, + }, }; diff --git a/packages/cli/src/cmds/validator/keymanager/impl.ts b/packages/cli/src/cmds/validator/keymanager/impl.ts index b4233f3162c..545f2f74f06 100644 --- a/packages/cli/src/cmds/validator/keymanager/impl.ts +++ b/packages/cli/src/cmds/validator/keymanager/impl.ts @@ -15,6 +15,7 @@ import { GraffitiData, GasLimitData, BuilderBoostFactorData, + ProposerConfigResponse, } from "@lodestar/api/keymanager"; import {KeymanagerApiMethods as Api} from "@lodestar/api/keymanager/server"; import {Interchange, SignerType, Validator} from "@lodestar/validator"; @@ -364,6 +365,23 @@ export class KeymanagerApi implements Api { return {status: 204}; } + async getProposerConfig({pubkey}: {pubkey: PubkeyHex}): ReturnType { + const config = this.validator.validatorStore.getProposerConfig(pubkey); + + const data: ProposerConfigResponse = { + ...config, + builder: config?.builder + ? { + ...config.builder, + // Default JSON serialization can't handle BigInt + boostFactor: config.builder.boostFactor ? config.builder.boostFactor.toString() : undefined, + } + : undefined, + }; + + return {data}; + } + async signVoluntaryExit({pubkey, epoch}: {pubkey: PubkeyHex; epoch?: Epoch}): ReturnType { if (!isValidatePubkeyHex(pubkey)) { throw new ApiError(400, `Invalid pubkey ${pubkey}`); diff --git a/packages/cli/src/util/proposerConfig.ts b/packages/cli/src/util/proposerConfig.ts index 2c12ce8f852..4cfa6fa7107 100644 --- a/packages/cli/src/util/proposerConfig.ts +++ b/packages/cli/src/util/proposerConfig.ts @@ -88,11 +88,14 @@ function parseProposerConfigSection( overrideConfig?.strictFeeRecipientCheck ?? (strict_fee_recipient_check ? stringtoBool(strict_fee_recipient_check) : undefined), feeRecipient: overrideConfig?.feeRecipient ?? (fee_recipient ? parseFeeRecipient(fee_recipient) : undefined), - builder: { - gasLimit: overrideConfig?.builder?.gasLimit ?? (gas_limit !== undefined ? Number(gas_limit) : undefined), - selection: overrideConfig?.builder?.selection ?? parseBuilderSelection(builderSelection), - boostFactor: overrideConfig?.builder?.boostFactor ?? parseBuilderBoostFactor(boost_factor), - }, + builder: + overrideConfig?.builder || builder + ? { + gasLimit: overrideConfig?.builder?.gasLimit ?? (gas_limit !== undefined ? Number(gas_limit) : undefined), + selection: overrideConfig?.builder?.selection ?? parseBuilderSelection(builderSelection), + boostFactor: overrideConfig?.builder?.boostFactor ?? parseBuilderBoostFactor(boost_factor), + } + : undefined, }; } diff --git a/packages/cli/test/unit/validator/parseProposerConfig.test.ts b/packages/cli/test/unit/validator/parseProposerConfig.test.ts index 83c8c63ef87..40fa57f7feb 100644 --- a/packages/cli/test/unit/validator/parseProposerConfig.test.ts +++ b/packages/cli/test/unit/validator/parseProposerConfig.test.ts @@ -26,8 +26,7 @@ const testValue = { builder: { gasLimit: 35000000, selection: routes.validator.BuilderSelection.BuilderAlways, - // biome-ignore lint/correctness/noPrecisionLoss: - boostFactor: BigInt(18446744073709551616), + boostFactor: 18446744073709551616n, }, }, }, diff --git a/packages/validator/src/services/validatorStore.ts b/packages/validator/src/services/validatorStore.ts index dfd856b18d1..f959b4e74ff 100644 --- a/packages/validator/src/services/validatorStore.ts +++ b/packages/validator/src/services/validatorStore.ts @@ -377,7 +377,9 @@ export class ValidatorStore { graffiti !== undefined || strictFeeRecipientCheck !== undefined || feeRecipient !== undefined || - builder?.gasLimit !== undefined + builder?.gasLimit !== undefined || + builder?.selection !== undefined || + builder?.boostFactor !== undefined ) { proposerConfig = {graffiti, strictFeeRecipientCheck, feeRecipient, builder}; }