Skip to content

Commit

Permalink
Fix bug with transaction and contract recovery (#54)
Browse files Browse the repository at this point in the history
* Fixed bug with transaction and contract recovery

* Updated versions

* Aligned text

* Resolved typos
  • Loading branch information
KyrylR authored Nov 2, 2023
1 parent e958f58 commit aaa4adf
Show file tree
Hide file tree
Showing 21 changed files with 384 additions and 101 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Version 2.0.0-alpha.11

* Initially, the fully qualified name is used to retrieve a contract from the Transaction Storage.
* If the fully qualified name is absent, the recovery process falls back to using the `ContractFieldsToSave` derived from the contract.
* Should the name be located, the `ContractFieldsToSave` is disregarded and is not used as a key within the Transaction Storage.

## Version 2.0.0-alpha.2

* Library linking fully relies on the hardhat artifacts.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@solarity/hardhat-migrate",
"version": "2.0.0-alpha.10",
"version": "2.0.0-alpha.11",
"description": "Automatic deployment and verification of smart contracts",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
Expand Down
16 changes: 10 additions & 6 deletions src/deployer/Deployer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@ import { catchError, getChainId, getSignerHelper, isDeployedContractAddress } fr
import { MigrateError } from "../errors";

import { Adapter } from "./adapters/Adapter";
import { TruffleAdapter } from "./adapters/TruffleAdapter";
import { BytecodeAdapter } from "./adapters/BytecodeAdapter";
import { EthersContractAdapter } from "./adapters/EthersContractAdapter";
import { TruffleAdapter } from "./adapters/TruffleAdapter";
import { EthersFactoryAdapter } from "./adapters/EthersFactoryAdapter";

import { OverridesAndLibs } from "../types/deployer";
import { KeyTransactionFields } from "../types/tools";
import { KeyTransactionFields, MigrationMetadata } from "../types/tools";
import { Instance, TypedArgs } from "../types/adapter";
import { isContractFactory, isEthersContract, isBytecodeFactory, isTruffleFactory } from "../types/type-checks";

import { Stats } from "../tools/Stats";
import { Reporter } from "../tools/reporters/Reporter";
import { TransactionProcessor } from "../tools/storage/TransactionProcessor";

Expand Down Expand Up @@ -58,8 +59,6 @@ export class Deployer {
throw new MigrateError(`Contract with address '${contractIdentifier}' is not deployed`);
}

TransactionProcessor.saveDeploymentTransactionWithContractName(defaultContractName, contractIdentifier);

return adapter.toInstance(contract, contractIdentifier, {});
}

