Skillshub solidity-deploy
[AUTO-INVOKE] MUST be invoked BEFORE deploying contracts or writing deployment scripts (*.s.sol). Covers pre-flight checks, forge script commands, post-deployment validation, and verification. Trigger: any task involving forge script, contract deployment, or block explorer verification.
install
source · Clone the upstream repo
git clone https://github.com/ComeOnOliver/skillshub
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/0xlayerghost/solidity-agent-kit/solidity-deploy" ~/.claude/skills/comeonoliver-skillshub-solidity-deploy && rm -rf "$T"
manifest:
skills/0xlayerghost/solidity-agent-kit/solidity-deploy/SKILL.mdsource content
Deployment Workflow
Language Rule
- Always respond in the same language the user is using. If the user asks in Chinese, respond in Chinese. If in English, respond in English.
Pre-deployment Checklist (all must pass)
| Step | Command / Action |
|---|---|
| Format code | |
| Run all tests | — zero failures required |
| Check gas report | — review critical functions |
| Verify config | Manually check parameters |
| Dry-run | (no ) |
| Check balance | — sufficient gas? |
| Gas limit set | Deployment command must include |
Deployment Decision Rules
| Situation | Rule |
|---|---|
| Default deployment | No — contracts are not verified on block explorers by default |
| User requests verification | Add and to the command |
| Post-deploy verification | Use as a separate step |
| Multi-chain deploy | Separate scripts per chain, never batch multiple chains in one script |
| Proxy deployment | Deploy implementation first, then proxy — verify both separately |
| Upgradeable contract | Use OpenZeppelin Upgrades Plugin (see below) — never hand-roll proxy deployment |
Post-deployment Operations (all required)
- Update addresses in
andconfig/*.jsondeployments/latest.env - Test critical functions:
to verify on-chain state is correctcast call - Record changes in
docs/CHANGELOG.md - Submit PR with deployment transaction hash link
- If verification needed, run
separatelyforge verify-contract
Key Security Rule
- Never pass private keys directly in commands. Use Foundry Keystore (
) to manage keys securely.cast wallet import - Never include
in templates. The user must explicitly add it when ready to deploy.--broadcast
Command Templates
# Dry-run (simulation only, no on-chain execution) forge script script/Deploy.s.sol:DeployScript \ --rpc-url <RPC_URL> \ --gas-limit 5000000 \ -vvvv # When user is ready to deploy, instruct them to add: # --account <KEYSTORE_NAME> --broadcast # Verify existing contract separately forge verify-contract <ADDRESS> <CONTRACT> \ --chain-id <CHAIN_ID> \ --etherscan-api-key <API_KEY> \ --constructor-args $(cast abi-encode "constructor(address)" <ARG>) # Quick on-chain read test after deployment cast call <CONTRACT_ADDRESS> "functionName()" --rpc-url <RPC_URL>
Upgradeable Contract Deployment (OpenZeppelin Upgrades Plugin)
For any upgradeable contract (UUPS, Transparent, Beacon), use the OpenZeppelin Foundry Upgrades Plugin instead of hand-rolling proxy deployment scripts.
Why Use the Plugin
| Manual Approach | With Plugin |
|---|---|
| ~30 lines: deploy impl → deploy proxy → encode initializer → wire up | 1 line: |
| ~20 lines: deploy new impl → validate storage → upgrade proxy | 1 line: |
| Storage layout compatibility: check by eye | Auto-checked, incompatible layouts are rejected |
Forgot ? No warning | Auto-validated |
Installation
forge install OpenZeppelin/openzeppelin-foundry-upgrades forge install OpenZeppelin/openzeppelin-contracts-upgradeable
Add to
remappings.txt:
@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/ @openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/
Deploy Script Template (UUPS)
// script/Deploy.s.sol import {Script, console} from "forge-std/Script.sol"; import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; import {MyContract} from "../src/MyContract.sol"; contract DeployScript is Script { function run() public { vm.startBroadcast(); // One line: deploys impl + proxy + calls initialize address proxy = Upgrades.deployUUPSProxy( "MyContract.sol", abi.encodeCall(MyContract.initialize, (msg.sender)) ); console.log("Proxy:", proxy); console.log("Impl:", Upgrades.getImplementationAddress(proxy)); vm.stopBroadcast(); } }
Upgrade Script Template
// script/Upgrade.s.sol import {Script, console} from "forge-std/Script.sol"; import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; contract UpgradeScript is Script { function run() public { address proxy = vm.envAddress("PROXY_ADDRESS"); vm.startBroadcast(); // One line: validates storage layout + deploys new impl + upgrades proxy Upgrades.upgradeProxy(proxy, "MyContractV2.sol", ""); console.log("Upgraded. New impl:", Upgrades.getImplementationAddress(proxy)); vm.stopBroadcast(); } }
Add
@custom:oz-upgrades-from MyContract annotation to V2 contract for automatic reference:
/// @custom:oz-upgrades-from MyContract contract MyContractV2 is Initializable, OwnableUpgradeable, UUPSUpgradeable { // ... }
Commands
# Deploy proxy (dry-run) — --ffi is required for storage layout checks forge script script/Deploy.s.sol --rpc-url <RPC_URL> --ffi -vvvv # Deploy proxy (broadcast) forge script script/Deploy.s.sol --rpc-url <RPC_URL> --ffi --account <KEYSTORE_NAME> --broadcast # Upgrade proxy (dry-run) PROXY_ADDRESS=0x... forge script script/Upgrade.s.sol --rpc-url <RPC_URL> --ffi -vvvv # Upgrade proxy (broadcast) PROXY_ADDRESS=0x... forge script script/Upgrade.s.sol --rpc-url <RPC_URL> --ffi --account <KEYSTORE_NAME> --broadcast # Validate upgrade without deploying (useful for CI) # Use Upgrades.validateUpgrade("MyContractV2.sol", opts) in a test
Plugin API Quick Reference
| Function | Purpose |
|---|---|
| Deploy UUPS proxy + impl + initialize |
| Deploy Transparent proxy + impl + initialize |
| Validate + deploy new impl + upgrade |
| Validate only, no deploy (for CI/tests) |
| Get current implementation address |
| Validate + deploy new impl, return address (for multisig) |
Key Rules
- Always use
flag — the plugin needs it for storage layout validation--ffi - Always add
for upgrades — must match proxy owner, otherwise--sender <ADDRESS>OwnableUnauthorizedAccount - Use
in scripts,Upgrades
only in tests —UnsafeUpgrades
skips all safety checksUnsafeUpgrades - Keep V1 source code in project when upgrading — plugin needs it for storage comparison. Or use
annotation@custom:oz-upgrades-from - Never hand-roll proxy deployment when this plugin is available — the storage layout check alone prevents critical bugs