How to Find Attackable Contracts
Query the AttackRegistry to discover contracts you can legally attack
Overview
The AttackRegistry tracks which contracts are in attack mode. This guide shows how to find and verify targets.
You can browse agreement statuses and contracts under scope on the BattleChain explorer or the Approvals dashboard — filter to Under Attack to see what's currently live, with bounty terms shown per request — or enumerate them programmatically with the API below.
List Attackable Contracts (HTTP API)
The block explorer exposes a JSON API that lists agreements by state — the fastest way for a script or AI agent to enumerate live targets, with no wallet, RPC, or Foundry FFI required.
# Every contract currently open to attack (paginated)
curl "https://block-explorer-api.testnet.battlechain.com/battlechain/agreements?state=UNDER_ATTACK"
Valid state values for finding targets are UNDER_ATTACK (open now) and PROMOTION_REQUESTED (still attackable, on a countdown to production). Paginate with page and limit.
The response is { items, meta }, where meta holds pagination (totalItems, totalPages, currentPage) and each item describes one agreement:
| Field | Meaning |
|---|---|
agreementAddress | The Safe Harbor agreement |
protocolName / owner | Who deployed it |
state | UNDER_ATTACK or PROMOTION_REQUESTED |
coveredContracts | Addresses in scope — the contracts you may attack |
coveredAccounts[].childContractScope | Whether child contracts are also in scope |
bountyPercentage / bountyCapUsd / aggregateBountyCapUsd | What you can earn (Bounty Terms) |
retainable | Whether you keep recovered funds or return them (Execute an Attack) |
contactDetails | How to reach the protocol |
valuePricedUsd / valueBand | Estimated value at risk |
commitmentDeadline | When the protocol's commitment window ends |
Look up a single agreement, find the agreement covering a contract, or read a contract's state directly:
# One agreement's full details
curl "https://block-explorer-api.testnet.battlechain.com/battlechain/agreement/<AGREEMENT_ADDRESS>"
# Which agreement (if any) covers a given contract
curl "https://block-explorer-api.testnet.battlechain.com/battlechain/agreement/by-contract/<CONTRACT_ADDRESS>"
# A contract's current security state
curl "https://block-explorer-api.testnet.battlechain.com/battlechain/contract-state/<CONTRACT_ADDRESS>"
The by-contract endpoint is what battlechain-lib's isAttackable calls under the hood. It returns { "hasCoverage": bool, "agreements": [{ "state": ... }] } — a contract is attackable when hasCoverage is true and any agreement's state is UNDER_ATTACK or PROMOTION_REQUESTED.
For mainnet, use the https://block-explorer-api.mainnet.battlechain.com host instead.
Point your AI agent straight at this API — e.g. "Fetch the UNDER_ATTACK agreements from the BattleChain explorer API and summarize the highest-bounty ones with their covered contracts."
Check a Specific Contract (in Solidity)
Inside a Foundry script, the easiest way to check whether a contract is attackable is with isAttackable from battlechain-lib. It queries the block explorer API and handles the full scope hierarchy — including child contracts spawned by in-scope factories.
Standalone usage (lightweight, no deploy/agreement overhead):
import { BCQuery } from "battlechain-lib/BCQuery.sol";
contract CheckTarget is BCQuery {
function run() external {
bool attackable = isAttackable(targetAddress);
if (attackable) {
// Contract is covered by an agreement in UNDER_ATTACK or PROMOTION_REQUESTED state
}
}
}
If you're already using BCScript for the full deploy + agreement flow, isAttackable is available directly — BCScript inherits BCQuery.
Run with the --ffi flag since the function uses vm.ffi to call the explorer API:
forge script script/CheckTarget.s.sol --ffi --rpc-url https://testnet.battlechain.com
isAttackable requires --ffi and curl on your PATH. For on-chain-only contexts where vm.ffi isn't available, you can use attackRegistry.isTopLevelContractUnderAttack(address) — but this only checks top-level contracts and will miss child contracts spawned by in-scope factories.
Monitor for New Targets
Watch for AgreementStateChanged events:
event AgreementStateChanged(address indexed agreementAddress, ContractState newState);
// newState = 3 (UNDER_ATTACK) - newly attackable
// newState = 4 (PROMOTION_REQUESTED) - still attackable, 3-day countdown
// newState = 5 (PRODUCTION) - no longer attackable
// newState = 6 (CORRUPTED) - no longer attackable
Get Agreement Details
// Get agreement for a contract
address agreementAddr = attackRegistry.getAgreementForContract(contractAddress);
// Get all contracts in scope
IAgreement agreement = IAgreement(agreementAddr);
address[] memory contracts = agreement.getBattleChainScopeAddresses();
// Get bounty terms
BountyTerms memory terms = agreement.getBountyTerms();
Verify Agreement Validity
Always verify before attacking:
// Check agreement was created by official factory
bool isValid = safeHarborRegistry.isAgreementValid(agreementAddress);
// Verify contract is in scope
bool inScope = agreement.isContractInScope(targetContract);
// Double-check state
IAttackRegistry.ContractState state = attackRegistry.getAgreementState(agreementAddress);
require(
state == ContractState.UNDER_ATTACK || state == ContractState.PROMOTION_REQUESTED,
"Not attackable"
);
Check Time Remaining
For contracts in PROMOTION_REQUESTED:
IAttackRegistry.AgreementInfo memory info = attackRegistry.getAgreementInfo(agreementAddress);
if (info.promotionRequestedTimestamp > 0) {
uint256 productionAt = info.promotionRequestedTimestamp + 3 days;
uint256 timeLeft = productionAt - block.timestamp;
// Attack must complete before productionAt
}
Red Flags
Be cautious of:
- Suspiciously high bounties
- Very new agreements (less community vetting)
- Missing contact details
- Contracts identical to mainnet protocols
How to Execute an Attack
Next: Execute your attack properly