Bug Bounty Management
The OtaconBountyRegistry smart contract manages bug bounties, staking of collectibles, and distribution of rewards in the Otacon ecosystem. This documentation provides a comprehensive breakdown of all contract functions, including explanations, TypeScript examples (using ethers.js), and simulations to showcase their usage.
1. Contract Initialization
The contract is initialized with important parameters and sets up immutable variables.
constructor() Ownable(msg.sender) ReentrancyGuard() {
otaconToken = IERC20(0xFooBar);
swapRouter = ISwapRouter(0xFooBar);
WETH9 = 0xFooBar;
transferOwnership(msg.sender);
}Parameters:
otaconToken: The Otacon ERC20 token.swapRouter: Uniswap V3 router for token swaps.WETH9: Wrapped ETH token address.
Ownership: The deployer becomes the contract owner.
2. Modifiers
onlyBountyOwner(uint256 bountyId): Restricts function access to the owner of the bounty.
3. Collectible Management Functions
a. setProofCollectibleContract
setProofCollectibleContractDescription: Sets the contract address for the ProofCollectible.
function setProofCollectibleContract(address tokenAddress) external onlyOwnerParameters:
tokenAddress: Address of the ProofCollectible contract.
TypeScript Example:
// Assuming ethers.js is set up with provider and signer (owner)
const otaconRegistry = new ethers.Contract(registryAddress, registryABI, signer);
// Set the ProofCollectible contract address
async function setProofCollectibleContract(tokenAddress: string) {
const tx = await otaconRegistry.setProofCollectibleContract(tokenAddress);
await tx.wait();
console.log('ProofCollectible contract address set.');
}Simulation:
Owner calls
setProofCollectibleContractwith the ProofCollectible contract address.The contract updates the
proofCollectiblestate variable.
b. setSnippetCollectibleContract
setSnippetCollectibleContractDescription: Sets the contract address for the SnippetCollectible (ERC721).
function setSnippetCollectibleContract(address tokenAddress) external onlyOwnerTypeScript Example:
async function setSnippetCollectibleContract(tokenAddress: string) {
const tx = await otaconRegistry.setSnippetCollectibleContract(tokenAddress);
await tx.wait();
console.log('SnippetCollectible contract address set.');
}Simulation:
Owner calls
setSnippetCollectibleContractwith the SnippetCollectible contract address.The contract updates the
snippetCollectiblestate variable.
c. setBountyPassCollectibleContract
setBountyPassCollectibleContractDescription: Sets the contract address for the BountyPassCollectible (ERC1155).
function setBountyPassCollectibleContract(address tokenAddress) external onlyOwnerTypeScript Example:
async function setBountyPassCollectibleContract(tokenAddress: string) {
const tx = await otaconRegistry.setBountyPassCollectibleContract(tokenAddress);
await tx.wait();
console.log('BountyPassCollectible contract address set.');
}Simulation:
Owner calls
setBountyPassCollectibleContractwith the BountyPassCollectible contract address.The contract updates the
bountyPassCollectiblestate variable.
d. setMultiplierCollectible
setMultiplierCollectibleDescription: Sets the multiplier collectible information for a specific tier.
function setMultiplierCollectible(
MultiplierTier tier,
address tokenAddress,
uint256 multiplierValue,
uint256 collectibleType
) external onlyOwnerParameters:
tier: Enum value representing the multiplier tier (S, A, B, C, D, F).tokenAddress: Address of the MultiplierCollectible contract.multiplierValue: The value of the multiplier (e.g.,2e18for 2x).collectibleType: Token ID representing the tier in the ERC1155 contract.
TypeScript Example:
async function setMultiplierCollectible(
tier: number, // Corresponding to the MultiplierTier enum
tokenAddress: string,
multiplierValue: ethers.BigNumber,
collectibleType: number
) {
const tx = await otaconRegistry.setMultiplierCollectible(
tier,
tokenAddress,
multiplierValue,
collectibleType
);
await tx.wait();
console.log(`Multiplier collectible for tier ${tier} set.`);
}Simulation:
Owner sets up multiplier collectibles for each tier with appropriate values.
The contract stores these configurations for use in staking and reward calculations.
4. Validator Management Functions
a. addValidator
addValidatorDescription: Adds a validator to a bounty.
function addValidator(uint256 bountyId, address validator) publicAccess Control: Can be called by the bounty owner or the contract owner.
TypeScript Example:
async function addValidator(bountyId: number, validatorAddress: string) {
const tx = await otaconRegistry.addValidator(bountyId, validatorAddress);
await tx.wait();
console.log(`Validator ${validatorAddress} added to bounty ${bountyId}.`);
}Simulation:
Bounty Owner or Contract Owner adds a validator to a specific bounty.
The validator is added to the
validatorsmapping andvalidatorListarray.
b. removeValidator
removeValidatorDescription: Removes a validator from a bounty.
function removeValidator(uint256 bountyId, address validator) publicTypeScript Example:
async function removeValidator(bountyId: number, validatorAddress: string) {
const tx = await otaconRegistry.removeValidator(bountyId, validatorAddress);
await tx.wait();
console.log(`Validator ${validatorAddress} removed from bounty ${bountyId}.`);
}Simulation:
Bounty Owner or Contract Owner removes a validator from a specific bounty.
The validator is removed from the
validatorsmapping andvalidatorListarray.
5. Bounty Management Functions
a. startBounty
startBountyDescription: Starts a new bounty program with specified parameters and handles the creation fee.
function startBounty(
BountyCreationParams calldata params,
bool useOtaconToken,
bool useETH,
uint256 bountyPassTokenId
) external payable nonReentrantParameters:
params: Struct containing bounty details.targetContract: Address of the contract under scrutiny.rewards: Array of rewards per severity level.rewardToken: ERC20 token used for rewards.requireSnippet: Whether a code snippet is required.targetNetwork: Network identifier.targetEnvironment: Environment identifier.validators: Initial list of validators.
useOtaconToken: Whether to pay the fee with Otacon tokens.useETH: Whether to pay the fee with ETH.bountyPassTokenId: Token ID of the Bounty Pass collectible (if not using Otacon or ETH).
TypeScript Example:
async function startBounty(
params: {
targetContract: string;
rewards: ethers.BigNumber[];
rewardToken: string;
requireSnippet: boolean;
targetNetwork: string;
targetEnvironment: string;
validators: string[];
},
useOtaconToken: boolean,
useETH: boolean,
bountyPassTokenId: number
) {
let tx;
if (useOtaconToken) {
// Approve Otacon tokens
const otaconToken = new ethers.Contract(otaconTokenAddress, erc20ABI, signer);
await otaconToken.approve(registryAddress, otaconFee);
tx = await otaconRegistry.startBounty(params, useOtaconToken, useETH, bountyPassTokenId);
} else if (useETH) {
tx = await otaconRegistry.startBounty(params, useOtaconToken, useETH, bountyPassTokenId, {
value: ethFee,
});
} else {
// Ensure you have the Bounty Pass collectible
tx = await otaconRegistry.startBounty(params, useOtaconToken, useETH, bountyPassTokenId);
}
await tx.wait();
console.log('Bounty started.');
}Simulation:
Scenario 1: Using Otacon Token
User approves
otaconFeeamount of Otacon tokens to the contract.Calls
startBountywithuseOtaconToken = true.The contract deducts the
otaconFeefrom the user's balance.Bounty is created and stored in the contract.
Scenario 2: Using ETH
User sends a transaction with
value = ethFee.Calls
startBountywithuseETH = true.The contract uses half of the ETH for an Otacon token buyback via Uniswap V3.
Bounty is created and stored in the contract.
Scenario 3: Using Bounty Pass Collectible
User must own a Bounty Pass collectible.
Calls
startBountywithbountyPassTokenIdof the collectible they own.The contract transfers and burns the Bounty Pass collectible.
Bounty is created and stored in the contract.
b. stopBounty
stopBountyDescription: Stops an active bounty.
function stopBounty(uint256 bountyId) external onlyBountyOwner(bountyId)TypeScript Example:
async function stopBounty(bountyId: number) {
const tx = await otaconRegistry.stopBounty(bountyId);
await tx.wait();
console.log(`Bounty ${bountyId} stopped.`);
}Simulation:
Bounty Owner calls
stopBounty.The bounty's
isActivestate is set tofalse.
c. setBountyReward
setBountyRewardDescription: Sets the reward for a specific severity level in a bounty.
function setBountyReward(uint256 bountyId, uint8 severity, uint256 reward) external onlyBountyOwner(bountyId)TypeScript Example:
async function setBountyReward(bountyId: number, severity: number, reward: ethers.BigNumber) {
const tx = await otaconRegistry.setBountyReward(bountyId, severity, reward);
await tx.wait();
console.log(`Reward for severity ${severity} set to ${reward.toString()} tokens.`);
}Simulation:
Bounty Owner updates the reward amounts for specific severity levels.
d. setProtocolFeePercentage
setProtocolFeePercentageDescription: Sets the protocol fee percentage.
function setProtocolFeePercentage(uint8 _protocolFeePercentage) external onlyOwnerTypeScript Example:
async function setProtocolFeePercentage(percentage: number) {
const tx = await otaconRegistry.setProtocolFeePercentage(percentage);
await tx.wait();
console.log(`Protocol fee percentage set to ${percentage}%.`);
}Simulation:
Contract Owner adjusts the protocol fee percentage used in reward calculations.
6. Staking Functions
a. stakeProofCollectible
stakeProofCollectibleDescription: Stake a ProofCollectible (ERC721) to a bounty.
function stakeProofCollectible(uint256 bountyId, uint256 collectibleId) external nonReentrantTypeScript Example:
async function stakeProofCollectible(bountyId: number, collectibleId: number) {
// Approve the transfer of the ProofCollectible to the registry
const proofCollectible = new ethers.Contract(proofCollectibleAddress, erc721ABI, signer);
await proofCollectible.approve(registryAddress, collectibleId);
const tx = await otaconRegistry.stakeProofCollectible(bountyId, collectibleId);
await tx.wait();
console.log(`ProofCollectible ${collectibleId} staked to bounty ${bountyId}.`);
}Simulation:
User approves the ProofCollectible for transfer.
Calls
stakeProofCollectiblewith the bounty ID and collectible ID.The contract transfers the ProofCollectible from the user to itself.
Records the staked collectible in
stakedProofs.
b. unstakeProofCollectible
unstakeProofCollectibleDescription: Unstake a ProofCollectible from a bounty.
function unstakeProofCollectible(uint256 bountyId, uint256 collectibleId) external nonReentrantTypeScript Example:
async function unstakeProofCollectible(bountyId: number, collectibleId: number) {
const tx = await otaconRegistry.unstakeProofCollectible(bountyId, collectibleId);
await tx.wait();
console.log(`ProofCollectible ${collectibleId} unstaked from bounty ${bountyId}.`);
}Simulation:
User calls
unstakeProofCollectible.The contract transfers the ProofCollectible back to the user.
Removes the collectible from
stakedProofs.
c. stakeSnippetCollectible
stakeSnippetCollectibleDescription: Stake a SnippetCollectible (ERC721) to a bounty.
function stakeSnippetCollectible(uint256 bountyId, uint256 collectibleId) external nonReentrantTypeScript Example:
async function stakeSnippetCollectible(bountyId: number, collectibleId: number) {
// Approve the transfer of the SnippetCollectible to the registry
const snippetCollectible = new ethers.Contract(snippetCollectibleAddress, erc721ABI, signer);
await snippetCollectible.approve(registryAddress, collectibleId);
const tx = await otaconRegistry.stakeSnippetCollectible(bountyId, collectibleId);
await tx.wait();
console.log(`SnippetCollectible ${collectibleId} staked to bounty ${bountyId}.`);
}Simulation:
User approves and stakes a SnippetCollectible to the bounty.
The contract records the staked snippet in
stakedSnippets.
d. unstakeSnippetCollectible
unstakeSnippetCollectibleDescription: Unstake a SnippetCollectible from a bounty.
function unstakeSnippetCollectible(uint256 bountyId, uint256 collectibleId) external nonReentrantTypeScript Example:
async function unstakeSnippetCollectible(bountyId: number, collectibleId: number) {
const tx = await otaconRegistry.unstakeSnippetCollectible(bountyId, collectibleId);
await tx.wait();
console.log(`SnippetCollectible ${collectibleId} unstaked from bounty ${bountyId}.`);
}Simulation:
User calls
unstakeSnippetCollectible.The contract transfers the SnippetCollectible back to the user.
Removes the collectible from
stakedSnippets.
e. stakeMultiplierCollectible
stakeMultiplierCollectibleDescription: Stake a MultiplierCollectible (ERC1155) to a bounty.
function stakeMultiplierCollectible(
uint256 bountyId,
MultiplierTier multiplierTier,
uint256 amount
) external nonReentrantTypeScript Example:
async function stakeMultiplierCollectible(bountyId: number, tier: number, amount: number) {
// Approve the transfer of the MultiplierCollectible to the registry
const multiplierCollectible = new ethers.Contract(multiplierCollectibleAddress, erc1155ABI, signer);
await multiplierCollectible.setApprovalForAll(registryAddress, true);
const tx = await otaconRegistry.stakeMultiplierCollectible(bountyId, tier, amount);
await tx.wait();
console.log(`Staked ${amount} multiplier(s) of tier ${tier} to bounty ${bountyId}.`);
}Simulation:
User approves the MultiplierCollectible for transfer.
Calls
stakeMultiplierCollectiblewith the bounty ID, tier, and amount.The contract transfers the specified amount of multiplier collectibles from the user to itself.
Records the staked multipliers in
stakedMultipliers.
f. unstakeMultiplierCollectible
unstakeMultiplierCollectibleDescription: Unstake MultiplierCollectibles from a bounty.
function unstakeMultiplierCollectible(uint256 bountyId, uint256 amount) external nonReentrantTypeScript Example:
async function unstakeMultiplierCollectible(bountyId: number, amount: number) {
const tx = await otaconRegistry.unstakeMultiplierCollectible(bountyId, amount);
await tx.wait();
console.log(`Unstaked ${amount} multiplier(s) from bounty ${bountyId}.`);
}Simulation:
User calls
unstakeMultiplierCollectible.The contract transfers the specified amount of multiplier collectibles back to the user.
Updates the
stakedMultipliersandtotalMultiplierValueStaked.
7. Validation and Reward Functions
a. validateProof
validateProofDescription: Validates a proof collectible, rewarding the bounty hunter and accumulating protocol fees.
function validateProof(uint256 bountyId, uint256 proofCollectibleId, uint8 severity) external nonReentrantTypeScript Example:
async function validateProof(bountyId: number, proofCollectibleId: number, severity: number) {
const tx = await otaconRegistry.validateProof(bountyId, proofCollectibleId, severity);
await tx.wait();
console.log(`ProofCollectible ${proofCollectibleId} validated for bounty ${bountyId}.`);
}Simulation:
Validator calls
validateProof.The contract checks that the validator is authorized and the proof is staked.
Calculates the reward amounts:
Total Reward: From
bounty.rewards[severity].Protocol Fee:
rewardAmount * protocolFeePercentage / 100.Hunter Reward:
rewardAmount - protocolFee.
Transfers the hunter reward to the bounty hunter.
Updates
totalProtocolBountyRewardandtotalUnclaimedProtocolRewards.Burns the proof collectible.
b. claimBountyShare
claimBountyShareDescription: Allows a staker to claim their share of the protocol bounty rewards.
function claimBountyShare(uint256 bountyId) external nonReentrantTypeScript Example:
async function claimBountyShare(bountyId: number) {
const tx = await otaconRegistry.claimBountyShare(bountyId);
await tx.wait();
console.log(`Bounty share claimed for bounty ${bountyId}.`);
}Simulation:
User must have staked a proof and a multiplier.
Calls
claimBountyShare.The contract calculates the user's share based on their staked multiplier.
Applies a maximum cap (cannot exceed half of the total protocol bounty reward).
Burns the staked multiplier collectibles.
Transfers the calculated share to the user.
c. claimProtocolBountyShare
claimProtocolBountyShareDescription: Allows the protocol owner to claim its share of the protocol bounty rewards.
function claimProtocolBountyShare(uint256 bountyId) external onlyOwner nonReentrantTypeScript Example:
async function claimProtocolBountyShare(bountyId: number) {
const tx = await otaconRegistry.claimProtocolBountyShare(bountyId);
await tx.wait();
console.log(`Protocol bounty share claimed for bounty ${bountyId}.`);
}Simulation:
Contract Owner calls
claimProtocolBountyShare.The contract calculates the remaining protocol share (cannot exceed half of the total protocol bounty reward).
Transfers the protocol share to the owner.
8. Administrative Functions
a. setFee
setFeeDescription: Sets the Otacon or ETH fee for bounty creation.
function setFee(string calldata feeType, uint256 fee) external onlyOwnerParameters:
feeType: Either"Otacon"or"ETH".fee: The new fee amount.
TypeScript Example:
async function setFee(feeType: string, fee: ethers.BigNumber) {
const tx = await otaconRegistry.setFee(feeType, fee);
await tx.wait();
console.log(`${feeType} fee set to ${fee.toString()}.`);
}Simulation:
Contract Owner adjusts the fee amounts for bounty creation.
b. claimRevenue
claimRevenueDescription: Withdraws accumulated bounty creation fees (Otacon tokens and ETH) to the owner.
function claimRevenue() external onlyOwnerTypeScript Example:
async function claimRevenue() {
const tx = await otaconRegistry.claimRevenue();
await tx.wait();
console.log('Revenue claimed.');
}Simulation:
Contract Owner calls
claimRevenue.The contract transfers all Otacon tokens and ETH held as fees to the owner.
c. getBounty
getBountyDescription: Returns all bounty information for a given bounty ID.
function getBounty(uint256 bountyId) external view returns (...)TypeScript Example:
async function getBounty(bountyId: number) {
const bounty = await otaconRegistry.getBounty(bountyId);
console.log('Bounty Details:', bounty);
}Simulation:
User calls
getBountyto retrieve bounty details.
9. ERC165 and Receiver Functions
supportsInterface: Indicates support for ERC1155Receiver and ERC721Receiver interfaces.
ERC1155Receiver Functions
onERC1155Received: Handles receipt of single ERC1155 token type.onERC1155BatchReceived: Handles receipt of multiple ERC1155 token types.
ERC721Receiver Function
onERC721Received: Handles receipt of ERC721 tokens.
Note: These functions are called automatically during token transfers to the contract and do not require manual interaction in TypeScript.
10. Fallback Functions
receive: Allows the contract to receive ETH.fallback: Fallback function to handle unexpected calls.
Last updated