BAP-578 Deployment
Use this skill to deploy, verify, configure, and validate BAP-578 Non-Fungible Agents contracts on BNB Chain testnet and mainnet. This covers the complete deployment lifecycle from environment setup through post-deployment verification.
When to use this skill
- Setting up the development environment for the first time.
- Deploying the contract to BSC Testnet for testing.
- Deploying to BSC Mainnet for production.
- Verifying the contract source on BscScan.
- Running post-deployment checks to confirm correct configuration.
- Configuring treasury, free mints, and other admin settings after deployment.
- Troubleshooting deployment failures.
The Four Identity Questions (Deployment View)
1) Who are you?
The deployment process is the bridge between code and a live contract. It creates the proxy and implementation on-chain, sets initial configuration, and establishes the contract's identity (name, symbol, owner, treasury). After deployment, the contract exists at a permanent proxy address that users will interact with forever.
2) What do you remember?
Deployment records include the proxy address, implementation address, deployer address, initialization parameters, deployment block number, and transaction hashes. These are critical for verification, upgrade planning, and incident response.
3) What can you do?
- Deploy the UUPS proxy and implementation contract
- Initialize with name, symbol, and treasury address
- Verify source code on BscScan
- Configure post-deployment settings (free mints, treasury, pause state)
- Run automated verification checks
- Generate deployment reports
4) How can I trust it?
Deployment trust comes from:
- Source code verification on BscScan (anyone can read the code)
- Post-deployment checks confirming correct initialization
- Transparent deployment scripts (reproducible)
- Test deployment on testnet before mainnet
Prerequisites
Required tools
- Node.js v18+ (check with
node --version) - npm or yarn
- Git
- A funded wallet (BSC Testnet faucet for testnet, real BNB for mainnet)
Required accounts
- BscScan API key (for contract verification)
- RPC endpoint (public or dedicated provider)
Environment Setup
Step 1: Clone and install
cd non-fungible-agents-BAP-578
npm install
Step 2: Configure environment
cp .env.example .env
Edit .env with your values:
# Deployer wallet private key (NEVER commit this)
DEPLOYER_PRIVATE_KEY=0xYourPrivateKeyHere
# RPC URLs
BSC_TESTNET_RPC_URL=your_bsc_testnet_rpc_url
BSC_MAINNET_RPC_URL=your_bsc_mainnet_rpc_url
# BscScan API key (for verification)
BSCSCAN_API_KEY=your_bscscan_api_key
# Treasury address (receives mint fees)
TREASURY_ADDRESS=0xYourTreasuryAddress
# Contract name and symbol
TOKEN_NAME="Non-Fungible Agents"
TOKEN_SYMBOL="NFA"
Step 3: Verify Hardhat config
The hardhat.config.js should include BSC network configurations:
require("@nomicfoundation/hardhat-toolbox");
require("@openzeppelin/hardhat-upgrades");
require("dotenv").config();
module.exports = {
solidity: {
version: "0.8.20",
settings: {
optimizer: { enabled: true, runs: 200 },
},
},
networks: {
bscTestnet: {
url: process.env.BSC_TESTNET_RPC_URL,
accounts: [process.env.DEPLOYER_PRIVATE_KEY],
chainId: 97,
},
bsc: {
url: process.env.BSC_MAINNET_RPC_URL,
accounts: [process.env.DEPLOYER_PRIVATE_KEY],
chainId: 56,
},
},
etherscan: {
apiKey: {
bscTestnet: process.env.BSCSCAN_API_KEY,
bsc: process.env.BSCSCAN_API_KEY,
},
},
};
Deployment Steps
Step 1: Compile
npm run compile
# or
npx hardhat compile
Expected output: compiled contracts in artifacts/ directory with no errors.
Step 2: Run tests
npm test
All tests must pass before deployment. Do not deploy with failing tests.
Step 3: Deploy to testnet
npm run deploy:testnet
# or
npx hardhat run scripts/deploy.js --network bscTestnet
Deployment script
const { ethers, upgrades } = require("hardhat");
async function main() {
const [deployer] = await ethers.getSigners();
console.log("Deploying with:", deployer.address);
console.log("Balance:", ethers.formatEther(
await ethers.provider.getBalance(deployer.address)
), "BNB");
const treasuryAddress = process.env.TREASURY_ADDRESS;
const tokenName = process.env.TOKEN_NAME || "Non-Fungible Agents";
const tokenSymbol = process.env.TOKEN_SYMBOL || "NFA";
console.log("\nDeploying NonFungibleAgents...");
console.log(" Name:", tokenName);
console.log(" Symbol:", tokenSymbol);
console.log(" Treasury:", treasuryAddress);
const NFA = await ethers.getContractFactory("NonFungibleAgents");
const nfa = await upgrades.deployProxy(
NFA,
[tokenName, tokenSymbol, treasuryAddress],
{
kind: "uups",
initializer: "initialize",
}
);
await nfa.waitForDeployment();
const proxyAddress = await nfa.getAddress();
const implAddress = await upgrades.erc1967.getImplementationAddress(proxyAddress);
console.log("\n✅ Deployment successful!");
console.log(" Proxy address:", proxyAddress);
console.log(" Implementation:", implAddress);
console.log(" Owner:", await nfa.owner());
console.log(" Treasury:", await nfa.treasuryAddress());
console.log(" Paused:", await nfa.paused());
// Save deployment info
const fs = require("fs");
const deploymentInfo = {
network: (await ethers.provider.getNetwork()).name,
chainId: (await ethers.provider.getNetwork()).chainId.toString(),
proxy: proxyAddress,
implementation: implAddress,
deployer: deployer.address,
treasury: treasuryAddress,
blockNumber: await ethers.provider.getBlockNumber(),
timestamp: new Date().toISOString(),
};
fs.writeFileSync(
`deployment-${deploymentInfo.network}.json`,
JSON.stringify(deploymentInfo, null, 2)
);
console.log("\nDeployment info saved to deployment-" + deploymentInfo.network + ".json");
}
main().catch((error) => {
console.error(error);
process.exit(1);
});
Step 4: Verify on BscScan
npx hardhat verify --network bscTestnet IMPLEMENTATION_ADDRESS
For proxies, you may also need to verify the proxy:
npx hardhat verify --network bscTestnet PROXY_ADDRESS
If automatic verification fails, use the BscScan web UI:
- Go to the contract address on BscScan.
- Click "Contract" → "Verify and Publish".
- Select compiler version, optimization settings.
- Paste flattened source code.
Step 5: Post-deployment configuration
After deployment, configure optional settings:
const nfa = await ethers.getContractAt("NonFungibleAgents", PROXY_ADDRESS);
// Set free mints per user (if different from default)
await nfa.setFreeMintsPerUser(3);
// Grant additional free mints to specific addresses
await nfa.grantAdditionalFreeMints("0xPartnerAddress", 10);
// Verify configuration
console.log("Free mints per user:", await nfa.freeMintsPerUser());
console.log("Treasury:", await nfa.treasuryAddress());
console.log("Owner:", await nfa.owner());
console.log("Paused:", await nfa.paused());
Post-Deployment Verification Checklist
Run this checklist after every deployment:
- [ ] Proxy address recorded and saved
- [ ] Implementation address recorded and saved
- [ ]
owner()returns deployer address (or intended owner) - [ ]
treasuryAddress()returns correct treasury - [ ]
paused()returnsfalse - [ ]
getTotalSupply()returns0 - [ ] Test free mint succeeds
- [ ] Test paid mint succeeds (after exhausting free mints)
- [ ] Test
getAgentState()returns correct data - [ ] Test
getAgentMetadata()returns correct data - [ ] Source code verified on BscScan
- [ ] Deployment info saved to file
Automated verification script
async function verifyDeployment(proxyAddress) {
const nfa = await ethers.getContractAt("NonFungibleAgents", proxyAddress);
const checks = [];
// Basic state checks
const owner = await nfa.owner();
checks.push({ name: "Owner set", pass: owner !== ethers.ZeroAddress, value: owner });
const treasury = await nfa.treasuryAddress();
checks.push({ name: "Treasury set", pass: treasury !== ethers.ZeroAddress, value: treasury });
const paused = await nfa.paused();
checks.push({ name: "Not paused", pass: !paused, value: paused });
const supply = await nfa.getTotalSupply();
checks.push({ name: "Supply is 0", pass: supply === 0n, value: supply.toString() });
// Test mint
try {
const [deployer] = await ethers.getSigners();
const tx = await nfa.createAgent(
deployer.address,
ethers.ZeroAddress,
"ipfs://test-deployment",
{
persona: '{"name":"DeployTest"}',
experience: "Deployment verification agent",
voiceHash: "",
animationURI: "",
vaultURI: "",
vaultHash: ethers.ZeroHash,
}
);
await tx.wait();
checks.push({ name: "Test mint", pass: true, value: "Token #1 minted" });
const state = await nfa.getAgentState(1);
checks.push({ name: "State readable", pass: state.active === true, value: "Active" });
const meta = await nfa.getAgentMetadata(1);
checks.push({ name: "Metadata readable", pass: meta.experience.length > 0, value: meta.experience });
} catch (e) {
checks.push({ name: "Test mint", pass: false, value: e.message });
}
// Print results
console.log("\n=== Deployment Verification ===");
for (const check of checks) {
const icon = check.pass ? "✅" : "❌";
console.log(`${icon} ${check.name}: ${check.value}`);
}
const allPassed = checks.every((c) => c.pass);
console.log(allPassed ? "\n✅ All checks passed!" : "\n❌ Some checks failed!");
return allPassed;
}
Mainnet Deployment
Additional precautions for mainnet
- Double-check treasury address. Fees go here permanently. Use a multisig.
- Verify deployer balance. Ensure enough BNB for deployment gas.
- Review all environment variables. Wrong RPC URL = wrong chain.
- Deploy during low-gas periods if possible.
- Have the team review the deployment script before execution.
- Record the deployment transaction hash for audit trail.
Mainnet command
npx hardhat run scripts/deploy.js --network bsc
Post-mainnet steps
- Verify source on BscScan immediately.
- Run the automated verification script.
- Transfer ownership to a multisig (if not already the deployer).
- Announce deployment with proxy address.
- Update frontend config with the new contract address.
Network Configuration Reference
BSC Testnet
Chain ID: 97
Currency: tBNB
Block time: ~3 seconds
BSC Mainnet
Chain ID: 56
Currency: BNB
Block time: ~3 seconds
Common Deployment Errors
"Insufficient funds"
- Deployer wallet doesn't have enough BNB for gas.
- Solution: Fund the wallet via faucet (testnet) or transfer (mainnet).
"Nonce too high" / "Nonce too low"
- Transaction count mismatch.
- Solution: Reset nonce in Hardhat config or wait for pending transactions.
"Contract size exceeds limit"
- Contract bytecode too large (24KB limit).
- Solution: Enable optimizer, increase runs, or split into libraries.
"Verification failed"
- Compiler version or settings mismatch.
- Solution: Ensure exact same compiler version and optimizer settings.
"Already initialized"
- Proxy was already initialized.
- Solution: This means deploy succeeded but script re-ran. Check proxy address.
Multi-Environment Deployment Strategy
Environment progression
Local Hardhat → BSC Testnet → BSC Mainnet
│ │ │
Development Staging Production
(instant) (verify) (permanent)
Local development deployment
For rapid iteration, deploy to Hardhat's local network:
npx hardhat node
# In another terminal:
npx hardhat run scripts/deploy.js --network localhost
Benefits: instant confirmation, free gas, full control. Resets on restart.
Testnet deployment
BSC Testnet mirrors mainnet behavior with free tBNB:
- Get tBNB from the BSC Testnet faucet.
- Deploy with
--network bscTestnet. - Verify on testnet BscScan.
- Run full integration tests against the deployed contract.
- Test with a frontend connected to testnet.
Mainnet deployment
Only after testnet is fully verified:
- Double-check all environment variables.
- Ensure treasury is a multisig.
- Deploy with
--network bsc. - Verify immediately.
- Run post-deployment verification script.
- Announce deployment.
Gas Cost Estimates
Estimated gas costs for BAP-578 operations on BSC:
| Operation | Estimated Gas | ~Cost at 5 gwei | |-----------|--------------|-----------------| | Deploy proxy | ~500,000 | ~0.0025 BNB | | Deploy implementation | ~3,500,000 | ~0.0175 BNB | | Initialize | ~200,000 | ~0.001 BNB | | createAgent (free) | ~350,000 | ~0.00175 BNB | | createAgent (paid) | ~380,000 | ~0.0019 BNB | | fundAgent | ~50,000 | ~0.00025 BNB | | withdrawFromAgent | ~60,000 | ~0.0003 BNB | | updateAgentMetadata | ~150,000 | ~0.00075 BNB | | setLogicAddress | ~50,000 | ~0.00025 BNB | | setAgentStatus | ~30,000 | ~0.00015 BNB |
Total deployment cost: ~0.02-0.03 BNB (including first test mint)
Note: Actual gas costs vary with network congestion and contract size. Always check current gas prices before mainnet deployment.
Security Checklist Before Deployment
Before deploying to any network, verify:
- [ ] All tests pass (
npm test) - [ ] No compiler warnings
- [ ] Optimizer enabled (200 runs recommended)
- [ ] Deployer private key is not committed to git
- [ ]
.envis in.gitignore - [ ] Treasury address is a multisig (for mainnet)
- [ ] Contract source will be verified on block explorer
- [ ]
_authorizeUpgradeisonlyOwner - [ ]
_disableInitializers()in constructor - [ ] ReentrancyGuard is used on fund operations
- [ ] No
selfdestructin the contract - [ ] Solidity version is ≥ 0.8.20
Ownership Transfer
After deployment, consider transferring ownership to a more secure setup:
Transfer to multisig
const nfa = await ethers.getContractAt("NonFungibleAgents", PROXY_ADDRESS);
// Transfer ownership to a Safe multisig
const MULTISIG_ADDRESS = "0xYourSafeAddress";
await nfa.transferOwnership(MULTISIG_ADDRESS);
// Verify
console.log("New owner:", await nfa.owner());
Two-step ownership transfer (safer)
If the contract supports OpenZeppelin's Ownable2Step:
// Step 1: Current owner proposes new owner
await nfa.transferOwnership(MULTISIG_ADDRESS);
// Step 2: New owner accepts (from multisig)
await nfa.connect(multisigSigner).acceptOwnership();
CI/CD Integration
GitHub Actions deployment workflow
name: Deploy to Testnet
on:
push:
branches: [main]
paths: [contracts/**]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- run: npm install
- run: npm test
- run: npx hardhat run scripts/deploy.js --network bscTestnet
env:
DEPLOYER_PRIVATE_KEY: ${{ secrets.DEPLOYER_KEY }}
BSC_TESTNET_RPC_URL: ${{ secrets.BSC_TESTNET_RPC }}
BSCSCAN_API_KEY: ${{ secrets.BSCSCAN_KEY }}
TREASURY_ADDRESS: ${{ vars.TREASURY_ADDRESS }}
Important: Never put mainnet deployment in CI/CD without manual approval gates.
Deployment Report Template
BAP-578 Deployment Report
═════════════════════════
Date: 2026-03-06
Network: BSC Testnet (97)
Deployer: 0xDeployerAddress
Addresses:
Proxy: 0xProxyAddress
Implementation: 0xImplAddress
Treasury: 0xTreasuryAddress
Configuration:
Token name: Non-Fungible Agents
Token symbol: NFA
Free mints: 3 per user
Mint fee: 0.01 BNB
Paused: false
Verification:
BscScan: ✅ Verified
Test mint: ✅ Passed
State reads: ✅ Working
Events: ✅ Emitting
Gas costs:
Deploy proxy: ~500,000 gas
Deploy impl: ~3,500,000 gas
Initialize: ~200,000 gas
Total cost: ~0.025 BNB
Output Format
When asked for deployment help, respond with:
- Prerequisites (tools, accounts, funding)
- Commands to run (step by step)
- Expected outputs (addresses, confirmations)
- Verification steps (BscScan, test mint, state checks)
- Post-deploy configuration (treasury, free mints, etc.)
- Troubleshooting for common errors
Related Skills
bap578bap578-testingbap578-security-audit
Scan to join WeChat group