Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix manifest error when connecting to an Anvil dev network #950

Merged
merged 6 commits into from
Dec 21, 2023
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
4 changes: 4 additions & 0 deletions packages/core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Unreleased

- Fix manifest error when connecting to an Anvil dev network. ([#950](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/950))

## 1.32.1 (2023-12-14)

- CLI: Fix ambiguous name error when passing in fully qualified contract names. ([#944](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/944))
Expand Down
16 changes: 14 additions & 2 deletions packages/core/src/manifest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -399,11 +399,23 @@ test('manifest name for an unknown network, development instance, non hardhat',
test('manifest name for an unknown network, development instance, hardhat', t => {
const chainId = 31337;
const instanceId = '0x22223';
const devInstanceMetadata = { networkName: 'dev', instanceId: instanceId };
const devInstanceMetadata = { networkName: 'hardhat', instanceId: instanceId };

const manifest = new Manifest(chainId, devInstanceMetadata, '/tmp');

const expectedPath = `/tmp/openzeppelin-upgrades/dev-${chainId}-${instanceId}.json`;
const expectedPath = `/tmp/openzeppelin-upgrades/hardhat-${chainId}-${instanceId}.json`;
t.is(manifest.file, expectedPath);
t.is(manifest.fallbackFile, `.openzeppelin/unknown-${chainId}.json`);
});

test('manifest name for an unknown network, development instance, anvil - forkedNetwork null', t => {
const chainId = 31337;
const instanceId = '0x22224';
const devInstanceMetadata = { networkName: 'anvil', instanceId: instanceId, forkedNetwork: null };

const manifest = new Manifest(chainId, devInstanceMetadata, '/tmp');

const expectedPath = `/tmp/openzeppelin-upgrades/anvil-${chainId}-${instanceId}.json`;
t.is(manifest.file, expectedPath);
t.is(manifest.fallbackFile, `.openzeppelin/unknown-${chainId}.json`);
});
Expand Down
39 changes: 27 additions & 12 deletions packages/core/src/manifest.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import os from 'os';
import path from 'path';
import { promises as fs } from 'fs';
import { EthereumProvider, getChainId, getHardhatMetadata, networkNames } from './provider';
import {
EthereumProvider,
HardhatMetadata,
getAnvilMetadata,
getChainId,
getHardhatMetadata,
networkNames,
} from './provider';
import lockfile from 'proper-lockfile';
import { compare as compareVersions } from 'compare-versions';

Expand Down Expand Up @@ -44,28 +51,36 @@ function defaultManifest(): ManifestData {
const MANIFEST_DEFAULT_DIR = '.openzeppelin';
const MANIFEST_TEMP_DIR = 'openzeppelin-upgrades';

type DevNetworkType = 'hardhat' | 'anvil';

async function getDevInstanceMetadata(
provider: EthereumProvider,
chainId: number,
): Promise<DevInstanceMetadata | undefined> {
let hardhatMetadata;

let networkMetadata: HardhatMetadata;
let networkType: DevNetworkType;
try {
hardhatMetadata = await getHardhatMetadata(provider);
networkMetadata = await getAnvilMetadata(provider);
networkType = 'anvil';
} catch (e: unknown) {
return undefined;
try {
networkMetadata = await getHardhatMetadata(provider);
networkType = 'hardhat';
} catch (e: unknown) {
return undefined;
}
}

if (hardhatMetadata.chainId !== chainId) {
if (networkMetadata.chainId !== chainId) {
throw new Error(
`Broken invariant: Hardhat metadata's chainId ${hardhatMetadata.chainId} does not match eth_chainId ${chainId}`,
`Broken invariant: Hardhat or Anvil metadata's chainId ${networkMetadata.chainId} does not match eth_chainId ${chainId}`,
);
}

return {
networkName: 'hardhat',
instanceId: hardhatMetadata.instanceId,
forkedNetwork: hardhatMetadata.forkedNetwork,
networkName: networkType,
instanceId: networkMetadata.instanceId,
forkedNetwork: networkMetadata.forkedNetwork,
};
}

Expand All @@ -83,7 +98,7 @@ interface DevInstanceMetadata {
forkedNetwork?: {
// The chainId of the network that is being forked
chainId: number;
};
} | null;
}

export class Manifest {
Expand Down Expand Up @@ -129,7 +144,7 @@ export class Manifest {
}
debug('development manifest file:', this.file, 'fallback file:', this.fallbackFile);

if (devInstanceMetadata.forkedNetwork !== undefined) {
if (devInstanceMetadata.forkedNetwork) {
const forkedChainId = devInstanceMetadata.forkedNetwork.chainId;
debug('forked network chain id:', forkedChainId);

Expand Down
13 changes: 11 additions & 2 deletions packages/core/src/provider.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export interface EthereumProvider {
send(method: 'anvil_metadata', params: []): Promise<HardhatMetadata>;
send(method: 'hardhat_metadata', params: []): Promise<HardhatMetadata>;
send(method: 'web3_clientVersion', params: []): Promise<string>;
send(method: 'net_version', params: []): Promise<string>;
Expand All @@ -12,13 +13,14 @@ export interface EthereumProvider {
send(method: string, params: unknown[]): Promise<unknown>;
}

interface HardhatMetadata {
export interface HardhatMetadata {
clientVersion: string;
chainId: number;
instanceId: string;
forkedNetwork?: {
// The chainId of the network that is being forked
chainId: number;
};
} | null;
}

interface EthereumTransaction {
Expand Down Expand Up @@ -57,6 +59,13 @@ export async function getHardhatMetadata(provider: EthereumProvider): Promise<Ha
return provider.send('hardhat_metadata', []);
}

/**
* Anvil could have anvil_metadata, for which hardhat_metadata is an alias.
*/
export async function getAnvilMetadata(provider: EthereumProvider): Promise<HardhatMetadata> {
return provider.send('anvil_metadata', []);
}

export async function getStorageAt(
provider: EthereumProvider,
address: string,
Expand Down