skills/bap578-deploy/SKILL.md
Use this skill when deploying, verifying, configuring, or validating BAP-578 Non-Fungible Agent contracts on BNB Chain testnet or mainnet, from environment setup through post-deployment checks.
npx skillsauth add chatandbuild/skills-repo BAP-578 DeploymentInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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.
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.
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.
Deployment trust comes from:
node --version)cd non-fungible-agents-BAP-578
npm install
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"
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,
},
},
};
npm run compile
# or
npx hardhat compile
Expected output: compiled contracts in artifacts/ directory with no errors.
npm test
All tests must pass before deployment. Do not deploy with failing tests.
npm run deploy:testnet
# or
npx hardhat run scripts/deploy.js --network bscTestnet
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);
});
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:
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());
Run this checklist after every deployment:
owner() returns deployer address (or intended owner)treasuryAddress() returns correct treasurypaused() returns falsegetTotalSupply() returns 0getAgentState() returns correct datagetAgentMetadata() returns correct dataasync 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;
}
npx hardhat run scripts/deploy.js --network bsc
Chain ID: 97
Currency: tBNB
Block time: ~3 seconds
Chain ID: 56
Currency: BNB
Block time: ~3 seconds
"Insufficient funds"
"Nonce too high" / "Nonce too low"
"Contract size exceeds limit"
"Verification failed"
"Already initialized"
Local Hardhat → BSC Testnet → BSC Mainnet
│ │ │
Development Staging Production
(instant) (verify) (permanent)
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.
BSC Testnet mirrors mainnet behavior with free tBNB:
--network bscTestnet.Only after testnet is fully verified:
--network bsc.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.
Before deploying to any network, verify:
npm test).env is in .gitignore_authorizeUpgrade is onlyOwner_disableInitializers() in constructorselfdestruct in the contractAfter deployment, consider transferring ownership to a more secure setup:
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());
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();
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.
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
When asked for deployment help, respond with:
bap578bap578-testingbap578-security-auditdocumentation
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.
development
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
devops
Deploy applications and infrastructure to Cloudflare using Workers, Pages, and related platform services. Use when the user asks to deploy, host, publish, or set up a project on Cloudflare.
tools
Use this skill when designing and building durable command-line tools from API docs, OpenAPI specs, SDKs, curl examples, admin tools, web apps, or local scripts, especially when the CLI should expose composable commands, stable JSON output, auth/config handling, install-on-PATH behavior, and a companion skill.