Links

Sandboxed Oracle Environment

Only required for testing the dispute flow
Whenever the assertion is disputed on a production network this triggers DVM request to be resolved by UMA voters. This might be impractical to fully simulate this in a testing environment, hence, developers can use mocked oracle contract to resolve requests as they had been returned by DVM.
Interacting with Optimistic Oracle requires having whitelisted price identifier and bonding currency. In production networks these are approved by UMA token holders as part of governance voting. When testing, it might be easier for developers to use their own set of UMA ecosystem contracts where they have full control over such whitelisting process. Follow the written tutorial below or check out the video tutorial on Youtube.

Deploying sandboxed Oracle environment

A set of sandboxed UMA ecosystem contracts can be deployed using forge script from Optimistic Oracle V3 developers quick-start repository.
After cloning the repository make sure you have the latest Foundry version by running foundryup command (see more installation details on Foundry book). From the root of the repository install dependencies with:
forge install
All script parameters are controlled through environment variables that should be exported before running the script:
  • ETH_RPC_URL: URL of the RPC node to use for broadcasting deployment transactions on the desired test network.
  • MNEMONIC: Mnemonic of the account to use for deployment (derived account with 0 index will be used by default).
  • ETHERSCAN_API_KEY: API key for Etherscan, used for verifying deployed contracts.
  • DEFAULT_IDENTIFIER: Default identifier used by Optimistic Oracle V3 when resolving disputes. If not provided, this defaults to ASSERT_TRUTH identifier. The script will also whitelist this identifier.
  • DEFAULT_LIVENESS: Default liveness in seconds used by Optimistic Oracle V3 when settling assertions. If not provided, this defaults to 7200 seconds.
  • DEFAULT_CURRENCY: Default currency used by Optimistic Oracle V3 when bonding assertions and disputes. The script will also whitelist this currency. If not provided, the script would also deploy a mintable ERC20 token and use it as the default currency based on following parameters:
    • DEFAULT_CURRENCY_NAME: Name of the new token. If not provided, this defaults to Default Bond Token.
    • DEFAULT_CURRENCY_SYMBOL: Symbol of the new token. If not provided, this defaults to DBT.
    • DEFAULT_CURRENCY_DECIMALS: Number of decimals of the new token. If not provided, this defaults to 18.
  • MINIMUM_BOND: Minimum bond amount in Wei of default currency required by Optimistic Oracle V3 when accepting new assertions. If not provided, this defaults to 100e18 Wei.
Once all required environment variables are setup, deploy and verify the set of sandboxed UMA ecosystem contracts with:
forge script \
--broadcast \
--fork-url $ETH_RPC_URL \
--mnemonics "$MNEMONIC" \
--sender $(cast wallet address --mnemonic "$MNEMONIC") \
--verify \
script/OracleSandbox.s.sol
At the top of the script output the addresses of the deployed contracts should be logged:
  • Finder: Used to locate other UMA ecosystem contract addresses. Deployment script will have added new Store, AddressWhitelist, IdentifierWhitelist, MockOracleAncillary and OptimisticOracleV3 implementations.
  • Store: Stores final fees of bonding currencies. The script will have set the final fee of default currency to half of provided minimum bond amount since Optimistic Oracle V3 calculates minimum bond twice the value of this final fee.
  • AddressWhitelist: Stores approved currency addresses that can be used for bonding assertions and disputes. The script will have already whitelisted the default currency.
  • IdentifierWhitelist: Stores approved identifiers that can be used to resolve disputes at DVM. The script will have already whitelisted the default identifier.
  • MockOracleAncillary: Optimistic Oracle will use this contract to resolve disputes equivalent to DVM voting on mainnet. Once requested, anyone can push desired test price through this mock oracle contract.
  • TestnetERC20: Unless existing default currency was provided the script will have deployed this new token that is whitelisted as a bonding currency. Anyone can mint new tokens by calling its allocateTo(address ownerAddress, uint256 value) method.
  • OptimisticOracleV3: Optimistic Oracle that resolves assertions made by integrating contracts.

Resolving dispute requests

