Skip to content

Commit

Permalink
Integrating hardhat-ledger support (#731)
Browse files Browse the repository at this point in the history
Integrated the new `hardhat-ledger` support for controlling its UI display. Ignition now takes control of displaying the messages from the ledger (e.g. "Connect wallet").

Fixes #720
  • Loading branch information
zoeyTM authored and kanej committed May 1, 2024
1 parent 4c3c6e6 commit 3e4949e
Show file tree
Hide file tree
Showing 7 changed files with 268 additions and 9 deletions.
68 changes: 65 additions & 3 deletions packages/hardhat-plugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import {
StatusResult,
} from "@nomicfoundation/ignition-core";
import {
ensureDir,
pathExists,
readFile,
readdirSync,
rm,
pathExists,
writeJSON,
ensureDir,
readFile,
} from "fs-extra";
import { extendConfig, extendEnvironment, scope } from "hardhat/config";
import { NomicLabsHardhatPluginError } from "hardhat/plugins";
Expand Down Expand Up @@ -257,6 +257,37 @@ ignitionScope
const strategyConfig = hre.config.ignition.strategyConfig?.[strategyName];

try {
try {
await hre.network.provider.send("hardhat_setLedgerOutputEnabled", [
false,
]);

hre.network.provider.once(
"connection_start",
executionEventListener.ledgerConnectionStart
);
hre.network.provider.once(
"connection_success",
executionEventListener.ledgerConnectionSuccess
);
hre.network.provider.once(
"connection_failure",
executionEventListener.ledgerConnectionFailure
);
hre.network.provider.on(
"confirmation_start",
executionEventListener.ledgerConfirmationStart
);
hre.network.provider.on(
"confirmation_success",
executionEventListener.ledgerConfirmationSuccess
);
hre.network.provider.on(
"confirmation_failure",
executionEventListener.ledgerConfirmationFailure
);
} catch {}

const result = await deploy({
config: hre.config.ignition,
provider: hre.network.provider,
Expand All @@ -273,6 +304,37 @@ ignitionScope
hre.config.networks[hre.network.name]?.ignition.maxFeePerGasLimit,
});

try {
await hre.network.provider.send("hardhat_setLedgerOutputEnabled", [
true,
]);

hre.network.provider.off(
"connection_start",
executionEventListener.ledgerConnectionStart
);
hre.network.provider.off(
"connection_success",
executionEventListener.ledgerConnectionSuccess
);
hre.network.provider.off(
"connection_failure",
executionEventListener.ledgerConnectionFailure
);
hre.network.provider.off(
"confirmation_start",
executionEventListener.ledgerConfirmationStart
);
hre.network.provider.off(
"confirmation_success",
executionEventListener.ledgerConfirmationSuccess
);
hre.network.provider.off(
"confirmation_failure",
executionEventListener.ledgerConfirmationFailure
);
} catch {}

if (result.type === "SUCCESSFUL_DEPLOYMENT" && verify) {
console.log("");
console.log(chalk.bold("Verifying deployed contracts"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export function calculateBatchDisplay(state: UiState): {
height: number;
} {
const batch = state.batches[state.currentBatch - 1];
const height = batch.length + 2;
const height = batch.length + (state.ledgerMessageIsDisplayed ? 4 : 2);

let text = `Batch #${state.currentBatch}\n`;

Expand All @@ -16,6 +16,10 @@ export function calculateBatchDisplay(state: UiState): {

text += "\n";

if (state.ledger) {
text += `\n Ledger: ${state.ledgerMessage}\n`;
}

return { text, height };
}

Expand Down
92 changes: 88 additions & 4 deletions packages/hardhat-plugin/src/ui/pretty-event-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,15 @@ export class PrettyEventHandler implements ExecutionEventListener {
maxFeeBumps: 0,
gasBumps: {},
strategy: null,
ledger: false,
ledgerMessage: "",
ledgerMessageIsDisplayed: false,
};

constructor(private _deploymentParams: DeploymentParameters = {}) {}
constructor(
private _deploymentParams: DeploymentParameters = {},
private _disableOutput = false
) {}

public get state(): UiState {
return this._uiState;
Expand Down Expand Up @@ -309,6 +315,77 @@ export class PrettyEventHandler implements ExecutionEventListener {
};
}

public ledgerConnectionStart(): void {
this.state = {
...this.state,
ledger: true,
ledgerMessage: "Connecting wallet",
};

this._redisplayCurrentBatch();

this.state = {
...this.state,
ledgerMessageIsDisplayed: true,
};
}

public ledgerConnectionSuccess(): void {
this.state = {
...this.state,
ledgerMessage: "Wallet connected",
};

this._redisplayCurrentBatch();
}

public ledgerConnectionFailure(): void {
this.state = {
...this.state,
ledgerMessage: "Wallet connection failed",
};

this._redisplayCurrentBatch();
}

public ledgerConfirmationStart(): void {
this.state = {
...this.state,
ledger: true,
ledgerMessage: "Waiting for confirmation on device",
};

this._redisplayCurrentBatch();

this.state = {
...this.state,
ledgerMessageIsDisplayed: true,
};
}

public ledgerConfirmationSuccess(): void {
this.state = {
...this.state,
ledgerMessage: "Transaction approved by device",
};

this._redisplayCurrentBatch();

this.state = {
...this.state,
ledger: false,
};
}

public ledgerConfirmationFailure(): void {
this.state = {
...this.state,
ledgerMessage: "Transaction confirmation failed",
};

this._redisplayCurrentBatch();
}

private _setFutureStatusInitializedAndRedisplayBatch({
futureId,
}: {
Expand All @@ -330,6 +407,11 @@ export class PrettyEventHandler implements ExecutionEventListener {
futureId,
this._getFutureStatusFromEventResult(result)
);

this.state = {
...this.state,
ledgerMessageIsDisplayed: false,
};
}

private _setFutureStatusAndRedisplayBatch(
Expand Down Expand Up @@ -454,11 +536,13 @@ export class PrettyEventHandler implements ExecutionEventListener {
}

private _redisplayCurrentBatch() {
const { height, text: batch } = calculateBatchDisplay(this.state);
if (!this._disableOutput) {
const { height, text: batch } = calculateBatchDisplay(this.state);

this._clearUpToHeight(height);
this._clearUpToHeight(height);

console.log(batch);
console.log(batch);
}
}

private _clearCurrentLine(): void {
Expand Down
3 changes: 3 additions & 0 deletions packages/hardhat-plugin/src/ui/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ export interface UiState {
maxFeeBumps: number;
gasBumps: Record<string, number>;
strategy: string | null;
ledger: boolean;
ledgerMessage: string;
ledgerMessageIsDisplayed: boolean;
}

export interface AddressMap {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ const exampleState: UiState = {
maxFeeBumps: 0,
gasBumps: {},
strategy: null,
ledger: false,
ledgerMessage: "",
ledgerMessageIsDisplayed: false,
};

describe("ui - calculate batch display", () => {
Expand Down Expand Up @@ -179,16 +182,44 @@ describe("ui - calculate batch display", () => {
expectedText
);
});

it("should render a batch when using a ledger device", () => {
const expectedText = testFormat(`
Batch #1
Executing ExampleModule#Token...
Ledger: Waiting for confirmation on device
`);

assertBatchText(
[
{
status: {
type: UiFutureStatusType.UNSTARTED,
},
futureId: "ExampleModule#Token",
},
],
3,
expectedText,
{
ledger: true,
ledgerMessage: "Waiting for confirmation on device",
}
);
});
});

function assertBatchText(
batch: UiFuture[],
expectedHeight: number,
expectedText: string
expectedText: string,
extraState?: Partial<UiState>
) {
const { text: actualText, height } = calculateBatchDisplay({
...exampleState,
batches: [batch],
...extraState,
});

assert.equal(height, expectedHeight);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ describe("ui - calculate starting message display", () => {
maxFeeBumps: 0,
gasBumps: {},
strategy: "basic",
ledger: false,
ledgerMessage: "",
ledgerMessageIsDisplayed: false,
};

it("should display the deploying module message", () => {
Expand Down
72 changes: 72 additions & 0 deletions packages/hardhat-plugin/test/ui/pretty-event-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { assert } from "chai";

import { PrettyEventHandler } from "../../src/ui/pretty-event-handler";

describe("ui - pretty event handler", () => {
describe("ledger", () => {
it("should set a message on connection start", () => {
const eventHandler = new PrettyEventHandler(undefined, true);

eventHandler.ledgerConnectionStart();

assert.equal(eventHandler.state.ledgerMessage, "Connecting wallet");
assert.isTrue(eventHandler.state.ledger);
assert.isTrue(eventHandler.state.ledgerMessageIsDisplayed);
});

it("should set a message on connection success", () => {
const eventHandler = new PrettyEventHandler(undefined, true);

eventHandler.ledgerConnectionSuccess();

assert.equal(eventHandler.state.ledgerMessage, "Wallet connected");
});

it("should set a message on connection failure", () => {
const eventHandler = new PrettyEventHandler(undefined, true);

eventHandler.ledgerConnectionFailure();

assert.equal(
eventHandler.state.ledgerMessage,
"Wallet connection failed"
);
});

it("should set a message on confirmation start", () => {
const eventHandler = new PrettyEventHandler(undefined, true);

eventHandler.ledgerConfirmationStart();

assert.equal(
eventHandler.state.ledgerMessage,
"Waiting for confirmation on device"
);
assert.isTrue(eventHandler.state.ledger);
assert.isTrue(eventHandler.state.ledgerMessageIsDisplayed);
});

it("should set a message on confirmation success", () => {
const eventHandler = new PrettyEventHandler(undefined, true);

eventHandler.ledgerConfirmationSuccess();

assert.equal(
eventHandler.state.ledgerMessage,
"Transaction approved by device"
);
assert.isFalse(eventHandler.state.ledger);
});

it("should set a message on confirmation failure", () => {
const eventHandler = new PrettyEventHandler(undefined, true);

eventHandler.ledgerConfirmationFailure();

assert.equal(
eventHandler.state.ledgerMessage,
"Transaction confirmation failed"
);
});
});
});

0 comments on commit 3e4949e

Please sign in to comment.