Skip to content

Commit 5243016

Browse files
committed
feat(price-pusher): allow mnemonic from MNEMONIC env var (closes #1015)
Previously the price pusher required `--mnemonic-file` pointing at a file on disk containing the signing mnemonic. On platforms that provide encrypted env vars (DigitalOcean, Fly.io, etc.) this forces operators to first write the secret to disk before launching the process. Make `--mnemonic-file` optional and fall back to the `MNEMONIC` environment variable when the flag is not supplied. If both are supplied, the file takes precedence (explicit beats implicit). If neither is supplied, the existing required-arg error is replaced with a clearer message naming both sources. The helper lives in `utils.ts` so all four chain commands (evm, sui, aptos, injective) share identical resolution behaviour.
1 parent 75472c0 commit 5243016

7 files changed

Lines changed: 35 additions & 18 deletions

File tree

apps/price_pusher/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ Pyth hosts [public endpoints](https://docs.pyth.network/price-feeds/api-instance
8383
Hermes RPC providers for more reliability. Please refer to [this
8484
document](https://docs.pyth.network/documentation/pythnet-price-feeds/hermes) for more information.
8585

86+
The signing mnemonic can be supplied via either the `--mnemonic-file` flag (path to a file containing the mnemonic) or the `MNEMONIC` environment variable. The environment variable is convenient for platforms that inject secrets as encrypted env vars (e.g. DigitalOcean, Fly.io). If both are supplied, `--mnemonic-file` takes precedence.
87+
8688
To run the price pusher, please run the following commands, replacing the command line arguments as necessary:
8789

8890
```sh

apps/price_pusher/src/aptos/command.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
/* eslint-disable @typescript-eslint/no-unsafe-argument */
44
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
55
/* eslint-disable @typescript-eslint/no-explicit-any */
6-
import fs from "node:fs";
7-
86
import { HermesClient } from "@pythnetwork/hermes-client";
97
import { AptosAccount } from "aptos";
108
import pino from "pino";
@@ -15,7 +13,7 @@ import { PricePusherMetrics } from "../metrics.js";
1513
import * as options from "../options.js";
1614
import { readPriceConfigFile } from "../price-config.js";
1715
import { PythPriceListener } from "../pyth-price-listener.js";
18-
import { filterInvalidPriceItems } from "../utils.js";
16+
import { filterInvalidPriceItems, readMnemonic } from "../utils.js";
1917
import {
2018
APTOS_ACCOUNT_HD_PATH,
2119
AptosPriceListener,
@@ -87,7 +85,7 @@ export default {
8785
logger.info(`Metrics server started on port ${metricsPort}`);
8886
}
8987

90-
const mnemonic = fs.readFileSync(mnemonicFile, "utf8").trim();
88+
const mnemonic = readMnemonic(mnemonicFile);
9189
const account = AptosAccount.fromDerivePath(
9290
APTOS_ACCOUNT_HD_PATH,
9391
mnemonic,

apps/price_pusher/src/evm/command.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
/* eslint-disable @typescript-eslint/no-unsafe-argument */
44
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
55
/* eslint-disable @typescript-eslint/no-explicit-any */
6-
import fs from "node:fs";
7-
86
import { HermesClient } from "@pythnetwork/hermes-client";
97
import pino from "pino";
108
import type { Options } from "yargs";
@@ -14,7 +12,11 @@ import { PricePusherMetrics } from "../metrics.js";
1412
import * as options from "../options.js";
1513
import { readPriceConfigFile } from "../price-config.js";
1614
import { PythPriceListener } from "../pyth-price-listener.js";
17-
import { filterInvalidPriceItems, isWsEndpoint } from "../utils.js";
15+
import {
16+
filterInvalidPriceItems,
17+
isWsEndpoint,
18+
readMnemonic,
19+
} from "../utils.js";
1820
import { createEvmBalanceTracker } from "./balance-tracker.js";
1921
import { getCustomGasStation } from "./custom-gas-station.js";
2022
import { EvmPriceListener, EvmPricePusher } from "./evm.js";
@@ -129,7 +131,7 @@ export default {
129131
accessToken: hermesAccessToken,
130132
});
131133

132-
const mnemonic = fs.readFileSync(mnemonicFile, "utf8").trim();
134+
const mnemonic = readMnemonic(mnemonicFile);
133135

134136
let priceItems = priceConfigs.map(({ id, alias }) => ({ alias, id }));
135137

apps/price_pusher/src/injective/command.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
/* eslint-disable @typescript-eslint/no-unsafe-argument */
22
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
33
/* eslint-disable @typescript-eslint/no-explicit-any */
4-
import fs from "node:fs";
5-
64
import { getNetworkInfo } from "@injectivelabs/networks";
75
import { HermesClient } from "@pythnetwork/hermes-client";
86
import { pino } from "pino";
@@ -11,7 +9,7 @@ import { Controller } from "../controller.js";
119
import * as options from "../options.js";
1210
import { readPriceConfigFile } from "../price-config.js";
1311
import { PythPriceListener } from "../pyth-price-listener.js";
14-
import { filterInvalidPriceItems } from "../utils.js";
12+
import { filterInvalidPriceItems, readMnemonic } from "../utils.js";
1513
import { InjectivePriceListener, InjectivePricePusher } from "./injective.js";
1614
export default {
1715
builder: {
@@ -83,7 +81,7 @@ export default {
8381
const hermesClient = new HermesClient(priceServiceEndpoint, {
8482
accessToken: hermesAccessToken,
8583
});
86-
const mnemonic = fs.readFileSync(mnemonicFile, "utf8").trim();
84+
const mnemonic = readMnemonic(mnemonicFile);
8785

8886
let priceItems = priceConfigs.map(({ id, alias }) => ({ alias, id }));
8987

apps/price_pusher/src/options.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,11 @@ export const pushingFrequency = {
6060

6161
export const mnemonicFile = {
6262
"mnemonic-file": {
63-
description: "Path to payer mnemonic (private key) file.",
64-
required: true,
63+
description:
64+
"Path to payer mnemonic (private key) file. " +
65+
"If omitted, the mnemonic is read from the `MNEMONIC` environment variable.",
6566
type: "string",
67+
required: false,
6668
} as Options,
6769
};
6870

apps/price_pusher/src/sui/command.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
/* eslint-disable @typescript-eslint/no-unsafe-argument */
44
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
55
/* eslint-disable @typescript-eslint/no-explicit-any */
6-
import fs from "node:fs";
7-
86
import { SuiClient } from "@mysten/sui/client";
97
import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
108
import { HermesClient } from "@pythnetwork/hermes-client";
@@ -16,7 +14,7 @@ import { PricePusherMetrics } from "../metrics.js";
1614
import * as options from "../options.js";
1715
import { readPriceConfigFile } from "../price-config";
1816
import { PythPriceListener } from "../pyth-price-listener.js";
19-
import { filterInvalidPriceItems } from "../utils.js";
17+
import { filterInvalidPriceItems, readMnemonic } from "../utils.js";
2018
import { createSuiBalanceTracker } from "./balance-tracker.js";
2119
import { SuiPriceListener, SuiPricePusher } from "./sui.js";
2220

@@ -114,7 +112,7 @@ export default {
114112
accessToken: hermesAccessToken,
115113
});
116114

117-
const mnemonic = fs.readFileSync(mnemonicFile, "utf8").trim();
115+
const mnemonic = readMnemonic(mnemonicFile);
118116
const keypair = Ed25519Keypair.deriveKeypair(
119117
mnemonic,
120118
`m/44'/784'/${accountIndex}'/0'/0'`,

apps/price_pusher/src/utils.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/* eslint-disable @typescript-eslint/no-unsafe-return */
22
/* eslint-disable @typescript-eslint/no-unnecessary-type-parameters */
33
/* eslint-disable @typescript-eslint/no-explicit-any */
4+
import fs from "node:fs";
5+
46
import type { HermesClient, HexString } from "@pythnetwork/hermes-client";
57

68
import type { PriceItem } from "./interface.js";
@@ -60,6 +62,21 @@ export const assertDefined = <T>(value: T | undefined): T => {
6062
}
6163
};
6264

65+
export function readMnemonic(mnemonicFile: string | undefined): string {
66+
if (mnemonicFile !== undefined && mnemonicFile !== "") {
67+
return fs.readFileSync(mnemonicFile, "utf8").trim();
68+
}
69+
70+
const envMnemonic = process.env.MNEMONIC;
71+
if (envMnemonic !== undefined && envMnemonic !== "") {
72+
return envMnemonic.trim();
73+
}
74+
75+
throw new Error(
76+
"No mnemonic provided. Pass --mnemonic-file or set the MNEMONIC environment variable.",
77+
);
78+
}
79+
6380
export async function filterInvalidPriceItems(
6481
hermesClient: HermesClient,
6582
priceItems: PriceItem[],

0 commit comments

Comments
 (0)