This was buried in discord and not yet in the official UMA documentation, but I wanted an easily referenced document on this process / setup / prereqs. This is a technical guide on how to run validation on IS_RELAY_VALID requests in the UMA Protocol
You can read more about UMA Protocol here and more specifics around the voting process here _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
These instructions are specific to OSX, and should work on *nix systems. If you happen to be a Windows developer and would like me to add to this, please reach out
You will need the following:
Node v14+
yarn
XCode Command Line Tools (to support project dependencies)
First, you’ll need to clone the UMA Protocol repository to your local environment.
git clone git@github.com:UMAprotocol/protocol.git
Then move into the cloned directory, install node packages: yarn
Next you will need to build the contracts using the quick build node script: yarn qbuild
dotenv is used for environment variables, in this example the following values should be added to .env in the project directory to specify the nodes to connect to, with each L2 listed as NODE_URL_<chainID> example using infura nodes:
CUSTOM_NODE_URL=https://mainnet.infura.io/v3/<YOURPROJECTID>
NODE_URL_42161=https://arbitrum-mainnet.infura.io/v3/<YOURPROJECTID>
NODE_URL_10=https://optimism-mainnet.infura.io/v3/<YOURPROJECTID>
Alternatively, you can use public RPC URLs from the rpc.info list (Less secure)
The script also uses the environment variable PRICE_FEED_CONFIG - it defaults to Arbitrum (NODE_URL_42161) but can be set to the appropriate node ID for the relay in question, for example optimism would require an env variable of:
PRICE_FEED_CONFIG='{"l2NetId": 10}'
Initial Check:
Once the project is set up, from the root of the protocols directory you will want to capture the Proposal, Ancillary Data (raw hexstring) and Proposal Timestamp from vote.umaproject.org on the proposal in question, then run the getHistoricalPrice.js script with those details :
HARDHAT_NETWORK=mainnet \
node ./packages/scripts/src/local/getHistoricalPrice.js \
--network mainnet \
--identifier <Proposal, ex: IS_RELAY_VALID> \
--time <UTC timestamp, ex: 1638976595>
--ancillaryData <Ancillary Data (raw), ex: 0x72656c6179486173683a633562366430353036326230393730643239643661393565353065646436383336633036653465613931626338343432626365616166663933363339363562322c6f6f5265717565737465723a37333535656663363361653733316635383433383061393833383239326337303436633165343333>
Running this command with the example values above:
HARDHAT_NETWORK=mainnet node ./packages/scripts/src/local/getHistoricalPrice.js --network mainnet --identifier IS_RELAY_VALID --time 1638976595 --ancillaryData 0x72656c6179486173683a633562366430353036326230393730643239643661393565353065646436383336633036653465613931626338343432626365616166663933363339363562322c6f6f5265717565737465723a37333535656663363361653733316635383433383061393833383239326337303436633165343333
This script will return 0 or 1, for NO/YES on whether the relay is valid. In the example above, we received a 0 and should analyze the output:
Data Validation:
The first thing to do after getting the output from the above commands is to validate the output. Pretty-printing the JSON and going through the pertinent info:
The first 2 objects show the InsuredBridgeL1Client and InsuredBridgeL2Client - the data here shows me that the script is going to try to check chainId 42161 and mainnet (no chainId)
{
"at": "InsuredBridgeL2Client",
"message": "Insured bridge l2 client updated",
"chainId": 42161,
"level": "debug"
} {
"at": "InsuredBridgeL1Client",
"message": "Insured bridge l1 client updated",
"level": "debug"
}
The next section will show the matchedRelay - where you can see the relayData object which has no chainId property, however the value at 0 of 1 tells us the relay is mainnet:
{
"at": "InsuredBridgePriceFeed",
"message": "Matched relay",
"matchedRelay": {
"relayData": {
"0": "1",
.........
},
and then, the depositData shows us the chainID is 10
"depositData": {
"0": "10",
......
"chainId": "10",
......
Notice that the chainId differs from the default of Arbitrum / 42161 and is actually Optimism / 10 - this tells us that we need to change the l2NetId in PRICE_FEED_CONFIG - within my .env file:
PRICE_FEED_CONFIG='{"l2NetId": 10}'
Now running the command again:
HARDHAT_NETWORK=mainnet node ./packages/scripts/src/local/getHistoricalPrice.js --network mainnet --identifier IS_RELAY_VALID --time 1635440180 --ancillaryData 0x72656c6179486173683a33643737356334356531626531353564636164626634646163383365346439343031666533313933623837386565353566633837383362656133346662333764
I get the following output:
{
"l2NetId": 10
} {
"at": "createReferencePriceFeedForFinancialContract",
"message": "Inferred default config from identifier or Financial Contract address",
"financialContractAddress": null,
"identifier": "IS_RELAY_VALID",
"defaultConfig": {
"type": "insuredbridge",
"l2BlockLookback": 99000,
"l2NetId": 42161,
"bridgeAdminAddress": "0x30B44C676A05F1264d1dE9cC31dB5F2A945186b6",
"rateModels": {
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2": {
"UBar": "650000000000000000",
"R0": "0",
"R1": "80000000000000000",
"R2": "1000000000000000000"
},
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48": {
"UBar": "800000000000000000",
"R0": "0",
"R1": "40000000000000000",
"R2": "600000000000000000"
},
"0x04fa0d235c4abf4bcf4787af4cf447de572ef828": {
"UBar": "500000000000000000",
"R0": "0",
"R1": "50000000000000000",
"R2": "2000000000000000000"
},
"0x3472A5A71965499acd81997a54BBA8D852C6E53d": {
"UBar": "500000000000000000",
"R0": "25000000000000000",
"R1": "25000000000000000",
"R2": "2000000000000000000"
}
},
"priceFeedDecimals": 18
},
"level": "debug"
} {
"at": "createReferencePriceFeedForFinancialContract",
"message": "Found both a default config and a user-config",
"defaultConfig": {
"type": "insuredbridge",
"l2BlockLookback": 99000,
"l2NetId": 42161,
"bridgeAdminAddress": "0x30B44C676A05F1264d1dE9cC31dB5F2A945186b6",
"rateModels": {
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2": {
"UBar": "650000000000000000",
"R0": "0",
"R1": "80000000000000000",
"R2": "1000000000000000000"
},
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48": {
"UBar": "800000000000000000",
"R0": "0",
"R1": "40000000000000000",
"R2": "600000000000000000"
},
"0x04fa0d235c4abf4bcf4787af4cf447de572ef828": {
"UBar": "500000000000000000",
"R0": "0",
"R1": "50000000000000000",
"R2": "2000000000000000000"
},
"0x3472A5A71965499acd81997a54BBA8D852C6E53d": {
"UBar": "500000000000000000",
"R0": "25000000000000000",
"R1": "25000000000000000",
"R2": "2000000000000000000"
}
},
"priceFeedDecimals": 18
},
"userConfig": {
"lookback": 3630364,
"priceFeedDecimals": 18,
"l2NetId": 10
},
"combinedConfig": {
"type": "insuredbridge",
"l2BlockLookback": 99000,
"l2NetId": 10,
"bridgeAdminAddress": "0x30B44C676A05F1264d1dE9cC31dB5F2A945186b6",
"rateModels": {
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2": {
"UBar": "650000000000000000",
"R0": "0",
"R1": "80000000000000000",
"R2": "1000000000000000000"
},
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48": {
"UBar": "800000000000000000",
"R0": "0",
"R1": "40000000000000000",
"R2": "600000000000000000"
},
"0x04fa0d235c4abf4bcf4787af4cf447de572ef828": {
"UBar": "500000000000000000",
"R0": "0",
"R1": "50000000000000000",
"R2": "2000000000000000000"
},
"0x3472A5A71965499acd81997a54BBA8D852C6E53d": {
"UBar": "500000000000000000",
"R0": "25000000000000000",
"R1": "25000000000000000",
"R2": "2000000000000000000"
}
},
"priceFeedDecimals": 18,
"lookback": 3630364
},
"level": "debug"
} {
"at": "createPriceFeed",
"message": "Creating InsuredBridgePriceFeed",
"config": {
"type": "insuredbridge",
"l2BlockLookback": 99000,
"l2NetId": 10,
"bridgeAdminAddress": "0x30B44C676A05F1264d1dE9cC31dB5F2A945186b6",
"rateModels": {
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2": {
"UBar": "650000000000000000",
"R0": "0",
"R1": "80000000000000000",
"R2": "1000000000000000000"
},
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48": {
"UBar": "800000000000000000",
"R0": "0",
"R1": "40000000000000000",
"R2": "600000000000000000"
},
"0x04fa0d235c4abf4bcf4787af4cf447de572ef828": {
"UBar": "500000000000000000",
"R0": "0",
"R1": "50000000000000000",
"R2": "2000000000000000000"
},
"0x3472A5A71965499acd81997a54BBA8D852C6E53d": {
"UBar": "500000000000000000",
"R0": "25000000000000000",
"R1": "25000000000000000",
"R2": "2000000000000000000"
}
},
"priceFeedDecimals": 18,
"lookback": 3630364
},
"level": "debug"
} {
"at": "InsuredBridgeL2Client",
"message": "Insured bridge l2 client updated",
"chainId": 10,
"level": "debug"
} {
"at": "InsuredBridgeL1Client",
"message": "Insured bridge l1 client updated",
"level": "debug"
} {
"at": "InsuredBridgePriceFeed",
"message": "No relay event found matching provided ancillary data. Has the relay been finalized already?",
"level": "debug"
}
⚠️ Truncating price to 18 decimals (default: 18)
💹 Median IS_RELAY_VALID price @ 1635440180 = 0
Which confirms that I should vote 0 / NO on this IS_RELAY_VALID request
Process/Script Details:
The getHistoricalPrice.js script mimics how the relayer bot works, but utilizes the ancillary data, which the relayer bot does not use at all.
First, the script tries to find a matching
DepositRelayedevent across all BridgePools, matching the event’srelayAncillaryDataHashfield with the parsed ancillaryData from theSkinnyOptimisticOracleevent. We use the handy parseAncillaryData library to deconstruct the ancillary data into a relayHashIf the relay is found, next we need to find a
FundsDepositedwhere the depositHash matches the deposit hash in the matched relay from the previous step. Note that this step can fail if you’re connecting to an unexpected L2. For example, setting your node to Optimism but the deposit actually occurred on ArbitrumFinally, if the relay and the deposit is found, we just need to validate the relay params. There is only one param to validate: the
realizedLpFeePct, so we use the same library that the bot and the front-end use to compute this and compare the relay’s fee set with the expected value for the deposit.quoteTime.
