mirror of
https://github.com/datahaven-xyz/datahaven
synced 2026-05-23 17:28:23 +00:00
feat(contracts): deploy stagenet-hoodi AVS contracts, fix verification and update-metadata CLI (#439)
## Summary - Add stagenet-hoodi deployment artifacts (contract addresses, rewards info) and update Snowbridge config with latest validator set - Fix the `bun cli contracts verify` command to correctly verify all deployed contracts, including proxy contracts and Snowbridge dependencies - Fix the `bun cli contracts update-metadata` command to use the correct config file when `--environment` is specified ## Contract verification fixes The verification CLI hardcoded all contract source paths as `src/<Name>.sol`, which failed for: - **Snowbridge contracts** (Gateway, BeefyClient, AgentExecutor) — these live in `lib/snowbridge/contracts/src/` - **Gateway proxy** — the `Gateway` deployment address is actually a `GatewayProxy`, not the Gateway implementation. The implementation address needs to be resolved from the ERC1967 storage slot - **ServiceManager proxy** — was not being verified at all Changes: - Added `contractPath` field to `ContractToVerify` so each contract specifies its source location relative to the contracts directory - Added `guessConstructorArgs` option for proxy contracts with complex encoded init data (uses forge's `--guess-constructor-args`) - Gateway is now verified as two separate contracts: Gateway Implementation (address resolved from ERC1967 proxy slot) and GatewayProxy - ServiceManager proxy is now verified as `TransparentUpgradeableProxy` ## Update-metadata fix The `update-metadata` command was ignoring the `--environment` flag when selecting the deployments file: 1. The handler received a pre-built networkId (`"stagenet-hoodi"`) as the chain parameter, which `getChainDeploymentParams` couldn't resolve (falling back to anvil). Now chain and environment are passed separately. 2. Commander.js routed `--environment` to the parent contracts command, leaving `options.environment` undefined on the subcommand. Added the same parent-fallback logic already used for `--chain`. ## Test plan - [x] `bun typecheck` passes - [x] Ran `bun cli contracts verify --chain hoodi --environment stagenet` — all contracts verified successfully on Etherscan - [x] `bun cli contracts update-metadata --chain hoodi --environment stagenet` now reads the correct `stagenet-hoodi.json` deployments file --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a2aec42254
commit
eaeb06dbbb
6 changed files with 81 additions and 22 deletions
|
|
@ -43,16 +43,16 @@
|
|||
"randaoCommitDelay": 4,
|
||||
"randaoCommitExpiration": 24,
|
||||
"minNumRequiredSignatures": 3,
|
||||
"startBlock": 1299215,
|
||||
"startBlock": 1303065,
|
||||
"rewardsMessageOrigin": "0x56490bd3f367447bfaf57bb18e7a45e1b2db7d538fe42098e87d2aa106c6afdd",
|
||||
"initialValidatorSetId": 2179,
|
||||
"initialValidatorSetId": 2186,
|
||||
"initialValidatorHashes": [
|
||||
"0x07ce4f2cd558f4d4b529a3362b6ff7d616ca0893b53252dc62829b8218ea5c10",
|
||||
"0xaea5344f086d3be7c94cf3a47436bcbb98de23cf1ee773a9180cfecab0453a50",
|
||||
"0xcd3a33755b27fe810dfb780b3f1df1c25efa1bb826ca618e41022fa900876087",
|
||||
"0x4f4ce8cad711a4b33d15095091f8a98eaf9bfd1b39a9159e605cf5d6783cc667"
|
||||
],
|
||||
"nextValidatorSetId": 2180,
|
||||
"nextValidatorSetId": 2187,
|
||||
"nextValidatorHashes": [
|
||||
"0x07ce4f2cd558f4d4b529a3362b6ff7d616ca0893b53252dc62829b8218ea5c10",
|
||||
"0xaea5344f086d3be7c94cf3a47436bcbb98de23cf1ee773a9180cfecab0453a50",
|
||||
|
|
|
|||
1
contracts/deployments/stagenet-hoodi-rewards-info.json
Normal file
1
contracts/deployments/stagenet-hoodi-rewards-info.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"RewardsAgent": "0x2E039a88838241d1Ac738cf2e3C5763ba12571e7","RewardsAgentOrigin": "0x56490bd3f367447bfaf57bb18e7a45e1b2db7d538fe42098e87d2aa106c6afdd"}
|
||||
1
contracts/deployments/stagenet-hoodi.json
Normal file
1
contracts/deployments/stagenet-hoodi.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"network": "stagenet-hoodi","BeefyClient": "0xE65dc4eCA2Fd428361076e1f204731224CeB4292","AgentExecutor": "0x35d3FdCB19A246a1763421168dF69dA3dE207063","Gateway": "0xE9352f1488F12bFEd722c133C129ca5F467463d1","ServiceManager": "0xED73cCaF067cebC706B2B3a6cf2b9af2c696c6d3","ServiceManagerImplementation": "0x5E1DA2eE025Dac2F8c391Ac86ebA20bd34c32465","RewardsAgent": "0x2E039a88838241d1Ac738cf2e3C5763ba12571e7","DelegationManager": "0x867837a9722C512e0862d8c2E15b8bE220E8b87d","StrategyManager": "0xeE45e76ddbEDdA2918b8C7E3035cd37Eab3b5D41","AVSDirectory": "0xD58f6844f79eB1fbd9f7091d05f7cb30d3363926","RewardsCoordinator": "0x29e8572678e0c272350aa0b4B8f304E47EBcd5e7","AllocationManager": "0x95a7431400F362F3647a69535C5666cA0133CAA0","PermissionController": "0xdcCF401fD121d8C542E96BC1d0078884422aFAD2"}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { logger, parseDeploymentsFile, printDivider } from "utils";
|
||||
import { createPublicClient, createWalletClient, encodeFunctionData, http } from "viem";
|
||||
import { privateKeyToAccount } from "viem/accounts";
|
||||
import { getChainDeploymentParams } from "../../../configs/contracts/config";
|
||||
import { buildNetworkId, getChainDeploymentParams } from "../../../configs/contracts/config";
|
||||
import { dataHavenServiceManagerAbi } from "../../../contract-bindings/generated";
|
||||
|
||||
/**
|
||||
|
|
@ -10,7 +10,7 @@ import { dataHavenServiceManagerAbi } from "../../../contract-bindings/generated
|
|||
export const updateAVSMetadataURI = async (
|
||||
chain: string,
|
||||
uri: string,
|
||||
opts: { execute?: boolean; avsOwnerKey?: string } = {}
|
||||
opts: { execute?: boolean; avsOwnerKey?: string; environment?: string } = {}
|
||||
) => {
|
||||
try {
|
||||
const execute = opts.execute ?? false;
|
||||
|
|
@ -22,14 +22,15 @@ export const updateAVSMetadataURI = async (
|
|||
throw new Error("AVS owner private key is required to execute this transaction");
|
||||
}
|
||||
|
||||
// Get chain configuration
|
||||
// Get chain configuration using base chain name, and build networkId for deployment file lookup
|
||||
const networkId = buildNetworkId(chain, opts.environment);
|
||||
const deploymentParams = getChainDeploymentParams(chain);
|
||||
logger.info(`🫎 Updating AVS metadata URI on ${chain} chain`);
|
||||
logger.info(`🫎 Updating AVS metadata URI on ${networkId}`);
|
||||
logger.info(`Network: ${deploymentParams.network} (Chain ID: ${deploymentParams.chainId})`);
|
||||
logger.info(`RPC URL: ${deploymentParams.rpcUrl}`);
|
||||
logger.info(`New URI: ${uri}`);
|
||||
|
||||
const deployments = await parseDeploymentsFile(chain);
|
||||
const deployments = await parseDeploymentsFile(networkId);
|
||||
const serviceManagerAddress = deployments.ServiceManager;
|
||||
|
||||
if (!serviceManagerAddress) {
|
||||
|
|
|
|||
|
|
@ -14,8 +14,12 @@ interface ContractToVerify {
|
|||
name: string;
|
||||
address: string;
|
||||
artifactName: string;
|
||||
/** Path to the contract source file relative to the contracts directory (e.g. "src/Foo.sol" or "lib/snowbridge/contracts/src/Bar.sol") */
|
||||
contractPath: string;
|
||||
constructorArgs: string[];
|
||||
constructorArgTypes: string[];
|
||||
/** When true, uses forge's --guess-constructor-args instead of explicit args (useful for proxies with complex init data) */
|
||||
guessConstructorArgs?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -41,11 +45,17 @@ export const verifyContracts = async (options: ContractsVerifyOptions) => {
|
|||
|
||||
const deployments = await parseDeploymentsFile(networkId);
|
||||
|
||||
// Resolve the Gateway implementation address from the ERC1967 proxy storage slot
|
||||
const chainConfig = CHAIN_CONFIGS[options.chain as keyof typeof CHAIN_CONFIGS];
|
||||
const rpcUrl = options.rpcUrl || chainConfig.RPC_URL;
|
||||
const gatewayImplAddress = await getProxyImplementation(deployments.Gateway, rpcUrl);
|
||||
|
||||
const contractsToVerify: ContractToVerify[] = [
|
||||
{
|
||||
name: "ServiceManager Implementation",
|
||||
address: deployments.ServiceManagerImplementation,
|
||||
artifactName: "DataHavenServiceManager",
|
||||
contractPath: "src/DataHavenServiceManager.sol",
|
||||
constructorArgs: [
|
||||
deployments.RewardsCoordinator,
|
||||
deployments.PermissionController,
|
||||
|
|
@ -54,16 +64,41 @@ export const verifyContracts = async (options: ContractsVerifyOptions) => {
|
|||
constructorArgTypes: ["address", "address", "address"]
|
||||
},
|
||||
{
|
||||
name: "Gateway",
|
||||
address: deployments.Gateway,
|
||||
artifactName: "Gateway",
|
||||
name: "ServiceManager Proxy",
|
||||
address: deployments.ServiceManager,
|
||||
artifactName: "TransparentUpgradeableProxy",
|
||||
contractPath:
|
||||
"lib/eigenlayer-contracts/lib/openzeppelin-contracts-v4.9.0/contracts/proxy/transparent/TransparentUpgradeableProxy.sol",
|
||||
constructorArgs: [],
|
||||
constructorArgTypes: []
|
||||
constructorArgTypes: [],
|
||||
guessConstructorArgs: true
|
||||
},
|
||||
...(gatewayImplAddress
|
||||
? [
|
||||
{
|
||||
name: "Gateway Implementation",
|
||||
address: gatewayImplAddress,
|
||||
artifactName: "Gateway",
|
||||
contractPath: "lib/snowbridge/contracts/src/Gateway.sol",
|
||||
constructorArgs: [deployments.BeefyClient, deployments.AgentExecutor],
|
||||
constructorArgTypes: ["address", "address"]
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
name: "Gateway Proxy",
|
||||
address: deployments.Gateway,
|
||||
artifactName: "GatewayProxy",
|
||||
contractPath: "lib/snowbridge/contracts/src/GatewayProxy.sol",
|
||||
constructorArgs: [],
|
||||
constructorArgTypes: [],
|
||||
guessConstructorArgs: true
|
||||
},
|
||||
{
|
||||
name: "BeefyClient",
|
||||
address: deployments.BeefyClient,
|
||||
artifactName: "BeefyClient",
|
||||
contractPath: "lib/snowbridge/contracts/src/BeefyClient.sol",
|
||||
constructorArgs: [],
|
||||
constructorArgTypes: []
|
||||
},
|
||||
|
|
@ -71,11 +106,18 @@ export const verifyContracts = async (options: ContractsVerifyOptions) => {
|
|||
name: "AgentExecutor",
|
||||
address: deployments.AgentExecutor,
|
||||
artifactName: "AgentExecutor",
|
||||
contractPath: "lib/snowbridge/contracts/src/AgentExecutor.sol",
|
||||
constructorArgs: [],
|
||||
constructorArgTypes: []
|
||||
}
|
||||
];
|
||||
|
||||
if (!gatewayImplAddress) {
|
||||
logger.warn(
|
||||
"⚠️ Could not resolve Gateway implementation address from proxy, skipping Gateway implementation verification"
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info("📋 Contracts to verify:");
|
||||
contractsToVerify.forEach((contract) => {
|
||||
|
|
@ -109,17 +151,29 @@ export const verifyContracts = async (options: ContractsVerifyOptions) => {
|
|||
async function verifySingleContract(contract: ContractToVerify, options: ContractsVerifyOptions) {
|
||||
logger.info(`\n🔍 Verifying ${contract.name} (${contract.address})...`);
|
||||
|
||||
const { address, artifactName, constructorArgs: args, constructorArgTypes: types } = contract;
|
||||
const {
|
||||
address,
|
||||
artifactName,
|
||||
contractPath,
|
||||
constructorArgs: args,
|
||||
constructorArgTypes: types,
|
||||
guessConstructorArgs
|
||||
} = contract;
|
||||
|
||||
const abiEncodedArgs = getEncodedConstructorArgs(args, types);
|
||||
const constructorArgsStr = abiEncodedArgs ? `--constructor-args ${abiEncodedArgs}` : "";
|
||||
let constructorArgsStr: string;
|
||||
if (guessConstructorArgs) {
|
||||
constructorArgsStr = "--guess-constructor-args";
|
||||
} else {
|
||||
const abiEncodedArgs = getEncodedConstructorArgs(args, types);
|
||||
constructorArgsStr = abiEncodedArgs ? `--constructor-args ${abiEncodedArgs}` : "";
|
||||
}
|
||||
|
||||
try {
|
||||
const chainConfig = CHAIN_CONFIGS[options.chain as keyof typeof CHAIN_CONFIGS];
|
||||
const rpcUrl = options.rpcUrl || chainConfig.RPC_URL;
|
||||
const chainParameter =
|
||||
options.chain === "hoodi" ? "--chain-id 560048" : `--chain ${options.chain}`;
|
||||
const verifyCommand = `forge verify-contract ${address} src/${artifactName}.sol:${artifactName} --rpc-url ${rpcUrl} ${chainParameter} ${constructorArgsStr} --watch`;
|
||||
const verifyCommand = `forge verify-contract ${address} ${contractPath}:${artifactName} --rpc-url ${rpcUrl} ${chainParameter} ${constructorArgsStr} --watch`;
|
||||
|
||||
logger.info(`Running: ${verifyCommand}`);
|
||||
|
||||
|
|
@ -142,7 +196,7 @@ async function verifySingleContract(contract: ContractToVerify, options: Contrac
|
|||
logger.info(`Check manually at: ${chainConfig.BLOCK_EXPLORER}address/${contract.address}`);
|
||||
logger.info("You can also try running the command manually from the contracts directory:");
|
||||
const rpcUrl = options.rpcUrl || chainConfig.RPC_URL;
|
||||
const manualCommand = `forge verify-contract ${contract.address} src/${contract.artifactName}.sol:${contract.artifactName} --rpc-url ${rpcUrl} --chain ${options.chain} ${constructorArgsStr}`;
|
||||
const manualCommand = `forge verify-contract ${contract.address} ${contract.contractPath}:${contract.artifactName} --rpc-url ${rpcUrl} --chain ${options.chain} ${constructorArgsStr}`;
|
||||
logger.info(`cd ../contracts && ${manualCommand}`);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -360,12 +360,14 @@ contractsCommand
|
|||
if (!chain) {
|
||||
throw new Error("--chain parameter is required");
|
||||
}
|
||||
// Build network identifier with environment prefix if specified
|
||||
const environment = options.environment;
|
||||
const networkId = environment ? `${environment}-${chain}` : chain;
|
||||
await updateAVSMetadataURI(networkId, options.uri, {
|
||||
let environment = options.environment;
|
||||
if (!environment && command.parent) {
|
||||
environment = command.parent.getOptionValue("environment");
|
||||
}
|
||||
await updateAVSMetadataURI(chain, options.uri, {
|
||||
execute: options.execute,
|
||||
avsOwnerKey: options.avsOwnerKey
|
||||
avsOwnerKey: options.avsOwnerKey,
|
||||
environment
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue