Bracket Smart Contracts
Bracket and BracketX
Below we describe our smart contracts and functions. We are currently using:
- Solidity version 0.8.12
- OpenZeppelin version 4.7.3
We have implemented the OpenZeppelin upgradeable contract pattern and will be using a multi-sig Gnosis wallet in production in conjunction with the OpenZeppelin Defender deployment and administration platform
We use many OpenZeppelin contracts, including:
- AddressUpgradeable
- CountersUpgradeable
- StringsUpgadable
- MathUpgradeable
- SafeMathUpgradeable
- OwnableUpgradeable
- PausableUpgradeable
- ReentrancyGuardUpgradeable
- Initializable
- ERC20
- ERC721Upgradeable / ERC721EnumerableUpgradeable
Important implementation details:
- Contract Structure Versions:
- Bracket Labs controls the version of the Contract Structure in terms of the 16 offers. The offers are 7 long and 7 short offers in terms of bracket width (e.g. 10%), the max payoff (e.g. 5x, 10x), and term (e.g. 15 days), the last 2 offer "slots" 14 and 15 are used for the de-pegging assets
- We associate a version with the Funder's offers to ensure we only show current offers, and ensure the offers match the current Contract Structure
- We associate the Contract Structure version with policies to ensure we use the correct information when processing the policy, even if that version no longer matches the current Contract Structure version
- Assets are defined by their Chainlink proxy addresses, which is different in Test than Production
- We implement the best practice of using the Solidity Withdrawal Pattern where funds are not instantly disbursed, but put into a withdraw-able state and then a second function call to execute the withdrawal. A brief explanation why this is preferable can be found here: https://docs.soliditylang.org/en/develop/common-patterns.html
The Bracket specific contracts include:
- StructLibx - This is a non-data library function where many of the data structures used are defined
- Config - this has almost no logic is just stores configuration information and data which includes:
- The Vers data, which stores the Contract Structure version information
- The key functions are:
- currentVer - returns the Contract Structure version being used
- getVer - returns the Contract Structure information for a given version number
- addVer - [OWNER ONLY] - Add a new Contract Structure version that becomes current
- Functions to modify the various contract constants such as:
- Minimum / Maximum available (that can be added)
- Minimum premium (buy amount)
- The two commission percentages for Buys (premium) and when a claim is made
- BXNFT is an extension of the OpenZeppelin ERC721 NFT contract and stores the contract information. When a contract is purchased, an NFT is issued that becomes the contract number. The owner of this NFT is the owner of the contract with all privileges granted. In addition to the standard NFT functionality of transferring ownership, we added the functions:
- onlyBrkt / setBrkt - Many functions can ONLY be performed by the BracketX contract. Most important of them is the MINT function
- mintBXNFTinsured - [BRKT ONLY] This creates a new NFT which can ONLY be minted by the BUY function of BracketX. The metadata used for the image shown is dynamically set based on either NFT number, time, or another data element passed in
- setPolicy - [BRKT ONLY] This associates policy information with an NFT, which can ONLY be called by the BUY function of BracketX
- setClaimed - [BRKT ONLY] this update the status of a Policy to be considered claimed. This is important so the Policy is not double paid. This can ONLY be called by the CLAIM function of BracketX
- Pricing is where the Chainlink oracle pricing is managed. If on Arbitrum we check if the L2's sequencer is running, so we don't return stale prices.
- getLatestPrice - Returns the Chainlink price as returned by their latestRoundData function
- getClaimPrice - for production it returns getClaimPrice
- addDepeg [Owner ONLY]- set an asset as a de-peg asset
- remDepeg [Owner ONLY] - set an asset to NOT being a de-peg asset
- setGracePeriod - this grace period used to determine if L2's sequencer is still running
- setClaimDelta - For TESTING ONLY to simulate market conditions where the contract is in-the-money. This is not implemented in production
- For de-pegging assets we limit price to 1.0
- For stETH, we return stETH / ETH as the price of stETH
- Offersx - This contract stores the Funder's Offers. The key functions are:
- getOffer - return an offer for a Funder for a given asset
- setOffer [Funder ONLY]- This sets the current offer. The front end sends what it believes is the current version and if it doesn't match the contract's current Contract Structure version, the offer is rejected. A Funder is not able to update an old offer as they are instantly expired once a new Contract Structure version is issued
- BracketX - this is the key contract where the majority of the logic is stored. As such, it is the last contract initialized since it must know the address of all the other contracts
- The data stored by this contract includes:
- FunderAvail - the available amount in USDC of Funders, which is available, but not yet committed to a Policy. These funds can take out at any time by the funder but reducing their Available and moving the funds into a withdrawable state
- pendingETHwithdrawals - the amount of ETH available to be withdrawn by an address holder, which could be a funder or buyer
- pendingUSDCwithdrawals - the amount of USDC available to be withdrawn by an address holder, which could be a funder or buyer
- The key functions include:
- getWithdrawableAmts - returns the amount of ETH and USDC available for immediate withdrawal
- addAvailable [Funder ONLY] - allows a funder to add USDC to their available amount
- reduceAvailable [Funder ONLY] - allows a funder to remove their USDC from being available. The reduced USDC funds move into a withdrawable state
- buyPolicy [payable function] - this is how a policy is bought from an offer. The funder and offerId must be given, so this is a 1:1 match. The algorithm of determining which offer gets executed is down in the frontend, so this function just executes that match. It is a payable function as it takes native coin for policy payment. Notice the version number is checked. A max and min asset price is sent to check for slippage. If the price moved out of bounds between the time the user committed on the front end until it executed on the blockchain, then the transaction is reverted. This function calls the iBNFT contract to issue an NFT as the policy id. Notice two Buy Events are emitted by this function. This is because buy Events are limited to only 10 variables
- claim - This is called to make a claim. A claim can only be made early by the owner of the policy's NFT. After expiration, it can be claimed by anyone, and usually it will be the BracketX's automation system. The claim function sets the policy's state to claimed and moves the buyers claim amount in USDC to a withdrawable state. For Funders, any unclaimed USDC moves back to being Available. Any commissions are moved into bracket's commission account
- withdraw - This disburses any USDC's or ETH's funds in a withdrawable state into to their recipient's wallet
- FakeUSDC - This is a TESTING ONLY contract we use as a substitute for the real USDC contract. This contract will NOT be used in production