Deploying Smart Contracts
This guide covers compiling and deploying Solidity contracts to AgentChain, with a complete worked example using a SimpleStorage contract.
AgentChain EVM Compatibility
AgentChain runs the Berlin EVM fork. This has important implications for contract development:
| Constraint | Detail |
|---|---|
| Solidity version | Must be ≤0.8.19 |
| No PUSH0 opcode | PUSH0 was introduced in Shanghai (Solidity 0.8.20+). Contracts compiled with 0.8.20 or later will fail to deploy unless you target an older EVM version. |
| No EIP-1559 | Use legacy gasPrice transactions only (transaction type: 0) |
| Chain ID | 7331 |
| Block gas limit | 10M – 60M (dynamic) |
Critical: If you compile with Solidity >= 0.8.20, the compiler emits PUSH0 by default. AgentChain does not support this opcode and your deployment transaction will revert. Always use Solidity ≤0.8.19 or explicitly set
evmVersion: "london"in your compiler settings.
Example Contract: SimpleStorage
We will use this contract throughout this guide and the next one (Interacting with Contracts).
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract SimpleStorage {
uint256 private value;
event ValueChanged(uint256 newValue);
function set(uint256 _value) external {
value = _value;
emit ValueChanged(_value);
}
function get() external view returns (uint256) {
return value;
}
}Compiling the Contract
You need two outputs from compilation: the ABI (interface definition) and the bytecode (deployable binary).
Using solc (command line)
# Install solc 0.8.19
pip install py-solc-x # Python
# or
npm install -g solc@0.8.19 # Node.js
# Compile to get ABI and bytecode
solcjs --abi --bin SimpleStorage.solUsing Hardhat
In hardhat.config.js, make sure to set the correct Solidity version and EVM target:
module.exports = {
solidity: {
version: "0.8.19",
settings: {
optimizer: { enabled: true, runs: 200 },
evmVersion: "london", // Berlin-compatible; avoids PUSH0
},
},
};Then compile:
npx hardhat compileThe ABI and bytecode will be in artifacts/contracts/SimpleStorage.sol/SimpleStorage.json.
Compilation output
For the examples below, we assume you have the ABI and bytecode available. Here is what they look like for the SimpleStorage contract:
# Python -- ABI and bytecode as variables
abi = [
{
"inputs": [{"internalType": "uint256", "name": "_value", "type": "uint256"}],
"name": "set",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function",
},
{
"inputs": [],
"name": "get",
"outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}],
"stateMutability": "view",
"type": "function",
},
{
"anonymous": False,
"inputs": [{"indexed": False, "internalType": "uint256", "name": "newValue", "type": "uint256"}],
"name": "ValueChanged",
"type": "event",
},
]
bytecode = "0x..." # the compiled bytecode hex string// JavaScript -- ABI and bytecode as variables
const abi = [
{
inputs: [{ internalType: "uint256", name: "_value", type: "uint256" }],
name: "set",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [],
name: "get",
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
stateMutability: "view",
type: "function",
},
{
anonymous: false,
inputs: [{ indexed: false, internalType: "uint256", name: "newValue", type: "uint256" }],
name: "ValueChanged",
type: "event",
},
];
const bytecode = "0x..."; // the compiled bytecode hex stringDeploying with Python (web3.py)
from web3 import Web3
from eth_account import Account
# --- Connect ---
w3 = Web3(Web3.HTTPProvider("http://localhost:8545"))
assert w3.is_connected() and w3.eth.chain_id == 7331
# --- Account ---
private_key = "0xYOUR_PRIVATE_KEY_HEX"
acct = Account.from_key(private_key)
# --- ABI and bytecode (from compilation) ---
abi = [...] # paste your ABI here
bytecode = "0x..." # paste your bytecode here
# --- Create the contract factory ---
SimpleStorage = w3.eth.contract(abi=abi, bytecode=bytecode)
# --- Build the deployment transaction ---
tx = SimpleStorage.constructor().build_transaction({
"chainId": 7331,
"from": acct.address,
"gas": 500000,
"gasPrice": w3.eth.gas_price,
"nonce": w3.eth.get_transaction_count(acct.address, "pending"),
})
# --- Sign and send ---
signed_tx = w3.eth.account.sign_transaction(tx, private_key)
tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction)
print(f"Deployment tx sent: {tx_hash.hex()}")
# --- Wait for receipt ---
receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120)
assert receipt["status"] == 1, "Deployment failed"
contract_address = receipt["contractAddress"]
print(f"Contract deployed at: {contract_address}")
print(f"Block: {receipt['blockNumber']}, Gas used: {receipt['gasUsed']}")Deploying with JavaScript (ethers.js v6)
import { ethers } from "ethers";
// --- Connect ---
const provider = new ethers.JsonRpcProvider("http://localhost:8545");
const network = await provider.getNetwork();
if (network.chainId !== 7331n) throw new Error("Wrong network");
// --- Account ---
const privateKey = "0xYOUR_PRIVATE_KEY_HEX";
const wallet = new ethers.Wallet(privateKey, provider);
// --- ABI and bytecode (from compilation) ---
const abi = [...]; // paste your ABI here
const bytecode = "0x..."; // paste your bytecode here
// --- Create the contract factory ---
const factory = new ethers.ContractFactory(abi, bytecode, wallet);
// --- Deploy (legacy transaction) ---
const contract = await factory.deploy({
gasLimit: 500000n,
gasPrice: (await provider.getFeeData()).gasPrice,
type: 0, // legacy transaction
});
console.log(`Deployment tx sent: ${contract.deploymentTransaction().hash}`);
// --- Wait for deployment to be mined ---
await contract.waitForDeployment();
const contractAddress = await contract.getAddress();
console.log(`Contract deployed at: ${contractAddress}`);
// Fetch receipt for gas details
const receipt = await contract.deploymentTransaction().wait();
console.log(`Block: ${receipt.blockNumber}, Gas used: ${receipt.gasUsed}`);Getting the Deployed Contract Address
The contract address is deterministically derived from the deployer's address and nonce. After mining, it is available in the transaction receipt.
Python (web3.py)
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
contract_address = receipt["contractAddress"]
print(f"Deployed at: {contract_address}")JavaScript (ethers.js v6)
const receipt = await contract.deploymentTransaction().wait();
const contractAddress = await contract.getAddress();
console.log(`Deployed at: ${contractAddress}`);Verifying Deployment
After deployment, verify that the contract code exists at the deployed address. An address with no code means the deployment failed or the address is wrong.
Python (web3.py)
from web3 import Web3
w3 = Web3(Web3.HTTPProvider("http://localhost:8545"))
contract_address = "0xYourDeployedContractAddress"
# Get the code at the address
code = w3.eth.get_code(contract_address)
if code == b"" or code == b"0x":
print("No contract code at this address -- deployment may have failed")
else:
print(f"Contract verified: {len(code)} bytes of code at {contract_address}")JavaScript (ethers.js v6)
import { ethers } from "ethers";
const provider = new ethers.JsonRpcProvider("http://localhost:8545");
const contractAddress = "0xYourDeployedContractAddress";
// Get the code at the address
const code = await provider.getCode(contractAddress);
if (code === "0x") {
console.log("No contract code at this address -- deployment may have failed");
} else {
console.log(`Contract verified: ${(code.length - 2) / 2} bytes of code at ${contractAddress}`);
}Complete Example: Compile and Deploy SimpleStorage
This end-to-end example uses py-solc-x to compile the contract in Python, then deploys it.
Python (web3.py) -- with solcx
from web3 import Web3
from eth_account import Account
from solcx import compile_source, install_solc
# Install and compile
install_solc("0.8.19")
compiled = compile_source(
"""
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract SimpleStorage {
uint256 private value;
event ValueChanged(uint256 newValue);
function set(uint256 _value) external {
value = _value;
emit ValueChanged(_value);
}
function get() external view returns (uint256) {
return value;
}
}
""",
output_values=["abi", "bin"],
solc_version="0.8.19",
evm_version="london",
)
# Extract ABI and bytecode
contract_id, contract_interface = compiled.popitem()
abi = contract_interface["abi"]
bytecode = contract_interface["bin"]
# Connect and deploy
w3 = Web3(Web3.HTTPProvider("http://localhost:8545"))
assert w3.is_connected() and w3.eth.chain_id == 7331
private_key = "0xYOUR_PRIVATE_KEY_HEX"
acct = Account.from_key(private_key)
SimpleStorage = w3.eth.contract(abi=abi, bytecode=bytecode)
tx = SimpleStorage.constructor().build_transaction({
"chainId": 7331,
"from": acct.address,
"gas": 500000,
"gasPrice": w3.eth.gas_price,
"nonce": w3.eth.get_transaction_count(acct.address, "pending"),
})
signed = w3.eth.account.sign_transaction(tx, private_key)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120)
assert receipt["status"] == 1, "Deployment failed"
print(f"SimpleStorage deployed at: {receipt['contractAddress']}")JavaScript (ethers.js v6) -- with Hardhat artifact
import { ethers } from "ethers";
import fs from "fs";
// Load the Hardhat compilation artifact
const artifact = JSON.parse(
fs.readFileSync("artifacts/contracts/SimpleStorage.sol/SimpleStorage.json", "utf-8")
);
// Connect
const provider = new ethers.JsonRpcProvider("http://localhost:8545");
const network = await provider.getNetwork();
if (network.chainId !== 7331n) throw new Error("Wrong network");
const wallet = new ethers.Wallet("0xYOUR_PRIVATE_KEY_HEX", provider);
// Deploy
const factory = new ethers.ContractFactory(artifact.abi, artifact.bytecode, wallet);
const contract = await factory.deploy({
gasLimit: 500000n,
gasPrice: (await provider.getFeeData()).gasPrice,
type: 0,
});
await contract.waitForDeployment();
const address = await contract.getAddress();
console.log(`SimpleStorage deployed at: ${address}`);
// Verify code exists
const code = await provider.getCode(address);
console.log(`Verified: ${(code.length - 2) / 2} bytes deployed`);Next Steps
Now that your contract is deployed, learn how to interact with it -- reading state, sending transactions, and listening for events.