Skip to main content

Overview

A Safe Harbor agreement defines the terms under which whitehats can attack your contracts. This guide covers all configuration options.

Create the Agreement

Use AgreementFactory to create your agreement:
AgreementDetails memory details = AgreementDetails({
    protocolName: "My Protocol",
    contactDetails: contacts,
    chains: chains,
    bountyTerms: bountyTerms,
    agreementURI: "ipfs://QmYourAgreementHash"
});

bytes32 salt = keccak256("my-protocol-v1");
address agreement = agreementFactory.create(details, owner, salt);

Configure Contact Details

Provide emergency contacts for whitehats:
Contact[] memory contacts = new Contact[](2);

contacts[0] = Contact({
    name: "Security Lead",
    contact: "[email protected]"
});

contacts[1] = Contact({
    name: "Emergency Telegram",
    contact: "@myprotocol_security"
});

Define Chain Scope

Specify which contracts on which chains are covered:
Account[] memory accounts = new Account[](2);

accounts[0] = Account({
    accountAddress: "0x1234...MyVault",
    childContractScope: ChildContractScope.All
});

accounts[1] = Account({
    accountAddress: "0x5678...MyStrategy",
    childContractScope: ChildContractScope.None
});

Chain[] memory chains = new Chain[](1);
chains[0] = Chain({
    caip2ChainId: "eip155:325",  // BattleChain
    assetRecoveryAddress: "0xYourRecoveryMultisig",
    accounts: accounts
});

Child Contract Scope Options

ValueMeaning
NoneOnly the listed contract
ExistingOnlyContract + children created before agreement
AllContract + all children (past and future)
FutureOnlyContract + children created after agreement

Configure Bounty Terms

BountyTerms memory bountyTerms = BountyTerms({
    bountyPercentage: 10,              // 10% of recovered funds
    bountyCapUsd: 5_000_000,           // Max $5M per whitehat
    retainable: true,                  // Whitehat keeps bounty from recovered
    identity: IdentityRequirements.Anonymous,
    diligenceRequirements: "",         // Only for Named identity
    aggregateBountyCapUsd: 0           // 0 = no aggregate cap
});

Retainable vs Return-All

  • Retainable (true): Whitehat keeps bounty from recovered funds, sends rest to recovery
  • Return-All (false): Whitehat sends all funds to recovery, protocol pays bounty separately

Identity Options

LevelRequirement
AnonymousNo identity verification
PseudonymousConsistent pseudonym required
NamedLegal name verification (specify process in diligenceRequirements)

Extend Commitment Window

Agreements must commit to terms for at least 7 days:
// Extend to 30 days
agreement.extendCommitmentWindow(block.timestamp + 30 days);
During the commitment window, you cannot make unfavorable changes to whitehats.

Adopt the Agreement

Link your protocol to the agreement:
safeHarborRegistry.adoptSafeHarbor(agreement);

Modify an Existing Agreement

You can update agreements, with restrictions during commitment:
// Always allowed
agreement.setProtocolName("New Name");
agreement.setContactDetails(newContacts);
agreement.addAccounts("eip155:325", newAccounts);

// Only favorable changes during commitment
agreement.setBountyTerms(betterTerms);

// Blocked during commitment
agreement.removeChains(chainIds);        // Reverts
agreement.removeAccounts(chainId, addrs); // Reverts

How to Request Attack Mode

Next: Submit your contracts for attack mode