Skip to content

Commit

Permalink
feat: Support reading mnemonic or private key from file
Browse files Browse the repository at this point in the history
Rather than read the mnemonic or private key directly from .env, specify
the location of a separate file via the SECRET env var. Then, open that
file and read the mnemonic or key from there. Global fs scope is applied
to the location, so it can be located in a totally different path, and
can even have more restrictive permissions. This significantly reduces
the chance of leaking critical secrets - i.e. when sharing screen.

A simple regex is applied to determine whether the format matches a
private key, and if not, it's assumed to be a mnemonic. This should make
it easier to manage for operators.
  • Loading branch information
pxrl committed Oct 6, 2023
1 parent 36b75aa commit 09c6656
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 4 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ typechain*
.DS_Store
dump.rdb*
.idea
.mnemonic
.keyfile
.secret

# Hardhat files
cache
Expand Down
6 changes: 3 additions & 3 deletions src/utils/CLIUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function retrieveSignerFromCLIArgs(): Promise<Wallet> {
// Resolve the wallet type & verify that it is valid.
const keyType = (args.wallet as string) ?? "mnemonic";
if (!isValidKeyType(keyType)) {
throw new Error(`Unsupported key type (${keyType}); expected "mnemonic", "privateKey" or "gckms"`);
throw new Error(`Unsupported key type (${keyType}); expected "secret", "mnemonic", "privateKey" or "gckms"`);
}

// Build out the signer options to pass to the signer utils.
Expand All @@ -30,6 +30,6 @@ export function retrieveSignerFromCLIArgs(): Promise<Wallet> {
* @param keyType The key type to check.
* @returns True if the key type is valid, false otherwise.
*/
function isValidKeyType(keyType: unknown): keyType is "mnemonic" | "privateKey" | "gckms" {
return ["mnemonic", "privateKey", "gckms"].includes(keyType as string);
function isValidKeyType(keyType: unknown): keyType is "secret" | "mnemonic" | "privateKey" | "gckms" {
return ["secret", "mnemonic", "privateKey", "gckms"].includes(keyType as string);
}
27 changes: 26 additions & 1 deletion src/utils/SignerUtils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { readFile } from "fs/promises";
import { typeguards } from "@across-protocol/sdk-v2";
import { Wallet, retrieveGckmsKeys, getGckmsConfig, isDefined } from "./";

/**
Expand Down Expand Up @@ -41,11 +43,14 @@ export async function getSigner({ keyType, gckmsKeys, cleanEnv }: SignerOptions)
case "gckms":
wallet = await getGckmsSigner(gckmsKeys);
break;
case "secret":
wallet = await getSecretSigner();
break;
default:
throw new Error(`getSigner: Unsupported key type (${keyType})`);
}
if (!wallet) {
throw new Error("Must define mnemonic, privatekey or gckms for wallet");
throw new Error("Must define secret, mnemonic, privateKey or gckms for wallet");
}
if (cleanEnv) {
cleanKeysFromEnvironment();
Expand Down Expand Up @@ -90,6 +95,26 @@ function getMnemonicSigner(): Wallet {
return Wallet.fromMnemonic(process.env.MNEMONIC);
}

/**
* Retrieves a signer based on the secret stored in ./.secret.
* @returns An ethers Signer object.
* @throws If a valid secret could not be read.
*/
async function getSecretSigner(): Promise<Wallet> {
const { SECRET = "./.secret" } = process.env;
let secret: string;
try {
secret = await readFile(SECRET, { encoding: "utf8" });
secret = secret.trim().replace("\n", "");
return /^0x[0-9a-f]{64}$/.test(secret)

Check warning on line 109 in src/utils/SignerUtils.ts

View workflow job for this annotation

GitHub Actions / Lint (16)

Replace `⏎······?·new·Wallet(secret)⏎·····` with `·?·new·Wallet(secret)`
? new Wallet(secret)
: Wallet.fromMnemonic(secret);
} catch (err) {
const msg = typeguards.isError(err) ? err.message : "unknown error";
throw new Error(`Unable to load secret (${SECRET}: ${msg})`);
}
}

/**
* Clears the mnemonic and private key from the env.
*/
Expand Down

0 comments on commit 09c6656

Please sign in to comment.