Expand Down Expand Up @@ -89,12 +88,17 @@ export class Deployer {

const txResponse = await signer.sendTransaction(tx);

await Promise.all([
const [receipt] = await Promise.all([
txResponse.wait(this._hre.config.migrate.wait),
Reporter.reportTransaction(txResponse, methodString),
]);

TransactionProcessor.saveTransaction(tx);
const saveMetadata: MigrationMetadata = {
migrationNumber: Stats.currentMigration,
methodName: methodString,
};

TransactionProcessor.saveTransaction(tx, receipt!, saveMetadata);
}

public async getSigner(from?: string): Promise<Signer> {
Expand Down
9 changes: 8 additions & 1 deletion src/deployer/MinimalContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import { catchError, fillParameters, getChainId, getInterfaceOnlyWithConstructor

import { MigrateError } from "../errors";

import { MigrationMetadata } from "../types/tools";
import { MigrateConfig } from "../types/migrations";
import { ContractDeployTransactionWithContractName, OverridesAndLibs } from "../types/deployer";

import { Stats } from "../tools/Stats";
import { Reporter } from "../tools/reporters/Reporter";
import { ArtifactProcessor } from "../tools/storage/ArtifactProcessor";
import { TransactionProcessor } from "../tools/storage/TransactionProcessor";
Expand Down Expand Up @@ -129,7 +131,12 @@ export class MinimalContract {

await this._saveContractForVerification(contractAddress, tx, args);

TransactionProcessor.saveDeploymentTransaction(tx, tx.contractName, contractAddress);
const saveMetadata: MigrationMetadata = {
migrationNumber: Stats.currentMigration,
contractName: tx.contractName,
};

TransactionProcessor.saveDeploymentTransaction(tx, tx.contractName, contractAddress, saveMetadata);

return contractAddress;
}
Expand Down
24 changes: 20 additions & 4 deletions src/deployer/adapters/AbstractEthersAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
BaseContract,
BaseContractMethod,
ContractFactory,
ContractTransactionReceipt,
ContractTransactionResponse,
defineProperties,
FunctionFragment,
Expand All @@ -16,9 +17,10 @@ import "../../type-extensions";
import { bytecodeToString, fillParameters, getMethodString, getSignerHelper } from "../../utils";

import { OverridesAndLibs, OverridesAndName } from "../../types/deployer";
import { KeyTransactionFields } from "../../types/tools";
import { EthersContract, BytecodeFactory } from "../../types/adapter";
import { KeyTransactionFields, MigrationMetadata, TransactionFieldsToSave } from "../../types/tools";

import { Stats } from "../../tools/Stats";
import { Reporter } from "../../tools/reporters/Reporter";
import { TransactionProcessor } from "../../tools/storage/TransactionProcessor";

Expand Down Expand Up @@ -141,11 +143,11 @@ export abstract class AbstractEthersAdapter extends Adapter {
args: any[],
) {
try {
const txResponse = TransactionProcessor.tryRestoreSavedTransaction(tx);
const savedTransaction = TransactionProcessor.tryRestoreSavedTransaction(tx);

Reporter.notifyTransactionRecovery(methodString);

return txResponse;
return this._wrapTransactionFieldsToSave(savedTransaction);
} catch {
Reporter.notifyTransactionSendingInsteadOfRecovery(methodString);

Expand All @@ -161,10 +163,24 @@ export abstract class AbstractEthersAdapter extends Adapter {
) {
const txResponse: ContractTransactionResponse = (await oldMethod(...args)) as ContractTransactionResponse;

TransactionProcessor.saveTransaction(tx);
const saveMetadata: MigrationMetadata = {
migrationNumber: Stats.currentMigration,
methodName: methodString,
};

TransactionProcessor.saveTransaction(tx, (await txResponse.wait())!, saveMetadata);

await Reporter.reportTransaction(txResponse, methodString);

return txResponse;
}

private _wrapTransactionFieldsToSave(data: TransactionFieldsToSave): ContractTransactionResponse {
return {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
wait(_confirms?: number): Promise<ContractTransactionReceipt | null> {
return data.receipt as unknown as Promise<ContractTransactionReceipt | null>;
},
} as unknown as ContractTransactionResponse;
}
}
3 changes: 2 additions & 1 deletion src/deployer/adapters/EthersContractAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { catchError, getSignerHelper } from "../../utils";

import { EthersContract } from "../../types/adapter";
import { OverridesAndName } from "../../types/deployer";
import { UNKNOWN_CONTRACT_NAME } from "../../types/tools";

import { ArtifactProcessor } from "../../tools/storage/ArtifactProcessor";

Expand All @@ -31,7 +32,7 @@ export class EthersContractAdapter extends AbstractEthersAdapter {
return (instance as any).contractName;
}

return "Unknown Contract";
return UNKNOWN_CONTRACT_NAME;
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/deployer/adapters/EthersFactoryAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { AbstractEthersAdapter } from "./AbstractEthersAdapter";

import { catchError } from "../../utils";

import { UNKNOWN_CONTRACT_NAME } from "../../types/tools";

import { ArtifactProcessor } from "../../tools/storage/ArtifactProcessor";

@catchError
Expand All @@ -20,7 +22,7 @@ export class EthersFactoryAdapter extends AbstractEthersAdapter {
try {
return ArtifactProcessor.tryGetContractName(this.getRawBytecode(instance));
} catch {
return "Unknown Contract";
return UNKNOWN_CONTRACT_NAME;
}
}

Expand Down
72 changes: 60 additions & 12 deletions src/deployer/adapters/TruffleAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Interface, toBigInt } from "ethers";
import { Interface, toBigInt, TransactionReceiptParams } from "ethers";

import { HardhatRuntimeEnvironment } from "hardhat/types";

Expand All @@ -8,7 +8,7 @@ import { Adapter } from "./Adapter";

import { MinimalContract } from "../MinimalContract";

import { bytecodeToString, catchError, fillParameters, getMethodString, toJSON } from "../../utils";
import { bytecodeToString, catchError, fillParameters, getMethodString } from "../../utils";

import { EthersContract, Instance, TruffleFactory } from "../../types/adapter";
import {
Expand All @@ -17,8 +17,9 @@ import {
OverridesAndName,
TruffleTransactionResponse,
} from "../../types/deployer";
import { KeyTransactionFields } from "../../types/tools";
import { KeyTransactionFields, MigrationMetadata, UNKNOWN_CONTRACT_NAME } from "../../types/tools";

import { Stats } from "../../tools/Stats";
import { Reporter } from "../../tools/reporters/Reporter";
import { TruffleReporter } from "../../tools/reporters/TruffleReporter";
import { ArtifactProcessor } from "../../tools/storage/ArtifactProcessor";
Expand Down Expand Up @@ -78,7 +79,7 @@ export class TruffleAdapter extends Adapter {
return (instance as any).contractName;
}

return "Unknown Contract";
return UNKNOWN_CONTRACT_NAME;
}
}

Expand Down Expand Up @@ -115,33 +116,47 @@ export class TruffleAdapter extends Adapter {
continue;
}

(contract as any)[methodName] = this._wrapOldMethod(contractName, methodName, oldMethod, to, parameters);
(contract as any)[methodName] = this._wrapOldMethod(
contractInterface,
contractName,
methodName,
oldMethod,
to,
parameters,
);
}

return contract;
}

protected _wrapOldMethod(
contractInterface: Interface,
contractName: string,
methodName: string,
oldMethod: BaseTruffleMethod,
to: string,
parameters: OverridesAndLibs,
): (...args: any[]) => Promise<TruffleTransactionResponse> {
return async (...args: any[]): Promise<TruffleTransactionResponse> => {
const onlyToSaveTx = await this._buildContractDeployTransaction(args, to, parameters);

const methodString = getMethodString(contractName, methodName);

TruffleReporter.notifyTransactionSending(methodString);
const txData = contractInterface.encodeFunctionData(methodName, args);
const onlyToSaveTx = await this._buildContractDeployTransaction(txData, to, parameters);

if (this._config.continue) {
return this._recoverTransaction(methodString, onlyToSaveTx, oldMethod, args);
}

TruffleReporter.notifyTransactionSending(methodString);

const txResult = await oldMethod(...args);

TransactionProcessor.saveTransaction(onlyToSaveTx);
const saveMetadata: MigrationMetadata = {
migrationNumber: Stats.currentMigration,
methodName: methodString,
};

TransactionProcessor.saveTransaction(onlyToSaveTx, this._toTransactionReceipt(txResult), saveMetadata);

await TruffleReporter.reportTransaction(txResult, methodString);

Expand Down Expand Up @@ -176,7 +191,12 @@ export class TruffleAdapter extends Adapter {
) {
const txResult = await oldMethod(...args);

TransactionProcessor.saveTransaction(tx);
const saveMetadata: MigrationMetadata = {
migrationNumber: Stats.currentMigration,
methodName: methodString,
};

TransactionProcessor.saveTransaction(tx, this._toTransactionReceipt(txResult), saveMetadata);

await TruffleReporter.reportTransaction(txResult, methodString);

Expand All @@ -187,7 +207,7 @@ export class TruffleAdapter extends Adapter {
* @dev Build a transaction ONLY to save it in the storage.
*/
private async _buildContractDeployTransaction(
args: any[],
data: string,
to: string,
parameters: OverridesAndLibs,
): Promise<KeyTransactionFields> {
Expand All @@ -196,9 +216,37 @@ export class TruffleAdapter extends Adapter {
return {
to: to,
from: parameters.from! as string,
data: toJSON(args),
data: data,
chainId: toBigInt(String(parameters.chainId)),
value: toBigInt(String(parameters.value)),
};
}

private _toTransactionReceipt(tx: TruffleTransactionResponse): TransactionReceiptParams {
let txGasPrice: bigint = 0n;

if (tx.receipt.effectiveGasPrice != null) {
txGasPrice = toBigInt(tx.receipt.effectiveGasPrice);
} else if (tx.receipt.gasPrice != null) {
txGasPrice = toBigInt(tx.receipt.gasPrice);
}

return {
to: tx.receipt.to,
from: tx.receipt.from,
contractAddress: tx.receipt.contractAddress !== undefined ? tx.receipt.contractAddress : null,
hash: tx.receipt.transactionHash,
index: Number(tx.receipt.transactionIndex),
blockHash: tx.receipt.blockHash,
blockNumber: Number(tx.receipt.blockNumber),
logsBloom: tx.receipt.logsBloom ? tx.receipt.logsBloom : "",
logs: tx.logs !== undefined ? tx.logs : [],
gasUsed: tx.receipt.gasUsed ? toBigInt(tx.receipt.gasUsed) : 0n,
cumulativeGasUsed: tx.receipt.cumulativeGasUsed ? toBigInt(tx.receipt.cumulativeGasUsed) : 0n,
gasPrice: txGasPrice,
type: tx.receipt.type ? Number(tx.receipt.type) : 0,
status: tx.receipt.status ? Number(tx.receipt.status) : null,
root: null,
};
}
}
12 changes: 10 additions & 2 deletions src/migrator/Migrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { MigrateConfig } from "../types/migrations";
import { Deployer } from "../deployer/Deployer";
import { Verifier } from "../verifier/Verifier";

import { Stats } from "../tools/Stats";
import { Reporter } from "../tools/reporters/Reporter";

export class Migrator {
Expand All @@ -40,7 +41,10 @@ export class Migrator {
Reporter.reportMigrationBegin(this._migrationFiles);

for (const element of this._migrationFiles) {
Stats.currentMigration = this._getMigrationNumber(element);

Reporter.reportMigrationFileBegin(element);

try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const migration = require(resolvePathToFile(this._config.pathToMigrations, element));
Expand All @@ -64,7 +68,7 @@ export class Migrator {

const files = directoryContents
.filter((file) => {
const migrationNumber = parseInt(basename(file));
const migrationNumber = this._getMigrationNumber(file);

if (
isNaN(migrationNumber) ||
Expand All @@ -80,7 +84,7 @@ export class Migrator {
return statSync(resolvePathToFile(this._config.pathToMigrations, file)).isFile();
})
.sort((a, b) => {
return parseInt(basename(a)) - parseInt(basename(b));
return this._getMigrationNumber(a) - this._getMigrationNumber(b);
});

if (files.length === 0) {
Expand All @@ -89,4 +93,8 @@ export class Migrator {

return files;
}

private _getMigrationNumber(file: string) {
return parseInt(basename(file));
}
}
Loading

0 comments on commit aaa4adf

Please sign in to comment.