Whenever the assertion is disputed, Optimistic Oracle V3 calls requestPrice method on the Oracle that triggers DVM vote on mainnet. In the sandboxed environment this would resolve to MockOracleAncillary contract instead.
The disputed request is uniquely identified by its identifier, timestamp and ancillary data that should be referenced by the testing developer pushing resolved price to the mocked oracle. The MockOracleAncillary contract will emit PriceRequestAdded event with following parameters:
  • address indexed requester: Address of the calling contract (Optimistic Oracle V3 in the sandboxed setup).
  • bytes32 indexed identifier: Identifier referencing the rules on how the request should be resolved. By default Optimistic Oracle V3 uses ASSERT_TRUTH identifier unless the integrating contract provided other value.
  • uint256 time: Optimistic Oracle V3 will set this to timestamp when the original assertion was made.
  • bytes ancillaryData: Optimistic Oracle V3 will put the assertionId and address of the asserter referencing the disputed assertion.
  • bytes32 indexed requestId: This is hashed value of identifier, time and ancillary data provided by the MockOracleAncillary contract just as convenient method to resolve request by one parameter instead of three separate parameters.
Note that production voting contract on mainnet emits this differently and above PriceRequestAdded is provided only as a convenience for developers when interacting with the MockOracleAncillary contract in their testing environment.
MockOracleAncillary contract provides two alternative methods for testing developers to resolve request:
  • pushPrice(bytes32 identifier, uint256 time, bytes memory ancillaryData, int256 price) where request is referenced by its individual parameters.
  • pushPriceByRequestId(bytes32 requestId, int256 price) where request is referenced by the hashed value of its identifying parameters.
While testing developer can pass any int256 price value, Optimistic Oracle V3 can only interpret value of 1e18 to represent truthful assertion. Any other value would settle assertion as being false. Also note that MockOracleAncillary contract will only accept the first pushed price to resolve a given request.

Example dispute flow

As an illustration to the section above this example walks through resolving disputed assertion with cast tool from Foundry. This also requires jq being installed for parsing returned values.
Just for the sake of simplicity set MINIMUM_BOND environment variable to 0 when running forge script command as discussed in the sandbox deployment section. This would allow to skip minting and approving assertion/dispute bonds. Take a note of deployed contracts log and export Optimistic Oracle V3 address to OOV3_ADDRESS and MockOracleAncillary address to MOCK_ORACLE_ADDRESS environment variables.
Also to keep this simple perform all steps from the same address:
export USER_ADDRESS=$(cast wallet address --mnemonic "$MNEMONIC")
Start with generating test assertion and grab the resulting assertionId emitted by Optimistic Oracle V3 (corresponds to topic index 1 in the AssertionMade event):
export ASSERTION_TX=$(cast send --mnemonic "$MNEMONIC" --json $OOV3_ADDRESS "assertTruthWithDefaults(bytes,address)" $(cast --from-utf8 "test claim") $USER_ADDRESS | jq -r '.transactionHash')
export ASSERTION_ID=$(cast receipt --json $ASSERTION_TX | jq -r --arg OOV3_LOWERCASE $(echo $OOV3_ADDRESS | tr [:upper:] [:lower:]) '.logs[] | select(.address==$OOV3_LOWERCASE).topics[1]')
Dispute the above assertion and grab the resulting requestId emitted by MockOracleAncillary (corresponds to topic index 3 in the PriceRequestAdded event):
export DISPUTE_TX=$(cast send --mnemonic "$MNEMONIC" --json $OOV3_ADDRESS "disputeAssertion(bytes32,address)" $ASSERTION_ID $USER_ADDRESS | jq -r '.transactionHash')
export REQUEST_ID=$(cast receipt --json $DISPUTE_TX | jq -r --arg MOCK_LOWERCASE $(echo $MOCK_ORACLE_ADDRESS | tr [:upper:] [:lower:]) '.logs[] | select(.address==$MOCK_LOWERCASE).topics[3]')
Now resolve the disputed assertion as truthful by pushing 1e18 as price to MockOracleAncillary contract:
cast send --mnemonic "$MNEMONIC" $MOCK_ORACLE_ADDRESS "pushPriceByRequestId(bytes32,int256)" $REQUEST_ID $(cast --to-wei 1)
This allows us to settle the assertion at Optimistic Oracle V3:
cast send --mnemonic "$MNEMONIC" $OOV3_ADDRESS "settleAssertion(bytes32)" $ASSERTION_ID
Finally, you can verify that Optimistic Oracle V3 has correctly settled the assertion (this should return true):
cast call $OOV3_ADDRESS "getAssertionResult(bytes32)(bool)" $ASSERTION_ID

