Skip to content
Merged
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
7 changes: 7 additions & 0 deletions .changeset/bright-spies-glow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@nomicfoundation/ignition-core": patch
"@nomicfoundation/ignition-ui": patch
"@nomicfoundation/hardhat-ignition": patch
---

Ignition: fix for bug when we fail to save transaction hash
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
},
"scripts": {
"postbuild": "cp packages/hardhat-core/src/internal/solidity/compiler/solcjs-runner.js packages/hardhat-core/internal/solidity/compiler/solcjs-runner.js",
"build": "tsc --build packages/hardhat-core packages/hardhat-ethers packages/hardhat-verify packages/hardhat-solhint packages/hardhat-solpp packages/hardhat-truffle4 packages/hardhat-truffle5 packages/hardhat-vyper packages/hardhat-web3 packages/hardhat-web3-v4 packages/hardhat-web3-legacy packages/hardhat-chai-matchers packages/hardhat-network-helpers packages/hardhat-toolbox packages/hardhat-foundry packages/hardhat-ledger packages/hardhat-viem packages/hardhat-toolbox-viem && pnpm run --filter \"./packages/hardhat-ignition*\" build",
"build": "tsc --build packages/hardhat-core packages/hardhat-ethers packages/hardhat-verify packages/hardhat-solhint packages/hardhat-solpp packages/hardhat-truffle4 packages/hardhat-truffle5 packages/hardhat-vyper packages/hardhat-web3 packages/hardhat-web3-v4 packages/hardhat-web3-legacy packages/hardhat-chai-matchers packages/hardhat-network-helpers packages/hardhat-toolbox packages/hardhat-foundry packages/hardhat-ledger packages/hardhat-viem packages/hardhat-toolbox-viem && pnpm build:ignition",
"build:ignition": "pnpm run --filter \"./packages/hardhat-ignition*\" build",
"watch": "tsc --build --watch packages/hardhat-core/src packages/hardhat-ethers packages/hardhat-verify packages/hardhat-solhint packages/hardhat-solpp packages/hardhat-truffle4 packages/hardhat-truffle5 packages/hardhat-vyper packages/hardhat-web3 packages/hardhat-web3-v4 packages/hardhat-web3-legacy packages/hardhat-chai-matchers packages/hardhat-network-helpers packages/hardhat-toolbox packages/hardhat-foundry packages/hardhat-ledger packages/hardhat-viem packages/hardhat-toolbox-viem",
"clean": "pnpm run --recursive clean",
"test": "node scripts/run-tests.js",
Expand Down
1 change: 1 addition & 0 deletions packages/hardhat-ignition-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ export * from "./types/provider";
export * from "./types/serialization";
export * from "./types/status";
export * from "./types/verify";
export { trackTransaction } from "./track-transaction";
export { getVerificationInformation } from "./verify";
export { wipe } from "./wipe";
6 changes: 3 additions & 3 deletions packages/hardhat-ignition-core/src/internal/deployer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { DeploymentState } from "./execution/types/deployment-state";
import {
ContractAtExecutionState,
DeploymentExecutionState,
ExecutionSateType,
ExecutionStateType,
ExecutionState,
ExecutionStatus,
} from "./execution/types/execution-state";
Expand Down Expand Up @@ -101,8 +101,8 @@ export class Deployer {
(
exState
): exState is DeploymentExecutionState | ContractAtExecutionState =>
exState.type === ExecutionSateType.DEPLOYMENT_EXECUTION_STATE ||
exState.type === ExecutionSateType.CONTRACT_AT_EXECUTION_STATE
exState.type === ExecutionStateType.DEPLOYMENT_EXECUTION_STATE ||
exState.type === ExecutionStateType.CONTRACT_AT_EXECUTION_STATE
),
"Invalid state map"
);
Expand Down
46 changes: 46 additions & 0 deletions packages/hardhat-ignition-core/src/internal/errors-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ export const ERROR_RANGES: {
max: 1299,
title: "List transactions errors",
},
TRACK_TRANSACTION: {
min: 1300,
max: 1399,
title: "Track transaction errors",
},
};

