Deploy and upgrade transparent proxies with automatic implementation detection, admin management, and support for various proxy patterns including OpenZeppelin and UUPS.
Configure proxy deployment with implementation contracts, admin settings, and initialization.
interface ProxyOptions {
/** Proxy admin owner address */
owner?: Address;
/** Number of upgrade iterations (for tracking) */
upgradeIndex?: number;
/** Custom proxy contract to use */
proxyContract?: string | ArtifactData;
/** Proxy constructor arguments template */
proxyArgs?: any[];
/** Proxy admin contract configuration */
viaAdminContract?:
| string
| {
name: string;
artifact?: string | ArtifactData;
};
/** Custom name for implementation deployment */
implementationName?: string;
/** Check for ABI conflicts between proxy and implementation */
checkABIConflict?: boolean;
/** Check proxy admin ownership */
checkProxyAdmin?: boolean;
/** Simple initialization method name */
methodName?: string;
/** Initialization function configuration */
execute?:
| {
methodName: string;
args: any[];
}
| {
init: {
methodName: string;
args: any[];
};
onUpgrade?: {
methodName: string;
args: any[];
};
};
/** Upgrade function configuration */
upgradeFunction?: {
methodName: string;
upgradeArgs: any[];
};
}Usage Examples:
import { deployments, getNamedAccounts } from "hardhat";
// Basic proxy deployment
const { deployer } = await getNamedAccounts();
await deployments.deploy("MyProxy", {
from: deployer,
proxy: {
proxyContract: "OptimizedTransparentProxy",
execute: {
methodName: "initialize",
args: ["param1", "param2"],
},
},
args: ["constructor", "args"], // Implementation constructor args
});
// UUPS proxy deployment
await deployments.deploy("UUPSProxy", {
from: deployer,
proxy: {
proxyContract: "UUPS",
execute: {
init: {
methodName: "initialize",
args: ["init", "params"],
},
onUpgrade: {
methodName: "reinitialize",
args: ["upgrade", "params"],
},
},
},
});
// Custom proxy admin
await deployments.deploy("AdminProxy", {
from: deployer,
proxy: {
proxyContract: "OpenZeppelinTransparentProxy",
viaAdminContract: {
name: "CustomProxyAdmin",
artifact: "MyProxyAdmin",
},
owner: "0x1234567890123456789012345678901234567890",
},
});Built-in support for multiple proxy patterns with automatic configuration.
// Built-in proxy contract options:
// - "EIP173Proxy" - Basic EIP-173 proxy
// - "EIP173ProxyWithReceive" - EIP-173 proxy with receive function
// - "OpenZeppelinTransparentProxy" - OpenZeppelin transparent proxy
// - "OptimizedTransparentProxy" - Gas-optimized transparent proxy
// - "UUPS" - Universal Upgradeable Proxy StandardUsage Examples:
// EIP-173 proxy with receive function
await deployments.deploy("PayableProxy", {
from: deployer,
proxy: {
proxyContract: "EIP173ProxyWithReceive",
methodName: "initialize",
},
value: ethers.utils.parseEther("1.0"), // Send ETH to proxy
});
// OpenZeppelin transparent proxy with default admin
await deployments.deploy("OZProxy", {
from: deployer,
proxy: {
proxyContract: "OpenZeppelinTransparentProxy",
viaAdminContract: "DefaultProxyAdmin", // Uses built-in ProxyAdmin
},
});
// Custom proxy contract
await deployments.deploy("CustomProxy", {
from: deployer,
proxy: {
proxyContract: {
abi: [...], // Custom proxy ABI
bytecode: "0x...", // Custom proxy bytecode
},
proxyArgs: ["{implementation}", "{admin}", "{data}"],
},
});Automatic upgrade detection and execution based on implementation changes.
Usage Examples:
// Upgrade tracking with upgradeIndex
await deployments.deploy("VersionedProxy", {
from: deployer,
proxy: {
upgradeIndex: 2, // Only upgrade if current version < 2
execute: {
onUpgrade: {
methodName: "upgradeToV2",
args: ["new", "features"],
},
},
},
});
// Custom upgrade function
await deployments.deploy("CustomUpgradeProxy", {
from: deployer,
proxy: {
upgradeFunction: {
methodName: "customUpgrade",
upgradeArgs: ["{proxy}", "{implementation}", "{data}"],
},
},
});
// Conditional initialization
await deployments.deploy("ConditionalProxy", {
from: deployer,
proxy: {
execute: {
init: {
methodName: "initialize",
args: ["initial", "setup"],
},
onUpgrade: {
methodName: "migrate",
args: ["migration", "data"],
},
},
},
});Control implementation deployment and naming for better organization.
Usage Examples:
// Custom implementation name
await deployments.deploy("MyProxy", {
from: deployer,
proxy: {
implementationName: "MyContract_V2_Implementation",
},
contract: "MyContractV2", // Different contract for implementation
});
// Skip ABI conflict checking for custom proxies
await deployments.deploy("FlexibleProxy", {
from: deployer,
proxy: {
checkABIConflict: false,
checkProxyAdmin: false,
proxyContract: "CustomMinimalProxy",
},
});
// Reuse existing implementation
await deployments.deploy("SharedImplementationProxy", {
from: deployer,
proxy: true,
contract: "SharedContract", // Will reuse if already deployed
skipIfAlreadyDeployed: true,
});interface ProxyOptionsBase {
owner?: Address;
upgradeIndex?: number;
proxyContract?: string | ArtifactData;
proxyArgs?: any[];
viaAdminContract?:
| string
| {
name: string;
artifact?: string | ArtifactData;
};
implementationName?: string;
checkABIConflict?: boolean;
checkProxyAdmin?: boolean;
upgradeFunction?: {
methodName: string;
upgradeArgs: any[];
};
}
type ProxyOptions =
| (ProxyOptionsBase & {
methodName?: string;
})
| (ProxyOptionsBase & {
execute?:
| {
methodName: string;
args: any[];
}
| {
init: {
methodName: string;
args: any[];
};
onUpgrade?: {
methodName: string;
args: any[];
};
};
});
interface ArtifactData {
abi: ABI;
bytecode: string;
deployedBytecode?: string;
metadata?: string;
methodIdentifiers?: any;
storageLayout?: any;
userdoc?: any;
devdoc?: any;
gasEstimates?: any;
}
type Address = string;
type ABI = any[];