Further sandbox configuration

The deployment script documented in the section above covers most common use cases and should be sufficient to test example contracts from the Optimistic Oracle V3 integration quickstart repository. Though, when developing your own integrating contracts it might be required to further configure the sandboxed oracle environment.

Whitelisting of bonding currencies

In case the integrating contract requires handling assertion bonds in multiple currencies all of them should be approved at the AddressWhitelist contract. On production this should go through UMA governance while in the sandboxed environment the deployer owns this contract and can call addToWhitelist method directly. Assuming the address of AddressWhitelist contract is exported to ADDRESS_WHITELIST and required bonding currency is exported to BONDING_CURRENCY environment variables, this can be done by:
cast send --mnemonic "$MNEMONIC" $ADDRESS_WHITELIST "addToWhitelist(address)" $BONDING_CURRENCY
In order to test the scenario on how the integrating contracts would behave if UMA governance removed support for a particular bonding currency, you can test this by calling removeFromWhitelist method on the AddressWhitelist contract:
cast send --mnemonic "$MNEMONIC" $ADDRESS_WHITELIST "removeFromWhitelist(address)" $BONDING_CURRENCY
Note that Optimistic Oracle V3 is caching whitelisted bonding currencies. Hence, before testing impact on the integrating contracts somebody should sync the contract cache (anyone has authority to do this):
cast send --mnemonic "$MNEMONIC" $OOV3_ADDRESS "syncUmaParams(bytes32,address)" $(cast --format-bytes32-string "") $BONDING_CURRENCY

Changing the final fee

In case the UMA governance was to change the configured final fee for a particular bonding currency this would also change the minimum assertion bond required by Optimistic Oracle V3. In order to test such impact on integrating contracts in the testing environment, the deployer would need to call setFinalFee on the Store contract (exported as STORE_ADDRESS). E.g. to set final fee to 1000e18, you would call:
cast send --mnemonic "$MNEMONIC" $STORE_ADDRESS "setFinalFee(address,(uint256))" $BONDING_CURRENCY "($(cast --to-wei 1000))"
Again, before testing impact on integrating contracts someone would need to sync new final fee at the Optimistic Oracle V3:
cast send --mnemonic "$MNEMONIC" $OOV3_ADDRESS "syncUmaParams(bytes32,address)" $(cast --format-bytes32-string "") $BONDING_CURRENCY

Whitelisting of identifiers

If the integrating contract plans to make assertions with multiple identifiers, the developer would need to add them to IdentifierWhitelist contract (exported as IDENTIFIER_ADDRESS) in the sandboxed environment, e.g. adding CUSTOM_IDENTIFIER:
cast send --mnemonic "$MNEMONIC" $IDENTIFIER_ADDRESS "addSupportedIdentifier(bytes32)" $(cast --format-bytes32-string "CUSTOM_IDENTIFIER")
In order to test the scenario on how the integrating contracts would behave if UMA governance removed support for a particular identifier, you can test this by calling removeSupportedIdentifier method on the IdentifierWhitelist contract:
cast send --mnemonic "$MNEMONIC" $IDENTIFIER_ADDRESS "removeSupportedIdentifier(bytes32)" $(cast --format-bytes32-string "CUSTOM_IDENTIFIER")
Note that Optimistic Oracle V3 is caching whitelisted identifiers. Hence, before testing impact on the integrating contracts somebody should sync the contract cache (anyone has authority to do this):
cast send --mnemonic "$MNEMONIC" $OOV3_ADDRESS "syncUmaParams(bytes32,address)" $(cast --format-bytes32-string "CUSTOM_IDENTIFIER") 0x0000000000000000000000000000000000000000