/**
Expand Down Expand Up @@ -201,6 +206,14 @@ export const ERRORS = {
number: 410,
message: "Gas estimation failed: %error%",
},
TRANSACTION_LOST: {
number: 411,
message: `An error occured while trying to send a transaction for future %futureId%.

Please use a block explorer to find the hash of the transaction with nonce %nonce% sent from account %sender% and use the following command to add it to your deployment:

npx hardhat ignition track-tx <txHash> <deploymentId> --network <networkName>`,
},
},
RECONCILIATION: {
INVALID_EXECUTION_STATUS: {
Expand Down Expand Up @@ -412,6 +425,39 @@ export const ERRORS = {
"Cannot list transactions for nonexistant deployment at %deploymentDir%",
},
},
TRACK_TRANSACTION: {
DEPLOYMENT_DIR_NOT_FOUND: {
number: 1300,
message: "Deployment directory %deploymentDir% not found",
},
UNINITIALIZED_DEPLOYMENT: {
number: 1301,
message:
"Cannot track transaction for nonexistant deployment at %deploymentDir%",
},
TRANSACTION_NOT_FOUND: {
number: 1302,
message: `Transaction %txHash% not found. Please double check the transaction hash and try again.`,
},
MATCHING_NONCE_NOT_FOUND: {
number: 1303,
message: `The transaction you provided doesn't seem to belong to your deployment.

Please double check the error you are getting when running Hardhat Ignition, and the instructions it's providing.`,
},
KNOWN_TRANSACTION: {
number: 1304,
message: `The transaction hash that you provided was already present in your deployment.

Please double check the error you are getting when running Hardhat Ignition, and the instructions it's providing.`,
},
INSUFFICIENT_CONFIRMATIONS: {
number: 1305,
message: `The transaction you provided doesn't have enough confirmations yet.

Please try again later.`,
},
},
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ export async function initializeDeploymentState(
* This function applies a new message to the deployment state, recording it to the
* journal if needed.
*
* @param message The message to apply.
* @param deploymentState The original deployment state.
* @param deploymentLoader The deployment loader that will be used to record the message.
* @param message - The message to apply.
* @param deploymentState - The original deployment state.
* @param deploymentLoader - The deployment loader that will be used to record the message.
* @returns The new deployment state.
*/
export async function applyNewMessage(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { IgnitionError } from "../../errors";
import { ArtifactResolver } from "../../types/artifact";
import { DeploymentParameters } from "../../types/deploy";
import {
Expand All @@ -10,8 +11,10 @@ import {
IgnitionModuleResult,
} from "../../types/module";
import { DeploymentLoader } from "../deployment-loader/types";
import { ERRORS } from "../errors-list";
import { assertIgnitionInvariant } from "../utils/assertions";
import { getFuturesFromModule } from "../utils/get-futures-from-module";
import { getNetworkExecutionStates } from "../views/execution-state/get-network-execution-states";
import { getPendingNonceAndSender } from "../views/execution-state/get-pending-nonce-and-sender";
import { hasExecutionSucceeded } from "../views/has-execution-succeeded";
import { isBatchFinished } from "../views/is-batch-finished";
Expand All @@ -25,6 +28,7 @@ import { JsonRpcNonceManager } from "./nonce-management/json-rpc-nonce-manager";
import { TransactionTrackingTimer } from "./transaction-tracking-timer";
import { DeploymentState } from "./types/deployment-state";
import { ExecutionStrategy } from "./types/execution-strategy";
import { NetworkInteractionType } from "./types/network-interaction";

/**
* This class is used to execute a module to completion, returning the new
Expand Down Expand Up @@ -68,6 +72,8 @@ export class ExecutionEngine {
deploymentParameters: DeploymentParameters,
defaultSender: string
): Promise<DeploymentState> {
await this._checkForMissingTransactions(deploymentState);

deploymentState = await this._syncNonces(
deploymentState,
module,
Expand Down Expand Up @@ -198,6 +204,32 @@ export class ExecutionEngine {
}
}

/**
* Checks the journal for missing transactions, throws if any are found
* and asks the user to track the missing transaction via the `track-tx` command.
*/
private async _checkForMissingTransactions(
deploymentState: DeploymentState
): Promise<void> {
const exStates = getNetworkExecutionStates(deploymentState);

for (const exState of exStates) {
for (const ni of exState.networkInteractions) {
if (
ni.type === NetworkInteractionType.ONCHAIN_INTERACTION &&
ni.nonce !== undefined &&
ni.transactions.length === 0
) {
throw new IgnitionError(ERRORS.EXECUTION.TRANSACTION_LOST, {
futureId: exState.id,
nonce: ni.nonce,
sender: exState.from,
});
}
}
}
}

/**
* Syncs the nonces of the deployment state with the blockchain, returning
* the new deployment state, and throwing if they can't be synced.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { ExecutionResultType } from "../types/execution-result";
import {
CallExecutionState,
DeploymentExecutionState,
ExecutionSateType,
ExecutionStateType,
SendDataExecutionState,
StaticCallExecutionState,
} from "../types/execution-state";
Expand Down Expand Up @@ -103,11 +103,11 @@ export class FutureProcessor {

while (!isExecutionStateComplete(exState)) {
assertIgnitionInvariant(
exState.type !== ExecutionSateType.CONTRACT_AT_EXECUTION_STATE &&
exState.type !== ExecutionStateType.CONTRACT_AT_EXECUTION_STATE &&
exState.type !==
ExecutionSateType.READ_EVENT_ARGUMENT_EXECUTION_STATE &&
ExecutionStateType.READ_EVENT_ARGUMENT_EXECUTION_STATE &&
exState.type !==
ExecutionSateType.ENCODE_FUNCTION_CALL_EXECUTION_STATE,
ExecutionStateType.ENCODE_FUNCTION_CALL_EXECUTION_STATE,
`Unexpected ExectutionState ${exState.id} with type ${exState.type} and status ${exState.status}: it should have been immediately completed`
);

Expand Down Expand Up @@ -183,7 +183,7 @@ export class FutureProcessor {

case NextAction.SEND_TRANSACTION:
assertIgnitionInvariant(
exState.type !== ExecutionSateType.STATIC_CALL_EXECUTION_STATE,
exState.type !== ExecutionStateType.STATIC_CALL_EXECUTION_STATE,
`Unexpected transaction request in StaticCallExecutionState ${exState.id}`
);

Expand All @@ -192,15 +192,16 @@ export class FutureProcessor {
this._executionStrategy,
this._jsonRpcClient,
this._nonceManager,
this._transactionTrackingTimer
this._transactionTrackingTimer,
this._deploymentLoader
);

case NextAction.QUERY_STATIC_CALL:
return queryStaticCall(exState, this._jsonRpcClient);

case NextAction.MONITOR_ONCHAIN_INTERACTION:
assertIgnitionInvariant(
exState.type !== ExecutionSateType.STATIC_CALL_EXECUTION_STATE,
exState.type !== ExecutionStateType.STATIC_CALL_EXECUTION_STATE,
`Unexpected transaction request in StaticCallExecutionState ${exState.id}`
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
import {
CallExecutionState,
DeploymentExecutionState,
ExecutionSateType,
ExecutionStateType,
SendDataExecutionState,
StaticCallExecutionState,
} from "../../types/execution-state";
Expand Down Expand Up @@ -75,7 +75,7 @@ export async function runStrategy(
lastNetworkInteraction.type === NetworkInteractionType.ONCHAIN_INTERACTION
) {
assertIgnitionInvariant(
exState.type !== ExecutionSateType.STATIC_CALL_EXECUTION_STATE,
exState.type !== ExecutionStateType.STATIC_CALL_EXECUTION_STATE,
`Unexpected StaticCallExecutionState ${exState.id} with onchain interaction ${lastNetworkInteraction.id} when trying to run a strategy`
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DeploymentLoader } from "../../../deployment-loader/types";
import { assertIgnitionInvariant } from "../../../utils/assertions";
import { JsonRpcClient } from "../../jsonrpc-client";
import { NonceManager } from "../../nonce-management/json-rpc-nonce-manager";
Expand Down Expand Up @@ -56,7 +57,8 @@ export async function sendTransaction(
executionStrategy: ExecutionStrategy,
jsonRpcClient: JsonRpcClient,
nonceManager: NonceManager,
transactionTrackingTimer: TransactionTrackingTimer
transactionTrackingTimer: TransactionTrackingTimer,
deploymentLoader: DeploymentLoader
): Promise<
| TransactionSendMessage
| DeploymentExecutionStateCompleteMessage
Expand Down Expand Up @@ -87,7 +89,9 @@ export async function sendTransaction(
exState.from,
lastNetworkInteraction,
nonceManager,
decodeSimulationResult(strategyGenerator, exState)
decodeSimulationResult(strategyGenerator, exState),
deploymentLoader,
exState.id
);

// If the transaction failed during simulation, we need to revert the nonce allocation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
CallExecutionState,
SendDataExecutionState,
StaticCallExecutionState,
ExecutionSateType,
ExecutionStateType,
} from "../../types/execution-state";
import {
DeploymentExecutionStateCompleteMessage,
Expand Down Expand Up @@ -45,7 +45,7 @@ export function createExecutionStateCompleteMessage(
| CallExecutionStateCompleteMessage
| SendDataExecutionStateCompleteMessage
| StaticCallExecutionStateCompleteMessage {
if (exState.type === ExecutionSateType.STATIC_CALL_EXECUTION_STATE) {
if (exState.type === ExecutionStateType.STATIC_CALL_EXECUTION_STATE) {
return {
type: JournalMessageType.STATIC_CALL_EXECUTION_STATE_COMPLETE,
futureId: exState.id,
Expand Down Expand Up @@ -84,21 +84,21 @@ export function createExecutionStateCompleteMessageForExecutionsWithOnchainInter
| CallExecutionStateCompleteMessage
| SendDataExecutionStateCompleteMessage {
switch (exState.type) {
case ExecutionSateType.DEPLOYMENT_EXECUTION_STATE:
case ExecutionStateType.DEPLOYMENT_EXECUTION_STATE:
return {
type: JournalMessageType.DEPLOYMENT_EXECUTION_STATE_COMPLETE,
futureId: exState.id,
result: result as DeploymentExecutionResult,
};

case ExecutionSateType.CALL_EXECUTION_STATE:
case ExecutionStateType.CALL_EXECUTION_STATE:
return {
type: JournalMessageType.CALL_EXECUTION_STATE_COMPLETE,
futureId: exState.id,
result: result as CallExecutionResult,
};

case ExecutionSateType.SEND_DATA_EXECUTION_STATE:
case ExecutionStateType.SEND_DATA_EXECUTION_STATE:
return {
type: JournalMessageType.SEND_DATA_EXECUTION_STATE_COMPLETE,
futureId: exState.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import { IgnitionError } from "../../../../errors";
import { DeploymentLoader } from "../../../deployment-loader/types";
import { ERRORS } from "../../../errors-list";
import { assertIgnitionInvariant } from "../../../utils/assertions";
import { JsonRpcClient, TransactionParams } from "../../jsonrpc-client";
Expand All @@ -19,6 +20,7 @@ import {
RawStaticCallResult,
Transaction,
} from "../../types/jsonrpc";
import { JournalMessageType } from "../../types/messages";
import {
OnchainInteraction,
StaticCall,
Expand Down Expand Up @@ -106,13 +108,15 @@ export async function sendTransactionForOnchainInteraction(
| SimulationErrorExecutionResult
| StrategySimulationErrorExecutionResult
| undefined
>
>,
deploymentLoader: DeploymentLoader,
futureId: string
): Promise<
| SimulationErrorExecutionResult
| StrategySimulationErrorExecutionResult
| {
type: typeof TRANSACTION_SENT_TYPE;
transaction: Transaction;
transaction: Pick<Transaction, "hash" | "fees">;
nonce: number;
}
> {
Expand Down Expand Up @@ -201,6 +205,13 @@ export async function sendTransactionForOnchainInteraction(
return decodedSimulationResult;
}

await deploymentLoader.recordToJournal({
type: JournalMessageType.TRANSACTION_PREPARE_SEND,
futureId,
networkInteractionId: onchainInteraction.id,
nonce: transactionParams.nonce,
});

const txHash = await client.sendTransaction(transactionParams);

return {
Expand Down
Loading
Loading