├── voter-dapp ├── src │ ├── common │ ├── contracts │ ├── App.css │ ├── logo.png │ ├── Styles.js │ ├── index.css │ ├── App.test.js │ ├── drizzleOptions.js │ ├── index.js │ ├── DrizzleInit.js │ ├── TokenTracker.js │ ├── App.js │ └── DesignatedVotingTransfer.js ├── public │ ├── logo.png │ ├── favicon.ico │ ├── manifest.json │ └── index.html ├── .gitignore ├── package.json └── README.md ├── .gitattributes ├── .github └── dco.yml ├── core ├── config │ ├── identifiersTest.json │ └── identifiers.json ├── scripts │ ├── umip-2 │ │ ├── 2_VoteSimulate.js │ │ ├── 3_Verify.js │ │ └── 1_Propose.js │ ├── CommandlineUtil.js │ ├── runCommand.sh │ ├── local │ │ ├── AdvanceToNextVotingPhase.js │ │ ├── AddMemberToRole.js │ │ ├── MintNewTokens.js │ │ ├── ApproveIdentifiers.js │ │ ├── CalculateContractBytecode.js │ │ ├── AdvanceEMP.js │ │ └── ClaimAllRewards.js │ ├── cli │ │ ├── wallet │ │ │ ├── getDefaultAccount.js │ │ │ ├── getTwoKeyContract.js │ │ │ ├── createNewAccount.js │ │ │ └── readDefaultAccountInfo.js │ │ ├── sponsor │ │ │ ├── transactionUtils.js │ │ │ ├── showExpiredMarketDetails.js │ │ │ ├── listMarkets.js │ │ │ ├── currencyUtils.js │ │ │ ├── listExpiredMarkets.js │ │ │ ├── viewLiquidationDetails.js │ │ │ └── deposit.js │ │ ├── textStyle.js │ │ ├── sponsor.js │ │ ├── cli_entry.sh │ │ ├── voting │ │ │ ├── votePhaseTiming.js │ │ │ ├── getResolvedVotesByRoundId.js │ │ │ └── filterRequestsByRound.js │ │ └── wallet.js │ ├── DeployDesignatedVoting.js │ ├── CheckDeploymentValidity.js │ ├── EmergencyShutdown.js │ └── DecodeTransactionData.js ├── truffle-config.js ├── .solcover.js ├── migrations │ ├── 2_deploy_finder.js │ ├── 13_deploy_tokenfactory.js │ ├── 1_initial_migration.js │ ├── 10_deploy_designated_voting_factory.js │ ├── 3_deploy_timer.js │ ├── 6_deploy_registry.js │ ├── 11_support_identifiers.js │ ├── 7_deploy_financial_contracts_admin.js │ ├── 12_deploy_testnet_token.js │ ├── 8_deploy_store.js │ ├── 9_deploy_governor.js │ ├── 15_deploy_local_weth.js │ ├── 14_deploy_expiring_multi_party_creator.js │ └── 5_deploy_voting.js ├── contracts │ ├── common │ │ ├── test │ │ │ ├── UniswapMock.sol │ │ │ ├── ReentrancyAttack.sol │ │ │ ├── TestableTest.sol │ │ │ ├── WithdrawableTest.sol │ │ │ ├── MultiRoleTest.sol │ │ │ ├── ReentrancyMock.sol │ │ │ └── ReentrancyChecker.sol │ │ ├── interfaces │ │ │ ├── Uniswap.sol │ │ │ └── ExpandedIERC20.sol │ │ └── implementation │ │ │ ├── Timer.sol │ │ │ ├── TestnetERC20.sol │ │ │ ├── Testable.sol │ │ │ └── ExpandedERC20.sol │ ├── oracle │ │ ├── implementation │ │ │ ├── Constants.sol │ │ │ ├── test │ │ │ │ ├── MockAdministratee.sol │ │ │ │ ├── GovernorTest.sol │ │ │ │ ├── VoteTimingTest.sol │ │ │ │ ├── VotingTest.sol │ │ │ │ └── ResultComputationTest.sol │ │ │ ├── ContractCreator.sol │ │ │ ├── FinancialContractsAdmin.sol │ │ │ ├── VotingToken.sol │ │ │ └── Finder.sol │ │ └── interfaces │ │ │ ├── AdministrateeInterface.sol │ │ │ ├── FinderInterface.sol │ │ │ ├── IdentifierWhitelistInterface.sol │ │ │ ├── OracleInterface.sol │ │ │ └── StoreInterface.sol │ ├── financial-templates │ │ ├── expiring-multiparty │ │ │ ├── ExpiringMultiParty.sol │ │ │ └── ExpiringMultiPartyLib.sol │ │ └── common │ │ │ └── TokenFactory.sol │ └── Migrations.sol ├── utils │ ├── Constants.js │ ├── Serving.js │ └── Voting.js ├── .env_sample ├── test │ ├── scripts │ │ ├── EmergencyShutdown.js │ │ └── DecodeTransactionData.js │ ├── common │ │ ├── Testable.js │ │ └── Lockable.js │ ├── oracle │ │ ├── DesignatedVotingFactory.js │ │ ├── VoteTiming.js │ │ └── FinancialContractsAdmin.js │ └── financial-templates │ │ └── ExpiringMultiParty.js └── networks │ ├── 1.json │ ├── 4.json │ └── 42.json ├── ci ├── false_positive.sh ├── build.sh ├── lint.sh ├── install_node_npm.sh ├── run_dapp_tests.sh ├── docgen.sh ├── coverage.sh ├── run_slither.sh ├── deploy_to_staging.sh └── run_truffle_tests.sh ├── .prettierignore ├── documentation ├── Logo.png ├── developer_reference │ ├── severity.png │ └── contract_addresses.md ├── oracle │ ├── architecture_diagram.jpeg │ ├── mainnet_deployment_info.md │ └── governance │ │ └── UMA_token_holder_responsibilities.md ├── prelude.hbs ├── synthetic_tokens │ ├── st_expiration.png │ ├── st_create_token.png │ ├── st_decision_tree.png │ ├── st_expiration@2x.png │ ├── st_liquidation_1.png │ ├── st_liquidation_2.png │ ├── st_liquidation_3.png │ ├── st_redeem_token.png │ ├── st_add_collateral.png │ ├── st_create_contract.png │ ├── st_create_token@2x.png │ ├── st_redeem_token@2x.png │ ├── st_add_collateral@2x.png │ ├── st_create_contract@2x.png │ ├── st_decision_tree@2x.png │ ├── st_liquidation_1@2x.png │ ├── st_liquidation_2@2x.png │ ├── st_liquidation_3@2x.png │ ├── tutorials │ │ ├── deposit_num.png │ │ ├── livemarket.png │ │ ├── redeem_start.png │ │ ├── toplevelmenu.png │ │ ├── withdraw_num.png │ │ ├── create_complete.png │ │ ├── create_confirm.png │ │ ├── create_numtokens.png │ │ ├── deployEMP_output.png │ │ ├── deposit_complete.png │ │ ├── deposit_options.png │ │ ├── redeem_complete.png │ │ ├── transfer_confirm.png │ │ ├── transfer_start.png │ │ ├── withdraw_execute.png │ │ ├── transfer_complete.png │ │ ├── withdraw_complete.png │ │ ├── withdraw_toplevel.png │ │ └── toplevelmenu_sponsor.png │ ├── st_withdraw_collateral.png │ └── st_withdraw_collateral@2x.png ├── community │ ├── connecting_with_UMA.md │ └── blog_posts.md ├── gae_app.yaml ├── map.txt ├── getting_started │ ├── priceless_defi_contracts.md │ └── architecture_overview.md └── ROOT │ └── index.md ├── .eslintignore ├── Architecture.md ├── financial-templates-lib ├── helpers │ └── delay.js ├── package.json ├── price-feed │ ├── Networker.js │ └── PriceFeedInterface.js ├── test │ └── price-feed │ │ ├── NetworkerMock.js │ │ └── PriceFeedMock.js └── logger │ ├── ConsoleTransport.js │ ├── SpyTransport.js │ ├── PagerDutyTransport.js │ └── Transports.js ├── scripts ├── buildContracts.sh ├── bot-deployment │ ├── UpdateBotGCP.sh │ ├── DeployBotGCP.sh │ └── PushBotHotFix.sh ├── deploy_docs.sh ├── generate_private_key.sh └── gen-nav.js ├── disputer └── package.json ├── liquidator └── package.json ├── common ├── globalSolcoverConfig.js ├── Random.js ├── Enums.js ├── Constants.js ├── PublicNetworks.js ├── EncryptionHelper.js ├── AdminUtils.js ├── AbiUtils.js └── ObjectUtils.js ├── playbook.yml ├── .dockerignore ├── .gitignore ├── .prettierrc ├── README.md ├── .eslintrc.js ├── Automated.md └── Dockerfile /voter-dapp/src/common: -------------------------------------------------------------------------------- 1 | ../../common -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.github/dco.yml: -------------------------------------------------------------------------------- 1 | require: 2 | members: false 3 | -------------------------------------------------------------------------------- /voter-dapp/src/contracts: -------------------------------------------------------------------------------- 1 | ../../core/build/contracts -------------------------------------------------------------------------------- /core/config/identifiersTest.json: -------------------------------------------------------------------------------- 1 | { 2 | "BTCETH": {} 3 | } 4 | -------------------------------------------------------------------------------- /core/scripts/umip-2/2_VoteSimulate.js: -------------------------------------------------------------------------------- 1 | ../umip-3/2_VoteSimulate.js -------------------------------------------------------------------------------- /voter-dapp/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | -------------------------------------------------------------------------------- /ci/false_positive.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Exit with no error 4 | exit 0 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | docs 4 | modules 5 | public 6 | ui 7 | build -------------------------------------------------------------------------------- /documentation/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/Logo.png -------------------------------------------------------------------------------- /voter-dapp/src/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/voter-dapp/src/logo.png -------------------------------------------------------------------------------- /voter-dapp/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/voter-dapp/public/logo.png -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | docs 4 | modules 5 | public 6 | ui 7 | sponsor-dapp-v2 8 | voter-dapp 9 | build -------------------------------------------------------------------------------- /voter-dapp/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/voter-dapp/public/favicon.ico -------------------------------------------------------------------------------- /Architecture.md: -------------------------------------------------------------------------------- 1 | # UMA DVM Architecture 2 | 3 | This file has been moved [here](./documentation/explainers/architecture.md). 4 | -------------------------------------------------------------------------------- /core/truffle-config.js: -------------------------------------------------------------------------------- 1 | const globalConfig = require("../common/globalTruffleConfig.js"); 2 | 3 | module.exports = globalConfig; 4 | -------------------------------------------------------------------------------- /ci/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | PROTOCOL_DIR=$(pwd) 5 | 6 | cd $PROTOCOL_DIR/core 7 | $(npm bin)/truffle compile 8 | -------------------------------------------------------------------------------- /ci/lint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | PROTOCOL_DIR=$(pwd) 5 | 6 | # Lint JS 7 | echo "Linting Solidity and js" 8 | npm run lint 9 | -------------------------------------------------------------------------------- /core/.solcover.js: -------------------------------------------------------------------------------- 1 | const globalSolcoverConfig = require("../common/globalSolcoverConfig.js"); 2 | 3 | module.exports = globalSolcoverConfig; 4 | -------------------------------------------------------------------------------- /financial-templates-lib/helpers/delay.js: -------------------------------------------------------------------------------- 1 | const delay = ms => new Promise(r => setTimeout(r, ms)); 2 | 3 | module.exports = { 4 | delay 5 | }; 6 | -------------------------------------------------------------------------------- /documentation/developer_reference/severity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/developer_reference/severity.png -------------------------------------------------------------------------------- /documentation/oracle/architecture_diagram.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/oracle/architecture_diagram.jpeg -------------------------------------------------------------------------------- /documentation/prelude.hbs: -------------------------------------------------------------------------------- 1 | {{#links}} 2 | :{{slug target.fullName}}: pass:normal[xref:{{path}}#{{target.anchor}}[`{{target.fullName}}`]] 3 | {{/links}} 4 | -------------------------------------------------------------------------------- /documentation/synthetic_tokens/st_expiration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/st_expiration.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/st_create_token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/st_create_token.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/st_decision_tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/st_decision_tree.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/st_expiration@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/st_expiration@2x.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/st_liquidation_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/st_liquidation_1.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/st_liquidation_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/st_liquidation_2.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/st_liquidation_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/st_liquidation_3.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/st_redeem_token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/st_redeem_token.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/st_add_collateral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/st_add_collateral.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/st_create_contract.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/st_create_contract.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/st_create_token@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/st_create_token@2x.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/st_redeem_token@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/st_redeem_token@2x.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/st_add_collateral@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/st_add_collateral@2x.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/st_create_contract@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/st_create_contract@2x.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/st_decision_tree@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/st_decision_tree@2x.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/st_liquidation_1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/st_liquidation_1@2x.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/st_liquidation_2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/st_liquidation_2@2x.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/st_liquidation_3@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/st_liquidation_3@2x.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/tutorials/deposit_num.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/tutorials/deposit_num.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/tutorials/livemarket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/tutorials/livemarket.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/st_withdraw_collateral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/st_withdraw_collateral.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/tutorials/redeem_start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/tutorials/redeem_start.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/tutorials/toplevelmenu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/tutorials/toplevelmenu.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/tutorials/withdraw_num.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/tutorials/withdraw_num.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/st_withdraw_collateral@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/st_withdraw_collateral@2x.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/tutorials/create_complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/tutorials/create_complete.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/tutorials/create_confirm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/tutorials/create_confirm.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/tutorials/create_numtokens.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/tutorials/create_numtokens.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/tutorials/deployEMP_output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/tutorials/deployEMP_output.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/tutorials/deposit_complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/tutorials/deposit_complete.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/tutorials/deposit_options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/tutorials/deposit_options.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/tutorials/redeem_complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/tutorials/redeem_complete.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/tutorials/transfer_confirm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/tutorials/transfer_confirm.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/tutorials/transfer_start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/tutorials/transfer_start.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/tutorials/withdraw_execute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/tutorials/withdraw_execute.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/tutorials/transfer_complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/tutorials/transfer_complete.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/tutorials/withdraw_complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/tutorials/withdraw_complete.png -------------------------------------------------------------------------------- /documentation/synthetic_tokens/tutorials/withdraw_toplevel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/tutorials/withdraw_toplevel.png -------------------------------------------------------------------------------- /scripts/buildContracts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | cd core 5 | rm -rf ./build 6 | $(npm bin)/truffle compile 7 | $(npm bin)/apply-registry 8 | echo "Done building contracts" 9 | -------------------------------------------------------------------------------- /documentation/synthetic_tokens/tutorials/toplevelmenu_sponsor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntropika-labs/potion-core/HEAD/documentation/synthetic_tokens/tutorials/toplevelmenu_sponsor.png -------------------------------------------------------------------------------- /core/scripts/CommandlineUtil.js: -------------------------------------------------------------------------------- 1 | function validateAddress(address) { 2 | return address.substring(0, 2) == "0x" || address.length == 42; 3 | } 4 | 5 | module.exports = { 6 | validateAddress 7 | }; 8 | -------------------------------------------------------------------------------- /disputer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "disputer", 3 | "version": "0.1.0", 4 | "description": "UMA Disputer", 5 | "private": true, 6 | "dependencies": { 7 | "winston": "^3.2.1", 8 | "winston-transport": "^4.3.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /liquidator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "liquidator", 3 | "version": "0.1.0", 4 | "description": "Liquidator", 5 | "private": true, 6 | "dependencies": { 7 | "winston": "^3.2.1", 8 | "winston-transport": "^4.3.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /voter-dapp/src/Styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/styles"; 2 | 3 | export const useTableStyles = makeStyles(theme => ({ 4 | root: { 5 | padding: "10px" 6 | }, 7 | tableHeaderCell: { 8 | fontWeight: "750" 9 | } 10 | })); 11 | -------------------------------------------------------------------------------- /ci/install_node_npm.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | curl -sSL "https://nodejs.org/dist/v12.16.2/node-v12.16.2-linux-x64.tar.xz" | sudo tar --strip-components=2 -xJ -C /usr/local/bin/ node-v12.16.2-linux-x64/bin/node 4 | curl https://www.npmjs.com/install.sh | sudo bash 5 | -------------------------------------------------------------------------------- /common/globalSolcoverConfig.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | providerOptions: { 3 | network_id: 1234 4 | }, 5 | skipFiles: [ 6 | "Migrations.sol", 7 | "tokenized-derivative/echidna-tests", 8 | "common/test", 9 | "oracle/test", 10 | "oracle/implementation/test" 11 | ] 12 | }; 13 | -------------------------------------------------------------------------------- /playbook.yml: -------------------------------------------------------------------------------- 1 | site: 2 | title: UMA Docs 3 | url: https://docs.umaproject.org 4 | keys: 5 | google_analytics: 'UA-130599982-5' 6 | start_page: uma::index.adoc 7 | content: 8 | sources: 9 | - url: . 10 | branches: HEAD 11 | ui: 12 | bundle: 13 | url: ./documentation/ui/build/uma-docs-ui.zip 14 | -------------------------------------------------------------------------------- /documentation/community/connecting_with_UMA.md: -------------------------------------------------------------------------------- 1 | # Connect with UMA 2 | 3 | - Join our [Slack](https://join.slack.com/t/umaprotocol/shared_invite/enQtNTk4MjQ4ODY0MDA1LTM4ODg0NGZhYWZkNjkzMDE4MjU0ZGFlYWQzZTFiZWFlZjI2NDE4OGI2NWY3OTdhYjYyZjg0MjAzMTgwODVhZTE) 4 | - Follow us on Twitter at [@UMAprotocol](https://twitter.com/UMAprotocol) 5 | -------------------------------------------------------------------------------- /voter-dapp/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "UMA Voter DApp", 3 | "name": "UMA Voter DApp", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone" 13 | } 14 | -------------------------------------------------------------------------------- /core/scripts/runCommand.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Simple script that simply runs a command input as an environment variable. Should be run from the core/ directory. 4 | # Note: this is used to allow the docker image to run arbitrary commands by specifying them in the environment rather 5 | # than the `docker run` command. 6 | $COMMAND 7 | -------------------------------------------------------------------------------- /documentation/gae_app.yaml: -------------------------------------------------------------------------------- 1 | runtime: python27 2 | api_version: 1 3 | threadsafe: true 4 | handlers: 5 | - url: /[^.]*$ 6 | static_files: www/index.html 7 | upload: www/index.html 8 | secure: always 9 | redirect_http_response_code: 301 10 | - url: / 11 | static_dir: www 12 | secure: always 13 | redirect_http_response_code: 301 14 | service: docs 15 | -------------------------------------------------------------------------------- /core/migrations/2_deploy_finder.js: -------------------------------------------------------------------------------- 1 | const Finder = artifacts.require("Finder"); 2 | const { getKeysForNetwork, deploy } = require("../../common/MigrationUtils.js"); 3 | 4 | module.exports = async function(deployer, network, accounts) { 5 | const keys = getKeysForNetwork(network, accounts); 6 | 7 | await deploy(deployer, network, Finder, { from: keys.deployer }); 8 | }; 9 | -------------------------------------------------------------------------------- /core/contracts/common/test/UniswapMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | import "../interfaces/Uniswap.sol"; 4 | 5 | 6 | /** 7 | * @title Uniswap v2 Mock that allows manual price injection. 8 | */ 9 | contract UniswapMock is Uniswap { 10 | function setPrice(uint112 reserve0, uint112 reserve1) external { 11 | emit Sync(reserve0, reserve1); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /core/utils/Constants.js: -------------------------------------------------------------------------------- 1 | // The interface names that Finder.sol uses to refer to interfaces in the UMA system. 2 | const interfaceName = { 3 | FinancialContractsAdmin: "FinancialContractsAdmin", 4 | Oracle: "Oracle", 5 | Registry: "Registry", 6 | Store: "Store", 7 | IdentifierWhitelist: "IdentifierWhitelist" 8 | }; 9 | 10 | module.exports = { 11 | interfaceName 12 | }; 13 | -------------------------------------------------------------------------------- /core/migrations/13_deploy_tokenfactory.js: -------------------------------------------------------------------------------- 1 | const TokenFactory = artifacts.require("TokenFactory"); 2 | const { getKeysForNetwork, deploy } = require("../../common/MigrationUtils.js"); 3 | 4 | module.exports = async function(deployer, network, accounts) { 5 | const keys = getKeysForNetwork(network, accounts); 6 | 7 | await deploy(deployer, network, TokenFactory, { from: keys.deployer }); 8 | }; 9 | -------------------------------------------------------------------------------- /core/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require("./Migrations.sol"); 2 | const { getKeysForNetwork, deploy, addToTdr } = require("../../common/MigrationUtils.js"); 3 | 4 | module.exports = async function(deployer, network, accounts) { 5 | const keys = getKeysForNetwork(network, accounts); 6 | await deploy(deployer, network, Migrations, { from: keys.deployer }); 7 | }; 8 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | circleci 2 | .github 3 | ci 4 | documentation 5 | voter-dapp 6 | node_modules 7 | build 8 | bridge.log 9 | package-lock.json 10 | .coverage_contracts 11 | .coverage 12 | coverage 13 | coverage.json 14 | out.log 15 | .GckmsOverride.js 16 | docs 17 | modules 18 | public 19 | ui 20 | .DS_Store 21 | antora.yml 22 | .eslintcache 23 | app.yaml 24 | .gae_deploy 25 | *-env.txt 26 | .env 27 | -------------------------------------------------------------------------------- /core/config/identifiers.json: -------------------------------------------------------------------------------- 1 | { 2 | "BTC/USD": {}, 3 | "USD/BTC": {}, 4 | "ETH/USD": {}, 5 | "CMC Total Market Cap": {}, 6 | "S&P 500": {}, 7 | "TSLA": {}, 8 | "Gold (Rolling Future)": {}, 9 | "Crude Oil (Rolling Future)": {}, 10 | "CNY/USD": {}, 11 | "EUR/USD": {}, 12 | "Telegram SAFT": {}, 13 | "USD/ETH": {}, 14 | "Custom Index (1)": {}, 15 | "Custom Index (100)": {} 16 | } 17 | -------------------------------------------------------------------------------- /financial-templates-lib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "financial-templates-lib", 3 | "version": "0.1.0", 4 | "description": "Arbitrage automation and libraries for UMA financial templates", 5 | "private": true, 6 | "dependencies": { 7 | "node-fetch": "^2.6.0", 8 | "winston": "^3.2.1", 9 | "winston-slack-webhook-transport": "^1.2.1", 10 | "winston-transport": "^4.3.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /core/contracts/common/interfaces/Uniswap.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | 4 | /** 5 | * @title Interface for Uniswap v2. 6 | * @dev This only contains the methods/events that we use in our contracts or offchain infrastructure. 7 | */ 8 | abstract contract Uniswap { 9 | // Called after every swap showing the new uniswap "price" for this token pair. 10 | event Sync(uint112 reserve0, uint112 reserve1); 11 | } 12 | -------------------------------------------------------------------------------- /voter-dapp/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /ci/run_dapp_tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | run_test() { 4 | local test_dir=$1 5 | cd $test_dir 6 | output=$(CI=true npm run test) 7 | echo "$output" 8 | num_failures=$(echo $output | grep -ci "error") 9 | if [ $num_failures -ne 0 ] 10 | then 11 | # If we found failures, exit 1. 12 | exit 1 13 | fi 14 | } 15 | 16 | PROTOCOL_DIR=$(pwd) 17 | 18 | run_test $PROTOCOL_DIR/voter-dapp 19 | -------------------------------------------------------------------------------- /voter-dapp/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | bridge.log 4 | package-lock.json 5 | .coverage_contracts 6 | .coverage 7 | coverage 8 | coverage.json 9 | out.log 10 | .GckmsOverride.js 11 | docs 12 | modules 13 | public 14 | ui 15 | .DS_Store 16 | antora.yml 17 | .eslintcache 18 | 19 | # GAE deployment files and directories for GAE deployments. 20 | app.yaml 21 | .gae_deploy 22 | *-env.txt 23 | 24 | # environment-specific values pulled by dotenv package 25 | .env 26 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "bracketSpacing": true, 4 | "explicitTypes": "preserve", 5 | "tabWidth": 2, 6 | "overrides": [ 7 | { 8 | "files": "*.sol", 9 | "options": { 10 | "tabWidth": 4, 11 | "useTabs": false, 12 | "singleQuote": false, 13 | "explicitTypes": "always" 14 | } 15 | }, 16 | { 17 | "files": "*.md", 18 | "options": { 19 | "semi": false 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /core/migrations/10_deploy_designated_voting_factory.js: -------------------------------------------------------------------------------- 1 | const DesignatedVotingFactory = artifacts.require("DesignatedVotingFactory"); 2 | const Finder = artifacts.require("Finder"); 3 | const { getKeysForNetwork, deploy } = require("../../common/MigrationUtils.js"); 4 | 5 | module.exports = async function(deployer, network, accounts) { 6 | const keys = getKeysForNetwork(network, accounts); 7 | 8 | await deploy(deployer, network, DesignatedVotingFactory, Finder.address, { from: keys.deployer }); 9 | }; 10 | -------------------------------------------------------------------------------- /voter-dapp/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import App from "./App"; 4 | 5 | const fakeEthereumObject = { 6 | // Enable goes through immediately and returns 0x0 as the selected address. 7 | enable: async () => "0x0" 8 | }; 9 | 10 | it("renders without crashing", () => { 11 | global.ethereum = fakeEthereumObject; 12 | const div = document.createElement("div"); 13 | ReactDOM.render(, div); 14 | ReactDOM.unmountComponentAtNode(div); 15 | }); 16 | -------------------------------------------------------------------------------- /voter-dapp/src/drizzleOptions.js: -------------------------------------------------------------------------------- 1 | import DesignatedVotingFactory from "./contracts/DesignatedVotingFactory.json"; 2 | import Governor from "./contracts/Governor.json"; 3 | import Voting from "./contracts/Voting.json"; 4 | import VotingToken from "./contracts/VotingToken.json"; 5 | 6 | const options = { 7 | contracts: [DesignatedVotingFactory, Governor, Voting, VotingToken], 8 | polls: { 9 | accounts: 1000, 10 | blocks: 3000 11 | }, 12 | syncAlways: true 13 | }; 14 | 15 | export default options; 16 | -------------------------------------------------------------------------------- /voter-dapp/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | import * as serviceWorker from "./serviceWorker"; 6 | 7 | ReactDOM.render(, document.getElementById("root")); 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: https://bit.ly/CRA-PWA 12 | serviceWorker.unregister(); 13 | -------------------------------------------------------------------------------- /ci/docgen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Note: because we've forked the solidity-docgen library, providing the path alias doesn't have an effect. However, 4 | # once external libraries are supported in v2 and we deprecate our fork, this will allow the docgen to find the 5 | # openzeppelin directory. See https://github.com/OpenZeppelin/solidity-docgen/issues/24 for progress on that front. 6 | $(npm bin)/solidity-docgen -i ./core/contracts -t documentation --contract-pages -x adoc -e core/contracts/oracle/test,core/contracts/tokenized-derivative/echidna-tests 7 | -------------------------------------------------------------------------------- /core/contracts/oracle/implementation/Constants.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | 4 | /** 5 | * @title Stores common interface names used throughout the DVM by registration in the Finder. 6 | */ 7 | library OracleInterfaces { 8 | bytes32 public constant Oracle = "Oracle"; 9 | bytes32 public constant IdentifierWhitelist = "IdentifierWhitelist"; 10 | bytes32 public constant Store = "Store"; 11 | bytes32 public constant FinancialContractsAdmin = "FinancialContractsAdmin"; 12 | bytes32 public constant Registry = "Registry"; 13 | } 14 | -------------------------------------------------------------------------------- /core/contracts/common/test/ReentrancyAttack.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | 4 | // Tests reentrancy guards defined in Lockable.sol. 5 | // Copied from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.1/contracts/mocks/ReentrancyAttack.sol. 6 | contract ReentrancyAttack { 7 | function callSender(bytes4 data) public { 8 | // solhint-disable-next-line avoid-low-level-calls 9 | (bool success, ) = msg.sender.call(abi.encodeWithSelector(data)); 10 | require(success, "ReentrancyAttack: failed call"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /core/scripts/local/AdvanceToNextVotingPhase.js: -------------------------------------------------------------------------------- 1 | const Voting = artifacts.require("Voting"); 2 | const { moveToNextPhase } = require("../../utils/Voting.js"); 3 | 4 | const run = async function(callback) { 5 | const voting = await Voting.deployed(); 6 | 7 | const startingPhase = await voting.getVotePhase(); 8 | await moveToNextPhase(voting); 9 | const endingPhase = await voting.getVotePhase(); 10 | 11 | console.log("Moved from phase", startingPhase.toString(), "to", endingPhase.toString()); 12 | callback(); 13 | }; 14 | 15 | module.exports = run; 16 | -------------------------------------------------------------------------------- /core/.env_sample: -------------------------------------------------------------------------------- 1 | MNEMONIC="" # truffle wallet mnemonic. Used in all scripts, tools & cli. 2 | # To enable slack messaging from winston a slack webhook is needed. 3 | SLACK_WEBHOOK="" # Webhook to send messages to a slack channel. See Logger.js 4 | # To enable Pager duty bot error logging all of the below variables are needed. 5 | PAGERDUTY_API_KEY="" # Pagerduty API key 6 | PAGERDUTY_SERVICE_ID="" # Service ID used to define the unique channel messages are sent in. 7 | PAGERDUTY_FROM_EMAIL="" # From Email to define who the messages in pagerduty originate from. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # POTION Protocol 2 | 3 | ## Documentation 📚 4 | 5 | Our docs site is [here](). It contains ... 6 | 7 | ## Developer Information and Tools 👩‍💻 8 | 9 | For information on how to initialize and interact with our smart contracts, please see the 10 | [documentation site](). 11 | 12 | ### Install dependencies 👷‍♂️ 13 | 14 | You'll need the latest LTS release of nodejs and npm installed. Assuming that's done, run: 15 | 16 | ``` 17 | npm install 18 | ``` 19 | 20 | ### Running the linter 🧽 21 | 22 | To run the formatter, run: 23 | 24 | ``` 25 | npm run lint-fix 26 | ``` 27 | 28 | -------------------------------------------------------------------------------- /ci/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | # The truffle directory is passed in as the first argument. 5 | TRUFFLE_DIR=$1 6 | 7 | PROTOCOL_DIR=$(pwd) 8 | 9 | # $1 is the truffle directory over which we want to run the coverage tool. 10 | cd $TRUFFLE_DIR 11 | # Truffle compile can take a lot of memory, which I've experienced with the solc-0.6 compatible versions, 12 | # so I explicitly increase the javascript heap size. 13 | # More details here: https://github.com/trufflesuite/truffle/issues/957 14 | node --max-old-space-size=4096 $(npm bin)/truffle run coverage --temp build --network coverage 15 | -------------------------------------------------------------------------------- /common/Random.js: -------------------------------------------------------------------------------- 1 | const web3 = require("web3"); 2 | 3 | function getRandomSignedInt() { 4 | // Generate a random unsigned 256 bit int. 5 | const unsignedValue = web3.utils.toBN(web3.utils.randomHex(32)); 6 | 7 | // The signed range is just the unsigned range decreased by 2^255. 8 | const signedOffset = web3.utils.toBN(2).pow(web3.utils.toBN(255)); 9 | return unsignedValue.sub(signedOffset); 10 | } 11 | 12 | function getRandomUnsignedInt() { 13 | return web3.utils.toBN(web3.utils.randomHex(32)); 14 | } 15 | 16 | module.exports = { 17 | getRandomSignedInt, 18 | getRandomUnsignedInt 19 | }; 20 | -------------------------------------------------------------------------------- /core/contracts/oracle/implementation/test/MockAdministratee.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | import "../../interfaces/AdministrateeInterface.sol"; 4 | 5 | 6 | // A mock implementation of AdministrateeInterface, taking the place of a financial contract. 7 | contract MockAdministratee is AdministrateeInterface { 8 | uint256 public timesRemargined; 9 | uint256 public timesEmergencyShutdown; 10 | 11 | function remargin() external override { 12 | timesRemargined++; 13 | } 14 | 15 | function emergencyShutdown() external override { 16 | timesEmergencyShutdown++; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /core/contracts/common/test/TestableTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | import "../implementation/Testable.sol"; 4 | 5 | 6 | // TestableTest is derived from the abstract contract Testable for testing purposes. 7 | contract TestableTest is Testable { 8 | // solhint-disable-next-line no-empty-blocks 9 | constructor(address _timerAddress) public Testable(_timerAddress) {} 10 | 11 | function getTestableTimeAndBlockTime() external view returns (uint256 testableTime, uint256 blockTime) { 12 | // solhint-disable-next-line not-rely-on-time 13 | return (getCurrentTime(), now); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /core/migrations/3_deploy_timer.js: -------------------------------------------------------------------------------- 1 | const Timer = artifacts.require("Timer"); 2 | const { getKeysForNetwork, deploy, enableControllableTiming } = require("../../common/MigrationUtils.js"); 3 | 4 | module.exports = async function(deployer, network, accounts) { 5 | const keys = getKeysForNetwork(network, accounts); 6 | const controllableTiming = enableControllableTiming(network); 7 | 8 | await deploy(deployer, network, Timer, { from: keys.deployer }); 9 | // if (controllableTiming) { 10 | // await deploy(deployer, network, Timer, { from: keys.deployer }); 11 | // } else { 12 | // return; 13 | // } 14 | }; 15 | -------------------------------------------------------------------------------- /scripts/bot-deployment/UpdateBotGCP.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | if [ $# -ne 2 ]; then 5 | echo "Incorrect number of arguments supplied! First argument is bot's name. Second argument is path to config file." 6 | echo "example: ./UpdateBotGCP.sh ethbtc-monitor-bot ./ethbtc-monitor-bot-env.txt" 7 | exit 1 8 | fi 9 | 10 | echo "Updating" $1 11 | echo "Using ENV file:" $2 12 | gcloud compute instances update-container $1 \ 13 | --container-image docker.io/umaprotocol/protocol:latest \ 14 | --container-env-file $2 \ 15 | --zone us-central1-a \ 16 | --container-restart-policy on-failure \ 17 | --container-stdin 18 | -------------------------------------------------------------------------------- /scripts/bot-deployment/DeployBotGCP.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | if [ $# -ne 2 ]; then 5 | echo "Incorrect number of arguments supplied! First argument is bot's name. Second argument is path to config file." 6 | echo "example: ./DeployBotGCP.sh ethbtc-monitor-bot ./ethbtc-monitor-bot-env.txt" 7 | exit 1 8 | fi 9 | 10 | echo "Deploying" $1 11 | echo "Using ENV file:" $1 12 | gcloud compute instances create-with-container $1 \ 13 | --container-image docker.io/umaprotocol/protocol:latest \ 14 | --container-env-file $2 \ 15 | --zone us-central1-a \ 16 | --container-restart-policy on-failure \ 17 | --container-stdin 18 | -------------------------------------------------------------------------------- /core/scripts/cli/wallet/getDefaultAccount.js: -------------------------------------------------------------------------------- 1 | // Main method for getting default account, used to sign all transactions in the CLI! 2 | const DEFAULT_ACCOUNT_INDEX = 0; 3 | 4 | /** 5 | * Returns the default account that the Voting CLI will use to commit and reveal votes. 6 | * @param {* Object} web3 Web3 provider 7 | */ 8 | const getDefaultAccount = async web3 => { 9 | const accounts = await web3.eth.getAccounts(); 10 | 11 | if (accounts.length === 0) { 12 | throw new Error("No accounts in web3.eth.accounts.wallet"); 13 | } else { 14 | return accounts[DEFAULT_ACCOUNT_INDEX]; 15 | } 16 | }; 17 | 18 | module.exports = getDefaultAccount; 19 | -------------------------------------------------------------------------------- /core/contracts/oracle/interfaces/AdministrateeInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | 4 | /** 5 | * @title Interface that all financial contracts expose to the admin. 6 | */ 7 | interface AdministrateeInterface { 8 | /** 9 | * @notice Initiates the shutdown process, in case of an emergency. 10 | */ 11 | function emergencyShutdown() external; 12 | 13 | /** 14 | * @notice A core contract method called independently or as a part of other financial contract transactions. 15 | * @dev It pays fees and moves money between margin accounts to make sure they reflect the NAV of the contract. 16 | */ 17 | function remargin() external; 18 | } 19 | -------------------------------------------------------------------------------- /core/utils/Serving.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const { decorateApp } = require("@awaitjs/express"); 3 | const app = decorateApp(express()); 4 | 5 | // Calls a function on every request. 6 | async function triggerOnRequest(fn) { 7 | // GCP PubSub pushes come as POSTs. 8 | app.postAsync("/", async (req, res) => { 9 | console.log("Received a request."); 10 | 11 | await fn(); 12 | res.status(200).send("Done."); 13 | console.log("Finished processing request."); 14 | }); 15 | 16 | app.listen(process.env.PORT, () => { 17 | console.log("Listening on port", process.env.PORT); 18 | }); 19 | } 20 | 21 | module.exports = { 22 | triggerOnRequest 23 | }; 24 | -------------------------------------------------------------------------------- /core/migrations/6_deploy_registry.js: -------------------------------------------------------------------------------- 1 | const Finder = artifacts.require("Finder"); 2 | const Registry = artifacts.require("Registry"); 3 | const { getKeysForNetwork, deploy } = require("../../common/MigrationUtils.js"); 4 | const { interfaceName } = require("../utils/Constants.js"); 5 | 6 | module.exports = async function(deployer, network, accounts) { 7 | const keys = getKeysForNetwork(network, accounts); 8 | 9 | const { contract: registry } = await deploy(deployer, network, Registry, { from: keys.deployer }); 10 | 11 | const finder = await Finder.deployed(); 12 | await finder.changeImplementationAddress(web3.utils.utf8ToHex(interfaceName.Registry), registry.address, { 13 | from: keys.deployer 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /core/test/scripts/EmergencyShutdown.js: -------------------------------------------------------------------------------- 1 | const EmergencyShutdown = require("../../scripts/EmergencyShutdown"); 2 | 3 | const MockAdministratee = artifacts.require("MockAdministratee"); 4 | const FinancialContractsAdmin = artifacts.require("FinancialContractsAdmin"); 5 | 6 | contract("scripts/EmergencyShutdown.js", function(accounts) { 7 | const owner = accounts[0]; 8 | 9 | it("Emergency shutdown", async function() { 10 | const administratee = await MockAdministratee.new(); 11 | 12 | // Call emergency shutdown 13 | await EmergencyShutdown.run(owner, administratee.address); 14 | 15 | // Emergency shutdown called. 16 | assert.equal((await administratee.timesEmergencyShutdown()).toString(), "1"); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /core/migrations/11_support_identifiers.js: -------------------------------------------------------------------------------- 1 | const Voting = artifacts.require("Voting"); 2 | const IdentiferWhitelist = artifacts.require("IdentifierWhitelist"); 3 | const { getKeysForNetwork } = require("../../common/MigrationUtils.js"); 4 | const identifiers = require("../config/identifiers"); 5 | 6 | module.exports = async function(deployer, network, accounts) { 7 | const keys = getKeysForNetwork(network, accounts); 8 | 9 | const supportedIdentifiers = await IdentiferWhitelist.deployed(); 10 | 11 | for (const identifier of Object.keys(identifiers)) { 12 | const identifierBytes = web3.utils.utf8ToHex(identifier); 13 | await supportedIdentifiers.addSupportedIdentifier(identifierBytes, { from: keys.deployer }); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /ci/run_slither.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | PROTOCOL_DIR=$(pwd) 5 | sudo chmod -R a+rwx /usr/local/lib/node_modules 6 | 7 | 8 | run_slither() { 9 | cd $1 10 | mkdir -p node_modules/ 11 | cp -r ../node_modules/@openzeppelin ./node_modules/@openzeppelin 12 | 13 | cd $PROTOCOL_DIR 14 | 15 | # print out slither version for debugging 16 | slither --version 17 | slither --exclude=divide-before-multiply,locked-ether,unused-return,timestamp,naming-convention,pragma,solc-version,external-function,reentrancy-benign,reentrancy-no-eth,arbitrary-send,incorrect-equality,reentrancy-events,assembly --filter-paths="@openzeppelin|WETH9.sol|ReentrancyAttack.sol|ReentrancyMock.sol" $1 18 | } 19 | 20 | run_slither $PROTOCOL_DIR/core 21 | -------------------------------------------------------------------------------- /financial-templates-lib/price-feed/Networker.js: -------------------------------------------------------------------------------- 1 | const fetch = require("node-fetch"); 2 | 3 | // This class makes networking calls on behalf of the caller. 4 | // Note: this is separated out to allow this functionality to be mocked out in tests so no real network calls have to 5 | // be made. 6 | class Networker { 7 | constructor(logger) { 8 | this.logger = logger; 9 | } 10 | 11 | async getJson(url) { 12 | const response = await fetch(url); 13 | const json = await response.json(); 14 | if (!json) { 15 | this.logger.error({ 16 | at: "Networker", 17 | message: "Failed to get json response", 18 | url: url 19 | }); 20 | } 21 | return json; 22 | } 23 | } 24 | 25 | module.exports = { 26 | Networker 27 | }; 28 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true 4 | }, 5 | extends: ["plugin:prettier/recommended"], 6 | plugins: ["prettier", "mocha"], 7 | parserOptions: { 8 | ecmaFeatures: { 9 | jsx: true, 10 | modules: true 11 | } 12 | }, 13 | parser: "babel-eslint", 14 | rules: { 15 | "prettier/prettier": ["error"], 16 | indent: ["error", 2, { SwitchCase: 1 }], 17 | "linebreak-style": ["error", "unix"], 18 | quotes: ["error", "double", { avoidEscape: true }], 19 | semi: ["error", "always"], 20 | "spaced-comment": ["error", "always", { exceptions: ["-", "+"] }], 21 | "mocha/no-exclusive-tests": "error" 22 | }, 23 | settings: { 24 | "mocha/additionalTestFunctions": ["describeModule"] 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /scripts/bot-deployment/PushBotHotFix.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | if [ $# -ne 1 ]; then 5 | echo "Incorrect number of arguments supplied! First and only argument is bot's name." 6 | echo "example: ./PushBotHotFix.sh ethbtc-monitor-bot" 7 | exit 1 8 | fi 9 | 10 | echo "Pushing hot fix to" $1 11 | echo "Building docker container..." 12 | docker build -t umaprotocol/protocol:hotfix . 13 | 14 | echo "Pushing docker container to docker hub..." 15 | docker push umaprotocol/protocol:hotfix 16 | 17 | echo "Pushing docker container to docker hub..." 18 | gcloud compute instances update-container $1 \ 19 | --container-image docker.io/umaprotocol/protocol:hotfix \ 20 | --zone us-central1-a \ 21 | --container-restart-policy on-failure \ 22 | --container-stdin -------------------------------------------------------------------------------- /common/Enums.js: -------------------------------------------------------------------------------- 1 | // Corresponds to Registry.Roles. 2 | const RegistryRolesEnum = { 3 | OWNER: "0", 4 | CONTRACT_CREATOR: "1" 5 | }; 6 | 7 | // Corresponds to VoteTiming.Phase. 8 | const VotePhasesEnum = { 9 | COMMIT: "0", 10 | REVEAL: "1" 11 | }; 12 | 13 | // States for an EMP's Liquidation to be in. 14 | const LiquidationStatesEnum = { 15 | UNINITIALIZED: "0", 16 | PRE_DISPUTE: "1", 17 | PENDING_DISPUTE: "2", 18 | DISPUTE_SUCCEEDED: "3", 19 | DISPUTE_FAILED: "4" 20 | }; 21 | 22 | // States for an EMP's Position to be in. 23 | const PositionStatesEnum = { 24 | OPEN: "0", 25 | EXPIRED_PRICE_REQUESTED: "1", 26 | EXPIRED_PRICE_RECEIVED: "2" 27 | }; 28 | 29 | module.exports = { 30 | RegistryRolesEnum, 31 | VotePhasesEnum, 32 | LiquidationStatesEnum, 33 | PositionStatesEnum 34 | }; 35 | -------------------------------------------------------------------------------- /core/contracts/common/interfaces/ExpandedIERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 4 | 5 | 6 | /** 7 | * @title ERC20 interface that includes burn and mint methods. 8 | */ 9 | abstract contract ExpandedIERC20 is IERC20 { 10 | /** 11 | * @notice Burns a specific amount of the caller's tokens. 12 | * @dev Only burns the caller's tokens, so it is safe to leave this method permissionless. 13 | */ 14 | function burn(uint256 value) external virtual; 15 | 16 | /** 17 | * @notice Mints tokens and adds them to the balance of the `to` address. 18 | * @dev This method should be permissioned to only allow designated parties to mint tokens. 19 | */ 20 | function mint(address to, uint256 value) external virtual returns (bool); 21 | } 22 | -------------------------------------------------------------------------------- /core/contracts/oracle/implementation/ContractCreator.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | import "../interfaces/FinderInterface.sol"; 4 | import "./Registry.sol"; 5 | import "./Constants.sol"; 6 | 7 | 8 | /** 9 | * @title Base contract for all financial contract creators 10 | */ 11 | abstract contract ContractCreator { 12 | address internal finderAddress; 13 | 14 | constructor(address _finderAddress) public { 15 | finderAddress = _finderAddress; 16 | } 17 | 18 | function _registerContract(address[] memory parties, address contractToRegister) internal { 19 | FinderInterface finder = FinderInterface(finderAddress); 20 | Registry registry = Registry(finder.getImplementationAddress(OracleInterfaces.Registry)); 21 | registry.registerContract(parties, contractToRegister); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /core/contracts/common/test/WithdrawableTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | import "../implementation/Withdrawable.sol"; 4 | 5 | 6 | // WithdrawableTest is derived from the abstract contract Withdrawable for testing purposes. 7 | contract WithdrawableTest is Withdrawable { 8 | enum Roles { Governance, Withdraw } 9 | 10 | // solhint-disable-next-line no-empty-blocks 11 | constructor() public { 12 | _createExclusiveRole(uint256(Roles.Governance), uint256(Roles.Governance), msg.sender); 13 | _createWithdrawRole(uint256(Roles.Withdraw), uint256(Roles.Governance), msg.sender); 14 | } 15 | 16 | function pay() external payable { 17 | require(msg.value > 0); 18 | } 19 | 20 | function setInternalWithdrawRole(uint256 setRoleId) public { 21 | _setWithdrawRole(setRoleId); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /core/scripts/local/AddMemberToRole.js: -------------------------------------------------------------------------------- 1 | const argv = require("minimist")(process.argv.slice(), { string: ["contract", "role_id", "address"] }); 2 | 3 | const MultiRole = artifacts.require("MultiRole"); 4 | 5 | const addMemberToRole = async function(callback) { 6 | try { 7 | const roleManager = (await web3.eth.getAccounts())[0]; 8 | 9 | // Initialize the MultiRole interface from the provided address. 10 | const multiRole = await MultiRole.at(argv.contract); 11 | 12 | // Add the new member. 13 | await multiRole.addMember(argv.role_id, argv.address, { from: roleManager }); 14 | 15 | console.log(`Added ${argv.address} to role id ${argv.role_id} to MultiRole contract at ${argv.contract}`); 16 | } catch (e) { 17 | console.log(`ERROR: ${e}`); 18 | } 19 | 20 | callback(); 21 | }; 22 | 23 | module.exports = addMemberToRole; 24 | -------------------------------------------------------------------------------- /financial-templates-lib/test/price-feed/NetworkerMock.js: -------------------------------------------------------------------------------- 1 | // A mock of the Networker to allow the user to check the inputs and set the outputs of network requests. 2 | class NetworkerMock { 3 | // Value that will hold the most recent input to getJson. 4 | getJsonInputs = []; 5 | 6 | // Value that will be returned on the next call to getJson. 7 | // Users of this mock should set this value to force getJson to return the value. 8 | getJsonReturns = []; 9 | 10 | // Mocked getJson function. 11 | async getJson(url) { 12 | // Note: shift and unshift add and remove from the front of the array, so the elements are ordered such that the 13 | // first elements in the arrays are the first in/out. 14 | this.getJsonInputs.unshift(url); 15 | return this.getJsonReturns.shift(); 16 | } 17 | } 18 | 19 | module.exports = { 20 | NetworkerMock 21 | }; 22 | -------------------------------------------------------------------------------- /core/contracts/financial-templates/expiring-multiparty/ExpiringMultiParty.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "./Liquidatable.sol"; 5 | 6 | 7 | /** 8 | * @title Expiring Multi Party. 9 | * @notice Convenient wrapper for Liquidatable. 10 | */ 11 | contract ExpiringMultiParty is Liquidatable { 12 | /** 13 | * @notice Constructs the ExpiringMultiParty contract. 14 | * @param params struct to define input parameters for construction of Liquidatable. Some params 15 | * are fed directly into the PricelessPositionManager's constructor within the inheritance tree. 16 | */ 17 | constructor(ConstructorParams memory params) 18 | public 19 | Liquidatable(params) 20 | // Note: since there is no logic here, there is no need to add a re-entrancy guard. 21 | { 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /core/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | 4 | /** 5 | * @title Used internally by Truffle migrations. 6 | * @dev See https://www.trufflesuite.com/docs/truffle/getting-started/running-migrations#initial-migration for details. 7 | */ 8 | contract Migrations { 9 | address public owner; 10 | uint256 public last_completed_migration; 11 | 12 | constructor() public { 13 | owner = msg.sender; 14 | } 15 | 16 | modifier restricted() { 17 | if (msg.sender == owner) _; 18 | } 19 | 20 | function setCompleted(uint256 completed) public restricted { 21 | last_completed_migration = completed; 22 | } 23 | 24 | function upgrade(address new_address) public restricted { 25 | Migrations upgraded = Migrations(new_address); 26 | upgraded.setCompleted(last_completed_migration); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /core/migrations/7_deploy_financial_contracts_admin.js: -------------------------------------------------------------------------------- 1 | const FinancialContractsAdmin = artifacts.require("FinancialContractsAdmin"); 2 | const Finder = artifacts.require("Finder"); 3 | const { getKeysForNetwork, deploy } = require("../../common/MigrationUtils.js"); 4 | const { interfaceName } = require("../utils/Constants.js"); 5 | 6 | module.exports = async function(deployer, network, accounts) { 7 | const keys = getKeysForNetwork(network, accounts); 8 | 9 | const { contract: financialContractsAdmin } = await deploy(deployer, network, FinancialContractsAdmin, { 10 | from: keys.deployer 11 | }); 12 | 13 | const finder = await Finder.deployed(); 14 | await finder.changeImplementationAddress( 15 | web3.utils.utf8ToHex(interfaceName.FinancialContractsAdmin), 16 | financialContractsAdmin.address, 17 | { 18 | from: keys.deployer 19 | } 20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /common/Constants.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // These enforce the maximum number of transactions that can fit within one batch-commit and batch-reveal. 3 | // Based off the current gas limit from Etherscan over the last 6 months of 9950000, 4 | // the following maximum batchCommit, batchReveal and retrieveRewards are possible: 5 | // - batchCommit: 28 commits, 6654676 gas used 6 | // - batchReveal: 58 commits, 5828051 gas used 7 | // - retrieveRewards: 129 commits, 3344083 gas used 8 | // Practically, we set a safe upper bound of 25 batch commits & reveals and 100 retrievals. 9 | BATCH_MAX_COMMITS: 25, 10 | BATCH_MAX_REVEALS: 25, 11 | BATCH_MAX_RETRIEVALS: 100, 12 | 13 | MAX_UINT_VAL: "115792089237316195423570985008687907853269984665640564039457584007913129639935", 14 | 15 | // Max integer that can be safely stored in a vanilla js int. 16 | MAX_SAFE_JS_INT: 2147483647 17 | }; 18 | -------------------------------------------------------------------------------- /core/contracts/oracle/implementation/test/GovernorTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | pragma experimental ABIEncoderV2; 4 | 5 | import "../Governor.sol"; 6 | 7 | 8 | // GovernorTest exposes internal methods in the Governor for testing. 9 | contract GovernorTest is Governor { 10 | constructor(address _timerAddress) public Governor(address(0), 0, _timerAddress) {} 11 | 12 | function addPrefix( 13 | bytes32 input, 14 | bytes32 prefix, 15 | uint256 prefixLength 16 | ) external pure returns (bytes32) { 17 | return _addPrefix(input, prefix, prefixLength); 18 | } 19 | 20 | function uintToUtf8(uint256 v) external pure returns (bytes32 ret) { 21 | return _uintToUtf8(v); 22 | } 23 | 24 | function constructIdentifier(uint256 id) external pure returns (bytes32 identifier) { 25 | return _constructIdentifier(id); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /voter-dapp/src/DrizzleInit.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { drizzleReactHooks } from "@umaprotocol/react-plugin"; 3 | 4 | // This component is just a wrapper to trigger a callback once drizzle has been initialized. 5 | // It must be a separate component to allow useDrizzleState to only be called after the user has initiated the login 6 | // flow. 7 | function DrizzleInit(props) { 8 | const { initializationFinished } = props; 9 | 10 | const drizzleState = drizzleReactHooks.useDrizzleState(drizzleState => { 11 | return { 12 | initialized: drizzleState.drizzleStatus.initialized 13 | }; 14 | }); 15 | 16 | useEffect(() => { 17 | // Once drizzle is initialized, trigger the callback. 18 | if (drizzleState.initialized) { 19 | initializationFinished(); 20 | } 21 | }, [initializationFinished, drizzleState]); 22 | 23 | return ""; 24 | } 25 | 26 | export default DrizzleInit; 27 | -------------------------------------------------------------------------------- /core/scripts/cli/sponsor/transactionUtils.js: -------------------------------------------------------------------------------- 1 | const style = require("../textStyle"); 2 | const PublicNetworks = require("../../../../common/PublicNetworks"); 3 | 4 | const submitTransaction = async (web3, submitFn, message, transactionNum, totalTransactions) => { 5 | const etherscanBaseUrl = PublicNetworks[web3.networkId] 6 | ? PublicNetworks[web3.networkId].etherscan 7 | : "https://fake-etherscan.com"; 8 | 9 | if (totalTransactions > 1) { 10 | console.log(`(${transactionNum}/${totalTransactions}) ${message}`); 11 | } else { 12 | console.log(message); 13 | } 14 | style.spinnerWritingContracts.start(); 15 | const { receipt } = await submitFn(); 16 | style.spinnerWritingContracts.stop(); 17 | const etherscanLink = `${etherscanBaseUrl}/tx/${receipt.transactionHash}`; 18 | console.log(`Transaction submitted. Transaction link: ${etherscanLink}`); 19 | }; 20 | 21 | module.exports = { 22 | submitTransaction 23 | }; 24 | -------------------------------------------------------------------------------- /core/contracts/oracle/implementation/test/VoteTimingTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | import "../../interfaces/VotingInterface.sol"; 4 | import "../VoteTiming.sol"; 5 | 6 | 7 | // Wraps the library VoteTiming for testing purposes. 8 | contract VoteTimingTest { 9 | using VoteTiming for VoteTiming.Data; 10 | 11 | VoteTiming.Data public voteTiming; 12 | 13 | constructor(uint256 phaseLength) public { 14 | wrapInit(phaseLength); 15 | } 16 | 17 | function wrapComputeCurrentRoundId(uint256 currentTime) external view returns (uint256) { 18 | return voteTiming.computeCurrentRoundId(currentTime); 19 | } 20 | 21 | function wrapComputeCurrentPhase(uint256 currentTime) external view returns (VotingInterface.Phase) { 22 | return voteTiming.computeCurrentPhase(currentTime); 23 | } 24 | 25 | function wrapInit(uint256 phaseLength) public { 26 | voteTiming.init(phaseLength); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /documentation/map.txt: -------------------------------------------------------------------------------- 1 | * getting_started 2 | ** architecture_overview.md 3 | ** priceless_defi_contracts.md 4 | ** uma_oracle_design.md 5 | * synthetic_tokens 6 | ** explainer.md 7 | ** tutorials 8 | *** prerequisites.md 9 | *** creating_from_truffle.md 10 | *** using_the_uma_sponsor_cli_tool.md 11 | ** known_issues.md 12 | ** glossary.md 13 | * oracle 14 | ** technical_architecture.md 15 | ** economic_architecture.md 16 | ** dvm_interfaces.md 17 | ** known_issues.md 18 | ** tutorials 19 | *** voting_with_uma_tokens.md 20 | *** voting_with_UMA_2-key_contract.md 21 | *** integrating_the_dvm.md 22 | ** governance 23 | *** UMA_token_holder_responsibilities.md 24 | *** UMIPs.md 25 | *** adding_a_price_identifier.md 26 | ** mainnet_deployment_info.md 27 | * developer_reference 28 | ** contract_addresses.md 29 | ** bug_bounty.md 30 | * community 31 | ** previous_and_upcoming_events.md 32 | ** interviews_and_press.md 33 | ** blog_posts.md 34 | ** connecting_with_UMA.md 35 | * contracts 36 | -------------------------------------------------------------------------------- /voter-dapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "voter-dapp", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@umaprotocol/react-plugin": "^1.5.5", 7 | "@umaprotocol/store": "^1.5.6", 8 | "@material-ui/core": "^4.3.1", 9 | "@material-ui/icons": "^3.0.2", 10 | "@material-ui/styles": "^4.3.0", 11 | "react": "^16.8.6", 12 | "react-dom": "^16.8.6", 13 | "react-scripts": "3.0.1" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": "react-app" 23 | }, 24 | "browserslist": { 25 | "production": [ 26 | ">0.2%", 27 | "not dead", 28 | "not op_mini all" 29 | ], 30 | "development": [ 31 | "last 1 chrome version", 32 | "last 1 firefox version", 33 | "last 1 safari version" 34 | ] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/scripts/local/MintNewTokens.js: -------------------------------------------------------------------------------- 1 | const argv = require("minimist")(process.argv.slice(), { string: ["to", "token", "amount"] }); 2 | 3 | // Note: this interface also contains a `burn` method, but it isn't used in this script, so it's safe to pass in 4 | // addresses that do not have the `burn` method. 5 | const Mintable = artifacts.require("ExpandedIERC20"); 6 | 7 | const mintNewTokens = async function(callback) { 8 | try { 9 | const deployer = (await web3.eth.getAccounts())[0]; 10 | 11 | // Initialize the token contract from the address. 12 | const marginToken = await Mintable.at(argv.token); 13 | 14 | // Mint new tokens. 15 | await marginToken.mint(argv.to, web3.utils.toWei(argv.amount, "ether"), { from: deployer }); 16 | 17 | console.log(`Added ${argv.amount} token(s) at ${marginToken.address} to account ${argv.to}`); 18 | } catch (e) { 19 | console.log(`ERROR: ${e}`); 20 | } 21 | 22 | callback(); 23 | }; 24 | 25 | module.exports = mintNewTokens; 26 | -------------------------------------------------------------------------------- /core/contracts/common/test/MultiRoleTest.sol: -------------------------------------------------------------------------------- 1 | /* 2 | MultiRoleTest contract. 3 | */ 4 | 5 | pragma solidity ^0.6.0; 6 | 7 | import "../implementation/MultiRole.sol"; 8 | 9 | 10 | // The purpose of this contract is to make the MultiRole creation methods externally callable for testing purposes. 11 | contract MultiRoleTest is MultiRole { 12 | function createSharedRole( 13 | uint256 roleId, 14 | uint256 managingRoleId, 15 | address[] calldata initialMembers 16 | ) external { 17 | _createSharedRole(roleId, managingRoleId, initialMembers); 18 | } 19 | 20 | function createExclusiveRole( 21 | uint256 roleId, 22 | uint256 managingRoleId, 23 | address initialMember 24 | ) external { 25 | _createExclusiveRole(roleId, managingRoleId, initialMember); 26 | } 27 | 28 | // solhint-disable-next-line no-empty-blocks 29 | function revertIfNotHoldingRole(uint256 roleId) external view onlyRoleHolder(roleId) {} 30 | } 31 | -------------------------------------------------------------------------------- /core/scripts/cli/textStyle.js: -------------------------------------------------------------------------------- 1 | const chalkPipe = require("chalk-pipe"); 2 | const ora = require("ora"); 3 | const moment = require("moment"); 4 | 5 | // General library for displaying text and graphics in terminal in (enjoyable) human readable form. 6 | // Used to standardize text styles across the CLI 7 | const style = { 8 | // Colors 9 | instruction: chalkPipe("bgRed"), 10 | success: chalkPipe("bgGreen"), 11 | warning: chalkPipe("bgYellow"), 12 | help: chalkPipe("bgCyan"), 13 | 14 | // Links 15 | link: chalkPipe("blue.underline"), 16 | 17 | // Spinners 18 | spinnerReadingContracts: ora({ 19 | text: "Reading contracts", 20 | color: "blue" 21 | }), 22 | spinnerWritingContracts: ora({ 23 | text: "Submitting contract transactions", 24 | color: "red" 25 | }), 26 | 27 | // Date format 28 | formatSecondsToUtc: timestampInSeconds => { 29 | return moment.utc(timestampInSeconds * 1000).format("MMMM Do YYYY, h:mm:ss a"); 30 | } 31 | }; 32 | 33 | module.exports = style; 34 | -------------------------------------------------------------------------------- /core/scripts/local/ApproveIdentifiers.js: -------------------------------------------------------------------------------- 1 | const IdentifierWhitelist = artifacts.require("IdentifierWhitelist"); 2 | const identifiers = require("../../config/identifiers"); 3 | 4 | const approveIdentifiers = async function(callback) { 5 | try { 6 | const deployer = (await web3.eth.getAccounts())[0]; 7 | 8 | const supportedIdentifiers = await IdentifierWhitelist.deployed(); 9 | 10 | for (const identifier of Object.keys(identifiers)) { 11 | const identifierBytes = web3.utils.utf8ToHex(identifier); 12 | if (!(await supportedIdentifiers.isIdentifierSupported(identifierBytes))) { 13 | await supportedIdentifiers.addSupportedIdentifier(identifierBytes, { from: deployer }); 14 | console.log(`Approved new identifier: ${identifier}`); 15 | } else { 16 | console.log(`${identifier} is already approved.`); 17 | } 18 | } 19 | } catch (e) { 20 | console.log(`ERROR: ${e}`); 21 | } 22 | 23 | callback(); 24 | }; 25 | 26 | module.exports = approveIdentifiers; 27 | -------------------------------------------------------------------------------- /core/contracts/common/implementation/Timer.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | 4 | /** 5 | * @title Universal store of current contract time for testing environments. 6 | */ 7 | contract Timer { 8 | uint256 private currentTime; 9 | 10 | constructor() public { 11 | currentTime = now; // solhint-disable-line not-rely-on-time 12 | } 13 | 14 | /** 15 | * @notice Sets the current time. 16 | * @dev Will revert if not running in test mode. 17 | * @param time timestamp to set `currentTime` to. 18 | */ 19 | function setCurrentTime(uint256 time) external { 20 | currentTime = time; 21 | } 22 | 23 | /** 24 | * @notice Gets the current time. Will return the last time set in `setCurrentTime` if running in test mode. 25 | * Otherwise, it will return the block timestamp. 26 | * @return uint256 for the current Testable timestamp. 27 | */ 28 | function getCurrentTime() public view returns (uint256) { 29 | return currentTime; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /core/migrations/12_deploy_testnet_token.js: -------------------------------------------------------------------------------- 1 | const TestnetERC20 = artifacts.require("TestnetERC20"); 2 | const { deploy, setToExistingAddress, getKeysForNetwork } = require("../../common/MigrationUtils.js"); 3 | const publicNetworks = require("../../common/PublicNetworks.js"); 4 | 5 | module.exports = async function(deployer, network, accounts) { 6 | const keys = getKeysForNetwork(network, accounts); 7 | 8 | let testnetERC20Address = null; 9 | 10 | for (const { name, daiAddress } of Object.values(publicNetworks)) { 11 | if (network.startsWith(name) && daiAddress) { 12 | await setToExistingAddress(network, TestnetERC20, daiAddress); 13 | testnetERC20Address = daiAddress; 14 | break; 15 | } 16 | } 17 | 18 | if (!testnetERC20Address) { 19 | // Deploy if the network isn't public or if there was no listed DAI address. 20 | ({ 21 | contract: { address: testnetERC20Address } 22 | } = await deploy(deployer, network, TestnetERC20, "Dai Stable Coin", "DAI", 18, { from: keys.deployer })); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /core/contracts/oracle/interfaces/FinderInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | 4 | /** 5 | * @title Provides addresses of the live contracts implementing certain interfaces. 6 | * @dev Examples are the Oracle or Store interfaces. 7 | */ 8 | interface FinderInterface { 9 | /** 10 | * @notice Updates the address of the contract that implements `interfaceName`. 11 | * @param interfaceName bytes32 encoding of the interface name that is either changed or registered. 12 | * @param implementationAddress address of the deployed contract that implements the interface. 13 | */ 14 | function changeImplementationAddress(bytes32 interfaceName, address implementationAddress) external; 15 | 16 | /** 17 | * @notice Gets the address of the contract that implements the given `interfaceName`. 18 | * @param interfaceName queried interface. 19 | * @return implementationAddress address of the deployed contract that implements the interface. 20 | */ 21 | function getImplementationAddress(bytes32 interfaceName) external view returns (address); 22 | } 23 | -------------------------------------------------------------------------------- /core/contracts/financial-templates/expiring-multiparty/ExpiringMultiPartyLib.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "./ExpiringMultiParty.sol"; 5 | 6 | 7 | /** 8 | * @title Provides convenient Expiring Multi Party contract utilities. 9 | * @dev Using this library to deploy EMP's allows calling contracts to avoid importing the full EMP bytecode. 10 | */ 11 | library ExpiringMultiPartyLib { 12 | /** 13 | * @notice Returns address of new EMP deployed with given `params` configuration. 14 | * @dev Caller will need to register new EMP with the Registry to begin requesting prices. Caller is also 15 | * responsible for enforcing constraints on `params`. 16 | * @param params is a `ConstructorParams` object from ExpiringMultiParty. 17 | * @return address of the deployed ExpiringMultiParty contract 18 | */ 19 | function deploy(ExpiringMultiParty.ConstructorParams memory params) public returns (address) { 20 | ExpiringMultiParty derivative = new ExpiringMultiParty(params); 21 | return address(derivative); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /core/contracts/oracle/implementation/test/VotingTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | pragma experimental ABIEncoderV2; 4 | 5 | import "../Voting.sol"; 6 | import "../../../common/implementation/FixedPoint.sol"; 7 | 8 | 9 | // Test contract used to access internal variables in the Voting contract. 10 | contract VotingTest is Voting { 11 | constructor( 12 | uint256 _phaseLength, 13 | FixedPoint.Unsigned memory _gatPercentage, 14 | FixedPoint.Unsigned memory _inflationRate, 15 | uint256 _rewardsExpirationTimeout, 16 | address _votingToken, 17 | address _finder, 18 | address _timerAddress 19 | ) 20 | public 21 | Voting( 22 | _phaseLength, 23 | _gatPercentage, 24 | _inflationRate, 25 | _rewardsExpirationTimeout, 26 | _votingToken, 27 | _finder, 28 | _timerAddress 29 | ) 30 | {} 31 | 32 | function getPendingPriceRequestsArray() external view returns (bytes32[] memory) { 33 | return pendingPriceRequests; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /voter-dapp/README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | ## Available Scripts 4 | 5 | In the project directory, you can run: 6 | 7 | ### `npm start` 8 | 9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 11 | 12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console. 14 | 15 | ### `CI=true npm test` 16 | 17 | Launches the test runner in non-interactive mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 19 | 20 | ### `npm run build` 21 | 22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance. 24 | 25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed! 27 | 28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 29 | -------------------------------------------------------------------------------- /common/PublicNetworks.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 1: { 3 | name: "mainnet", 4 | ethFaucet: null, 5 | etherscan: "https://etherscan.io/", 6 | daiAddress: "0x6B175474E89094C44Da98b954EedeAC495271d0F", 7 | wethAddress: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" 8 | }, 9 | 3: { 10 | name: "ropsten", 11 | ethFaucet: "https://faucet.metamask.io/", 12 | etherscan: "https://ropsten.etherscan.io/", 13 | daiAddress: "0xB5E5D0F8C0cbA267CD3D7035d6AdC8eBA7Df7Cdd", 14 | wethAddress: "0xc778417E063141139Fce010982780140Aa0cD5Ab" 15 | }, 16 | 4: { 17 | name: "rinkeby", 18 | ethFaucet: "https://faucet.rinkeby.io/", 19 | etherscan: "https://rinkeby.etherscan.io/", 20 | daiAddress: "0x5592EC0cfb4dbc12D3aB100b257153436a1f0FEa", 21 | wethAddress: "0xc778417E063141139Fce010982780140Aa0cD5Ab" 22 | }, 23 | 42: { 24 | name: "kovan", 25 | ethFaucet: "https://faucet.kovan.network/", 26 | etherscan: "https://kovan.etherscan.io/", 27 | daiAddress: "0xbF7A7169562078c96f0eC1A8aFD6aE50f12e5A99", 28 | wethAddress: "0xd0A1E359811322d97991E03f863a0C30C2cF029C" 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /core/utils/Voting.js: -------------------------------------------------------------------------------- 1 | const web3 = require("web3"); 2 | 3 | const { VotePhasesEnum } = require("../../common/Enums.js"); 4 | 5 | const secondsPerDay = web3.utils.toBN(86400); 6 | 7 | // Moves the voting contract to the first phase of the next round. 8 | async function moveToNextRound(voting) { 9 | const phase = await voting.getVotePhase(); 10 | const currentTime = await voting.getCurrentTime(); 11 | let timeIncrement; 12 | if (phase.toString() === VotePhasesEnum.COMMIT) { 13 | // Commit phase, so it will take 2 days to move to the next round. 14 | timeIncrement = secondsPerDay.muln(2); 15 | } else { 16 | // Reveal phase, so it will take 1 day to move to the next round. 17 | timeIncrement = secondsPerDay; 18 | } 19 | 20 | await voting.setCurrentTime(currentTime.add(timeIncrement)); 21 | } 22 | 23 | // Moves the voting contract to the next phase. 24 | async function moveToNextPhase(voting) { 25 | const currentTime = await voting.getCurrentTime(); 26 | await voting.setCurrentTime(currentTime.add(secondsPerDay)); 27 | } 28 | 29 | module.exports = { 30 | moveToNextRound, 31 | moveToNextPhase 32 | }; 33 | -------------------------------------------------------------------------------- /scripts/deploy_docs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | # Usage: 5 | # ./scripts/deploy_dapp.sh 6 | 7 | # Note: you must have the gcloud CLI tool installed and authenticated before using this script. 8 | # Note: you must also have pandoc installed before running. 9 | 10 | # Get the absolute path of a file. 11 | # Credit: https://stackoverflow.com/a/21188136 12 | get_abs_filename() { 13 | # $1 : relative filename 14 | echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")" 15 | } 16 | 17 | # Grab the absolute path for the provided file. 18 | APP_YAML_PATH=$(get_abs_filename $1) 19 | 20 | # Shift the arguments to provide to gcloud app deploy. 21 | shift 22 | 23 | # Build the docs site. 24 | echo "Building docs site locally." 25 | npm install 26 | ./scripts/build_docs_site.sh 27 | 28 | # Prepare for gcloud deploy. 29 | echo "Moving files." 30 | rm -rf build/docs 31 | mkdir -p build/docs 32 | cp -R build/site build/docs/www 33 | cp $APP_YAML_PATH build/docs/ 34 | cd build/docs 35 | 36 | # Deploy. 37 | echo "Deploying." 38 | gcloud app deploy gae_app.yaml "$@" 39 | 40 | -------------------------------------------------------------------------------- /voter-dapp/src/TokenTracker.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Button from "@material-ui/core/Button"; 3 | import { drizzleReactHooks } from "@umaprotocol/react-plugin"; 4 | import { useTableStyles } from "./Styles.js"; 5 | 6 | function TokenTracker() { 7 | const { drizzle } = drizzleReactHooks.useDrizzle(); 8 | const classes = useTableStyles(); 9 | 10 | const addTokenToWallet = () => { 11 | window.web3.currentProvider.sendAsync({ 12 | method: "wallet_watchAsset", 13 | params: { 14 | type: "ERC20", 15 | options: { 16 | address: drizzle.contracts.VotingToken.address, 17 | symbol: "UMA", 18 | decimals: 18, 19 | image: "https://umaproject.org/assets/images/UMA_square_red_logo.png" 20 | } 21 | }, 22 | id: Math.round(Math.random() * 100000) 23 | }); 24 | }; 25 | 26 | return ( 27 |
28 | 31 |
32 | ); 33 | } 34 | 35 | export default TokenTracker; 36 | -------------------------------------------------------------------------------- /core/contracts/oracle/implementation/test/ResultComputationTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | pragma experimental ABIEncoderV2; 4 | 5 | import "../ResultComputation.sol"; 6 | import "../../../common/implementation/FixedPoint.sol"; 7 | 8 | 9 | // Wraps the library ResultComputation for testing purposes. 10 | contract ResultComputationTest { 11 | using ResultComputation for ResultComputation.Data; 12 | 13 | ResultComputation.Data public data; 14 | 15 | function wrapAddVote(int256 votePrice, uint256 numberTokens) external { 16 | data.addVote(votePrice, FixedPoint.Unsigned(numberTokens)); 17 | } 18 | 19 | function wrapGetResolvedPrice(uint256 minVoteThreshold) external view returns (bool isResolved, int256 price) { 20 | return data.getResolvedPrice(FixedPoint.Unsigned(minVoteThreshold)); 21 | } 22 | 23 | function wrapWasVoteCorrect(bytes32 revealHash) external view returns (bool) { 24 | return data.wasVoteCorrect(revealHash); 25 | } 26 | 27 | function wrapGetTotalCorrectlyVotedTokens() external view returns (uint256) { 28 | return data.getTotalCorrectlyVotedTokens().rawValue; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /core/scripts/cli/sponsor.js: -------------------------------------------------------------------------------- 1 | const inquirer = require("inquirer"); 2 | const listMarkets = require("./sponsor/listMarkets"); 3 | const listExpiredMarkets = require("./sponsor/listExpiredMarkets"); 4 | 5 | const ACTIONS = { 6 | listMarkets: "List live markets", 7 | listExpiredMarkets: "List expired markets", 8 | back: "Back" 9 | }; 10 | 11 | const sponsor = async () => { 12 | const prompts = [ 13 | { 14 | type: "list", 15 | name: "sponsorTopMenu", 16 | message: "Sponsor top level menu. What would you like to do?", 17 | choices: Object.values(ACTIONS) 18 | } 19 | ]; 20 | return await inquirer.prompt(prompts); 21 | }; 22 | 23 | const sponsorMenu = async (web3, artifacts) => { 24 | const inputs = (await sponsor())["sponsorTopMenu"]; 25 | switch (inputs) { 26 | case ACTIONS.listMarkets: 27 | await listMarkets(web3, artifacts); 28 | break; 29 | case ACTIONS.listExpiredMarkets: 30 | await listExpiredMarkets(web3, artifacts); 31 | break; 32 | case ACTIONS.back: 33 | return; 34 | default: 35 | console.log("unimplemented state"); 36 | } 37 | }; 38 | 39 | module.exports = sponsorMenu; 40 | -------------------------------------------------------------------------------- /common/EncryptionHelper.js: -------------------------------------------------------------------------------- 1 | const web3 = require("web3"); 2 | 3 | // Web3's soliditySha3 will attempt to auto-detect the type of given input parameters, 4 | // but this won't produce expected behavior for certain types such as `bytes32` or `address`. 5 | // Therefore, these helper methods will explicitly set types. 6 | 7 | function computeTopicHash(request, roundId) { 8 | return web3.utils.soliditySha3( 9 | { t: "bytes32", v: request.identifier }, 10 | { t: "uint", v: request.time }, 11 | { t: "uint", v: roundId } 12 | ); 13 | } 14 | 15 | function computeVoteHash(request) { 16 | return web3.utils.soliditySha3( 17 | { t: "int", v: request.price }, 18 | { t: "int", v: request.salt }, 19 | { t: "address", v: request.account }, 20 | { t: "uint", v: request.time }, 21 | { t: "uint", v: request.roundId }, 22 | { t: "bytes32", v: request.identifier } 23 | ); 24 | } 25 | 26 | function getKeyGenMessage(roundId) { 27 | // TODO: discuss dApp tradeoffs for changing this to a per-topic hash keypair. 28 | return `UMA Protocol one time key for round: ${roundId.toString()}`; 29 | } 30 | 31 | module.exports = { computeTopicHash, computeVoteHash, getKeyGenMessage }; 32 | -------------------------------------------------------------------------------- /core/scripts/cli/wallet/getTwoKeyContract.js: -------------------------------------------------------------------------------- 1 | const TWO_KEY_ADDRESS = process.env.TWO_KEY_ADDRESS; 2 | 3 | /** 4 | * Validate that 2-Key contract's voter is same as provided default account and return 5 | * an instance of the contract, or return nothing. 6 | */ 7 | const getDefaultAccount = require("./getDefaultAccount"); 8 | 9 | const getTwoKeyContract = async (web3, artifacts) => { 10 | if (TWO_KEY_ADDRESS) { 11 | const DesignatedVoting = artifacts.require("DesignatedVoting"); 12 | try { 13 | const voterAccount = await getDefaultAccount(web3); 14 | const designatedVoting = await DesignatedVoting.at(TWO_KEY_ADDRESS); 15 | // Role ID '0' = Cold Storage Address 16 | // Role ID '1' = Voter/Hot Storage Address 17 | const designatedVoterAccount = await designatedVoting.getMember("1"); 18 | if (designatedVoterAccount !== voterAccount) { 19 | // Provided voting account is not the designated voter for the provided Two Key Contract 20 | } else { 21 | return designatedVoting; 22 | } 23 | } catch (err) { 24 | // Two Key Contract likely does not exist 25 | } 26 | } 27 | // No Two Key Address supplied 28 | }; 29 | 30 | module.exports = getTwoKeyContract; 31 | -------------------------------------------------------------------------------- /core/migrations/8_deploy_store.js: -------------------------------------------------------------------------------- 1 | const Finder = artifacts.require("Finder"); 2 | const Store = artifacts.require("Store"); 3 | const Timer = artifacts.require("Timer"); 4 | 5 | const { getKeysForNetwork, deploy, enableControllableTiming } = require("../../common/MigrationUtils.js"); 6 | const { interfaceName } = require("../utils/Constants.js"); 7 | 8 | module.exports = async function(deployer, network, accounts) { 9 | const keys = getKeysForNetwork(network, accounts); 10 | const controllableTiming = enableControllableTiming(network); 11 | 12 | // Initialize both fees to 0. 13 | const initialFixedOracleFeePerSecondPerPfc = { rawValue: "0" }; 14 | const initialWeeklyDelayFeePerSecondPerPfc = { rawValue: "0" }; 15 | 16 | const { contract: store } = await deploy( 17 | deployer, 18 | network, 19 | Store, 20 | initialFixedOracleFeePerSecondPerPfc, 21 | initialWeeklyDelayFeePerSecondPerPfc, 22 | controllableTiming ? Timer.address : "0x0000000000000000000000000000000000000000", 23 | { from: keys.deployer } 24 | ); 25 | 26 | const finder = await Finder.deployed(); 27 | await finder.changeImplementationAddress(web3.utils.utf8ToHex(interfaceName.Store), store.address, { 28 | from: keys.deployer 29 | }); 30 | }; 31 | -------------------------------------------------------------------------------- /core/scripts/local/CalculateContractBytecode.js: -------------------------------------------------------------------------------- 1 | // This simple script tells you how big your contract byte code is and how much you have until you exceed 2 | // the current block limit as defined by EIP170. This script should be run from the /core directory. 3 | // To run the script navigate to /core and then run: 4 | // truffle exec -c ./scripts/local/CalculateContractBytecode.js --contract Voting --network test 5 | // where voting is the name of the contract you want to check. 6 | 7 | const argv = require("minimist")(process.argv.slice(), { string: ["contract"] }); 8 | 9 | module.exports = async function(callback) { 10 | if (!argv.contract) { 11 | console.log("Please enter the contract name as a parameter as `--contract `."); 12 | callback(); 13 | } 14 | 15 | // Load contracts into script and output info. 16 | console.log("loading", argv.contract + ".json"); 17 | let obj = require("./../../build/contracts/" + argv.contract + ".json"); 18 | const byteCodeSize = (obj.deployedBytecode.length - 2) / 2; 19 | const remainingSize = 2 ** 14 + 2 ** 13 - byteCodeSize; 20 | console.log("Contract is", byteCodeSize, "bytes in size."); 21 | console.log("This leaves a total of", remainingSize, "bytes within the EIP170 limit."); 22 | callback(); 23 | }; 24 | -------------------------------------------------------------------------------- /core/contracts/oracle/implementation/FinancialContractsAdmin.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | import "../interfaces/AdministrateeInterface.sol"; 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | 6 | 7 | /** 8 | * @title Admin for financial contracts in the UMA system. 9 | * @dev Allows appropriately permissioned admin roles to interact with financial contracts. 10 | */ 11 | contract FinancialContractsAdmin is Ownable { 12 | /** 13 | * @notice Calls emergency shutdown on the provided financial contract. 14 | * @param financialContract address of the FinancialContract to be shut down. 15 | */ 16 | function callEmergencyShutdown(address financialContract) external onlyOwner { 17 | AdministrateeInterface administratee = AdministrateeInterface(financialContract); 18 | administratee.emergencyShutdown(); 19 | } 20 | 21 | /** 22 | * @notice Calls remargin on the provided financial contract. 23 | * @param financialContract address of the FinancialContract to be remargined. 24 | */ 25 | function callRemargin(address financialContract) external onlyOwner { 26 | AdministrateeInterface administratee = AdministrateeInterface(financialContract); 27 | administratee.remargin(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /core/scripts/DeployDesignatedVoting.js: -------------------------------------------------------------------------------- 1 | const argv = require("minimist")(process.argv.slice(), { string: ["ownershipAddress"] }); 2 | 3 | const Finder = artifacts.require("Finder"); 4 | const DesignatedVotingFactory = artifacts.require("DesignatedVotingFactory"); 5 | 6 | async function run(deployedFactory, ownershipAddress) { 7 | const account = (await web3.eth.getAccounts())[0]; 8 | // TODO(ptare): Handle the case where a DesignatedVoting is already deployed for this voting address `account`. 9 | await deployedFactory.newDesignatedVoting(ownershipAddress, { from: account }); 10 | const designatedVotingAddress = await deployedFactory.designatedVotingContracts(account); 11 | console.log("VOTING ADDRESS:", account); 12 | console.log("DESIGNATED VOTING ADDRESS:", designatedVotingAddress); 13 | } 14 | 15 | const deployDesignatedVoting = async function(callback) { 16 | if (!argv.ownershipAddress) { 17 | callback("Must include "); 18 | } 19 | try { 20 | const factory = await DesignatedVotingFactory.deployed(); 21 | await run(factory, argv.ownershipAddress); 22 | } catch (e) { 23 | console.log("ERROR:", e); 24 | callback(e); 25 | } 26 | callback(); 27 | }; 28 | 29 | deployDesignatedVoting.run = run; 30 | module.exports = deployDesignatedVoting; 31 | -------------------------------------------------------------------------------- /core/contracts/oracle/interfaces/IdentifierWhitelistInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | pragma experimental ABIEncoderV2; 4 | 5 | 6 | /** 7 | * @title Interface for whitelists of supported identifiers that the oracle can provide prices for. 8 | */ 9 | interface IdentifierWhitelistInterface { 10 | /** 11 | * @notice Adds the provided identifier as a supported identifier. 12 | * @dev Price requests using this identifier will succeed after this call. 13 | * @param identifier bytes32 encoding of the string identifier. Eg: BTC/USD. 14 | */ 15 | function addSupportedIdentifier(bytes32 identifier) external; 16 | 17 | /** 18 | * @notice Removes the identifier from the whitelist. 19 | * @dev Price requests using this identifier will no longer succeed after this call. 20 | * @param identifier bytes32 encoding of the string identifier. Eg: BTC/USD. 21 | */ 22 | function removeSupportedIdentifier(bytes32 identifier) external; 23 | 24 | /** 25 | * @notice Checks whether an identifier is on the whitelist. 26 | * @param identifier bytes32 encoding of the string identifier. Eg: BTC/USD. 27 | * @return bool if the identifier is supported (or not). 28 | */ 29 | function isIdentifierSupported(bytes32 identifier) external view returns (bool); 30 | } 31 | -------------------------------------------------------------------------------- /documentation/community/blog_posts.md: -------------------------------------------------------------------------------- 1 | # Blog Posts and Resources 2 | 3 | ## UMA Papers and Tools 4 | 5 | - Read our [whitepapers](https://github.com/UMAprotocol/whitepaper) - last updated July 11, 2019 6 | - Read our [research paper](https://twitter.com/UMAprotocol/status/1179045704918011906) on using priceless contract design to build a decentralized BitMEX - last updated Sep 30, 2019 7 | - Create a Rinkeby custom token using the Synthetic Token Builder [tool](http://tokenbuilder.umaproject.org), [intro](https://medium.com/uma-project/announcing-the-uma-synthetic-token-builder-8bf37c645e94) 8 | 9 | ## Blog Posts 10 | 11 | - Oct 25, 2019: [Interaxis video explainer](https://www.youtube.com/watch?v=eiQckeh4szU) 12 | - Sep 11, 2019: [DeFi Prime Interview](https://defiprime.com/uma) 13 | - Aug 8, 2019: [Adding Economic Guarantees to Blockchain Oracles](https://medium.com/uma-project/umas-data-verification-mechanism-3c5342759eb8) 14 | - Aug 5, 2019: How to trade the price of time, or an Interest Rate Swaps Workshop, [Video](https://youtu.be/TRITa2JJIyU) 15 | - Jul 23, 2019: [Liquidity Mechanisms for Derivatives](https://medium.com/uma-project/liquidity-mechanisms-for-derivatives-5568cc688b57) 16 | - Jul 16, 2019: [Intro to Synthetic Derivatives](https://medium.com/uma-project/intro-to-blockchain-based-synthetic-derivatives-d3a61f3e6e79) 17 | -------------------------------------------------------------------------------- /core/scripts/cli/sponsor/showExpiredMarketDetails.js: -------------------------------------------------------------------------------- 1 | const inquirer = require("inquirer"); 2 | const getDefaultAccount = require("../wallet/getDefaultAccount"); 3 | const { PositionStatesEnum } = require("../../../../common/Enums"); 4 | 5 | const showExpiredMarketDetails = async (web3, artifacts, emp) => { 6 | const SyntheticToken = artifacts.require("SyntheticToken"); 7 | 8 | const { fromWei } = web3.utils; 9 | 10 | const contractState = (await emp.contractState()).toString(); 11 | if (contractState === PositionStatesEnum.EXPIRED_PRICE_REQUESTED) { 12 | console.log("This contract is waiting for a price from the Oracle. Please check back later."); 13 | } else { 14 | const expiryPrice = (await emp.expiryPrice()).toString(); 15 | console.log(`This market settled to ${fromWei(expiryPrice)}`); 16 | 17 | const sponsorAddress = await getDefaultAccount(web3); 18 | const collateral = (await emp.getCollateral(sponsorAddress)).toString(); 19 | 20 | const tokenAddress = await emp.tokenCurrency(); 21 | const token = await SyntheticToken.at(tokenAddress); 22 | const walletTokens = (await token.balanceOf(sponsorAddress)).toString(); 23 | 24 | console.log(`You have ${fromWei(walletTokens)} tokens and ${fromWei(collateral)} collateral`); 25 | } 26 | }; 27 | 28 | module.exports = showExpiredMarketDetails; 29 | -------------------------------------------------------------------------------- /documentation/oracle/mainnet_deployment_info.md: -------------------------------------------------------------------------------- 1 | # Mainnet Deployment Information 2 | 3 | ## Current DVM Parameters 4 | 5 | The current DVM parameters are as follows. The parameters can be adjusted via the introduction of a new [UMIP](../oracle/governance/UMIPs.md). 6 | 7 | 1. Inflation percentage per vote: 0.05% 8 | 9 | - The token supply is inflated on each resolved vote. If the inflation rate is 5%, then that 5% is split pro rata amongst those who voted correctly. This means that on a per-voter bases, the rewards are >= 5% (you get more if fewer people vote). 10 | 11 | 2. GAT: 5% 12 | 13 | - This is the minimum % of tokens that need to participate in a vote for the vote to resolve and not be rolled to the next round of voting. 14 | - Cannot be changed without upgrading Voting.sol. 15 | 16 | 3. Voting phase length: 24 hours 17 | 18 | - Commit and reveal are phases, so this means a _round_ of voting will take twice as long as a phase. 19 | - Cannot be changed without upgrading Voting.sol. 20 | 21 | 4. Contract tax rate per annum: 0.0% 22 | 5. Tax delay penalty per week: 0% 23 | 6. Final fee: 0 24 | 25 | ## Registered Price Identifiers 26 | 27 | There are currently no price identifiers registered with the DVM. 28 | 29 | ## Registered Financial Contracts 30 | 31 | There are currently no financial contracts registered with the DVM. 32 | -------------------------------------------------------------------------------- /core/scripts/umip-2/3_Verify.js: -------------------------------------------------------------------------------- 1 | // This script verify that the UMPIP-3 upgrade was executed correctly by checking deployed bytecodes, 2 | // assigned ownerships and roles. It can be run on the main net after the upgrade is completed 3 | // or on the local Ganache mainnet fork to validate the execution of the previous two scripts. 4 | // This script does not need any wallets unlocked and does not make any on-chain state changes. It can be run as: 5 | // truffle exec ./scripts/umip-3/3_Verify.js --network mainnet-fork 6 | 7 | const assert = require("assert").strict; 8 | 9 | const IdentifierWhitelist = artifacts.require("IdentifierWhitelist"); 10 | 11 | async function runExport() { 12 | console.log("Running UMIP-2 Upgrade Verifier🔥"); 13 | 14 | const identifierWhitelist = await IdentifierWhitelist.deployed(); 15 | 16 | assert.equal(await identifierWhitelist.isIdentifierSupported(web3.utils.utf8ToHex("ETH/BTC")), true); 17 | 18 | console.log("Upgrade Verified!"); 19 | } 20 | 21 | run = async function(callback) { 22 | try { 23 | await runExport(); 24 | } catch (err) { 25 | callback(err); 26 | return; 27 | } 28 | callback(); 29 | }; 30 | 31 | // Attach this function to the exported function in order to allow the script to be executed through both truffle and a test runner. 32 | run.runExport = runExport; 33 | module.exports = run; 34 | -------------------------------------------------------------------------------- /core/networks/1.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "contractName": "Migrations", 4 | "address": "0x28a5dA04a94F0501A6F77dF8f8A9529d27b92744" 5 | }, 6 | { 7 | "contractName": "Finder", 8 | "address": "0x40f941E48A552bF496B154Af6bf55725f18D77c3" 9 | }, 10 | { 11 | "contractName": "VotingToken", 12 | "address": "0x04Fa0d235C4abf4BcF4787aF4CF447DE572eF828" 13 | }, 14 | { 15 | "contractName": "IdentifierWhitelist", 16 | "address": "0xcF649d9Da4D1362C4DAEa67573430Bd6f945e570" 17 | }, 18 | { 19 | "contractName": "Voting", 20 | "address": "0x9921810C710E7c3f7A7C6831e30929f19537a545" 21 | }, 22 | { 23 | "contractName": "Registry", 24 | "address": "0x3e532e6222afe9Bcf02DCB87216802c75D5113aE" 25 | }, 26 | { 27 | "contractName": "FinancialContractsAdmin", 28 | "address": "0x4E6CCB1dA3C7844887F9A5aF4e8450d9fd90317A" 29 | }, 30 | { 31 | "contractName": "Store", 32 | "address": "0x54f44eA3D2e7aA0ac089c4d8F7C93C27844057BF" 33 | }, 34 | { 35 | "contractName": "Governor", 36 | "address": "0x592349F7DeDB2b75f9d4F194d4b7C16D82E507Dc" 37 | }, 38 | { 39 | "contractName": "DesignatedVotingFactory", 40 | "address": "0xE81EeE5Da165fA6863bBc82dF66E62d18625d592" 41 | }, 42 | { 43 | "contractName": "WETH9", 44 | "address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" 45 | } 46 | ] 47 | -------------------------------------------------------------------------------- /core/networks/4.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "contractName": "Migrations", 4 | "address": "0x50039fAEfebef707cFD94D6d462fE6D10B39207a" 5 | }, 6 | { 7 | "contractName": "Finder", 8 | "address": "0x9e416a429De8773e05563a92de4eD589aB5E5f1E" 9 | }, 10 | { 11 | "contractName": "VotingToken", 12 | "address": "0x00526C1ede46255b752D280620318ab50B24c20e" 13 | }, 14 | { 15 | "contractName": "IdentifierWhitelist", 16 | "address": "0x26f65d7Da395F01A3e9C21595D1A2C0e173d82A4" 17 | }, 18 | { 19 | "contractName": "Voting", 20 | "address": "0x5545092553Cf5Bf786e87a87192E902D50D8f022" 21 | }, 22 | { 23 | "contractName": "Registry", 24 | "address": "0x35E63eA3eb0fb7A3bc543C71FB66412e1F6B0E04" 25 | }, 26 | { 27 | "contractName": "FinancialContractsAdmin", 28 | "address": "0xD8c6dD978a3768F7DDfE3A9aAD2c3Fd75Fa9B6Fd" 29 | }, 30 | { 31 | "contractName": "Store", 32 | "address": "0xa4199d73ae206d49c966cF16c58436851f87d47F" 33 | }, 34 | { 35 | "contractName": "Governor", 36 | "address": "0x09AFD24Acc170c16f4fF64BDf2A4818C515440e8" 37 | }, 38 | { 39 | "contractName": "DesignatedVotingFactory", 40 | "address": "0xad7c5516b25661e0A204646b08024cD82ffe6C48" 41 | }, 42 | { 43 | "contractName": "WETH9", 44 | "address": "0xc778417E063141139Fce010982780140Aa0cD5Ab" 45 | } 46 | ] 47 | -------------------------------------------------------------------------------- /core/scripts/CheckDeploymentValidity.js: -------------------------------------------------------------------------------- 1 | const Finder = artifacts.require("Finder"); 2 | const Migrations = artifacts.require("Migrations"); 3 | const Registry = artifacts.require("Registry"); 4 | const { interfaceName } = require("../utils/Constants.js"); 5 | 6 | const checkDeploymentValidity = async function(callback) { 7 | try { 8 | // Note: this script pulls the all contracts that are deployed as singletons and does a rough verification that 9 | // the deployed address points to a contract of the correct type. This will not catch minor bytecode mismatches. 10 | 11 | // Migrations 12 | const migrations = await Migrations.deployed(); 13 | await migrations.last_completed_migration(); 14 | 15 | // Finder 16 | const finder = await Finder.deployed(); 17 | const registryImplementationAddress = await finder.getImplementationAddress( 18 | web3.utils.utf8ToHex(interfaceName.Registry) 19 | ); 20 | if (registryImplementationAddress != Registry.address) { 21 | throw "Incorrect implementation address for Registry"; 22 | } 23 | 24 | console.log("Deployment looks good!"); 25 | } catch (e) { 26 | // Forces the script to return a nonzero error code so failure can be detected in bash. 27 | callback(e); 28 | return; 29 | } 30 | 31 | callback(); 32 | }; 33 | 34 | module.exports = checkDeploymentValidity; 35 | -------------------------------------------------------------------------------- /financial-templates-lib/logger/ConsoleTransport.js: -------------------------------------------------------------------------------- 1 | // This transport enables Winston logging to the console. 2 | const winston = require("winston"); 3 | const { format } = winston; 4 | const { combine, timestamp, colorize, printf } = format; 5 | 6 | const customFormat = combine( 7 | // Adds level-based coloring. 8 | colorize(), 9 | // Adds timestamp. 10 | timestamp(), 11 | printf(info => { 12 | const { timestamp, level, error, ...args } = info; 13 | 14 | // This slice changes a timestamp formatting from `2020-03-25T10:50:57.168Z` -> `2020-03-25 10:50:57` 15 | const ts = timestamp.slice(0, 19).replace("T", " "); 16 | let log = `${ts} [${level}]: ${Object.keys(args).length ? JSON.stringify(args, null, 2) : ""}`; 17 | 18 | // Winston does not properly log Error objects like console.error() does, so this formatter will search for the Error object 19 | // in the "error" property of "info", and add the error stack to the log. 20 | // Discussion at https://github.com/winstonjs/winston/issues/1338. 21 | if (error && error.stack) { 22 | log = `${log}\n${error.stack}`; 23 | } 24 | 25 | return log; 26 | }) 27 | ); 28 | 29 | function createConsoleTransport() { 30 | return new winston.transports.Console({ 31 | handleExceptions: true, 32 | format: customFormat 33 | }); 34 | } 35 | 36 | module.exports = { createConsoleTransport }; 37 | -------------------------------------------------------------------------------- /financial-templates-lib/logger/SpyTransport.js: -------------------------------------------------------------------------------- 1 | // This transport enables unit tests to validate values passed Winston using a Sinon Spy. 2 | 3 | const Transport = require("winston-transport"); 4 | 5 | class SpyTransport extends Transport { 6 | constructor(winstonOptions, spyOptions) { 7 | super(winstonOptions); 8 | this.spy = spyOptions.spy; // local instance of the spy to capture passed messages. 9 | } 10 | 11 | async log(info, callback) { 12 | // Add info sent to the winston transport to the spy. This enables unit tests to validate what is passed to winston. 13 | this.spy(info); 14 | callback(); 15 | } 16 | } 17 | 18 | // Helper function used by unit tests to check if the last message sent to winston contains a particular string value. 19 | // Caller feeds in the spy instance and the value to check. 20 | const lastSpyLogIncludes = (spy, value) => { 21 | // Sinon's getCall(n) function returns the values sent in in the nth call the the spy. We want to check both the mrkdown 22 | // sent and the message sent to the bot. 23 | const lastReturnedArgMrkdwn = spy.getCall(-1).lastArg.mrkdwn.toString(); 24 | const lastReturnedArgMessage = spy.getCall(-1).lastArg.message.toString(); 25 | return lastReturnedArgMrkdwn.indexOf(value) != -1 || lastReturnedArgMessage.indexOf(value) != -1; 26 | }; 27 | 28 | module.exports = { SpyTransport, lastSpyLogIncludes }; 29 | -------------------------------------------------------------------------------- /financial-templates-lib/test/price-feed/PriceFeedMock.js: -------------------------------------------------------------------------------- 1 | const { PriceFeedInterface } = require("../../price-feed/PriceFeedInterface"); 2 | 3 | // An implementation of PriceFeedInterface that medianizes other price feeds. 4 | class PriceFeedMock extends PriceFeedInterface { 5 | // Constructs the MedianizerPriceFeed. 6 | // priceFeeds a list of priceFeeds to medianize. All elements must be of type PriceFeedInterface. Must be an array of 7 | // at least one element. 8 | constructor(currentPrice, historicalPrice, lastUpdateTime) { 9 | super(); 10 | this.updateCalled = 0; 11 | this.currentPrice = currentPrice; 12 | this.historicalPrice = historicalPrice; 13 | this.lastUpdateTime = lastUpdateTime; 14 | } 15 | 16 | setCurrentPrice(currentPrice) { 17 | this.currentPrice = currentPrice; 18 | } 19 | 20 | setHistoricalPrice(historicalPrice) { 21 | this.historicalPrice = historicalPrice; 22 | } 23 | 24 | setLastUpdateTime(lastUpdateTime) { 25 | this.lastUpdateTime = lastUpdateTime; 26 | } 27 | 28 | getCurrentPrice() { 29 | return this.currentPrice; 30 | } 31 | 32 | getHistoricalPrice(time) { 33 | return this.historicalPrice; 34 | } 35 | 36 | getLastUpdateTime() { 37 | return this.lastUpdateTime; 38 | } 39 | 40 | async update() { 41 | this.updateCalled++; 42 | } 43 | } 44 | 45 | module.exports = { 46 | PriceFeedMock 47 | }; 48 | -------------------------------------------------------------------------------- /core/contracts/common/implementation/TestnetERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 4 | 5 | 6 | /** 7 | * @title An implementation of ERC20 with the same interface as the Compound project's testnet tokens (mainly DAI) 8 | * @dev This contract can be deployed or the interface can be used to communicate with Compound's ERC20 tokens. Note: 9 | * this token should never be used to store real value since it allows permissionless minting. 10 | */ 11 | contract TestnetERC20 is ERC20 { 12 | /** 13 | * @notice Constructs the TestnetERC20. 14 | * @param _name The name which describes the new token. 15 | * @param _symbol The ticker abbreviation of the name. Ideally < 5 chars. 16 | * @param _decimals The number of decimals to define token precision. 17 | */ 18 | constructor( 19 | string memory _name, 20 | string memory _symbol, 21 | uint8 _decimals 22 | ) public ERC20(_name, _symbol) { 23 | _setupDecimals(_decimals); 24 | } 25 | 26 | // Sample token information. 27 | 28 | /** 29 | * @notice Mints value tokens to the owner address. 30 | * @param ownerAddress the address to mint to. 31 | * @param value the amount of tokens to mint. 32 | */ 33 | function allocateTo(address ownerAddress, uint256 value) external { 34 | _mint(ownerAddress, value); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/contracts/financial-templates/common/TokenFactory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | import "./SyntheticToken.sol"; 4 | import "../../common/interfaces/ExpandedIERC20.sol"; 5 | import "../../common/implementation/Lockable.sol"; 6 | 7 | 8 | /** 9 | * @title Factory for creating new mintable and burnable tokens. 10 | */ 11 | 12 | contract TokenFactory is Lockable { 13 | /** 14 | * @notice Create a new token and return it to the caller. 15 | * @dev The caller will become the only minter and burner and the new owner capable of assigning the roles. 16 | * @param tokenName used to describe the new token. 17 | * @param tokenSymbol short ticker abbreviation of the name. Ideally < 5 chars. 18 | * @param tokenDecimals used to define the precision used in the token's numerical representation. 19 | * @return newToken an instance of the newly created token interface. 20 | */ 21 | function createToken( 22 | string calldata tokenName, 23 | string calldata tokenSymbol, 24 | uint8 tokenDecimals 25 | ) external nonReentrant() returns (ExpandedIERC20 newToken) { 26 | SyntheticToken mintableToken = new SyntheticToken(tokenName, tokenSymbol, tokenDecimals); 27 | mintableToken.addMinter(msg.sender); 28 | mintableToken.addBurner(msg.sender); 29 | mintableToken.resetOwner(msg.sender); 30 | newToken = ExpandedIERC20(address(mintableToken)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /core/scripts/cli/sponsor/listMarkets.js: -------------------------------------------------------------------------------- 1 | const inquirer = require("inquirer"); 2 | const showMarketDetails = require("./showMarketDetails"); 3 | const { getMarketSummary } = require("./marketUtils"); 4 | const { PositionStatesEnum } = require("../../../../common/Enums"); 5 | 6 | const listMarkets = async (web3, artifacts) => { 7 | const markets = await getMarketSummary(web3, artifacts); 8 | 9 | // Format a useful display message for each market. 10 | const backChoice = "Back"; 11 | const choices = []; 12 | for (let i = 0; i < markets.length; i++) { 13 | const market = markets[i]; 14 | if (market.contractState !== PositionStatesEnum.OPEN) { 15 | continue; 16 | } 17 | const asPercent = web3.utils.fromWei(market.collateralRequirement.muln(100).toString()); 18 | const display = `${market.symbol}. ${asPercent}% collateralization requirement in ${market.collateralSymbol}. ${market.etherscanLink}`; 19 | 20 | // Using the index as the value lets us easily find the right EMP. 21 | choices.push({ name: display, value: i }); 22 | } 23 | choices.push({ name: backChoice }); 24 | const prompt = { 25 | type: "list", 26 | name: "chosenEmpIdx", 27 | message: "Pick a market", 28 | choices: choices 29 | }; 30 | const input = await inquirer.prompt(prompt); 31 | if (input["chosenEmpIdx"] !== backChoice) { 32 | await showMarketDetails(web3, artifacts, markets[input["chosenEmpIdx"]].emp); 33 | } 34 | }; 35 | 36 | module.exports = listMarkets; 37 | -------------------------------------------------------------------------------- /core/test/common/Testable.js: -------------------------------------------------------------------------------- 1 | const { didContractThrow } = require("../../../common/SolidityTestUtils.js"); 2 | 3 | const TestableTest = artifacts.require("TestableTest"); 4 | const Timer = artifacts.require("Timer"); 5 | 6 | contract("Testable", function() { 7 | it("isTest on", async function() { 8 | const testable = await TestableTest.new(Timer.address); 9 | 10 | await testable.setCurrentTime(0); 11 | assert.equal(await testable.getCurrentTime(), 0); 12 | }); 13 | 14 | it("isTest off", async function() { 15 | const testable = await TestableTest.new("0x0000000000000000000000000000000000000000"); 16 | 17 | // Assert that the latest block's timestamp equals the testable contract's current time. 18 | const { testableTime, blockTime } = await testable.getTestableTimeAndBlockTime(); 19 | assert.equal(testableTime.toString(), blockTime.toString()); 20 | 21 | // Assert that setCurrentTime fails 22 | assert(await didContractThrow(testable.setCurrentTime(0))); 23 | }); 24 | 25 | it("In test environment, different Testable contracts reference the same Timer", async function() { 26 | const testable1 = await TestableTest.new(Timer.address); 27 | const testable2 = await TestableTest.new(Timer.address); 28 | 29 | // Set time on testable1, should be the same on testable2. 30 | await testable1.setCurrentTime(0); 31 | assert.equal(await testable1.getCurrentTime(), 0); 32 | assert.equal(await testable2.getCurrentTime(), 0); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /core/scripts/EmergencyShutdown.js: -------------------------------------------------------------------------------- 1 | const assert = require("assert"); 2 | const { interfaceName } = require("../utils/Constants.js"); 3 | 4 | const FinancialContractsAdmin = artifacts.require("FinancialContractsAdmin"); 5 | const Finder = artifacts.require("Finder"); 6 | 7 | const argv = require("minimist")(process.argv.slice(), { string: ["derivative"] }); 8 | 9 | async function run(account, derivative) { 10 | try { 11 | // Usage: `truffle exec scripts/EmergencyShutdown.js --derivative --keys --network 12 | // Requires the contract to be live and for accounts[0] to be the owner of the oracle. 13 | const deployedFinder = await Finder.deployed(); 14 | 15 | // Emergency shutdown the contract using the admin. 16 | const admin = await FinancialContractsAdmin.at( 17 | await deployedFinder.getImplementationAddress(web3.utils.utf8ToHex(interfaceName.FinancialContractsAdmin)) 18 | ); 19 | await admin.callEmergencyShutdown(derivative); 20 | 21 | console.log("Emergency shutdown complete"); 22 | } catch (e) { 23 | console.log(e); 24 | } 25 | } 26 | 27 | async function runScript(callback) { 28 | const account = (await web3.eth.getAccounts())[0]; 29 | await run(account, argv.derivative); 30 | callback(); 31 | } 32 | 33 | // Attach this function to the exported function 34 | // in order to allow the script to be executed through both truffle and a test runner. 35 | runScript.run = run; 36 | module.exports = runScript; 37 | -------------------------------------------------------------------------------- /voter-dapp/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { drizzleReactHooks } from "@umaprotocol/react-plugin"; 3 | import "./App.css"; 4 | import Dashboard from "./Dashboard"; 5 | import DrizzleLogin from "./DrizzleLogin.js"; 6 | import { createMuiTheme } from "@material-ui/core/styles"; 7 | import { ThemeProvider } from "@material-ui/styles"; 8 | 9 | function App() { 10 | const theme = createMuiTheme({ 11 | palette: { 12 | primary: { 13 | main: "#FF4A4A" 14 | }, 15 | secondary: { 16 | main: "#272528" 17 | } 18 | }, 19 | typography: { 20 | useNextVariants: true, 21 | fontFamily: "Verdana" 22 | }, 23 | overrides: { 24 | MuiTableHead: { 25 | root: { 26 | background: "#b2b7bf", 27 | fontWeight: "750" 28 | } 29 | }, 30 | MuiTable: { 31 | root: { 32 | background: "#e4e7ed" 33 | } 34 | } 35 | } 36 | }); 37 | 38 | const [drizzle, setDrizzle] = useState(null); 39 | 40 | if (drizzle) { 41 | return ( 42 | 43 | 44 | 45 | 46 | 47 | ); 48 | } else { 49 | return ( 50 | 51 | 52 | 53 | ); 54 | } 55 | } 56 | 57 | export default App; 58 | -------------------------------------------------------------------------------- /core/migrations/9_deploy_governor.js: -------------------------------------------------------------------------------- 1 | const Governor = artifacts.require("Governor"); 2 | const Finder = artifacts.require("Finder"); 3 | const Registry = artifacts.require("Registry"); 4 | const Timer = artifacts.require("Timer"); 5 | const { getKeysForNetwork, deploy, enableControllableTiming } = require("../../common/MigrationUtils.js"); 6 | const { RegistryRolesEnum } = require("../../common/Enums.js"); 7 | 8 | module.exports = async function(deployer, network, accounts) { 9 | const keys = getKeysForNetwork(network, accounts); 10 | const controllableTiming = enableControllableTiming(network); 11 | const startingId = "0"; 12 | 13 | const { contract: governor } = await deploy( 14 | deployer, 15 | network, 16 | Governor, 17 | Finder.address, 18 | startingId, 19 | controllableTiming ? Timer.address : "0x0000000000000000000000000000000000000000", 20 | { 21 | from: keys.deployer 22 | } 23 | ); 24 | 25 | // Add governor to registry so it can send price requests. 26 | const registry = await Registry.deployed(); 27 | await registry.addMember(RegistryRolesEnum.CONTRACT_CREATOR, keys.deployer, { from: keys.deployer }); 28 | await registry.registerContract([], governor.address, { from: keys.deployer }); 29 | await registry.removeMember(RegistryRolesEnum.CONTRACT_CREATOR, keys.deployer, { from: keys.deployer }); 30 | 31 | // TODO: make the governor the owner of the Registry, Finder, FinancialContractsAdmin, Store, Voting, and 32 | // VotingToken for prod deployments. 33 | }; 34 | -------------------------------------------------------------------------------- /core/migrations/15_deploy_local_weth.js: -------------------------------------------------------------------------------- 1 | const WETH9 = artifacts.require("WETH9"); 2 | const AddressWhitelist = artifacts.require("AddressWhitelist"); 3 | const ExpiringMultiPartyCreator = artifacts.require("ExpiringMultiPartyCreator"); 4 | const { deploy, setToExistingAddress, getKeysForNetwork } = require("../../common/MigrationUtils.js"); 5 | const publicNetworks = require("../../common/PublicNetworks.js"); 6 | 7 | module.exports = async function(deployer, network, accounts) { 8 | const keys = getKeysForNetwork(network, accounts); 9 | 10 | let wethTokenAddress = null; 11 | 12 | for (const { name, wethAddress } of Object.values(publicNetworks)) { 13 | if (network.startsWith(name) && wethAddress) { 14 | await setToExistingAddress(network, WETH9, wethAddress); 15 | wethTokenAddress = wethAddress; 16 | break; 17 | } 18 | } 19 | 20 | if (!wethTokenAddress) { 21 | // Deploy if the network isn't public or if there was no listed WETH address. 22 | ({ 23 | contract: { address: wethTokenAddress } 24 | } = await deploy(deployer, network, WETH9, { from: keys.deployer })); 25 | } 26 | 27 | // Add wethTokenAddress to the margin currency whitelist. 28 | const empCreator = await ExpiringMultiPartyCreator.deployed(); 29 | const collateralWhitelistAddress = await empCreator.collateralTokenWhitelist(); 30 | const collateralWhitelist = await AddressWhitelist.at(collateralWhitelistAddress); 31 | await collateralWhitelist.addToWhitelist(wethTokenAddress, { from: keys.deployer }); 32 | }; 33 | -------------------------------------------------------------------------------- /core/scripts/cli/cli_entry.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Any errors in this script should immediately terminate execution. 4 | set -e 5 | 6 | # This script should be installed with the UMA project and symlinked to the "uma" file in the npm binary directory. 7 | # Usage wherever the UMA package is installed: 8 | # $(npm bin)/uma --network mainnet_ledger 9 | 10 | # Platform independent way of evaluating symlinks to find the real location of the script. 11 | SCRIPT_FILE=$(/usr/bin/env python -c "import os; print(os.path.realpath(\"$0\"))") 12 | SCRIPT_DIR=$(dirname $SCRIPT_FILE) 13 | 14 | # cd into the directory containing the cli file. 15 | # This file is in protocol/core/scripts -- so we need to go up one directory to get to the base truffle directory. 16 | cd $SCRIPT_DIR/../.. 17 | 18 | # Execute the script from within the scripts directory so all project artifacts/scripts are available. 19 | # Note: if the user needs to pass in a file path, we will need to pass the original working directory to the script so 20 | # we can correctly resolve the file location. 21 | echo "Setting up UMA contracts..." 22 | $(npm bin)/truffle compile > /dev/null || (echo "Contract compilation failed! Please check your @umaprotocol/protocol installation."; exit 1) 23 | $(npm bin)/apply-registry > /dev/null || (echo "Could not read contract addresses! Please check your @umaprotocol/protocol installation."; exit 1) 24 | echo "...UMA contracts set up successfully!" 25 | 26 | echo "Starting Truffle..." 27 | $(npm bin)/truffle exec ./scripts/cli/cli.js "$@" 28 | -------------------------------------------------------------------------------- /ci/deploy_to_staging.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | # Downloading gcloud package 5 | curl https://dl.google.com/dl/cloudsdk/release/google-cloud-sdk.tar.gz > /tmp/google-cloud-sdk.tar.gz 6 | 7 | # Installing the package 8 | sudo mkdir -p /usr/local/gcloud \ 9 | && sudo tar -C /usr/local/gcloud -xvf /tmp/google-cloud-sdk.tar.gz \ 10 | && sudo /usr/local/gcloud/google-cloud-sdk/install.sh -q 11 | 12 | # Adding the package path to local 13 | export PATH=$PATH:/usr/local/gcloud/google-cloud-sdk/bin 14 | 15 | # Save the gcloud credentials to a json file 16 | GCLOUD_FNAME=$(mktemp -q --suffix=.json) 17 | echo $GCLOUD_SERVICE_KEY > $GCLOUD_FNAME 18 | 19 | # Auth service account 20 | gcloud auth activate-service-account --key-file=$GCLOUD_FNAME 21 | gcloud --quiet config set project ${GOOGLE_PROJECT_ID} 22 | gcloud --quiet config set compute/zone ${GOOGLE_COMPUTE_ZONE} 23 | 24 | # Copy the staging config into the voter-dapp dir 25 | gsutil cp gs://staging-deployment-configuration/voter-app.yaml voter-dapp/app.yaml 26 | 27 | # Deploy voter dapp 28 | ./scripts/deploy_dapp.sh voter-dapp voter-dapp/app.yaml -q 29 | 30 | # Deploy docs 31 | ./scripts/deploy_docs.sh documentation/gae_app.yaml -q 32 | 33 | # Delete old versions. 34 | # TODO: this currently deletes any versions that aren't being used. It'd be preferable to leave the last few versions 35 | # for each service. 36 | VERSIONS_TO_DELETE=$(gcloud app versions list --filter="TRAFFIC_SPLIT=0.00" --format="value(VERSION.ID)") 37 | 38 | gcloud app versions delete -q $VERSIONS_TO_DELETE 39 | -------------------------------------------------------------------------------- /core/scripts/cli/sponsor/currencyUtils.js: -------------------------------------------------------------------------------- 1 | const { submitTransaction } = require("./transactionUtils"); 2 | 3 | const wrapToWeth = async (web3, artifacts, emp, amount, transactionNum, totalTransactions) => { 4 | const WETH9 = artifacts.require("WETH9"); 5 | const weth = await WETH9.deployed(); 6 | 7 | await submitTransaction( 8 | web3, 9 | async () => await weth.deposit({ value: amount.toString() }), 10 | "Wrapping ETH to WETH", 11 | transactionNum, 12 | totalTransactions 13 | ); 14 | }; 15 | 16 | const unwrapToEth = async (web3, artifacts, emp, amount, transactionNum, totalTransactions) => { 17 | const WETH9 = artifacts.require("WETH9"); 18 | const weth = await WETH9.deployed(); 19 | 20 | await submitTransaction( 21 | web3, 22 | async () => await weth.withdraw(amount.toString()), 23 | "Unwrapping WETH to ETH", 24 | transactionNum, 25 | totalTransactions 26 | ); 27 | }; 28 | 29 | const getIsWeth = async (web3, artifacts, collateralCurrency) => { 30 | const WETH9 = artifacts.require("WETH9"); 31 | return collateralCurrency.address === WETH9.address; 32 | }; 33 | 34 | const getCurrencySymbol = async (web3, artifacts, collateralCurrency) => { 35 | if (await getIsWeth(web3, artifacts, collateralCurrency)) { 36 | return "WETH"; 37 | } else { 38 | // TODO: Do all collateral currencies we care about support `symbol()`? 39 | return "collateral tokens"; 40 | } 41 | }; 42 | 43 | module.exports = { 44 | getCurrencySymbol, 45 | getIsWeth, 46 | wrapToWeth, 47 | unwrapToEth 48 | }; 49 | -------------------------------------------------------------------------------- /documentation/developer_reference/contract_addresses.md: -------------------------------------------------------------------------------- 1 | # Deployed DVM Contracts 2 | 3 | Below is where you can find smart contract addresses for UMA-supported mainnet and testnet deployments of the DVM. 4 | 5 | ## UMA Token Holders 6 | 7 | If you are a UMA token holder, you will probably only interact with `Voting`, `Finder`, `DesignatedVotingFactory`, and `Governor`. 8 | These are the relevant contracts used to vote on price requests and UMIPs. 9 | 10 | ## Financial Contract Developers 11 | 12 | If you are building your own financial contract template, you will probably interact with `Store`, `Voting`, `Finder`, `IdentifierWhitelist`, and `Registry`. 13 | These contracts are used by the DVM to keep track of which financial contracts depend on it, how they impact the economic guarantee of the oracle, and which price identifiers UMA token holders need to be prepared to vote on. 14 | 15 | ## Contract Addresses 16 | 17 | - [Mainnet (network id: 1)](https://github.com/UMAprotocol/protocol/blob/master/core/networks/1.json) 18 | - [Rinkeby (network id: 4)](https://github.com/UMAprotocol/protocol/blob/master/core/networks/4.json) 19 | - [Kovan (network id: 42)](https://github.com/UMAprotocol/protocol/blob/master/core/networks/42.json) 20 | 21 | # Deployed Synthetic Tokens 22 | 23 | You can also find a list of supported deployments of the priceless synthetic token contract template on various networks. 24 | 25 | ## Kovan (network id: 42) 26 | 27 | - [Kovan Synthetic Tokens](https://docs.google.com/spreadsheets/d/1gLjt58hFh-l5SDhoRyz4t8oQCYx74tYRypMmIuYwJ1c/edit?usp=sharing) 28 | -------------------------------------------------------------------------------- /scripts/generate_private_key.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | # Note: this script should be run from inside the ubuntu docker container with this 5 | # repository mounted at ~/protocol. 6 | # An example run command would look like the following: 7 | # docker run -v `pwd`:/protocol -v :/keys -w /protocol ubuntu scripts/generate_private_key.sh 8 | 9 | apt-get update && apt-get install -y git make gcc 10 | 11 | cd / 12 | git clone https://github.com/maandree/libkeccak 13 | cd libkeccak 14 | 15 | # Checkout a specific hash to avoid code contamination later. 16 | git checkout 47139985115e175ed9c3f7d648d6d9ec7c48b89b 17 | make 18 | make install PREFIX=/usr 19 | 20 | 21 | cd / 22 | git clone https://github.com/maandree/sha3sum.git 23 | cd sha3sum 24 | 25 | # Checkout a specific hash to avoid code contamination later. 26 | git checkout e17cf813fa38fbc13df6dbecdad5e6d0e8223ba2 27 | make 28 | make install 29 | 30 | DEST_DIR=/keys 31 | cd $DEST_DIR 32 | 33 | # Everything below was shamelessly stolen from https://kobl.one/blog/create-full-ethereum-keypair-and-address/. 34 | # Generate the private and public keys 35 | openssl ecparam -name secp256k1 -genkey -noout | openssl ec -text -noout > Key 36 | 37 | # Extract the public key and remove the EC prefix 0x04 38 | cat Key | grep pub -A 5 | tail -n +2 | tr -d '\n[:space:]:' | sed 's/^04//' > pub 39 | 40 | # Extract the private key and remove the leading zero byte 41 | cat Key | grep priv -A 3 | tail -n +2 | tr -d '\n[:space:]:' | sed 's/^00//' > priv 42 | 43 | # Generate the hash and take the address part 44 | cat pub | keccak-256sum -x -l | tr -d ' -' | tail -c 41 > address 45 | -------------------------------------------------------------------------------- /common/AdminUtils.js: -------------------------------------------------------------------------------- 1 | const abiDecoder = require("./AbiUtils.js").getAbiDecoder(); 2 | 3 | function decodeTransaction(transaction) { 4 | let returnValue = ""; 5 | 6 | // Give to and value. 7 | returnValue += "To: " + transaction.to; 8 | returnValue += "\nValue (in Wei): " + transaction.value; 9 | 10 | if (!transaction.data || transaction.data.length === 0 || transaction.data === "0x") { 11 | // No data -> simple ETH send. 12 | returnValue += "\nTransaction is a simple ETH send (no data)."; 13 | } else { 14 | // Txn data isn't empty -- attempt to decode. 15 | const decodedTxn = abiDecoder.decodeMethod(transaction.data); 16 | if (!decodedTxn) { 17 | // Cannot decode txn, just give the user the raw data. 18 | returnValue += "\nCannot decode transaction (does not match any UMA Protocol Signature)."; 19 | returnValue += "\nRaw transaction data: " + transaction.data; 20 | } else { 21 | // Decode was successful -- pretty print the results. 22 | returnValue += "\nTransaction details:\n"; 23 | returnValue += JSON.stringify(decodedTxn, null, 4); 24 | } 25 | } 26 | return returnValue; 27 | } 28 | 29 | const adminPrefix = "Admin "; 30 | 31 | function isAdminRequest(identifierUtf8) { 32 | return identifierUtf8.startsWith(adminPrefix); 33 | } 34 | 35 | // Assumes that `identifierUtf8` is an admin request, i.e., `isAdminRequest()` returns true for it. 36 | function getAdminRequestId(identifierUtf8) { 37 | return parseInt(identifierUtf8.slice(adminPrefix.length), 10); 38 | } 39 | 40 | module.exports = { 41 | decodeTransaction, 42 | isAdminRequest, 43 | getAdminRequestId 44 | }; 45 | -------------------------------------------------------------------------------- /core/contracts/oracle/implementation/VotingToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | import "../../common/implementation/ExpandedERC20.sol"; 4 | import "@openzeppelin/contracts/token/ERC20/ERC20Snapshot.sol"; 5 | 6 | 7 | /** 8 | * @title Ownership of this token allows a voter to respond to price requests. 9 | * @dev Supports snapshotting and allows the Oracle to mint new tokens as rewards. 10 | */ 11 | contract VotingToken is ExpandedERC20, ERC20Snapshot { 12 | /** 13 | * @notice Constructs the VotingToken. 14 | */ 15 | constructor() public ExpandedERC20("UMA Voting Token v1", "UMA", 18) {} 16 | 17 | /** 18 | * @notice Creates a new snapshot ID. 19 | * @return uint256 Thew new snapshot ID. 20 | */ 21 | function snapshot() external returns (uint256) { 22 | return _snapshot(); 23 | } 24 | 25 | // _transfer, _mint and _burn are ERC20 internal methods that are overridden by ERC20Snapshot, 26 | // therefore the compiler will complain that VotingToken must override these methods 27 | // because the two base classes (ERC20 and ERC20Snapshot) both define the same functions 28 | 29 | function _transfer( 30 | address from, 31 | address to, 32 | uint256 value 33 | ) internal override(ERC20, ERC20Snapshot) { 34 | super._transfer(from, to, value); 35 | } 36 | 37 | function _mint(address account, uint256 value) internal override(ERC20, ERC20Snapshot) { 38 | super._mint(account, value); 39 | } 40 | 41 | function _burn(address account, uint256 value) internal override(ERC20, ERC20Snapshot) { 42 | super._burn(account, value); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /core/migrations/14_deploy_expiring_multi_party_creator.js: -------------------------------------------------------------------------------- 1 | const Finder = artifacts.require("Finder"); 2 | const ExpiringMultiPartyCreator = artifacts.require("ExpiringMultiPartyCreator"); 3 | const ExpiringMultiPartyLib = artifacts.require("ExpiringMultiPartyLib"); 4 | const AddressWhitelist = artifacts.require("AddressWhitelist"); 5 | const TokenFactory = artifacts.require("TokenFactory"); 6 | const { getKeysForNetwork, deploy, enableControllableTiming } = require("../../common/MigrationUtils.js"); 7 | const Timer = artifacts.require("Timer"); 8 | const TestnetERC20 = artifacts.require("TestnetERC20"); 9 | 10 | module.exports = async function(deployer, network, accounts) { 11 | const keys = getKeysForNetwork(network, accounts); 12 | const controllableTiming = enableControllableTiming(network); 13 | 14 | // Deploy whitelists. 15 | const { contract: collateralCurrencyWhitelist } = await deploy(deployer, network, AddressWhitelist, { 16 | from: keys.deployer 17 | }); 18 | 19 | const finder = await Finder.deployed(); 20 | const tokenFactory = await TokenFactory.deployed(); 21 | const collateralToken = await TestnetERC20.deployed(); 22 | 23 | // Deploy EMPLib and link to EMPCreator. 24 | await deploy(deployer, network, ExpiringMultiPartyLib); 25 | await deployer.link(ExpiringMultiPartyLib, ExpiringMultiPartyCreator); 26 | 27 | await deploy( 28 | deployer, 29 | network, 30 | ExpiringMultiPartyCreator, 31 | finder.address, 32 | collateralCurrencyWhitelist.address, 33 | tokenFactory.address, 34 | controllableTiming ? Timer.address : "0x0000000000000000000000000000000000000000", 35 | collateralToken.address, 36 | { from: keys.deployer } 37 | ); 38 | }; 39 | -------------------------------------------------------------------------------- /core/scripts/DecodeTransactionData.js: -------------------------------------------------------------------------------- 1 | // This script allows you to decode a transaction data blob revealing the method that the transaction is calling and 2 | // the parameters. 3 | // Example: 4 | // $(npm bin)/truffle exec --network test ./scripts/DecodeTransactionData.js --data 0x10a7e2014554482f55534400000000000000000000000000000000000000000000000000 5 | 6 | const { getAbiDecoder } = require("../../common/AbiUtils.js"); 7 | 8 | const argv = require("minimist")(process.argv.slice(), { string: ["data"] }); 9 | 10 | function run(data) { 11 | return getAbiDecoder().decodeMethod(data); 12 | } 13 | 14 | const decodeTransactionData = async function(callback) { 15 | try { 16 | if (!argv.data) { 17 | callback("You must provide the transaction data using the --data argument, e.g. --data 0x1234"); 18 | } else if (!argv.data.startsWith("0x")) { 19 | callback("The --data argument must be a hex string starting with `0x`, e.g. --data 0x1234"); 20 | } 21 | 22 | const txnObj = run(argv.data); 23 | 24 | if (!txnObj) { 25 | console.log( 26 | "Could not identify the method that this transaction is calling.", 27 | "Are you sure it corresponds to a contract in the UMAprotocol/protocol repository?" 28 | ); 29 | } else { 30 | // Pretty print. 31 | console.log("Your decoded transaction information:"); 32 | console.log(JSON.stringify(txnObj, null, 4)); 33 | } 34 | } catch (e) { 35 | // Forces the script to return a nonzero error code so failure can be detected in bash. 36 | callback(e); 37 | return; 38 | } 39 | 40 | callback(); 41 | }; 42 | 43 | decodeTransactionData.run = run; 44 | module.exports = decodeTransactionData; 45 | -------------------------------------------------------------------------------- /core/scripts/local/AdvanceEMP.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This script advances time in all EMP's and the Voting contract forward by some specified amount of seconds (or one hour and one second by default). 3 | * 4 | * `truffle exec ./scripts/local/AdvanceEMPTime --time 10` 5 | * Advances time for all contracts by 10 seconds 6 | */ 7 | const { toBN } = web3.utils; 8 | const argv = require("minimist")(process.argv.slice(), { string: ["time"] }); 9 | const { interfaceName } = require("../../utils/Constants.js"); 10 | 11 | // Deployed contract ABI's and addresses we need to fetch. 12 | const Finder = artifacts.require("Finder"); 13 | const Voting = artifacts.require("Voting"); 14 | 15 | const advanceTime = async callback => { 16 | try { 17 | const leapForward = argv.time ? argv.time : 7200; 18 | console.log(`Advancing contract time forward by ${leapForward} seconds`); 19 | 20 | // Since MockOracle and EMP share the same Timer, it suffices to just advance the oracle's time. 21 | const finder = await Finder.deployed(); 22 | const deployedVoting = await Voting.at( 23 | await finder.getImplementationAddress(web3.utils.utf8ToHex(interfaceName.Oracle)) 24 | ); 25 | let currentTime = await deployedVoting.getCurrentTime(); 26 | const newTime = toBN(currentTime).add(toBN(leapForward)); 27 | await deployedVoting.setCurrentTime(newTime); 28 | currentTime = await deployedVoting.getCurrentTime(); 29 | const currentTimeReadable = new Date(Number(currentTime) * 1000); 30 | console.log(`Set time to ${currentTimeReadable} for the DVM @ ${deployedVoting.address}`); 31 | } catch (err) { 32 | callback(err); 33 | } 34 | callback(); 35 | }; 36 | 37 | module.exports = advanceTime; 38 | -------------------------------------------------------------------------------- /core/scripts/cli/sponsor/listExpiredMarkets.js: -------------------------------------------------------------------------------- 1 | const inquirer = require("inquirer"); 2 | const { getMarketSummary } = require("./marketUtils"); 3 | const { PositionStatesEnum } = require("../../../../common/Enums"); 4 | const showExpiredMarketDetails = require("./showExpiredMarketDetails"); 5 | 6 | const listExpiredMarkets = async (web3, artifacts) => { 7 | const markets = await getMarketSummary(web3, artifacts); 8 | 9 | // Format a useful display message for each market. 10 | const backChoice = "Back"; 11 | const choices = []; 12 | for (let i = 0; i < markets.length; i++) { 13 | const market = markets[i]; 14 | if (market.contractState === PositionStatesEnum.OPEN) { 15 | continue; 16 | } 17 | const state = market.contractState === PositionStatesEnum.EXPIRED_PRICE_REQUESTED ? "Pending" : "Settled"; 18 | const expirationTimeReadable = new Date(Number(market.expirationTimestamp) * 1000); 19 | const display = `${market.symbol}. Expired at ${expirationTimeReadable}. ${state}. ${market.etherscanLink}`; 20 | 21 | // Using the index as the value lets us easily find the right EMP. 22 | choices.push({ name: display, value: i }); 23 | } 24 | if (choices.length === 0) { 25 | console.log("No expired markets"); 26 | return; 27 | } 28 | choices.push({ name: backChoice }); 29 | const prompt = { 30 | type: "list", 31 | name: "chosenEmpIdx", 32 | message: "Pick a market", 33 | choices: choices 34 | }; 35 | const input = await inquirer.prompt(prompt); 36 | if (input["chosenEmpIdx"] !== backChoice) { 37 | await showExpiredMarketDetails(web3, artifacts, markets[input["chosenEmpIdx"]].emp); 38 | } 39 | }; 40 | 41 | module.exports = listExpiredMarkets; 42 | -------------------------------------------------------------------------------- /voter-dapp/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 22 | UMA Voting Dashboard 23 | 24 | 25 | 26 |
27 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /core/test/oracle/DesignatedVotingFactory.js: -------------------------------------------------------------------------------- 1 | const { didContractThrow } = require("../../../common/SolidityTestUtils.js"); 2 | 3 | const DesignatedVoting = artifacts.require("DesignatedVoting"); 4 | const DesignatedVotingFactory = artifacts.require("DesignatedVotingFactory"); 5 | const Finder = artifacts.require("Finder"); 6 | 7 | contract("DesignatedVotingFactory", function(accounts) { 8 | const owner = accounts[1]; 9 | const voter = accounts[2]; 10 | const voter2 = accounts[3]; 11 | 12 | let factory; 13 | 14 | before(async function() { 15 | factory = await DesignatedVotingFactory.deployed(); 16 | }); 17 | 18 | it("Deploy new", async function() { 19 | const designatedVotingAddress = await factory.newDesignatedVoting.call(owner, { from: voter }); 20 | await factory.newDesignatedVoting(owner, { from: voter }); 21 | assert(await didContractThrow(factory.newDesignatedVoting(owner, { from: voter }))); 22 | 23 | assert.equal(designatedVotingAddress.toString(), (await factory.designatedVotingContracts(voter)).toString()); 24 | const designatedVoting = await DesignatedVoting.at(designatedVotingAddress); 25 | const ownerRole = "0"; 26 | assert(await designatedVoting.holdsRole(ownerRole, owner)); 27 | const voterRole = "1"; 28 | assert(await designatedVoting.holdsRole(voterRole, voter)); 29 | 30 | // Reassign. 31 | await designatedVoting.resetMember(voterRole, voter2, { from: owner }); 32 | assert(await didContractThrow(factory.setDesignatedVoting(designatedVotingAddress, { from: voter }))); 33 | await factory.setDesignatedVoting(designatedVotingAddress, { from: voter2 }); 34 | assert.equal(designatedVotingAddress.toString(), (await factory.designatedVotingContracts(voter2)).toString()); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /Automated.md: -------------------------------------------------------------------------------- 1 | # Automated voting system 2 | 3 | The automated voting system assists UMA token holders cast their votes for price requests. 4 | 5 | ## Initial set up 6 | 7 | Follow these steps to run the automated voting system on Google Cloud: 8 | 9 | 1. Log in to [Google Cloud](https://console.cloud.google.com). Create a new account if you don't have one. 10 | 2. Create a service account [here](https://console.cloud.google.com/iam-admin/serviceaccounts) by clicking `Create Service Account`. 11 | 3. Sign up for the [SendGrid](https:://app.sendgrid.com) service for sending email notifications with the email address 12 | you want to use to send emails. 13 | 4. Get a SendGrid API key by clicking `Settings` -> `API Keys` -> `Create API Key`. 14 | 5. Create a new `Compute Engine` instance [here](https://console.cloud.google.com/compute/instances) by clicking `Create Instance` and configure it with the following steps. 15 | 1. Click the box next to `Deploy a container image to this VM instance` and put in the URL for the UMA 16 | automated voting system Docker image ``. 17 | 2. Under `Identity and API access`, put in the service account you created in step 2. 18 | 3. Set the following environment variables: ``. 19 | 20 | You'll receive an email notification when the system commits votes for a new round, at which point you'll be able to 21 | review those commits. 22 | 23 | ## Redeploying 24 | 25 | You may need to redeploy under certain circumstances. For example, you'll need to redeploy when a new identifier is 26 | supported. To do so, restart the instance with the following steps: 27 | 28 | 1. Go the `Compute Engine` instances [page](https://console.cloud.google.com/compute/instances). 29 | 2. Select the instance running the automated voting system and hit `Reset`. 30 | -------------------------------------------------------------------------------- /ci/run_truffle_tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | PROTOCOL_DIR=$(pwd) 5 | 6 | # Verifies persistent deployments stored in the networks/ directory. 7 | check_deployment() { 8 | # $1 is the operating directory 9 | cd $1 10 | 11 | # Remove the build directory. 12 | rm -rf build 13 | 14 | # Make sure the contracts are compiled and the registry is applied. 15 | $(npm bin)/truffle compile 16 | $(npm bin)/apply-registry 17 | 18 | # $2 is network_id 19 | local fname=networks/$2.json 20 | local network_name=$3 21 | if [ -e $fname ] 22 | then 23 | echo "Checking ${network_name} deployment stored in ${fname}." 24 | $(npm bin)/truffle exec ./scripts/CheckDeploymentValidity.js --network $network_name 25 | else 26 | echo "No ${network_name} deployment to verify." 27 | exit 1 28 | fi 29 | } 30 | 31 | run_tests() { 32 | # $1 is the operating directory 33 | cd $1 34 | 35 | # Test migration 36 | $(npm bin)/truffle migrate --reset --network ci 37 | 38 | # Check the ci deployment. 39 | check_deployment ./ 1234 ci 40 | 41 | # Run standard truffle tests 42 | $(npm bin)/truffle test --network ci 43 | } 44 | 45 | # Run tests for core. 46 | run_tests $PROTOCOL_DIR/core 47 | 48 | # Hacky way of running truffle tests outside of core. 49 | cd $PROTOCOL_DIR/core 50 | 51 | $(npm bin)/truffle test $(find ../financial-templates-lib/test -name '*.js') --network ci 52 | $(npm bin)/truffle test $(find ../liquidator/test -name '*.js') --network ci 53 | $(npm bin)/truffle test $(find ../disputer/test -name '*.js') --network ci 54 | $(npm bin)/truffle test $(find ../monitors/test -name '*.js') --network ci 55 | 56 | # Check the Kovan deployment. 57 | check_deployment $PROTOCOL_DIR/core 4 rinkeby_mnemonic 58 | -------------------------------------------------------------------------------- /core/scripts/cli/voting/votePhaseTiming.js: -------------------------------------------------------------------------------- 1 | const { VotePhasesEnum } = require("../../../../common/Enums"); 2 | const moment = require("moment"); 3 | const SECONDS_IN_DAY = 86400; 4 | const MINUTES_IN_HOUR = 60; 5 | 6 | /** 7 | * Returns the time remaining until the next vote phase and round, broken down 8 | * conveniently into full hours and minutes. It is simply a convenience method. 9 | * 10 | * For example: If the contract time is Feb 7 2020, 8:04:53 PM, then this method will return 11 | * that there are 3 hours and 55 minutes remaining until midnight when the next phase begins. 12 | * If this is a Commit phase, then there will be 27 hours and 55 minutes until the next round begins because 13 | * a full round is a Commit followed by a Reveal phase. If this is a Reveal phase, there will simply be 3 hours and 55 minutes 14 | * until the next round. 15 | * 16 | * @param {* String} contractTime What time the contract thinks it is, in seconds 17 | * @param {* String} roundPhase 0 = Commit or 1 = Reveal 18 | */ 19 | const votePhaseTiming = (contractTime, roundPhase) => { 20 | // Phase length is one day, round length is two days, phases 21 | // begin at the precise beginning of the day 22 | const secondsUntilNextPhase = SECONDS_IN_DAY - (parseInt(contractTime) % SECONDS_IN_DAY); 23 | const hoursUntilNextPhase = Math.floor(moment.duration(secondsUntilNextPhase, "seconds").asHours()); 24 | const minutesInLastHour = Math.floor(moment.duration(secondsUntilNextPhase, "seconds").asMinutes()) % MINUTES_IN_HOUR; 25 | const hoursUntilNextRound = roundPhase === VotePhasesEnum.COMMIT ? hoursUntilNextPhase + 24 : hoursUntilNextPhase; 26 | 27 | return { 28 | minutesInLastHour, 29 | hoursUntilNextPhase, 30 | hoursUntilNextRound 31 | }; 32 | }; 33 | 34 | module.exports = votePhaseTiming; 35 | -------------------------------------------------------------------------------- /core/scripts/local/ClaimAllRewards.js: -------------------------------------------------------------------------------- 1 | // Usage: $(npm bin)/truffle exec ./scripts/ClaimAllRewards.js --round --network mainnet_mnemonic 2 | 3 | const Voting = artifacts.require("Voting"); 4 | 5 | const argv = require("minimist")(process.argv.slice(), { string: ["round"] }); 6 | 7 | // This script claims all voter's rewards for the round provided. 8 | async function claimRewards() { 9 | const voting = await Voting.deployed(); 10 | 11 | const events = await voting.contract.getPastEvents("VoteRevealed", { 12 | filter: { roundId: argv.round }, 13 | fromBlock: 0, 14 | toBlock: "latest" 15 | }); 16 | 17 | const votersToPriceRequests = {}; 18 | for (const event of events) { 19 | const voter = event.returnValues.voter; 20 | const newPriceRequest = { identifier: event.returnValues.identifier, time: event.returnValues.time }; 21 | if (votersToPriceRequests[voter]) { 22 | votersToPriceRequests[voter].push(newPriceRequest); 23 | } else { 24 | votersToPriceRequests[voter] = [newPriceRequest]; 25 | } 26 | } 27 | 28 | for (const [voter, priceRequests] of Object.entries(votersToPriceRequests)) { 29 | try { 30 | await voting.retrieveRewards.call(voter, argv.round, priceRequests); 31 | } catch (err) { 32 | console.log("Could not reveal for voter", voter); 33 | console.log(err); 34 | continue; 35 | } 36 | 37 | await voting.retrieveRewards(voter, argv.round, priceRequests); 38 | } 39 | } 40 | 41 | async function wrapper(callback) { 42 | try { 43 | await claimRewards(argv.multisig); 44 | } catch (e) { 45 | // Forces the script to return a nonzero error code so failure can be detected in bash. 46 | callback(e); 47 | return; 48 | } 49 | 50 | callback(); 51 | } 52 | 53 | module.exports = wrapper; 54 | -------------------------------------------------------------------------------- /core/test/financial-templates/ExpiringMultiParty.js: -------------------------------------------------------------------------------- 1 | const { toWei } = web3.utils; 2 | 3 | // Tested Contract 4 | const ExpiringMultiParty = artifacts.require("ExpiringMultiParty"); 5 | 6 | // Helper Contracts 7 | const Finder = artifacts.require("Finder"); 8 | const IdentifierWhitelist = artifacts.require("IdentifierWhitelist"); 9 | const TokenFactory = artifacts.require("TokenFactory"); 10 | const Token = artifacts.require("ExpandedERC20"); 11 | const Timer = artifacts.require("Timer"); 12 | 13 | contract("ExpiringMultiParty", function(accounts) { 14 | it("Can deploy", async function() { 15 | const collateralToken = await Token.new("UMA", "UMA", 18, { from: accounts[0] }); 16 | 17 | const constructorParams = { 18 | expirationTimestamp: (Math.round(Date.now() / 1000) + 1000).toString(), 19 | withdrawalLiveness: "1000", 20 | collateralAddress: collateralToken.address, 21 | finderAddress: Finder.address, 22 | tokenFactoryAddress: TokenFactory.address, 23 | priceFeedIdentifier: web3.utils.utf8ToHex("UMATEST"), 24 | syntheticName: "Test UMA Token", 25 | syntheticSymbol: "UMATEST", 26 | liquidationLiveness: "1000", 27 | collateralRequirement: { rawValue: toWei("1.5") }, 28 | disputeBondPct: { rawValue: toWei("0.1") }, 29 | sponsorDisputeRewardPct: { rawValue: toWei("0.1") }, 30 | disputerDisputeRewardPct: { rawValue: toWei("0.1") }, 31 | minSponsorTokens: { rawValue: toWei("1") }, 32 | timerAddress: Timer.address 33 | }; 34 | 35 | identifierWhitelist = await IdentifierWhitelist.deployed(); 36 | await identifierWhitelist.addSupportedIdentifier(constructorParams.priceFeedIdentifier, { 37 | from: accounts[0] 38 | }); 39 | 40 | await ExpiringMultiParty.new(constructorParams); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /core/scripts/cli/wallet/createNewAccount.js: -------------------------------------------------------------------------------- 1 | const inquirer = require("inquirer"); 2 | const style = require("../textStyle"); 3 | 4 | /** 5 | * Prompt user to confirm that they want to generate a new Ethereum account. 6 | * Displays the new public and private key to the user without storing anything, and 7 | * provides instructions on how to use this account in the CLI tool. 8 | * 9 | * @param {* Object} web3 Web3 provider 10 | */ 11 | const createNewAccount = async web3 => { 12 | const confirm = await inquirer.prompt({ 13 | type: "confirm", 14 | name: "createNewAccount", 15 | message: style.instruction( 16 | "We will never store your private keys, so please ensure that your computer is secure and you keep it a secret. Anybody who knows your private key controls your account! Type 'y' to confirm your understanding." 17 | ), 18 | default: false 19 | }); 20 | if (confirm["createNewAccount"]) { 21 | const newAccount = web3.eth.accounts.create(); 22 | console.group(style.success("\n** Generated a New Ethereum Account **")); 23 | console.log(`${style.success("- Public Key")}: ${newAccount.address}`); 24 | console.log(`${style.success("- Private Key")}: ${newAccount.privateKey}`); 25 | console.log( 26 | `${style.success( 27 | "- Instructions for using your new account" 28 | )}: If you want to use this account as your default account when sending UMA transactions, then exit the CLI tool, set your "PRIVATE_KEY" environment variable to the above secret (i.e. "export PRIVATE_KEY=0x348ce564d427a3311b6536bbcff9390d69395b06ed6c486954e971d960fe8709"), and restart the CLI via "uma --network mainnet_privatekey", replacing "mainnet" with the network of your choice` 29 | ); 30 | console.log("\n"); 31 | console.groupEnd(); 32 | } 33 | }; 34 | 35 | module.exports = createNewAccount; 36 | -------------------------------------------------------------------------------- /core/contracts/oracle/implementation/Finder.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | import "@openzeppelin/contracts/access/Ownable.sol"; 4 | import "../interfaces/FinderInterface.sol"; 5 | 6 | 7 | /** 8 | * @title Provides addresses of the live contracts implementing certain interfaces. 9 | * @dev Examples of interfaces with implementations that Finder locates are the Oracle and Store interfaces. 10 | */ 11 | contract Finder is FinderInterface, Ownable { 12 | mapping(bytes32 => address) public interfacesImplemented; 13 | 14 | event InterfaceImplementationChanged(bytes32 indexed interfaceName, address indexed newImplementationAddress); 15 | 16 | /** 17 | * @notice Updates the address of the contract that implements `interfaceName`. 18 | * @param interfaceName bytes32 of the interface name that is either changed or registered. 19 | * @param implementationAddress address of the implementation contract. 20 | */ 21 | function changeImplementationAddress(bytes32 interfaceName, address implementationAddress) 22 | external 23 | override 24 | onlyOwner 25 | { 26 | interfacesImplemented[interfaceName] = implementationAddress; 27 | 28 | emit InterfaceImplementationChanged(interfaceName, implementationAddress); 29 | } 30 | 31 | /** 32 | * @notice Gets the address of the contract that implements the given `interfaceName`. 33 | * @param interfaceName queried interface. 34 | * @return implementationAddress address of the defined interface. 35 | */ 36 | function getImplementationAddress(bytes32 interfaceName) external override view returns (address) { 37 | address implementationAddress = interfacesImplemented[interfaceName]; 38 | require(implementationAddress != address(0x0), "Implementation not found"); 39 | return implementationAddress; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /scripts/gen-nav.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const path = require("path"); 4 | const proc = require("child_process"); 5 | const fs = require("fs"); 6 | const startCase = require("lodash.startcase"); 7 | 8 | const mapFile = process.argv[2]; 9 | const baseDir = process.argv[3]; 10 | 11 | // Path should look like: 12 | // modules/.../section_name/pages 13 | const pathArr = baseDir.split("/"); 14 | const moduleName = pathArr[pathArr.length - 2]; 15 | 16 | console.log("." + startCase(moduleName)); 17 | 18 | // Special case for contracts. 19 | if (moduleName === "contracts") { 20 | const files = proc 21 | .execFileSync("find", [baseDir, "-type", "f"], { encoding: "utf8" }) 22 | .split("\n") 23 | .filter(s => s !== ""); 24 | 25 | for (const file of files) { 26 | const doc = file.replace(baseDir, ""); 27 | const title = path.parse(file).name; 28 | console.log(`* xref:${doc}[${startCase(title)}]`); 29 | } 30 | 31 | return; 32 | } 33 | 34 | // Read the map file in 35 | const lines = require("fs") 36 | .readFileSync(mapFile, "utf-8") 37 | .split("\n") 38 | .filter(Boolean); 39 | 40 | const moduleIndex = lines.findIndex(line => line.startsWith(`* ${moduleName}`)); 41 | 42 | if (moduleIndex === -1) { 43 | throw `Could not find ${moduleName} module in mapFile`; 44 | } 45 | 46 | for (let i = moduleIndex + 1; i < lines.length; i++) { 47 | const line = lines[i]; 48 | const depth = (line.match(/\*/g) || []).length; 49 | 50 | // Reached another top level. 51 | if (depth === 1) { 52 | break; 53 | } 54 | 55 | const prefix = `${"*".repeat(depth - 1)} `; 56 | const name = line.split(" ")[1]; 57 | 58 | if (name.includes(".")) { 59 | const baseName = name.split(".")[0]; 60 | console.log(`${prefix} xref:${baseName}.adoc[${startCase(baseName)}]`); 61 | } else { 62 | console.log(`${prefix} ${startCase(name)}`); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # This docker container can be pulled from umaprotocol/protocol on dockerhub. 2 | # To get the latest image, run: docker pull umaprotocol/protocol 3 | # This docker container is used to access all components of the UMA ecosystem 4 | # including liquidator, disputors and monitor bots. Settings for these bots are 5 | # defined via enviroment variables. For example to run a liquidator bot run: 6 | # docker run --env MNEMONIC="" \ 7 | # --env PAGERDUTY_API_KEY="" \ 8 | # --env PAGERDUTY_SERVICE_ID="" \ 9 | # --env PAGERDUTY_FROM_EMAIL="" \ 10 | # --env SLACK_WEBHOOK="" \ 11 | # --env EMP_ADDRESS="" \ 12 | # --env POLLING_DELAY="" \ 13 | # --env COMMAND="npx truffle exec ../liquidator/index.js --network mainnet_mnemonic" \ 14 | # umaprotocol/protocol:latest 15 | # 16 | # To build the docker image locally, run the following command from the `protocol` directory: 17 | # docker build -t / . 18 | # 19 | # To `docker run` with your locally built image, replace `umaprotocol/protocol` with /. 20 | 21 | # Fix node version due to high potential for incompatibilities. 22 | FROM node:lts 23 | 24 | # All source code and execution happens from the protocol directory. 25 | WORKDIR /protocol 26 | 27 | # Copy the latest state of the repo into the protocol directory. 28 | COPY . ./ 29 | 30 | # Install dependencies and compile contracts. 31 | RUN apt-get update 32 | RUN apt-get install -y libudev-dev libusb-1.0-0-dev 33 | RUN npm install 34 | RUN scripts/buildContracts.sh 35 | 36 | # For now all logic in the mono-repo must be executed from the `core` directory. 37 | WORKDIR /protocol/core/ 38 | 39 | # Command to run any command provided by the COMMAND env variable. 40 | ENTRYPOINT ["/bin/bash", "scripts/runCommand.sh"] 41 | -------------------------------------------------------------------------------- /core/contracts/oracle/interfaces/OracleInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | 4 | /** 5 | * @title Financial contract facing Oracle interface. 6 | * @dev Interface used by financial contracts to interact with the Oracle. Voters will use a different interface. 7 | */ 8 | interface OracleInterface { 9 | /** 10 | * @notice Enqueues a request (if a request isn't already present) for the given `identifier`, `time` pair. 11 | * @dev Time must be in the past and the identifier must be supported. 12 | * @param identifier uniquely identifies the price requested. eg BTC/USD (encoded as bytes32) could be requested. 13 | * @param time unix timestamp for the price request. 14 | */ 15 | function requestPrice(bytes32 identifier, uint256 time) external; 16 | 17 | /** 18 | * @notice Whether the price for `identifier` and `time` is available. 19 | * @dev Time must be in the past and the identifier must be supported. 20 | * @param identifier uniquely identifies the price requested. eg BTC/USD (encoded as bytes32) could be requested. 21 | * @param time unix timestamp for the price request. 22 | * @return bool if the DVM has resolved to a price for the given identifier and timestamp. 23 | */ 24 | function hasPrice(bytes32 identifier, uint256 time) external view returns (bool); 25 | 26 | /** 27 | * @notice Gets the price for `identifier` and `time` if it has already been requested and resolved. 28 | * @dev If the price is not available, the method reverts. 29 | * @param identifier uniquely identifies the price requested. eg BTC/USD (encoded as bytes32) could be requested. 30 | * @param time unix timestamp for the price request. 31 | * @return int256 representing the resolved price for the given identifier and timestamp. 32 | */ 33 | function getPrice(bytes32 identifier, uint256 time) external view returns (int256); 34 | } 35 | -------------------------------------------------------------------------------- /core/test/scripts/DecodeTransactionData.js: -------------------------------------------------------------------------------- 1 | const DecodeTransactionData = require("../../scripts/DecodeTransactionData"); 2 | const { getRandomSignedInt, getRandomUnsignedInt } = require("../../../common/Random.js"); 3 | 4 | const Registry = artifacts.require("Registry"); 5 | const Voting = artifacts.require("Voting"); 6 | 7 | contract("scripts/DecodeTransactionData.js", function(accounts) { 8 | it("Decode registerContract", async function() { 9 | const contractAddress = web3.utils.randomHex(20); 10 | 11 | const registry = await Registry.deployed(); 12 | const txnData = registry.contract.methods.registerContract([], contractAddress).encodeABI(); 13 | 14 | const expectedObject = { 15 | name: "registerContract", 16 | params: { 17 | parties: [], 18 | contractAddress: contractAddress 19 | } 20 | }; 21 | 22 | assert.equal( 23 | JSON.stringify(DecodeTransactionData.run(txnData)).toLowerCase(), 24 | JSON.stringify(expectedObject).toLowerCase() 25 | ); 26 | }); 27 | 28 | it("Decode batchReveal", async function() { 29 | const voting = await Voting.deployed(); 30 | 31 | // Generate 5 random reveals to test. 32 | const revealArray = []; 33 | for (let i = 0; i < 5; i++) { 34 | revealArray.push({ 35 | identifier: web3.utils.randomHex(32), 36 | time: getRandomUnsignedInt().toString(), 37 | price: getRandomSignedInt().toString(), 38 | salt: getRandomSignedInt().toString() 39 | }); 40 | } 41 | 42 | const txnData = voting.contract.methods.batchReveal(revealArray).encodeABI(); 43 | const expectedObject = { 44 | name: "batchReveal", 45 | params: { 46 | reveals: revealArray 47 | } 48 | }; 49 | 50 | assert.equal( 51 | JSON.stringify(DecodeTransactionData.run(txnData)).toLowerCase(), 52 | JSON.stringify(expectedObject).toLowerCase() 53 | ); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /financial-templates-lib/logger/PagerDutyTransport.js: -------------------------------------------------------------------------------- 1 | // This transport enables winston logging to send messages to pager duty. All pager duty logs are either `low` or `high` 2 | // urgency. If set to `low` the incident has a less aggressive escalation policy. In this `low` setting if the 3 | // notification is not acknowledged by the person on call within 30 mins a second person is contacted until the warning 4 | // is acknowledged. If set to `high` the incident is aggressively escalated. If no acknowledgement within 5 minutes a 5 | // second person is contacted until the message is acknowledged. 6 | 7 | const Transport = require("winston-transport"); 8 | const pdClient = require("node-pagerduty"); 9 | 10 | module.exports = class PagerDutyTransport extends Transport { 11 | constructor(winstonOpts, pagerDutyOptions) { 12 | super(winstonOpts); 13 | this.serviceId = pagerDutyOptions.pdServiceId; 14 | this.fromEmail = pagerDutyOptions.fromEmail; 15 | this.pd = new pdClient(pagerDutyOptions.pdApiToken); 16 | } 17 | 18 | async log(info, callback) { 19 | try { 20 | await this.pd.incidents.createIncident(this.fromEmail, { 21 | incident: { 22 | type: "incident", 23 | title: `${info.level}: ${info.at} ⭢ ${info.message}`, 24 | service: { 25 | id: this.serviceId, 26 | type: "service_reference" 27 | }, 28 | urgency: info.level == "warn" ? "low" : "high", // If level is warn then urgency is low. If level is error then urgency is high. 29 | body: { 30 | type: "incident_body", 31 | details: info.mrkdwn ? info.mrkdwn : JSON.stringify(info) // If the message has markdown then add it. Else put the whole info object. 32 | } 33 | } 34 | }); 35 | callback(); 36 | } catch (error) { 37 | console.error("PagerDuty error", error); 38 | } 39 | 40 | callback(); 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /core/contracts/common/implementation/Testable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | import "./Timer.sol"; 4 | 5 | 6 | /** 7 | * @title Base class that provides time overrides, but only if being run in test mode. 8 | */ 9 | abstract contract Testable { 10 | // If the contract is being run on the test network, then `timerAddress` will be the 0x0 address. 11 | // Note: this variable should be set on construction and never modified. 12 | address public timerAddress; 13 | 14 | /** 15 | * @notice Constructs the Testable contract. Called by child contracts. 16 | * @param _timerAddress Contract that stores the current time in a testing environment. 17 | * Must be set to 0x0 for production environments that use live time. 18 | */ 19 | constructor(address _timerAddress) internal { 20 | timerAddress = _timerAddress; 21 | } 22 | 23 | /** 24 | * @notice Reverts if not running in test mode. 25 | */ 26 | modifier onlyIfTest { 27 | require(timerAddress != address(0x0)); 28 | _; 29 | } 30 | 31 | /** 32 | * @notice Sets the current time. 33 | * @dev Will revert if not running in test mode. 34 | * @param time timestamp to set current Testable time to. 35 | */ 36 | function setCurrentTime(uint256 time) external onlyIfTest { 37 | Timer(timerAddress).setCurrentTime(time); 38 | } 39 | 40 | /** 41 | * @notice Gets the current time. Will return the last time set in `setCurrentTime` if running in test mode. 42 | * Otherwise, it will return the block timestamp. 43 | * @return uint for the current Testable timestamp. 44 | */ 45 | function getCurrentTime() public view returns (uint256) { 46 | if (timerAddress != address(0x0)) { 47 | return Timer(timerAddress).getCurrentTime(); 48 | } else { 49 | return now; // solhint-disable-line not-rely-on-time 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /documentation/oracle/governance/UMA_token_holder_responsibilities.md: -------------------------------------------------------------------------------- 1 | # UMA Token Holder Responsibilities 2 | 3 | Owners of UMA tokens have 2 major categories of responsibilities: 4 | 5 | 1. Voting on price requests from financial contracts using the DVM 6 | 1. Governing the UMA ecosystem by voting on parameter changes and approving system upgrades 7 | 8 | A tutorial on how to vote on price requests from CLI is [here](../tutorials/voting_with_uma_tokens.md). Voters can also use the [Voting dApp](https://vote.umaproject.org/). 9 | 10 | Voters who participate and vote correctly earn an inflationary reward (currently 0.05% of total network token supply), distributed pro-rata by stake. 11 | 12 | ## Voting on price requests 13 | 14 | Owners of UMA tokens will occasionally be asked to vote on price requests from financial contracts in a 2-stage (commit and reveal) voting period. Each stage is open for 24 hours, so each voting period is 48 hours. 15 | They will have the opportunity to discuss their votes in the #voting channel of the UMA Slack before voting. 16 | 17 | ## Governing the UMA ecosystem 18 | 19 | Owners of UMA tokens govern 2 areas of the UMA ecosystem: 20 | 21 | 1. Financial contracts depending on the DVM 22 | 1. The UMA DVM 23 | 24 | All governance issues will be addressed via an UMIP process. The UMIP process is detailed [here](./governance/UMIPs.md). 25 | 26 | Because UMA token owners will be responsible for voting on price requests from financial contracts depending on the DVM, they need to approve any price identifiers that they may be asked to opine on and any financial contracts that may create price requests. 27 | To that end, they need to: 28 | 29 | - Approve new price identifiers 30 | - Register and de-register contract templates 31 | - Shut down contract instantiations in rare circumstances 32 | 33 | Owners of UMA tokens also participate in governance of the DVM by upgrading the core DVM protocol and modifying DVM parameters. 34 | -------------------------------------------------------------------------------- /core/networks/42.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "contractName": "Migrations", 4 | "address": "0xC278606d5Ee02eDADd3237f4750Da2e9ED70306A" 5 | }, 6 | { 7 | "contractName": "Finder", 8 | "address": "0x6850e2B45D2951301a0BFED44F0a80E9F32fAfEe" 9 | }, 10 | { 11 | "contractName": "VotingToken", 12 | "address": "0xbFD3F61F3dd2AFBd8a0B9Ab081620352268913F4" 13 | }, 14 | { 15 | "contractName": "IdentifierWhitelist", 16 | "address": "0x851588DD61B11D52180563cfCEeF9a87Db5D7c69" 17 | }, 18 | { 19 | "contractName": "Voting", 20 | "address": "0x473C5F35Bab382DCef3EA34180E13c74C1428401" 21 | }, 22 | { 23 | "contractName": "Registry", 24 | "address": "0x519D00b1a6d56205D3d13a4d28aDC82CC849d0E4" 25 | }, 26 | { 27 | "contractName": "FinancialContractsAdmin", 28 | "address": "0xC0659dA4baC51217489B5987f59f7a2F43bb46D4" 29 | }, 30 | { 31 | "contractName": "Store", 32 | "address": "0x27C5488d9F798CB841C3B584C098d76e12290660" 33 | }, 34 | { 35 | "contractName": "Governor", 36 | "address": "0x80f46D5887cfFCa7E8F43e475d182A5A1Dde8721" 37 | }, 38 | { 39 | "contractName": "DesignatedVotingFactory", 40 | "address": "0x957fA30B5c9C5991b62FF71a85C83EDE53Cd50aA" 41 | }, 42 | { 43 | "contractName": "TokenFactory", 44 | "address": "0x478049C316035a3Cf0e1d73fdeD5BC45D1CeFde4" 45 | }, 46 | { 47 | "contractName": "WETH9", 48 | "address": "0xd0A1E359811322d97991E03f863a0C30C2cF029C" 49 | }, 50 | { 51 | "contractName": "AddressWhitelist", 52 | "address": "0x47AaC2e4563c2557A39D8c73Fc977F07bFF15b5f" 53 | }, 54 | { 55 | "contractName": "ExpiringMultiPartyLib", 56 | "address": "0xbCe5654047A98405B94Ad342EF3136B179C75890" 57 | }, 58 | { 59 | "contractName": "ExpiringMultiPartyCreator", 60 | "address": "0x0139d00c416e9F40465a95481F4E36422a0A5fcc" 61 | }, 62 | { 63 | "contractName": "TestnetERC20", 64 | "address": "0xbF7A7169562078c96f0eC1A8aFD6aE50f12e5A99" 65 | } 66 | ] 67 | -------------------------------------------------------------------------------- /core/test/oracle/VoteTiming.js: -------------------------------------------------------------------------------- 1 | const { didContractThrow } = require("../../../common/SolidityTestUtils.js"); 2 | 3 | const VoteTimingTest = artifacts.require("VoteTimingTest"); 4 | 5 | contract("VoteTiming", function(accounts) { 6 | const COMMIT_PHASE = "0"; 7 | const REVEAL_PHASE = "1"; 8 | beforeEach(async function() { 9 | voteTiming = await VoteTimingTest.new("100"); 10 | }); 11 | it("Reject invalid init params", async function() { 12 | // Should not be able to create an instance of VoteTiming with 0 phase length. 13 | assert(await didContractThrow(VoteTimingTest.new("0"))); 14 | }); 15 | 16 | it("Phasing", async function() { 17 | // If time % 200 is between 0 and 99 (inclusive), the phase should be commit. 18 | assert.equal((await voteTiming.wrapComputeCurrentPhase("50")).toString(), COMMIT_PHASE); 19 | assert.equal((await voteTiming.wrapComputeCurrentPhase("1401")).toString(), COMMIT_PHASE); 20 | 21 | // If time % 200 is between 100 and 199 (inclusive), the phase should be reveal. 22 | assert.equal((await voteTiming.wrapComputeCurrentPhase("100")).toString(), REVEAL_PHASE); 23 | assert.equal((await voteTiming.wrapComputeCurrentPhase("17145")).toString(), REVEAL_PHASE); 24 | }); 25 | 26 | it("Compute Round Id", async function() { 27 | const startTime = 1579202864; 28 | // Round Id is a function of the current time defined by floor(timestamp/phaseLength) 29 | const initialRoundId = (await voteTiming.wrapComputeCurrentRoundId(startTime)).toNumber(); 30 | assert.equal(initialRoundId, Math.floor(startTime / 200)); 31 | 32 | // Incremented by +200 should result in the next Round Id 33 | assert.equal((await voteTiming.wrapComputeCurrentRoundId(startTime + 200)).toNumber(), initialRoundId + 1); 34 | 35 | // Incremented by +250 should result in the same round Id as +200 as it rounds down 36 | assert.equal((await voteTiming.wrapComputeCurrentRoundId(startTime + 250)).toNumber(), initialRoundId + 1); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /core/scripts/cli/sponsor/viewLiquidationDetails.js: -------------------------------------------------------------------------------- 1 | const inquirer = require("inquirer"); 2 | const getDefaultAccount = require("../wallet/getDefaultAccount"); 3 | const { LiquidationStatesEnum } = require("../../../../common/Enums.js"); 4 | const { getIsWeth, unwrapToEth } = require("./currencyUtils"); 5 | const { submitTransaction } = require("./transactionUtils"); 6 | 7 | const viewLiquidationDetails = async (web3, artifacts, emp, liquidation, id) => { 8 | const ExpandedERC20 = artifacts.require("ExpandedERC20"); 9 | 10 | const sponsorAddress = await getDefaultAccount(web3); 11 | const display = `Liquidated at epoch time ${liquidation.liquidationTime} by ${liquidation.liquidator}`; 12 | const backChoice = "Back"; 13 | const withdrawAction = "Withdraw"; 14 | choices = [{ name: backChoice }]; 15 | // Check if the sponsor can withdraw by seeing if `withdrawLiquidation` reverts. 16 | try { 17 | await emp.withdrawLiquidation.call(id, sponsorAddress); 18 | choices.push({ name: withdrawAction }); 19 | } catch (err) { 20 | // Withdraw wouldn't work so it shouldn't be a valid option. 21 | } 22 | const input = await inquirer.prompt({ 23 | type: "list", 24 | name: "choice", 25 | message: display, 26 | choices 27 | }); 28 | if (input["choice"] === backChoice) { 29 | return; 30 | } 31 | const confirmation = await inquirer.prompt({ 32 | type: "confirm", 33 | message: "Withdrawing collateral. Continue?", 34 | name: "confirm" 35 | }); 36 | if (confirmation["confirm"]) { 37 | const collateralCurrency = await ExpandedERC20.at(await emp.collateralCurrency()); 38 | const isWeth = await getIsWeth(web3, artifacts, collateralCurrency); 39 | 40 | const withdrawalAmount = await emp.withdrawLiquidation.call(id, sponsorAddress); 41 | await submitTransaction(web3, async () => await emp.withdrawLiquidation(id, sponsorAddress), "Withdrawing"); 42 | if (isWeth) { 43 | await unwrapToEth(web3, artifacts, emp, withdrawalAmount.toString()); 44 | } 45 | } 46 | }; 47 | 48 | module.exports = viewLiquidationDetails; 49 | -------------------------------------------------------------------------------- /financial-templates-lib/logger/Transports.js: -------------------------------------------------------------------------------- 1 | // This module generates transport objects for the winston logger to push messages to. Primarily this module separates 2 | // the logic for reading in state environment variables from the logger itself. All Winston transport objects and their 3 | // associated formatting are created within this module. 4 | 5 | // Transport objects 6 | const ConsoleTransport = require("./ConsoleTransport"); 7 | const SlackTransport = require("./SlackTransport"); 8 | const PagerDutyTransport = require("./PagerDutyTransport"); 9 | 10 | require("dotenv").config(); 11 | const argv = require("minimist")(process.argv.slice(), {}); 12 | 13 | // Transports array to store all winston transports. 14 | let transports = []; 15 | 16 | // If the logger is running in production mode then add the GCE winston transport. Else, add a console transport. 17 | if (process.env.ENVIRONMENT == "production") { 18 | const { LoggingWinston } = require("@google-cloud/logging-winston"); 19 | require("@google-cloud/trace-agent").start(); 20 | transports.push(new LoggingWinston()); 21 | } else { 22 | // Add a console transport to log to the console. 23 | transports.push(ConsoleTransport.createConsoleTransport()); 24 | } 25 | 26 | // If there is "test" in the environment then skip the slack or pagerduty. 27 | if (argv._.indexOf("test") == -1) { 28 | // If there is a slack web hook, add to the transports array to enable slack messages. 29 | if (process.env.SLACK_WEBHOOK) { 30 | transports.push(SlackTransport.createSlackTransport(process.env.SLACK_WEBHOOK)); 31 | } 32 | 33 | // If there is a Pagerduty API key then add the pagerduty winston transport. 34 | if (process.env.PAGERDUTY_API_KEY) { 35 | transports.push( 36 | new PagerDutyTransport( 37 | { level: "warn" }, 38 | { 39 | pdApiToken: process.env.PAGERDUTY_API_KEY, 40 | pdServiceId: process.env.PAGERDUTY_SERVICE_ID, 41 | fromEmail: process.env.PAGERDUTY_FROM_EMAIL 42 | } 43 | ) 44 | ); 45 | } 46 | } 47 | 48 | module.exports = { transports }; 49 | -------------------------------------------------------------------------------- /core/test/common/Lockable.js: -------------------------------------------------------------------------------- 1 | const { didContractThrow } = require("../../../common/SolidityTestUtils.js"); 2 | 3 | const ReentrancyMock = artifacts.require("ReentrancyMock"); 4 | const ReentrancyAttack = artifacts.require("ReentrancyAttack"); 5 | 6 | // Extends https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.1/test/utils/ReentrancyGuard.test.js. 7 | contract("Lockable", function(accounts) { 8 | let reentrancyMock; 9 | describe("nonReentrant and nonReentrant modifiers", function() { 10 | beforeEach(async function() { 11 | reentrancyMock = await ReentrancyMock.new(); 12 | assert.equal((await reentrancyMock.counter()).toString(), "0"); 13 | }); 14 | 15 | it("should not allow remote callback to a state-changing function", async function() { 16 | const attacker = await ReentrancyAttack.new(); 17 | assert(await didContractThrow(reentrancyMock.countAndSend(attacker.address))); 18 | }); 19 | 20 | it("should not allow remote callback to a view-only function", async function() { 21 | const attacker = await ReentrancyAttack.new(); 22 | assert(await didContractThrow(reentrancyMock.countAndCall(attacker.address))); 23 | }); 24 | 25 | // The following are more side-effects than intended behavior: 26 | // I put them here as documentation, and to monitor any changes 27 | // in the side-effects. 28 | 29 | it("should not allow local recursion", async function() { 30 | assert(await didContractThrow(reentrancyMock.countLocalRecursive(10))); 31 | }); 32 | 33 | it("should not allow indirect local recursion", async function() { 34 | assert(await didContractThrow(reentrancyMock.countThisRecursive(10))); 35 | }); 36 | 37 | it("should not allow local calls to view-only functions", async function() { 38 | assert(await didContractThrow(reentrancyMock.countLocalCall())); 39 | }); 40 | 41 | it("should not allow indirect local calls to view-only functions", async function() { 42 | assert(await didContractThrow(reentrancyMock.countThisCall())); 43 | }); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /documentation/getting_started/priceless_defi_contracts.md: -------------------------------------------------------------------------------- 1 | # Priceless DeFi Contracts 2 | 3 | “Priceless” financial contracts are contracts that don’t require an on-chain price feed to function, and minimize on-chain oracle usage to reduce the frequency and surface area for oracle attacks. 4 | They are designed with mechanisms to incentivize counterparties to properly collateralize their positions without requiring any on-chain price feed. 5 | These mechanisms include a liquidation and dispute process that allows counterparties to be rewarded for identifying improperly collateralized positions. 6 | Unless a position is liquidated, it is assumed to be solvent (properly collateralized). 7 | Oracles are only used when a liquidation is disputed — which is designed to be rare. 8 | 9 | ## Priceless Synthetic Tokens 10 | 11 | One can write priceless financial contract templates to create various kinds of financial products. 12 | The UMA team has written one to create synthetic tokens. 13 | These are ERC-20 tokens whose required backing collateral is determined by the value of a price identifier. 14 | There is no on-chain price feed for the values of the price identifier; rather, token holders and token sponsors should monitor the value of this price identifier off-chain to inform their decisions about how much collateral to maintain on-chain. 15 | If token sponsors are improperly collateralized, liquidators can liquidate token sponsors’ positions. 16 | Improper liquidations can be disputed by disputers. Details on these mechanisms are available [here](../synthetic_tokens/explainer.md). 17 | 18 | ### Additional Resources 19 | 20 | Here are some additional resources to look at to better understand how the priceless synthetic token contract works: 21 | 22 | - [Documentation](../synthetic_tokens/explainer.md) 23 | - [Blog post](https://medium.com/uma-project/priceless-synthetic-tokens-f28e6452c18b) 24 | - [Twitter thread](https://twitter.com/UMAprotocol/status/1242891550872535042?s=20) 25 | - [Github implementation](https://github.com/UMAprotocol/protocol/tree/master/core/contracts/financial-templates/implementation) 26 | -------------------------------------------------------------------------------- /voter-dapp/src/DesignatedVotingTransfer.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Button from "@material-ui/core/Button"; 3 | import Typography from "@material-ui/core/Typography"; 4 | import { drizzleReactHooks } from "@umaprotocol/react-plugin"; 5 | import { useTableStyles } from "./Styles.js"; 6 | 7 | function DesignatedVotingTransfer({ votingAccount }) { 8 | const { drizzle, useCacheCall, useCacheSend } = drizzleReactHooks.useDrizzle(); 9 | const { web3 } = drizzle; 10 | const classes = useTableStyles(); 11 | 12 | const { account } = drizzleReactHooks.useDrizzleState(drizzleState => ({ 13 | account: drizzleState.accounts[0] 14 | })); 15 | 16 | const currentAccountBalance = useCacheCall("VotingToken", "balanceOf", account); 17 | 18 | // Transfer tokens in the current account to the DesignatedVoting account. 19 | const { send: transfer, status: transferStatus } = useCacheSend("VotingToken", "transfer"); 20 | const onTransfer = () => { 21 | transfer(votingAccount, currentAccountBalance); 22 | }; 23 | 24 | const fetchComplete = currentAccountBalance; 25 | if (!fetchComplete) { 26 | return
LOADING
; 27 | } 28 | 29 | // No tokens in the current wallet: nothing to transfer. 30 | if (currentAccountBalance.toString() === "0") { 31 | return null; 32 | } 33 | 34 | const hasPendingTransactions = transferStatus; 35 | return ( 36 |
37 | 38 | Two key voting 39 | 40 |
41 | You have {web3.utils.fromWei(currentAccountBalance)} tokens that will NOT be voted with. You'll need to first 42 | transfer them to your DesignatedVoting instance at address {votingAccount}. 43 |
44 | Make sure you control the cold wallet key before transferring! 45 |
46 |
47 | 50 |
51 |
52 | ); 53 | } 54 | 55 | export default DesignatedVotingTransfer; 56 | -------------------------------------------------------------------------------- /common/AbiUtils.js: -------------------------------------------------------------------------------- 1 | // This library has two functions that it exports: getAllContracts() and getAbiDecoder(). 2 | // 3 | // getAllContracts() returns an array of all JSON artifacts from the core/build/contracts directory. 4 | // 5 | // getAbiDecoder returns an abi decoder (see https://github.com/UMAprotocol/abi-decoder) object preloaded with the ABIs 6 | // pulled from the core/build/contracts directory. Example usage: 7 | // getAbiDecoder().decodeMethod(data); // This decodes the txn data into the function name and arguments. 8 | 9 | const abiDecoder = require("abi-decoder"); 10 | 11 | function importAll(r) { 12 | return r.keys().map(r); 13 | } 14 | 15 | function getAllContracts() { 16 | let importedObjects; 17 | 18 | // Note: we use a try here because we don't want to install the require-context package in node.js contexts where 19 | // it won't work. 20 | try { 21 | // This only works in webpack. 22 | const requireContext = require("require-context"); 23 | 24 | // Note: all arguments must be hardcoded here for webpack to bundle the files correctly. 25 | // This line also generates a few build warnings that should be ignored. 26 | const contractContext = require.context("../core/build/contracts/", true, /\.json$/); 27 | 28 | importedObjects = importAll(contractContext); 29 | } catch (e) { 30 | // This only works in node.js. 31 | const fs = require("fs"); 32 | const path = require("path"); 33 | const contractsPath = path.join(__dirname, "../core/build/contracts/"); 34 | 35 | const fileList = fs.readdirSync(contractsPath).filter(name => name.match(/\.json$/)); 36 | importedObjects = fileList.map(filename => { 37 | const fileContents = fs.readFileSync(path.join(contractsPath, filename)); 38 | return JSON.parse(fileContents); 39 | }); 40 | } 41 | 42 | return importedObjects; 43 | } 44 | 45 | function getAbiDecoder() { 46 | const contracts = getAllContracts(); 47 | for (const contract of contracts) { 48 | abiDecoder.addABI(contract.abi); 49 | } 50 | 51 | return abiDecoder; 52 | } 53 | 54 | module.exports = { 55 | getAllContracts, 56 | getAbiDecoder 57 | }; 58 | -------------------------------------------------------------------------------- /core/scripts/cli/wallet/readDefaultAccountInfo.js: -------------------------------------------------------------------------------- 1 | const style = require("../textStyle"); 2 | const getDefaultAccount = require("./getDefaultAccount"); 3 | const getTwoKeyContract = require("./getTwoKeyContract"); 4 | 5 | /** 6 | * Displays information about the default account: 7 | * - Address 8 | * - ETH balance 9 | * - UMA voting token balance 10 | * 11 | * @param {* Object} web3 Web3 provider 12 | * @param {* Object} artifacts Contract artifacts 13 | */ 14 | const readDefaultAccountInfo = async (web3, artifacts) => { 15 | const { fromWei } = web3.utils; 16 | const { getBalance } = web3.eth; 17 | const VotingToken = artifacts.require("VotingToken"); 18 | 19 | try { 20 | style.spinnerReadingContracts.start(); 21 | const account = await getDefaultAccount(web3); 22 | const address = account; 23 | const balance = await getBalance(address); 24 | const votingToken = await VotingToken.deployed(); 25 | const votingBalance = await votingToken.balanceOf(address); 26 | let designatedVotingContract = await getTwoKeyContract(web3, artifacts); 27 | style.spinnerReadingContracts.stop(); 28 | 29 | console.group(style.success("\n** Ethereum Account Info **")); 30 | console.log(`- ${style.success("Address")}: ${address}`); 31 | console.log(`- ${style.success("Balance")}: ${fromWei(balance)} ETH`); 32 | console.log(`- ${style.success("Balance")}: ${fromWei(votingBalance)} UMA voting token`); 33 | console.log("\n"); 34 | console.groupEnd(); 35 | 36 | if (designatedVotingContract) { 37 | console.group(style.success("\n** Two Key Contract Info **")); 38 | const designatedVotingBalance = await votingToken.balanceOf(designatedVotingContract.address); 39 | console.log(`- ${style.success("Balance")}: ${fromWei(designatedVotingBalance)} UMA voting token`); 40 | console.log("\n"); 41 | console.groupEnd(); 42 | } 43 | } catch (err) { 44 | console.error(err); 45 | console.error( 46 | "Failed to read default account information. Are you sure the contracts are deployed to this network?" 47 | ); 48 | } 49 | }; 50 | 51 | module.exports = readDefaultAccountInfo; 52 | -------------------------------------------------------------------------------- /common/ObjectUtils.js: -------------------------------------------------------------------------------- 1 | // Contains helpful methods for interacting with the Object data type. 2 | 3 | /** 4 | * @notice Given `overrideProps` and `defaultProps` Objects, returns a new Object, `newObject`, 5 | * with the same properties as `defaultProps`, but replaces any property values that overlap with 6 | * those also contained in `overrideProps`. Performs validation tests on all `newObject` properties, as specified 7 | * by `defaultProps`. 8 | * @dev Throws an Error if any validation tests fail. 9 | * @param {Object} [overrideProps] specifies property values in newly created Object that should differ from 10 | * those contained in `defaultProps`. 11 | * @param {Object{Object, Function}} defaultProps its properties will be the same as the newly created Object, but each 12 | * property itself is an Object that has `value` and `isValid` properties. The `value` determine the default values of 13 | * the new Object and `isValid` will be called to validate each of `newObject`'s properties. 14 | * @return `newObject` a new Object with the same properties as `defaultProps`, or `defaultProps` if undefined `overrideProps`. 15 | */ 16 | const createObjectFromDefaultProps = (overrideProps, defaultProps) => { 17 | if (!defaultProps) { 18 | throw new Error("Undefined `defaultProps`"); 19 | } 20 | if (!overrideProps) { 21 | return defaultProps; 22 | } 23 | 24 | const newObject = {}; 25 | 26 | Object.keys(defaultProps).forEach(prop => { 27 | // Set property value to that contained in `overrideProps` if it exists, else set to `defaultProps`. 28 | newObject[prop] = prop in overrideProps ? overrideProps[prop] : defaultProps[prop].value; 29 | 30 | if (!("isValid" in defaultProps[prop])) { 31 | throw new Error(`Property (${prop}) must define an "isValid" method`); 32 | } 33 | 34 | // Validate property value, regardless if coming from `overrideProps` or `defaultProps`. 35 | if (!defaultProps[prop].isValid(newObject[prop])) { 36 | throw new Error("Attempting to set configuration field with invalid value"); 37 | } 38 | }); 39 | 40 | return newObject; 41 | }; 42 | 43 | module.exports = { 44 | createObjectFromDefaultProps 45 | }; 46 | -------------------------------------------------------------------------------- /core/scripts/cli/voting/getResolvedVotesByRoundId.js: -------------------------------------------------------------------------------- 1 | const style = require("../textStyle"); 2 | const argv = require("minimist")(process.argv.slice()); 3 | 4 | /** 5 | * Return the list of votes (that the voter has participated in) that have successfully resolved a price 6 | * mapped to their round ID's. 7 | * 8 | * @param {* Object} web3 Web3 provider 9 | * @param {* Object} voting deployed Voting.sol contract instance 10 | * @param {* String} account Etheruem account of voter 11 | */ 12 | const getResolvedVotesByRound = async (web3, votingContract, account) => { 13 | // TODO(#901): MetaMask provider sometimes has trouble reading past events 14 | if (argv.network === "metamask") { 15 | return; 16 | } 17 | 18 | // First check list of votes revealed by user to determine which 19 | // price requests a user has voted on 20 | const revealedVotes = await votingContract.getPastEvents("VoteRevealed", { 21 | filter: { voter: account }, 22 | fromBlock: 0 23 | }); 24 | 25 | // Construct list of round ID's participated in by voter 26 | const roundIds = {}; 27 | for (let i = 0; i < revealedVotes.length; i++) { 28 | const roundId = revealedVotes[i].args.roundId.toString(); 29 | // If this is a new roundId, count it 30 | if (!roundIds[roundId]) { 31 | roundIds[roundId] = []; 32 | } else { 33 | continue; 34 | } 35 | } 36 | 37 | // Now filter resolved prices by the voter participation 38 | if (Object.keys(roundIds).length > 0) { 39 | const resolvedPrices = await votingContract.getPastEvents("PriceResolved", { 40 | filter: { roundId: Object.keys(roundIds) }, 41 | fromBlock: 0 42 | }); 43 | 44 | resolvedPrices.forEach(price => { 45 | const roundId = price.args.roundId; 46 | const resolvedPrice = { 47 | roundId: roundId.toString(), 48 | identifier: web3.utils.hexToUtf8(price.args.identifier), 49 | time: style.formatSecondsToUtc(price.args.time), 50 | price: web3.utils.fromWei(price.args.price) 51 | }; 52 | roundIds[roundId].push(resolvedPrice); 53 | }); 54 | } 55 | 56 | return roundIds; 57 | }; 58 | 59 | module.exports = getResolvedVotesByRound; 60 | -------------------------------------------------------------------------------- /core/contracts/common/test/ReentrancyMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | import "../implementation/Lockable.sol"; 4 | import "./ReentrancyAttack.sol"; 5 | 6 | 7 | // Tests reentrancy guards defined in Lockable.sol. 8 | // Extends https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.1/contracts/mocks/ReentrancyMock.sol. 9 | contract ReentrancyMock is Lockable { 10 | uint256 public counter; 11 | 12 | constructor() public { 13 | counter = 0; 14 | } 15 | 16 | function callback() external nonReentrant { 17 | _count(); 18 | } 19 | 20 | function countAndSend(ReentrancyAttack attacker) external nonReentrant { 21 | _count(); 22 | bytes4 func = bytes4(keccak256("callback()")); 23 | attacker.callSender(func); 24 | } 25 | 26 | function countAndCall(ReentrancyAttack attacker) external nonReentrant { 27 | _count(); 28 | bytes4 func = bytes4(keccak256("getCount()")); 29 | attacker.callSender(func); 30 | } 31 | 32 | function countLocalRecursive(uint256 n) public nonReentrant { 33 | if (n > 0) { 34 | _count(); 35 | countLocalRecursive(n - 1); 36 | } 37 | } 38 | 39 | function countThisRecursive(uint256 n) public nonReentrant { 40 | if (n > 0) { 41 | _count(); 42 | // solhint-disable-next-line avoid-low-level-calls 43 | (bool success, ) = address(this).call(abi.encodeWithSignature("countThisRecursive(uint256)", n - 1)); 44 | require(success, "ReentrancyMock: failed call"); 45 | } 46 | } 47 | 48 | function countLocalCall() public nonReentrant { 49 | getCount(); 50 | } 51 | 52 | function countThisCall() public nonReentrant { 53 | // solhint-disable-next-line avoid-low-level-calls 54 | (bool success, ) = address(this).call(abi.encodeWithSignature("getCount()")); 55 | require(success, "ReentrancyMock: failed call"); 56 | } 57 | 58 | function getCount() public view nonReentrantView returns (uint256) { 59 | return counter; 60 | } 61 | 62 | function _count() private { 63 | counter += 1; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /core/scripts/cli/sponsor/deposit.js: -------------------------------------------------------------------------------- 1 | const inquirer = require("inquirer"); 2 | const getDefaultAccount = require("../wallet/getDefaultAccount"); 3 | const { wrapToWeth, getIsWeth, getCurrencySymbol } = require("./currencyUtils.js"); 4 | const { submitTransaction } = require("./transactionUtils"); 5 | 6 | const deposit = async (web3, artifacts, emp) => { 7 | const ExpandedERC20 = artifacts.require("ExpandedERC20"); 8 | const { toWei } = web3.utils; 9 | const collateralCurrency = await ExpandedERC20.at(await emp.collateralCurrency()); 10 | const isWeth = await getIsWeth(web3, artifacts, collateralCurrency); 11 | const collateralSymbol = await getCurrencySymbol(web3, artifacts, collateralCurrency); 12 | const requiredCollateralSymbol = isWeth ? "ETH" : collateralSymbol; 13 | 14 | const input = await inquirer.prompt({ 15 | name: "depositCollateral", 16 | message: `How much ${requiredCollateralSymbol} would you like to deposit as collateral?`, 17 | validate: value => value > 0 || `Amount of ${requiredCollateralSymbol} must be positive` 18 | }); 19 | const confirmation = await inquirer.prompt({ 20 | type: "confirm", 21 | message: `Depositing ${input["depositCollateral"]} ${requiredCollateralSymbol}. Continue?`, 22 | name: "confirm" 23 | }); 24 | if (confirmation["confirm"]) { 25 | const collateral = toWei(input["depositCollateral"]); 26 | 27 | let totalTransactions = 2; 28 | let transactionNum = 1; 29 | if (isWeth) { 30 | totalTransactions = 3; 31 | await wrapToWeth(web3, artifacts, emp, collateral, transactionNum, totalTransactions); 32 | transactionNum++; 33 | } 34 | 35 | await submitTransaction( 36 | web3, 37 | async () => await collateralCurrency.approve(emp.address, collateral), 38 | `Approving ${collateralSymbol} transfer`, 39 | transactionNum, 40 | totalTransactions 41 | ); 42 | transactionNum++; 43 | await submitTransaction( 44 | web3, 45 | async () => await emp.deposit({ rawValue: collateral.toString() }), 46 | "Depositing collateral", 47 | transactionNum, 48 | totalTransactions 49 | ); 50 | } 51 | }; 52 | 53 | module.exports = deposit; 54 | -------------------------------------------------------------------------------- /core/scripts/cli/wallet.js: -------------------------------------------------------------------------------- 1 | const inquirer = require("inquirer"); 2 | const createNewAccount = require("./wallet/createNewAccount"); 3 | const readDefaultAccount = require("./wallet/readDefaultAccountInfo"); 4 | const style = require("./textStyle"); 5 | 6 | const ACTIONS = { 7 | info: "Info", 8 | generate: "Generate Account", 9 | help: "Help", 10 | back: "Back" 11 | }; 12 | 13 | const wallet = async () => { 14 | const prompts = [ 15 | { 16 | type: "list", 17 | name: "walletTopMenu", 18 | message: "UMA wallet actions", 19 | choices: Object.values(ACTIONS) 20 | } 21 | ]; 22 | 23 | return await inquirer.prompt(prompts); 24 | }; 25 | 26 | /** 27 | * Menu for Wallet subactions of CLI 28 | */ 29 | const walletMenu = async function(web3, artifacts) { 30 | try { 31 | const inputs = (await wallet())["walletTopMenu"]; 32 | switch (inputs) { 33 | // INFO: Display default account information for user 34 | case ACTIONS.info: 35 | await readDefaultAccount(web3, artifacts); 36 | break; 37 | 38 | // GENERATE: Create a new account for user 39 | case ACTIONS.generate: 40 | await createNewAccount(web3); 41 | break; 42 | 43 | // HELP 44 | case ACTIONS.help: 45 | console.group(`${style.help("Wallet actions")}:`); 46 | console.log( 47 | `${style.help( 48 | ACTIONS.info 49 | )}: Displays balance information for your default account from which you will send UMA-related transactions` 50 | ); 51 | console.log( 52 | `${style.help( 53 | ACTIONS.generate 54 | )}: Create and display credentials for a new Ethereum account. If you want to make this your default signing account for UMA-related transactions then you can import it into Metamask or save it into your environment variable "MNEMONIC".` 55 | ); 56 | console.groupEnd(); 57 | break; 58 | 59 | // BACK 60 | case ACTIONS.back: 61 | return; 62 | 63 | default: 64 | console.log("unimplemented state"); 65 | } 66 | } catch (err) { 67 | console.error('Unknown "wallet" error:', err); 68 | } 69 | return; 70 | }; 71 | 72 | module.exports = walletMenu; 73 | -------------------------------------------------------------------------------- /core/test/oracle/FinancialContractsAdmin.js: -------------------------------------------------------------------------------- 1 | const { didContractThrow } = require("../../../common/SolidityTestUtils.js"); 2 | 3 | const truffleAssert = require("truffle-assertions"); 4 | 5 | const FinancialContractsAdmin = artifacts.require("FinancialContractsAdmin"); 6 | const MockAdministratee = artifacts.require("MockAdministratee"); 7 | 8 | contract("FinancialContractsAdmin", function(accounts) { 9 | let financialContractsAdmin; 10 | let mockAdministratee; 11 | 12 | const owner = accounts[0]; 13 | const rando = accounts[1]; 14 | 15 | beforeEach(async function() { 16 | financialContractsAdmin = await FinancialContractsAdmin.deployed(); 17 | mockAdministratee = await MockAdministratee.new(); 18 | }); 19 | 20 | it("Remargin", async function() { 21 | assert.equal(await mockAdministratee.timesRemargined(), "0"); 22 | 23 | // Can't call remargin without being the owner. 24 | assert(await didContractThrow(financialContractsAdmin.callRemargin(mockAdministratee.address, { from: rando }))); 25 | 26 | // Change the owner and verify that remargin can be called. 27 | await financialContractsAdmin.transferOwnership(rando); 28 | await financialContractsAdmin.callRemargin(mockAdministratee.address, { from: rando }); 29 | assert.equal(await mockAdministratee.timesRemargined(), "1"); 30 | 31 | // Return ownership to owner. 32 | await financialContractsAdmin.transferOwnership(owner, { from: rando }); 33 | }); 34 | 35 | it("Emergency Shutdown", async function() { 36 | assert.equal(await mockAdministratee.timesEmergencyShutdown(), "0"); 37 | 38 | // Can't call emergencyShutdown without being the owner. 39 | assert( 40 | await didContractThrow(financialContractsAdmin.callEmergencyShutdown(mockAdministratee.address, { from: rando })) 41 | ); 42 | 43 | // Change the owner and verify that emergencyShutdown can be called. 44 | await financialContractsAdmin.transferOwnership(rando); 45 | await financialContractsAdmin.callEmergencyShutdown(mockAdministratee.address, { from: rando }); 46 | assert.equal(await mockAdministratee.timesEmergencyShutdown(), "1"); 47 | 48 | // Return ownership to owner. 49 | await financialContractsAdmin.transferOwnership(owner, { from: rando }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /core/contracts/oracle/interfaces/StoreInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import "../../common/implementation/FixedPoint.sol"; 6 | 7 | 8 | /** 9 | * @title Interface that allows financial contracts to pay oracle fees for their use of the system. 10 | */ 11 | interface StoreInterface { 12 | /** 13 | * @notice Pays Oracle fees in ETH to the store. 14 | * @dev To be used by contracts whose margin currency is ETH. 15 | */ 16 | function payOracleFees() external payable; 17 | 18 | /** 19 | * @notice Pays oracle fees in the margin currency, erc20Address, to the store. 20 | * @dev To be used if the margin currency is an ERC20 token rather than ETH. 21 | * @param erc20Address address of the ERC20 token used to pay the fee. 22 | * @param amount number of tokens to transfer. An approval for at least this amount must exist. 23 | */ 24 | function payOracleFeesErc20(address erc20Address, FixedPoint.Unsigned calldata amount) external; 25 | 26 | /** 27 | * @notice Computes the regular oracle fees that a contract should pay for a period. 28 | * @param startTime defines the beginning time from which the fee is paid. 29 | * @param endTime end time until which the fee is paid. 30 | * @param pfc "profit from corruption", or the maximum amount of margin currency that a 31 | * token sponsor could extract from the contract through corrupting the price feed in their favor. 32 | * @return regularFee amount owed for the duration from start to end time for the given pfc. 33 | * @return latePenalty for paying the fee after the deadline. 34 | */ 35 | function computeRegularFee( 36 | uint256 startTime, 37 | uint256 endTime, 38 | FixedPoint.Unsigned calldata pfc 39 | ) external view returns (FixedPoint.Unsigned memory regularFee, FixedPoint.Unsigned memory latePenalty); 40 | 41 | /** 42 | * @notice Computes the final oracle fees that a contract should pay at settlement. 43 | * @param currency token used to pay the final fee. 44 | * @return finalFee amount due. 45 | */ 46 | function computeFinalFee(address currency) external view returns (FixedPoint.Unsigned memory); 47 | } 48 | -------------------------------------------------------------------------------- /financial-templates-lib/price-feed/PriceFeedInterface.js: -------------------------------------------------------------------------------- 1 | // Price feed interface -- all price feed implementations should override all functions (except for _abstractFunctionCalled). 2 | class PriceFeedInterface { 3 | // Updates the internal state of the price feed. Should pull in any async data so the get*Price methods can be called. 4 | // Note: derived classes *must* override this method. 5 | async update() { 6 | this._abstractFunctionCalled(); 7 | } 8 | 9 | // Gets the current price (as a BN) for this feed synchronously from the in-memory state of this price feed object. 10 | // This price should be up-to-date as of the last time `update()` was called. If `update()` has never been called, 11 | // this should return `null` or `undefined`. If no price could be retrieved, it should return `null` or `undefined`. 12 | // Note: derived classes *must* override this method. 13 | getCurrentPrice() { 14 | this._abstractFunctionCalled(); 15 | } 16 | 17 | // Gets the price (as a BN) for the time specified. Similar to `getCurrentPrice()`, the price is derived from the 18 | // in-memory state of the price feed object, so this method is syncrhonous. This price should be up-to-date as of the 19 | // last time `update()` was called. If `update()` has never been called, this should return `null` or `undefined. If 20 | // the time is before the pre-determined historical lookback window of this PriceFeed object, then this method should 21 | // return `null` or `undefined`. If the historical price could not be computed for any other reason, this method 22 | // should return `null` or `undefined`. 23 | // Note: derived classes *must* override this method. 24 | getHistoricalPrice(time) { 25 | this._abstractFunctionCalled(); 26 | } 27 | 28 | // This returns the last time that the `update()` method was called. If it hasn't been called, this method should 29 | // return `null` or `undefined`. 30 | // Note: derived classes *must* override this method. 31 | getLastUpdateTime() { 32 | this._abstractFunctionCalled(); 33 | } 34 | 35 | // Common function to throw an error if an interface method is called. 36 | _abstractFunctionCalled() { 37 | throw "Abstract function called -- derived class should implement this function"; 38 | } 39 | } 40 | 41 | module.exports = { 42 | PriceFeedInterface 43 | }; 44 | -------------------------------------------------------------------------------- /core/contracts/common/implementation/ExpandedERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 4 | import "./MultiRole.sol"; 5 | import "../interfaces/ExpandedIERC20.sol"; 6 | 7 | 8 | /** 9 | * @title An ERC20 with permissioned burning and minting. The contract deployer will initially 10 | * be the owner who is capable of adding new roles. 11 | */ 12 | contract ExpandedERC20 is ExpandedIERC20, ERC20, MultiRole { 13 | enum Roles { 14 | // Can set the minter and burner. 15 | Owner, 16 | // Addresses that can mint new tokens. 17 | Minter, 18 | // Addresses that can burn tokens that address owns. 19 | Burner 20 | } 21 | 22 | /** 23 | * @notice Constructs the ExpandedERC20. 24 | * @param _tokenName The name which describes the new token. 25 | * @param _tokenSymbol The ticker abbreviation of the name. Ideally < 5 chars. 26 | * @param _tokenDecimals The number of decimals to define token precision. 27 | */ 28 | constructor( 29 | string memory _tokenName, 30 | string memory _tokenSymbol, 31 | uint8 _tokenDecimals 32 | ) public ERC20(_tokenName, _tokenSymbol) { 33 | _setupDecimals(_tokenDecimals); 34 | _createExclusiveRole(uint256(Roles.Owner), uint256(Roles.Owner), msg.sender); 35 | _createSharedRole(uint256(Roles.Minter), uint256(Roles.Owner), new address[](0)); 36 | _createSharedRole(uint256(Roles.Burner), uint256(Roles.Owner), new address[](0)); 37 | } 38 | 39 | /** 40 | * @dev Mints `value` tokens to `recipient`, returning true on success. 41 | * @param recipient address to mint to. 42 | * @param value amount of tokens to mint. 43 | * @return True if the mint succeeded, or False. 44 | */ 45 | function mint(address recipient, uint256 value) 46 | external 47 | override 48 | onlyRoleHolder(uint256(Roles.Minter)) 49 | returns (bool) 50 | { 51 | _mint(recipient, value); 52 | return true; 53 | } 54 | 55 | /** 56 | * @dev Burns `value` tokens owned by `msg.sender`. 57 | * @param value amount of tokens to burn. 58 | */ 59 | function burn(uint256 value) external override onlyRoleHolder(uint256(Roles.Burner)) { 60 | _burn(msg.sender, value); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /documentation/getting_started/architecture_overview.md: -------------------------------------------------------------------------------- 1 | # Architecture Overview 2 | 3 | UMA builds open-source infrastructure for “priceless” financial contracts on Ethereum. Specifically, this is two things: 4 | 5 | - Data Verification Mechanism (DVM), a decentralized oracle service 6 | - Priceless financial contract designs, which can be used to create synthetic tokens 7 | 8 | Together, these two technologies enable the creation of fast, efficient, and secure synthetic derivatives on the Ethereum blockchain. 9 | 10 | UMA is focused on building “priceless” derivatives on Ethereum. 11 | These financial contracts are designed to ensure proper collateralization by counterparties without the use of an on-chain price feed. 12 | They can do so by providing rewards to counterparties or third parties for identifying improperly collateralized positions. 13 | To confirm that these positions are improperly collateralized, these contracts may rely on a “Data Verification Mechanism” (DVM). 14 | 15 | The DVM is a decentralized oracle service available to respond to price requests made by financial contracts that are registered with it. 16 | These price requests ask UMA token holders to vote on the value of a price identifier at a historic timestamp. 17 | UMA token holders commit and reveal their votes on-chain in a process that can take 2-4 days. 18 | Once the votes are revealed, the mode of these votes is returned to the financial contract as the value determined by the UMA voters for the price request. 19 | The financial contract then distributes collateral to its counterparties based on the value returned by the DVM. 20 | 21 | Because the DVM requires 2-4 days to respond to a price request, it is not intended to be used as an on-chain price feed that pushes prices to financial contracts that need it. 22 | Rather, it is complementary to “priceless” financial contracts. 23 | 24 | The DVM is designed to include an economic guarantee around its cost of corruption and profit from corruption. 25 | The cost of corrupting the DVM, as measured by the cost of 51% of the UMA voting tokens, should be greater than the profit from corrupting the DVM, as measured by the collateral stored in the financial contracts that are registered with it. 26 | To ensure that this inequality holds, the DVM may charge fees to financial contracts that are used to raise the price of the UMA voting tokens. 27 | -------------------------------------------------------------------------------- /documentation/ROOT/index.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | UMA builds open-source infrastructure for “priceless” financial contracts on Ethereum. Specifically, this is two things: 4 | 5 | - Priceless financial contract designs, which can be used to create synthetic tokens 6 | - Data Verification Mechanism (DVM), a decentralized oracle service 7 | 8 | Together, these two technologies enable the creation of fast, efficient, and secure synthetic derivatives on the Ethereum blockchain. 9 | 10 | ## General Information 11 | 12 | If you would like to learn more about the concepts behind the DVM and priceless financial contracts, start by reading this [overview](../getting_started/architecture_overview.md) of the UMA ecosystem. The following sections go into additional detail on priceless DeFi contracts and the DVM. 13 | 14 | ## For Developers 15 | 16 | If you are a developer interested in building with UMA’s technology, start with this [explainer](../synthetic_tokens/explainer.md) of how UMA's priceless synthetic tokens work, and then try out this [tutorial](../synthetic_tokens/tutorials/creating_from_truffle.md) for how to deploy your own token on testnet. You can find additional reference materials, including a list of UMA-supported mainnet and testnet deployments, [here](../developer_reference/contract_addresses.md). 17 | 18 | ## For Market Makers 19 | 20 | If you are a market maker interested in market making or acting as a liquidator or disputer for financial products deployed with UMA’s priceless synthetic token template, you can find more information about the economic incentives for these roles in this [explainer](../synthetic_tokens/explainer.md) for synthetic tokens. 21 | 22 | ## For UMA Token Holders 23 | 24 | If you are interested in the UMA project token, please read this [section](../oracle/governance/UMA_token_holder_responsibilities.md) to learn more about the role of the UMA project token in the DVM and the rights and responsibilities of UMA project token holders. 25 | 26 | ## Contracts 27 | 28 | For a more detailed view, API documentation is available for all UMA smart contracts. 29 | 30 | ## Contact Us 31 | 32 | If you have any questions or want to reach out, please join our [Slack](https://umaprotocol.slack.com/) using this 33 | [invite link](https://join.slack.com/t/umaprotocol/shared_invite/enQtNTk4MjQ4ODY0MDA1LTM4ODg0NGZhYWZkNjkzMDE4MjU0ZGFlYWQzZTFiZWFlZjI2NDE4OGI2NWY3OTdhYjYyZjg0MjAzMTgwODVhZTE). 34 | -------------------------------------------------------------------------------- /core/scripts/cli/voting/filterRequestsByRound.js: -------------------------------------------------------------------------------- 1 | const { computeTopicHash } = require("../../../../common/EncryptionHelper"); 2 | const { VotePhasesEnum } = require("../../../../common/Enums"); 3 | const { getLatestEvent } = require("../../../../common/VotingUtils"); 4 | 5 | /** 6 | * First, sorts all price requests chronologically from earliest to latest. 7 | * 8 | * Next, if the phase is a Commit phase, then return all sorted requests. 9 | * If the phase is a Reveal phase, then only return price requests that have yet to be revealed (i.e. you can only reveal a price request once). 10 | * Conversely, a commit can be redone as many times as the user wants in a round, therefore we should display them all to the user. 11 | * 12 | * @param {* Object[] Array} pendingRequests List of pending price requests => {identifier, time} 13 | * @param {* String} account Etheruem account of voter 14 | * @param {* String} roundId Round ID number 15 | * @param {* String} roundPhase 0 = Commit or 1 = Reveal 16 | * @param {* Object} votingContract deployed Voting.sol contract instance 17 | */ 18 | const filterRequestsByRound = async (pendingRequests, account, roundId, roundPhase, votingContract) => { 19 | let filteredRequests = []; 20 | if (pendingRequests.length > 0) { 21 | // Sort requests by timestamp requested 22 | const chronologicalPriceRequests = pendingRequests.sort((a, b) => { 23 | return parseInt(a.time) - parseInt(b.time); 24 | }); 25 | 26 | // Depending on the round phase, determine which requests to display 27 | if (roundPhase.toString() === VotePhasesEnum.COMMIT) { 28 | // Display all requests during commit phase even if 29 | // user has already committed a vote, for they 30 | // might want to change it 31 | filteredRequests = chronologicalPriceRequests; 32 | } else { 33 | // Only display committed votes during the reveal phase (i.e. 34 | // if an EncryptedVote event exists for the identifier-timestamp) 35 | for (let i = 0; i < chronologicalPriceRequests.length; i++) { 36 | const request = chronologicalPriceRequests[i]; 37 | const ev = await getLatestEvent("EncryptedVote", request, roundId, account, votingContract); 38 | if (ev !== null) { 39 | filteredRequests.push(request); 40 | } 41 | } 42 | } 43 | } 44 | return filteredRequests; 45 | }; 46 | 47 | module.exports = filterRequestsByRound; 48 | -------------------------------------------------------------------------------- /core/scripts/umip-2/1_Propose.js: -------------------------------------------------------------------------------- 1 | // This script generates and submits UMIP-2 upgrade transactions to the DVM. It can be run on a local ganache 2 | // fork of the main net or can be run directly on the main net to execute the upgrade transactions. 3 | // To run this on the localhost first fork main net into Ganache with the proposerWallet unlocked as follows: 4 | // ganache-cli --fork https://mainnet.infura.io/v3/d70106f59aef456c9e5bfbb0c2cc7164 --unlock 0x2bAaA41d155ad8a4126184950B31F50A1513cE25 5 | // Then execute the script as: truffle exec ./scripts/umip-2/1_Propose.js --network mainnet-fork from core 6 | 7 | const IdentifierWhitelist = artifacts.require("IdentifierWhitelist"); 8 | const Governor = artifacts.require("Governor"); 9 | 10 | const { RegistryRolesEnum } = require("../../../common/Enums.js"); 11 | 12 | const tdr = require("truffle-deploy-registry"); 13 | 14 | const proposerWallet = "0x2bAaA41d155ad8a4126184950B31F50A1513cE25"; 15 | const zeroAddress = "0x0000000000000000000000000000000000000000"; 16 | 17 | async function runExport() { 18 | console.log("Running UMIP-2 Upgrade🔥"); 19 | console.log("Connected to network id", await web3.eth.net.getId()); 20 | 21 | const identifierWhitelist = await IdentifierWhitelist.deployed(); 22 | const governor = await Governor.deployed(); 23 | 24 | // After it's given ownership, the upgrade transaction needs to be executed. 25 | const identifierBytes = web3.utils.utf8ToHex("ETH/BTC"); 26 | const addEthBtcIdentifierTx = identifierWhitelist.contract.methods 27 | .addSupportedIdentifier(identifierBytes) 28 | .encodeABI(); 29 | 30 | console.log("addEthBtcIdentifierTx", addEthBtcIdentifierTx); 31 | 32 | await governor.propose( 33 | [ 34 | { 35 | to: identifierWhitelist.address, 36 | value: 0, 37 | data: addEthBtcIdentifierTx 38 | } 39 | ], 40 | { from: proposerWallet } 41 | ); 42 | 43 | console.log(` 44 | 45 | Newly Proposed DVM Identifier: 46 | 47 | ETH/BTC (UTF8) 48 | ${identifierBytes} (HEX) 49 | 50 | `); 51 | } 52 | 53 | run = async function(callback) { 54 | try { 55 | await runExport(); 56 | } catch (err) { 57 | callback(err); 58 | return; 59 | } 60 | callback(); 61 | }; 62 | 63 | // Attach this function to the exported function in order to allow the script to be executed through both truffle and a test runner. 64 | run.runExport = runExport; 65 | module.exports = run; 66 | -------------------------------------------------------------------------------- /core/contracts/common/test/ReentrancyChecker.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | 4 | // The Reentrancy Checker causes failures if it is successfully able to re-enter a contract. 5 | // How to use: 6 | // 1. Call setTransactionData with the transaction data you want the Reentrancy Checker to reenter the calling 7 | // contract with. 8 | // 2. Get the calling contract to call into the reentrancy checker with any call. The fallback function will receive 9 | // this call and reenter the contract with the transaction data provided in 1. If that reentrancy call does not 10 | // revert, then the reentrancy checker reverts the initial call, likely causeing the entire transaction to revert. 11 | // 12 | // Note: the reentrancy checker has a guard to prevent an infinite cycle of reentrancy. Inifinite cycles will run out 13 | // of gas in all cases, potentially causing a revert when the contract is adequately protected from reentrancy. 14 | contract ReentrancyChecker { 15 | bytes public txnData; 16 | bool hasBeenCalled; 17 | 18 | // Used to prevent infinite cycles where the reentrancy is cycled forever. 19 | modifier skipIfReentered { 20 | if (hasBeenCalled) { 21 | return; 22 | } 23 | hasBeenCalled = true; 24 | _; 25 | hasBeenCalled = false; 26 | } 27 | 28 | function setTransactionData(bytes memory _txnData) public { 29 | txnData = _txnData; 30 | } 31 | 32 | function _executeCall( 33 | address to, 34 | uint256 value, 35 | bytes memory data 36 | ) private returns (bool success) { 37 | // Mostly copied from: 38 | // solhint-disable-next-line max-line-length 39 | // https://github.com/gnosis/safe-contracts/blob/59cfdaebcd8b87a0a32f87b50fead092c10d3a05/contracts/base/Executor.sol#L23-L31 40 | // solhint-disable-next-line no-inline-assembly 41 | 42 | assembly { 43 | let inputData := add(data, 0x20) 44 | let inputDataSize := mload(data) 45 | success := call(gas(), to, value, inputData, inputDataSize, 0, 0) 46 | } 47 | } 48 | 49 | fallback() external skipIfReentered { 50 | // Attampt to re-enter with the set txnData. 51 | bool success = _executeCall(msg.sender, 0, txnData); 52 | 53 | // Fail if the call succeeds because that means the re-entrancy was successful. 54 | require(!success, "Re-entrancy was successful"); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /core/migrations/5_deploy_voting.js: -------------------------------------------------------------------------------- 1 | const Finder = artifacts.require("Finder"); 2 | const Voting = artifacts.require("Voting"); 3 | const VotingToken = artifacts.require("VotingToken"); 4 | const IdentifierWhitelist = artifacts.require("IdentifierWhitelist"); 5 | const Timer = artifacts.require("Timer"); 6 | const { getKeysForNetwork, deploy, enableControllableTiming } = require("../../common/MigrationUtils.js"); 7 | const { interfaceName } = require("../utils/Constants.js"); 8 | 9 | module.exports = async function(deployer, network, accounts) { 10 | const keys = getKeysForNetwork(network, accounts); 11 | const controllableTiming = enableControllableTiming(network); 12 | 13 | // Deploy whitelist of identifiers 14 | const { contract: identifierWhitelist } = await deploy(deployer, network, IdentifierWhitelist, { 15 | from: keys.deployer 16 | }); 17 | 18 | // Set the GAT percentage to 5% 19 | const gatPercentage = { rawValue: web3.utils.toWei("0.05", "ether") }; 20 | 21 | // Set the inflation rate. 22 | const inflationRate = { rawValue: web3.utils.toWei("0.0005", "ether") }; 23 | 24 | // Set the rewards expiration timeout. 25 | const rewardsExpirationTimeout = 60 * 60 * 24 * 14; // Two weeks. 26 | 27 | // Get the previously deployed VotingToken and Finder. 28 | const votingToken = await VotingToken.deployed(); 29 | const finder = await Finder.deployed(); 30 | 31 | // Set phase length to one day. 32 | const secondsPerDay = "86400"; 33 | 34 | const { contract: voting } = await deploy( 35 | deployer, 36 | network, 37 | Voting, 38 | secondsPerDay, 39 | gatPercentage, 40 | inflationRate, 41 | rewardsExpirationTimeout, 42 | votingToken.address, 43 | finder.address, 44 | controllableTiming ? Timer.address : "0x0000000000000000000000000000000000000000", 45 | { from: keys.deployer } 46 | ); 47 | 48 | await finder.changeImplementationAddress(web3.utils.utf8ToHex(interfaceName.Oracle), voting.address, { 49 | from: keys.deployer 50 | }); 51 | await finder.changeImplementationAddress( 52 | web3.utils.utf8ToHex(interfaceName.IdentifierWhitelist), 53 | identifierWhitelist.address, 54 | { 55 | from: keys.deployer 56 | } 57 | ); 58 | 59 | // Corresponds to VotingToken.Roles.Minter. 60 | const minterRoleEnumValue = 1; 61 | 62 | // Set the minter to be the Voting contract. 63 | await votingToken.addMember(minterRoleEnumValue, voting.address, { from: keys.deployer }); 64 | }; 65 | --------------------------------------------------------------------------------