Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/price_pusher/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ Pyth hosts [public endpoints](https://docs.pyth.network/price-feeds/api-instance
Hermes RPC providers for more reliability. Please refer to [this
document](https://docs.pyth.network/documentation/pythnet-price-feeds/hermes) for more information.

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.

To run the price pusher, please run the following commands, replacing the command line arguments as necessary:

```sh
Expand Down
5 changes: 3 additions & 2 deletions apps/price_pusher/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -215,9 +215,10 @@
"dev": "ts-node src/index.ts",
"prepublishOnly": "pnpm run build",
"start": "node dist/index.cjs",
"test:types": "tsc"
"test:types": "tsc",
"test:unit": "test-unit"
},
"type": "module",
"types": "./dist/index.d.ts",
"version": "10.4.0"
"version": "10.5.0"
}
6 changes: 2 additions & 4 deletions apps/price_pusher/src/aptos/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-explicit-any */
import fs from "node:fs";

import { HermesClient } from "@pythnetwork/hermes-client";
import { AptosAccount } from "aptos";
import pino from "pino";
Expand All @@ -20,7 +18,7 @@ import {
AptosPricePusher,
APTOS_ACCOUNT_HD_PATH,
} from "./aptos.js";
import { filterInvalidPriceItems } from "../utils.js";
import { filterInvalidPriceItems, readMnemonic } from "../utils.js";
import { createAptosBalanceTracker } from "./balance-tracker.js";

export default {
Expand Down Expand Up @@ -87,7 +85,7 @@ export default {
logger.info(`Metrics server started on port ${metricsPort}`);
}

const mnemonic = fs.readFileSync(mnemonicFile, "utf8").trim();
const mnemonic = readMnemonic(mnemonicFile);
const account = AptosAccount.fromDerivePath(
APTOS_ACCOUNT_HD_PATH,
mnemonic,
Expand Down
10 changes: 6 additions & 4 deletions apps/price_pusher/src/evm/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-explicit-any */
import fs from "node:fs";

import { HermesClient } from "@pythnetwork/hermes-client";
import pino from "pino";
import type { Options } from "yargs";
Expand All @@ -18,7 +16,11 @@ import { EvmPriceListener, EvmPricePusher } from "./evm.js";
import { createPythContract } from "./pyth-contract.js";
import { createClient } from "./super-wallet.js";
import { PricePusherMetrics } from "../metrics.js";
import { isWsEndpoint, filterInvalidPriceItems } from "../utils.js";
import {
isWsEndpoint,
filterInvalidPriceItems,
readMnemonic,
} from "../utils.js";
import { createEvmBalanceTracker } from "./balance-tracker.js";

export default {
Expand Down Expand Up @@ -129,7 +131,7 @@ export default {
accessToken: hermesAccessToken,
});

const mnemonic = fs.readFileSync(mnemonicFile, "utf8").trim();
const mnemonic = readMnemonic(mnemonicFile);

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

Expand Down
6 changes: 2 additions & 4 deletions apps/price_pusher/src/injective/command.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-explicit-any */
import fs from "node:fs";

import { getNetworkInfo } from "@injectivelabs/networks";
import { HermesClient } from "@pythnetwork/hermes-client";
import { pino } from "pino";
Expand All @@ -13,7 +11,7 @@ import { readPriceConfigFile } from "../price-config.js";
import { InjectivePriceListener, InjectivePricePusher } from "./injective.js";
import { Controller } from "../controller.js";
import { PythPriceListener } from "../pyth-price-listener.js";
import { filterInvalidPriceItems } from "../utils.js";
import { filterInvalidPriceItems, readMnemonic } from "../utils.js";
export default {
command: "injective",
describe: "run price pusher for injective",
Expand Down Expand Up @@ -84,7 +82,7 @@ export default {
const hermesClient = new HermesClient(priceServiceEndpoint, {
accessToken: hermesAccessToken,
});
const mnemonic = fs.readFileSync(mnemonicFile, "utf8").trim();
const mnemonic = readMnemonic(mnemonicFile);

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

Expand Down
6 changes: 4 additions & 2 deletions apps/price_pusher/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,11 @@ export const pushingFrequency = {

export const mnemonicFile = {
"mnemonic-file": {
description: "Path to payer mnemonic (private key) file.",
description:
"Path to payer mnemonic (private key) file. " +
"If omitted, the mnemonic is read from the `MNEMONIC` environment variable.",
type: "string",
required: true,
required: false,
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
} as Options,
};

Expand Down
6 changes: 2 additions & 4 deletions apps/price_pusher/src/sui/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-explicit-any */
import fs from "node:fs";

import { SuiClient } from "@mysten/sui/client";
import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
import { HermesClient } from "@pythnetwork/hermes-client";
Expand All @@ -18,7 +16,7 @@ import { readPriceConfigFile } from "../price-config";
import { PythPriceListener } from "../pyth-price-listener.js";
import { createSuiBalanceTracker } from "./balance-tracker.js";
import { SuiPriceListener, SuiPricePusher } from "./sui.js";
import { filterInvalidPriceItems } from "../utils.js";
import { filterInvalidPriceItems, readMnemonic } from "../utils.js";

export default {
command: "sui",
Expand Down Expand Up @@ -114,7 +112,7 @@ export default {
accessToken: hermesAccessToken,
});

const mnemonic = fs.readFileSync(mnemonicFile, "utf8").trim();
const mnemonic = readMnemonic(mnemonicFile);
const keypair = Ed25519Keypair.deriveKeypair(
mnemonic,
`m/44'/784'/${accountIndex}'/0'/0'`,
Expand Down
17 changes: 17 additions & 0 deletions apps/price_pusher/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unnecessary-type-parameters */
/* eslint-disable @typescript-eslint/no-explicit-any */
import fs from "node:fs";

import type { HexString } from "@pythnetwork/hermes-client";
import { HermesClient } from "@pythnetwork/hermes-client";

Expand Down Expand Up @@ -61,6 +63,21 @@ export const assertDefined = <T>(value: T | undefined): T => {
}
};

export function readMnemonic(mnemonicFile: string | undefined): string {
if (mnemonicFile !== undefined && mnemonicFile !== "") {
return fs.readFileSync(mnemonicFile, "utf8").trim();
}

const envMnemonic = process.env.MNEMONIC;
if (envMnemonic !== undefined && envMnemonic !== "") {
return envMnemonic.trim();
}

throw new Error(
"No mnemonic provided. Pass --mnemonic-file or set the MNEMONIC environment variable.",
);
}
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.

export async function filterInvalidPriceItems(
hermesClient: HermesClient,
priceItems: PriceItem[],
Expand Down
62 changes: 62 additions & 0 deletions apps/price_pusher/tests/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// biome-ignore-all lint/style/noProcessEnv: test file manipulates env vars

import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";

import { readMnemonic } from "../src/utils.js";

describe("readMnemonic", () => {
let tmpDir: string;
const originalMnemonic = process.env.MNEMONIC;

beforeEach(() => {
tmpDir = mkdtempSync(join(tmpdir(), "price-pusher-test-"));
delete process.env.MNEMONIC;
});

afterEach(() => {
rmSync(tmpDir, { recursive: true, force: true });
if (originalMnemonic === undefined) {
delete process.env.MNEMONIC;
} else {
process.env.MNEMONIC = originalMnemonic;
}
});

it("reads mnemonic from file when path supplied", () => {
const path = join(tmpDir, "mnemonic");
writeFileSync(path, "from file mnemonic\n");
expect(readMnemonic(path)).toBe("from file mnemonic");
});

it("falls back to MNEMONIC env var when no file path supplied", () => {
process.env.MNEMONIC = "from env mnemonic";
expect(readMnemonic(undefined)).toBe("from env mnemonic");
});

it("treats empty-string file path as not supplied", () => {
process.env.MNEMONIC = "from env mnemonic";
expect(readMnemonic("")).toBe("from env mnemonic");
});

it("file source takes precedence over env var", () => {
const path = join(tmpDir, "mnemonic");
writeFileSync(path, "from file\n");
process.env.MNEMONIC = "from env";
expect(readMnemonic(path)).toBe("from file");
});

it("throws when neither file nor env var supplied", () => {
expect(() => readMnemonic(undefined)).toThrow(
/No mnemonic provided/,
);
});

it("throws when env var is empty string", () => {
process.env.MNEMONIC = "";
expect(() => readMnemonic(undefined)).toThrow(
/No mnemonic provided/,
);
});
});