Skip to content

Commit f08f7a7

Browse files
authored
Merge pull request #18 from Layr-Labs/jb/custom-derivation-paths-2
[1.1.2] Support custom derivation paths
2 parents effdf91 + cf6f04e commit f08f7a7

File tree

7 files changed

+52
-26
lines changed

7 files changed

+52
-26
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11

22
**[Current]**
3+
1.1.2:
4+
- Bump to support custom derivation paths.
5+
36
1.1.1:
47
- Minor patches to the zeus state machine to make errors more legible.
58

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@layr-labs/zeus",
3-
"version": "1.1.1",
3+
"version": "1.1.2",
44
"description": "web3 deployer / metadata manager",
55
"main": "src/index.ts",
66
"scripts": {

src/commands/prompts.ts

+27-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { select } from './utils';
2-
import { privateKeyToAccount } from 'viem/accounts';
2+
import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts';
33
import { search, input, password as inquirerPassword } from '@inquirer/prompts';
44
import chalk from 'chalk';
55
import * as AllChains from "viem/chains";
@@ -125,8 +125,32 @@ export const privateKey: (chainId: number, overridePrompt?: string) => Promise<`
125125
return res as `0x${string}`;
126126
}
127127

128-
export const accountIndex = async () => {
129-
const cont = await wouldYouLikeToContinue("Would you like to use a custom bip39 account index? (NOTE: the default 'm/44'/60'/0'/0/[0]s' will be used otherwise)");
128+
export const bip32Path = async () => {
129+
const cont = await wouldYouLikeToContinue("Would you like to use a custom bip-32 derivation path? (NOTE: the default 'm/44'/60'/0'/0/0' will be used otherwise)");
130+
if (!cont) {
131+
return `m/44'/60'/0'/0/0`
132+
}
133+
134+
const val = await envVarOrPrompt({
135+
title: `Enter the derivation path (default m/44'/60'/0'/0/0)`,
136+
directEntryInputType: 'text',
137+
reuseKey: `derivationPathSuffix`,
138+
isValid: (val: string) => {
139+
try {
140+
const account = mnemonicToAccount('legal winner thank year wave sausage worth useful legal winner thank yellow')
141+
account.getHdKey().derive(val);
142+
return true;
143+
} catch {
144+
return false;
145+
}
146+
}
147+
})
148+
149+
return val;
150+
}
151+
152+
export const addressIndex = async () => {
153+
const cont = await wouldYouLikeToContinue("Would you like to use a custom bip39 address index? (NOTE: the default 'm/44'/60'/0'/0/[0]s' will be used otherwise)");
130154
if (!cont) {
131155
return 0;
132156
}

src/signing/strategies/eoa/ledger.ts

+7-8
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,31 @@ import { ICachedArg, TStrategyOptions } from "../../strategy";
44
import { SavebleDocument, Transaction } from "../../../metadata/metadataStore";
55
import { TDeploy } from "../../../metadata/schema";
66
import * as prompts from '../../../commands/prompts';
7-
import { DEFAULT_BASE_DERIVATION_PATH } from "../ledgerTransport";
87

98
export class LedgerSigningStrategy extends EOABaseSigningStrategy {
109
id = "ledger";
1110
description = "Signing w/ ledger";
1211

13-
public accountIndex: ICachedArg<number>
12+
public bip32Path: ICachedArg<string>
1413

1514
constructor(deploy: SavebleDocument<TDeploy>, transaction: Transaction, options?: TStrategyOptions) {
1615
super(deploy, transaction, options);
17-
this.accountIndex = this.arg(async () => {
18-
return await prompts.accountIndex();
19-
}, 'accountIndex')
16+
this.bip32Path = this.arg(async () => {
17+
return await prompts.bip32Path();
18+
}, 'bip32path')
2019
}
2120

2221
async getSignerAddress(): Promise<`0x${string}`> {
2322
console.warn(`If your ledger is not working, you may need to open LedgerLive, navigate to: Accounts -> <Signer> -> Receive and follow the prompts on device. Once your Ledger says "Application is Ready", you can force quit LedgerLive and retry Zeus.`)
24-
const signer = await getLedgerAccount(await this.accountIndex.get());
23+
const signer = await getLedgerAccount(await this.bip32Path.get());
2524
return await signer.address as `0x${string}`;
2625
}
2726

2827
async subclassForgeArgs(): Promise<string[]> {
2928
const derivationPathArgs = await (async () => {
3029
try {
31-
const accountIndex = await this.accountIndex.getImmediately();
32-
return [`--mnemonic-derivation-paths`, `${DEFAULT_BASE_DERIVATION_PATH}/${accountIndex}`]
30+
const path = await this.bip32Path.getImmediately();
31+
return [`--mnemonic-derivation-paths`, path]
3332
} catch {
3433
return [];
3534
}

src/signing/strategies/gnosis/api/gnosisLedger.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,17 @@ export class GnosisLedgerStrategy extends GnosisApiStrategy {
1515
id = "gnosis.api.ledger";
1616
description = "Gnosis API - Ledger (Not For Private Hotfixes)";
1717

18-
public accountIndex: ICachedArg<number>
18+
public bip32Path: ICachedArg<string>
1919

2020
constructor(deploy: SavebleDocument<TDeploy>, transaction: Transaction, options?: TStrategyOptions) {
2121
super(deploy, transaction, options);
22-
this.accountIndex = this.arg(async () => {
23-
return await prompts.accountIndex();
24-
}, 'accountIndex')
22+
this.bip32Path = this.arg(async () => {
23+
return await prompts.bip32Path();
24+
}, 'bip32path')
2525
}
2626

2727
async getSignature(version: string, txn: SafeTransaction, safeAddress: `0x${string}`): Promise<`0x${string}`> {
28-
const signer = await getLedgerAccount(await this.accountIndex.get());
28+
const signer = await getLedgerAccount(await this.bip32Path.get());
2929
if (!signer.signTypedData) {
3030
throw new Error(`This ledger does not support signing typed data, and cannot be used with zeus.`);
3131
}
@@ -81,7 +81,7 @@ export class GnosisLedgerStrategy extends GnosisApiStrategy {
8181
try {
8282
while (true) {
8383
try {
84-
const accountIndex = await this.accountIndex.get();
84+
const accountIndex = await this.bip32Path.get();
8585
const signer = await getLedgerAccount(accountIndex);
8686
console.log(`Detected ledger address: ${signer.address}`);
8787

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
import {ledgerToAccount, BASE_DERIVATION_PATH} from './ledger/account'
1+
import {ledgerToAccount} from './ledger/account'
22
import { Account } from "viem";
33

44
export const DEFAULT_BASE_DERIVATION_PATH = `m/44'/60'/0'/0`
55

6-
const ledgerAccounts: Record<number, Account> = {};
6+
const ledgerAccounts: Record<string, Account> = {};
77

8-
export const getLedgerAccount = async (accountIndex = 0) => {
9-
if (ledgerAccounts[accountIndex] === undefined) {
10-
ledgerAccounts[accountIndex] = await ledgerToAccount({
11-
derivationPath: `${BASE_DERIVATION_PATH}/${accountIndex}`
8+
export const getLedgerAccount = async (derivationPath = DEFAULT_BASE_DERIVATION_PATH) => {
9+
if (ledgerAccounts[derivationPath] === undefined) {
10+
ledgerAccounts[derivationPath] = await ledgerToAccount({
11+
derivationPath: derivationPath.slice(2) as `44'/60'/0'/0${string}`
1212
})
1313
}
1414

15-
return ledgerAccounts[accountIndex];
15+
return ledgerAccounts[derivationPath];
1616
}
1717

src/signing/strategy.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export interface TExecuteOptions {
7272
nonInteractive?: boolean,
7373
fork?: TForkType,
7474
etherscanApiKey?: string | boolean,
75-
accountIndex?: number,
75+
bip32path?: string,
7676
anvil?: AnvilService,
7777
testClient?: TestClient,
7878
rpcUrl?: string,

0 commit comments

Comments
 (0)