Overview
Beacon can register agent identities on-chain using the ERC-7527 standard. This creates a verifiable, immutable link between your repository and an Ethereum wallet address, establishing ownership and provenance for AI agent capabilities.
What is ERC-7527?
ERC-7527 is an Ethereum standard for App-specific NFTs with dynamic metadata . Beacon uses it to:
Mint unique identity NFTs for repositories
Link wallet addresses to AGENTS.md files
Create verifiable on-chain records of agent capabilities
Enable payment and authentication flows tied to agent identities
Architecture
Beacon’s on-chain identity system consists of:
BeaconApp : ERC-721 NFT contract (stores agent identities)
BeaconAgency : Minting controller with bonding curve pricing
Beacon CLI : Rust client that registers identities
Smart Contract Implementation
BeaconApp Contract
From BeaconIdentity.sol:28-62:
contract BeaconApp is ERC721Enumerable , Ownable {
address public agency;
uint256 public immutable maxSupply;
constructor ( string memory name , string memory symbol , uint256 _maxSupply )
ERC721 (name, symbol)
Ownable (msg.sender)
{
maxSupply = _maxSupply;
}
modifier onlyAgency () {
require ( msg.sender == agency, "Only agency can mint" );
_ ;
}
function setAgency ( address _agency ) external onlyOwner {
require (agency == address ( 0 ), "Agency already set" );
agency = _agency;
}
function mint ( address to , bytes calldata data ) external onlyAgency returns ( uint256 ) {
uint256 tokenId = abi . decode (data, ( uint256 ));
require (tokenId < maxSupply, "Max supply reached" );
_safeMint (to, tokenId);
return tokenId;
}
}
Key features :
ERC-721 compliance : Standard NFT functionality
Max supply cap : Prevents unlimited minting
Agency-only minting : Only the agency contract can create identities
Token ID encoding : Repository URL stored in mint data
BeaconAgency Contract
From BeaconIdentity.sol:64-115:
contract BeaconAgency is IERC7527Agency {
BeaconApp public immutable app;
address public immutable currency; // address(0) for native ETH
uint256 public immutable basePremium;
uint256 public immutable priceStep;
event Wrap ( address indexed to , uint256 indexed tokenId , uint256 premium );
function getWrapOracle ( uint256 ) public view override returns ( Asset memory ) {
uint256 supply = app. totalSupply ();
// Linear bonding curve: Price = base + (supply * step)
uint256 premium = basePremium + (supply * priceStep);
return Asset (currency, premium, 0 );
}
function wrap ( address to , bytes calldata data ) external payable override returns ( uint256 ) {
Asset memory asset = getWrapOracle ( 0 );
require ( msg .value >= asset.premium, "Insufficient ETH sent" );
uint256 tokenId = app. totalSupply ();
bytes memory mintData = abi . encode (tokenId);
app. mint (to, mintData);
emit Wrap (to, tokenId, asset.premium);
return tokenId;
}
}
Key features :
Bonding curve pricing : Cost increases linearly with supply
ETH payments : Pay in native ETH (not USDC)
Automatic token ID : Uses current supply as next ID
Event emission : Tracks all registrations on-chain
Registering an Identity
Prerequisites
Have a Git repository
Your repository must have a .git/config file with a remote URL: git remote -v
# origin https://github.com/user/repo.git (fetch)
Generate AGENTS.md first
Run beacon generate to create the AGENTS.md file: beacon generate ./my-project
Have ETH on Base
You need ETH on Base network to pay for minting. Get some from: Cost varies based on bonding curve: basePremium + (supply * priceStep) wei
Using the CLI
# Register identity (will prompt for or generate wallet)
beacon register ./my-project
# Use existing wallet
export AGENT_PRIVATE_KEY = "0x1234abcd..."
beacon register ./my-project
# Use custom agency contract
beacon register ./my-project --agency 0xCustomAgencyAddress
# Register on different chain (future)
beacon register ./my-project --chain ethereum
Registration Flow
From src/identity.rs:23-66:
pub async fn register_agent_identity (
repo_path : & str ,
_chain : & str ,
agency_address : Option < & str >
) -> Result <()> {
let agency_addr_str = agency_address . unwrap_or ( CANONICAL_AGENCY );
let agency_addr = agency_addr_str . parse :: < Address >() ? ;
let provider_url = std :: env :: var ( "BASE_RPC_URL" )
. unwrap_or_else ( | _ | BASE_RPC . to_string ());
let provider = Provider :: < Http > :: try_from ( provider_url ) ? ;
// Load or generate wallet
let wallet = match std :: env :: var ( "AGENT_PRIVATE_KEY" ) {
Ok ( key ) => key . parse :: < LocalWallet >() ? ,
Err ( _ ) => {
let new_wallet = LocalWallet :: new ( & mut rand :: thread_rng ());
println! ( " 🔑 Generated new agent wallet: {:?}" , new_wallet . address ());
println! ( " ⚠️ SAVE THIS PRIVATE KEY: 0x{}" , hex :: encode ( new_wallet . signer () . to_bytes ()));
new_wallet
}
};
let client = Arc :: new ( SignerMiddleware :: new (
provider . clone (),
wallet . with_chain_id ( 8453 u64 ) // Base chain ID
));
let address = client . address ();
let agency = IERC7527Agency :: new ( agency_addr , client . clone ());
// Get repository URL from .git/config
let repo_url = get_repo_url ( repo_path )
. context ( "Could not find repository URL in .git/config" ) ? ;
let data = Bytes :: from ( repo_url . as_bytes () . to_vec ());
// Check pricing
let ( premium , fee ) = agency . get_wrap_oracle ( data . clone ()) . call () . await ? ;
let total = premium + fee ;
let balance = provider . get_balance ( address , None ) . await ? ;
if balance < total {
anyhow :: bail! ( "Insufficient balance on Base. Need {} wei, have {} wei" , total , balance );
}
println! ( " 💸 Registering identity via ERC-7527 (Cost: {} wei)..." , total );
// Submit transaction
let tx = agency . wrap ( address , data ) . value ( total );
let pending_tx = tx . send () . await ? ;
let receipt = pending_tx . await ?. context ( "Transaction failed" ) ? ;
println! ( " ✅ Registration confirmed: {:?}" , receipt . transaction_hash);
// Update AGENTS.md with wallet address
update_agents_md ( repo_path , & format! ( "{:?}" , address )) ? ;
Ok (())
}
Steps :
Parse agency contract address
Connect to Base RPC
Load wallet from AGENT_PRIVATE_KEY or generate new one
Extract repository URL from .git/config
Query bonding curve price via getWrapOracle()
Check wallet has sufficient ETH balance
Call wrap(address, repoURL) with ETH value
Wait for transaction confirmation
Update AGENTS.md with registered address
Wallet Management
Generate New Wallet
Use Existing Wallet
If AGENT_PRIVATE_KEY is not set, Beacon generates a new wallet: let new_wallet = LocalWallet :: new ( & mut rand :: thread_rng ());
println! ( " 🔑 Generated new agent wallet: {:?}" , new_wallet . address ());
println! ( " ⚠️ SAVE THIS PRIVATE KEY: 0x{}" , hex :: encode ( new_wallet . signer () . to_bytes ()));
Save the private key immediately! Beacon does not store it. If you lose it, you lose access to the registered identity.
Export your private key before running: export AGENT_PRIVATE_KEY = "0xYourPrivateKeyHere"
beacon register ./my-project
Use a dedicated wallet for agent identities, separate from your main funds.
AGENTS.md Integration
After registration, Beacon updates your AGENTS.md with the wallet address:
# AGENTS.md — my-project
> My awesome AI-powered project
**Agent Identity:** `0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb`
**Version:** 1.0.0
From src/identity.rs:82-114:
fn update_agents_md ( repo_path : & str , address : & str ) -> Result <()> {
let path = Path :: new ( repo_path ) . join ( "AGENTS.md" );
if ! path . exists () {
anyhow :: bail! ( "AGENTS.md not found. Run 'generate' first." );
}
let content = fs :: read_to_string ( & path ) ? ;
let mut lines : Vec < String > = content . lines () . map ( | s | s . to_string ()) . collect ();
// Try to update existing identity line
let mut found = false ;
for line in & mut lines {
if line . contains ( "Agent Identity:" ) {
* line = format! ( "**Agent Identity:** `{}`" , address );
found = true ;
break ;
}
}
// If not found, insert after description block
if ! found {
let mut insert_pos = 0 ;
for ( i , line ) in lines . iter () . enumerate () {
if line . starts_with ( "> " ) {
insert_pos = i + 1 ;
}
}
lines . insert ( insert_pos , "" . to_string ());
lines . insert ( insert_pos + 1 , format! ( "**Agent Identity:** `{}`" , address ));
}
fs :: write ( path , lines . join ( " \n " )) ? ;
println! ( " 📝 Updated AGENTS.md with Agent Identity." );
Ok (())
}
Use Cases
1. Verifiable Ownership
Prove you control a repository by signing messages with the registered wallet:
// Verify signer owns the agent identity
function verifyOwnership ( uint256 tokenId , bytes memory signature , bytes32 message ) public view returns ( bool ) {
address owner = beaconApp. ownerOf (tokenId);
address signer = recoverSigner (message, signature);
return owner == signer;
}
2. Payment & Monetization
Accept payments for agent API calls:
function payAgent ( uint256 agentTokenId ) external payable {
address agentOwner = beaconApp. ownerOf (agentTokenId);
payable (agentOwner). transfer ( msg .value);
}
3. Reputation Systems
Build on-chain reputation tied to agent identities:
mapping ( uint256 => uint256 ) public agentReputationScores;
function rateAgent ( uint256 tokenId , uint256 score ) external {
require ( hasUsedAgent ( msg.sender , tokenId), "Must use agent first" );
agentReputationScores[tokenId] += score;
}
4. Composability
Other contracts can query agent capabilities:
function getAgentCapabilities ( uint256 tokenId ) external view returns ( string memory ) {
address owner = beaconApp. ownerOf (tokenId);
// Fetch AGENTS.md from IPFS or similar
return fetchCapabilities (owner);
}
Pricing Model
The BeaconAgency uses a linear bonding curve :
premium = basePremium + (totalSupply * priceStep)
Example pricing (assuming basePremium = 0.001 ETH, priceStep = 0.0001 ETH):
Identity # Supply Price (ETH) Price (USD at $3000/ETH) 1st 0 0.001 $3.00 10th 9 0.0019 $5.70 100th 99 0.0109 $32.70 1000th 999 0.1009 $302.70
Pricing parameters are set at deployment and cannot be changed. Check the agency contract for current values.
Configuration
Environment Variables
Private key for the wallet that will own the agent identity. Format : 0x + 64 hex charactersExample : 0x1234abcd...
BASE_RPC_URL
string
default: "https://mainnet.base.org"
RPC endpoint for Base network. Alternatives :
Alchemy: https://base-mainnet.g.alchemy.com/v2/YOUR_KEY
Infura: https://base-mainnet.infura.io/v3/YOUR_KEY
Contract Addresses
Canonical Agency
ERC-7527 Interface
const CANONICAL_AGENCY : & str = "0xd8b934580fcE35a11B58C6D73aDeE468a2833fa8" ;
Querying Identities
Get Token Owner
# Using cast (Foundry)
cast call 0xAppAddress "ownerOf(uint256)" 42 --rpc-url https://mainnet.base.org
Get Total Supply
cast call 0xAppAddress "totalSupply()" --rpc-url https://mainnet.base.org
Get Current Price
cast call 0xAgencyAddress "getWrapOracle(uint256)" 0 --rpc-url https://mainnet.base.org
Security Considerations
Never share your private key. Anyone with access to AGENT_PRIVATE_KEY can control your agent identity and transfer the NFT.
Use hardware wallets (Ledger, Trezor) for high-value agent identities. Beacon supports ethers-rs signer interfaces.
Best Practices
Separate wallets : Use dedicated wallets for agent identities, not your main funds
Backup keys : Store private keys securely (password manager, hardware wallet)
Test on testnets : Try Base Sepolia before mainnet registration
Monitor transactions : Watch for unauthorized transfers of your identity NFT
Smart contract audits : Verify BeaconAgency and BeaconApp contracts before registering
Troubleshooting
”Could not find repository URL in .git/config”
Problem : Not running in a Git repository.
Solution :
cd /path/to/your/git/repo
git remote -v # Verify remote exists
beacon register .
“Insufficient balance on Base”
Problem : Wallet doesn’t have enough ETH to pay minting cost.
Solution :
# Check current price
cast call 0xAgencyAddress "getWrapOracle(uint256)" 0 --rpc-url https://mainnet.base.org
# Bridge ETH to Base
# https://bridge.base.org
“AGENTS.md not found. Run ‘generate’ first.”
Problem : Trying to register before generating AGENTS.md.
Solution :
beacon generate ./my-project
beacon register ./my-project
“Transaction failed”
Common causes:
Gas price too low (rare on Base)
Contract reverted (check Basescan for reason)
Network congestion
Solution : Check transaction on Basescan for revert reason.
Future Enhancements
Multi-Chain Support Register identities on Ethereum, Arbitrum, Optimism
IPFS Integration Store AGENTS.md content on IPFS, reference via NFT metadata
Dynamic Metadata Update agent capabilities on-chain as repository evolves
Identity Delegation Allow team members to act on behalf of registered agents
Next Steps
Beacon Cloud Use Beacon with USDC payments instead of API keys
API Reference Integrate on-chain identities into your API workflows