├── compound-protocol ├── migrations │ └── .gitkeep ├── networks │ ├── .gitkeep │ └── README.md ├── .soliumignore ├── spec │ ├── formal │ │ ├── dummy.cvl │ │ ├── contracts │ │ │ ├── SimulationInterface.sol │ │ │ ├── CEtherCertora.sol │ │ │ ├── MaximillionCertora.sol │ │ │ ├── CTokenCollateral.sol │ │ │ └── UnderlyingModelNonStandard.sol │ │ ├── CompoundIgnoreExtCalls.spclnk │ │ ├── maximillion.cvl │ │ └── _setMarketPriceOracle.cvl │ └── scenario │ │ ├── ReEntry.scen │ │ ├── SetComptroller.scen │ │ ├── CTokenAdmin.scen │ │ ├── admin │ │ ├── setMarketPriceOracle.scen.old │ │ └── setMarketPolicyHook.scen.old │ │ ├── Borrow.scen │ │ ├── Seize.scen │ │ ├── BorrowEth.scen │ │ ├── Withdraw.scen.old │ │ └── Supply.scen.old ├── .dockerignore ├── script │ ├── README.md │ ├── console │ ├── lint │ ├── certora │ │ └── Dockerfile │ ├── scenario │ ├── ganache │ ├── ganache-coverage │ ├── utility │ │ ├── truffle_solidity_caching │ │ ├── solidity_skip.patch │ │ ├── setup-local │ │ └── truffle-cli.patch │ ├── coverage │ ├── verify │ └── test ├── reporterConfig.json ├── scenario │ ├── src │ │ ├── Expectation.ts │ │ ├── Invariant.ts │ │ ├── Artifact.ts │ │ ├── Event.ts │ │ ├── Repl.d.ts │ │ ├── Contract │ │ │ ├── Maximillion.ts │ │ │ ├── PriceOracleProxy.ts │ │ │ ├── Unitroller.ts │ │ │ ├── ComptrollerImpl.ts │ │ │ ├── InterestRateModel.ts │ │ │ ├── Erc20.ts │ │ │ ├── PriceOracle.ts │ │ │ ├── Comptroller.ts │ │ │ └── CToken.ts │ │ ├── Action.ts │ │ ├── Assert.ts │ │ ├── Runner.ts │ │ ├── Invariant │ │ │ ├── SuccessInvariant.ts │ │ │ ├── StaticInvariant.ts │ │ │ └── RemainsInvariant.ts │ │ ├── Formatter.ts │ │ ├── Completer.ts │ │ ├── Value │ │ │ ├── UserValue.ts │ │ │ ├── UnitrollerValue.ts │ │ │ ├── MaximillionValue.ts │ │ │ ├── ComptrollerImplValue.ts │ │ │ ├── InterestRateModelValue.ts │ │ │ ├── PriceOracleValue.ts │ │ │ └── PriceOracleProxyValue.ts │ │ ├── HistoricReadline.ts │ │ ├── File.ts │ │ ├── Accounts.ts │ │ ├── Expectation │ │ │ ├── RemainsExpectation.ts │ │ │ └── ChangesExpectation.ts │ │ ├── Web.ts │ │ ├── Encoding.ts │ │ ├── Builder │ │ │ ├── UnitrollerBuilder.ts │ │ │ ├── PriceOracleProxyBuilder.ts │ │ │ └── MaximillionBuilder.ts │ │ ├── Event │ │ │ ├── TrxEvent.ts │ │ │ └── ExpectationEvent.ts │ │ ├── Utils.ts │ │ ├── Settings.ts │ │ └── Help.ts │ ├── scen │ │ ├── dev │ │ │ ├── oracle.scen │ │ │ ├── redeem.scen │ │ │ ├── mint.scen │ │ │ ├── tokens.scen │ │ │ └── accrue.scen │ │ ├── prod │ │ │ ├── zero_comptroller_recome.scen │ │ │ ├── zero_comptroller_deploy.scen │ │ │ └── oracle.scen │ │ ├── set_price_oracle.scen │ │ └── verify_contracts.scen │ ├── script │ │ ├── generate_parser │ │ ├── tsc │ │ ├── webpack │ │ └── repl │ ├── webpack.config.js │ └── package.json ├── docs │ ├── CompoundProtocol.pdf │ └── CompoundWhitepaper.pdf ├── test │ ├── contracts │ │ ├── NotPriceOracle.sol │ │ ├── FalseMarkerMethodComptroller.sol │ │ ├── MathHelpers.sol │ │ ├── ERC20Basic.sol │ │ ├── ERC20.sol │ │ ├── FixedPriceOracle.sol │ │ ├── ComptrollerBorked.sol │ │ ├── ERC20BasicNS.sol │ │ ├── ComptrollerHarness.sol │ │ ├── ERC20NS.sol │ │ ├── DSValueHarness.sol │ │ ├── EIP20NonCompliantHarness.sol │ │ ├── CEvil.sol │ │ ├── EchoTypesComptroller.sol │ │ ├── FaucetToken.sol │ │ ├── FaucetNonStandardToken.sol │ │ ├── InterestRateModelHarness.sol │ │ ├── CErc20Scenario.sol │ │ ├── CEtherScenario.sol │ │ ├── BasicToken.sol │ │ ├── FalseMarkerMethodInterestRateModel.sol │ │ ├── SafeMath.sol │ │ ├── BasicTokenNS.sol │ │ ├── EvilToken.sol │ │ ├── ComptrollerScenario.sol │ │ └── AnchorPriceOracleHarness.sol │ ├── Utils │ │ └── JS.js │ ├── Errors.js │ └── Tokens │ │ ├── setComptrollerTest.js │ │ └── transferTest.js ├── .soliumrc.json ├── .solcover.js ├── .gitignore ├── Dockerfile ├── contracts │ ├── PriceOracle.sol │ ├── SimplePriceOracle.sol │ ├── ReentrancyGuard.sol │ ├── ComptrollerStorage.sol │ ├── InterestRateModel.sol │ ├── Maximillion.sol │ └── CarefulMath.sol ├── aragon-app-scripts │ ├── giveAcc0Dai.js │ └── mineBlocks.js └── package.json ├── .gitattributes ├── compound-aragon-app ├── .gitattributes ├── .babelrc ├── .soliumignore ├── .eslintignore ├── .gitignore ├── .solcover.js ├── app │ ├── public │ │ └── meta │ │ │ ├── screenshot-1.png │ │ │ ├── screenshot-2.png │ │ │ ├── screenshot-3.png │ │ │ ├── details.md │ │ │ └── icon.svg │ ├── src │ │ ├── lib │ │ │ ├── shared-constants.js │ │ │ ├── rx-error-operators.js │ │ │ ├── abi-utils.js │ │ │ ├── retry-every.js │ │ │ ├── token-utils.js │ │ │ ├── format-utils.js │ │ │ └── web3-utils.js │ │ ├── abi │ │ │ └── proxy-deposit-event.json │ │ ├── index.js │ │ ├── assets │ │ │ └── supply-icon.svg │ │ ├── components │ │ │ ├── balances │ │ │ │ └── side-panel-input │ │ │ │ │ ├── token-selector │ │ │ │ │ ├── AddressBadge.js │ │ │ │ │ ├── TokenSelectorInstance.js │ │ │ │ │ └── TokenSelector.js │ │ │ │ │ └── TransferPanel.js │ │ │ ├── supply │ │ │ │ ├── Supply.js │ │ │ │ ├── CompoundTokenDetails.js │ │ │ │ └── side-panel-input │ │ │ │ │ ├── TransferPanel.js │ │ │ │ │ └── SupplyInput.js │ │ │ └── GenericInputPanel.js │ │ ├── hooks │ │ │ ├── tabs.js │ │ │ ├── redeem-panel.js │ │ │ ├── side-panels.js │ │ │ ├── transfer-panels.js │ │ │ └── app-logic.js │ │ ├── web3 │ │ │ ├── ExternalContracts.js │ │ │ └── CompoundData.js │ │ └── app-state-reducer.js │ ├── index.html │ ├── .babelrc │ └── package.json ├── migrations │ ├── 2_deploy_contracts.js │ └── 1_initial_migration.js ├── .ipfsignore ├── contracts │ ├── misc │ │ ├── TestImports.sol │ │ ├── MockCErc20.sol │ │ ├── Migrations.sol │ │ └── TestERC20.sol │ ├── CErc20Interface.sol │ └── lib │ │ └── AddressArrayUtils.sol ├── manifest.json ├── test │ └── helpers │ │ ├── helpers.js │ │ ├── rpc.js │ │ └── ChainSetup.js ├── .eslintrc.json ├── .soliumrc.json ├── arapp.json └── package.json ├── audit ├── MixBytes.png ├── Final_Report.pdf └── Certificate of Compliance.pdf ├── .gitignore └── README.md /compound-protocol/migrations/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /compound-protocol/networks/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /compound-protocol/.soliumignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity -------------------------------------------------------------------------------- /compound-protocol/spec/formal/dummy.cvl: -------------------------------------------------------------------------------- 1 | // hello 2 | -------------------------------------------------------------------------------- /compound-protocol/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | test 3 | *.DS_Store -------------------------------------------------------------------------------- /compound-aragon-app/.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /compound-aragon-app/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } 4 | -------------------------------------------------------------------------------- /compound-aragon-app/.soliumignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | contracts/Migrations.sol 3 | -------------------------------------------------------------------------------- /compound-protocol/script/README.md: -------------------------------------------------------------------------------- 1 | Scripts to make common developer tasks easy to type. 2 | -------------------------------------------------------------------------------- /compound-protocol/reporterConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "reporterEnabled": "spec, mocha-junit-reporter" 3 | } -------------------------------------------------------------------------------- /audit/MixBytes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/empowerthedao/compound-aragon-app/HEAD/audit/MixBytes.png -------------------------------------------------------------------------------- /audit/Final_Report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/empowerthedao/compound-aragon-app/HEAD/audit/Final_Report.pdf -------------------------------------------------------------------------------- /compound-aragon-app/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | app/node_modules 3 | build 4 | .cache 5 | dist 6 | coverage 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules 3 | 4 | # Build dirs 5 | .cache/ 6 | build/ 7 | dist/ 8 | 9 | package-lock.json -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Expectation.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface Expectation { 3 | checker: (world: any) => Promise; 4 | } 5 | -------------------------------------------------------------------------------- /audit/Certificate of Compliance.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/empowerthedao/compound-aragon-app/HEAD/audit/Certificate of Compliance.pdf -------------------------------------------------------------------------------- /compound-aragon-app/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | .cache 4 | dist 5 | ipfs.cmd 6 | package-lock.json 7 | coverage.json 8 | coverage 9 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Invariant.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface Invariant { 3 | held: boolean 4 | checker: (world: any) => Promise; 5 | } 6 | -------------------------------------------------------------------------------- /compound-protocol/docs/CompoundProtocol.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/empowerthedao/compound-aragon-app/HEAD/compound-protocol/docs/CompoundProtocol.pdf -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Artifact.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface Artifact {} 3 | export interface Artifacts { 4 | require(file: string): Artifact 5 | } 6 | -------------------------------------------------------------------------------- /compound-aragon-app/.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | norpc: true, 3 | copyPackages: [], 4 | skipFiles: [ 5 | 'test', 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /compound-protocol/docs/CompoundWhitepaper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/empowerthedao/compound-aragon-app/HEAD/compound-protocol/docs/CompoundWhitepaper.pdf -------------------------------------------------------------------------------- /compound-aragon-app/app/public/meta/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/empowerthedao/compound-aragon-app/HEAD/compound-aragon-app/app/public/meta/screenshot-1.png -------------------------------------------------------------------------------- /compound-aragon-app/app/public/meta/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/empowerthedao/compound-aragon-app/HEAD/compound-aragon-app/app/public/meta/screenshot-2.png -------------------------------------------------------------------------------- /compound-aragon-app/app/public/meta/screenshot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/empowerthedao/compound-aragon-app/HEAD/compound-aragon-app/app/public/meta/screenshot-3.png -------------------------------------------------------------------------------- /compound-protocol/spec/formal/contracts/SimulationInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | interface SimulationInterface { 4 | function dummy() external; 5 | } 6 | -------------------------------------------------------------------------------- /compound-protocol/scenario/scen/dev/oracle.scen: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env yarn run repl -s 2 | -- Deploys dev price oracle 3 | 4 | Gate (PriceOracle Address) (PriceOracle Deploy Simple) 5 | -------------------------------------------------------------------------------- /compound-protocol/script/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | DIR=`dirname $0` 4 | PROJ_ROOT="$DIR/.." 5 | 6 | "$PROJ_ROOT/node_modules/truffle/build/cli.bundled.js" console $@ 7 | -------------------------------------------------------------------------------- /compound-protocol/test/contracts/NotPriceOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | contract NotPriceOracle { 4 | // marker function 5 | bool public constant isPriceOracle = false; 6 | } 7 | -------------------------------------------------------------------------------- /compound-protocol/scenario/scen/dev/redeem.scen: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env yarn run repl -s 2 | 3 | -- Performs a simple development redeem call 4 | CToken (Default $ctoken (Address cZRX)) Redeem (Default $amount 1e9) -------------------------------------------------------------------------------- /compound-aragon-app/migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | const TestERC20 = artifacts.require('TestERC20'); 2 | 3 | module.exports = async (deployer) => { 4 | 5 | await deployer.deploy(TestERC20) 6 | 7 | } 8 | -------------------------------------------------------------------------------- /compound-aragon-app/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | /* global artifacts */ 2 | var Migrations = artifacts.require('./Migrations.sol') 3 | 4 | module.exports = function(deployer) { 5 | deployer.deploy(Migrations) 6 | } 7 | -------------------------------------------------------------------------------- /compound-protocol/scenario/scen/prod/zero_comptroller_recome.scen: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env yarn run repl -s 2 | -- ZeroComptroller recomes Comptroller (becomes without re-initializing) 3 | 4 | ComptrollerImpl ZeroComptroller Recome 5 | -------------------------------------------------------------------------------- /compound-protocol/scenario/scen/dev/mint.scen: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env yarn run repl -s 2 | 3 | -- Performs a simple development mint call 4 | QuickMint (Default $amount 10.0e18) (Default $ctoken (Address cZRX)) (User default Address) 5 | -------------------------------------------------------------------------------- /compound-aragon-app/.ipfsignore: -------------------------------------------------------------------------------- 1 | # Git files 2 | .gitignore 3 | 4 | # Build files 5 | .cache 6 | node_modules 7 | build 8 | coverage 9 | 10 | # Lock files 11 | package-lock.json 12 | yarn.lock 13 | 14 | # Others 15 | test 16 | -------------------------------------------------------------------------------- /compound-aragon-app/app/src/lib/shared-constants.js: -------------------------------------------------------------------------------- 1 | 2 | const ETHER_TOKEN_FAKE_ADDRESS = '0x0000000000000000000000000000000000000000' 3 | const ETH_DECIMALS = 18 4 | 5 | export { 6 | ETHER_TOKEN_FAKE_ADDRESS, 7 | ETH_DECIMALS 8 | } -------------------------------------------------------------------------------- /compound-aragon-app/contracts/misc/TestImports.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "@aragon/test-helpers/contracts/TokenMock.sol"; 4 | 5 | contract TestImports { 6 | 7 | constructor() public { 8 | 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Event.ts: -------------------------------------------------------------------------------- 1 | 2 | type ScalarEvent = string; 3 | interface EventArray extends Array { 4 | [index: number]: ScalarEvent | EventArray; 5 | } 6 | 7 | export type Event = EventArray; 8 | -------------------------------------------------------------------------------- /compound-protocol/scenario/scen/prod/zero_comptroller_deploy.scen: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env yarn run repl -s 2 | -- Deploys the Zero-checking Comptroller 3 | 4 | Gate (ComptrollerImpl ZeroComptroller Address) (ComptrollerImpl Deploy Standard ZeroComptroller) -------------------------------------------------------------------------------- /compound-protocol/script/lint: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | set -e 3 | 4 | DIR=`dirname $0` 5 | PROJ_ROOT="$DIR/.." 6 | 7 | "$PROJ_ROOT/node_modules/.bin/solium" -d "$PROJ_ROOT/test" 8 | "$PROJ_ROOT/node_modules/.bin/solium" -d "$PROJ_ROOT/contracts" 9 | -------------------------------------------------------------------------------- /compound-protocol/test/contracts/FalseMarkerMethodComptroller.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | import "./BoolComptroller.sol"; 3 | 4 | contract FalseMarkerMethodComptroller is BoolComptroller { 5 | bool public isComptroller = false; 6 | } 7 | -------------------------------------------------------------------------------- /compound-protocol/scenario/scen/prod/oracle.scen: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env yarn run repl -s 2 | -- Deploys anchored price oracle with no readers 3 | 4 | Gate ($poster) (Throw "Must set var poster") 5 | 6 | Gate (PriceOracle Address) (PriceOracle Deploy Anchor $poster) 7 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Repl.d.ts: -------------------------------------------------------------------------------- 1 | import {Artifacts} from './Artifact'; 2 | import {Web3} from './Web3'; 3 | 4 | declare namespace NodeJS { 5 | interface Global { 6 | Web3: Web3 7 | Artifacts: Artifacts 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /compound-aragon-app/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Aragon App 5 | 6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /compound-protocol/script/certora/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:13-alpine 2 | 3 | # Install solc 4 | RUN wget https://github.com/ethereum/solidity/releases/download/v0.5.8/solc-static-linux -O /usr/bin/solc && chmod +x /usr/bin/solc 5 | 6 | # Install bash & z3 7 | RUN apk add bash z3 8 | -------------------------------------------------------------------------------- /compound-protocol/script/scenario: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | dir=`dirname $0` 4 | proj_root="$dir/.." 5 | verbose=${verbose:-} 6 | 7 | # Combine any params as simple regex (a|b|c) 8 | IFS='|' 9 | verbose="$verbose" SCENARIOS="$*" "$dir/test" "test/ScenarioTest.js" 10 | -------------------------------------------------------------------------------- /compound-protocol/.soliumrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solium:recommended", 3 | "plugins": [ 4 | "security" 5 | ], 6 | "rules": { 7 | "quotes": [ 8 | "error", 9 | "double" 10 | ], 11 | "indentation": [ 12 | "error", 13 | 4 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /compound-protocol/script/ganache: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | dir=`dirname $0` 4 | proj_root="$dir/.." 5 | 6 | node "$proj_root/node_modules/ganache-cli/cli.js" \ 7 | --gasLimit 20000000 \ 8 | --gasPrice 20000 \ 9 | --defaultBalanceEther 1000000000 \ 10 | --allowUnlimitedContractSize \ 11 | $@ 12 | -------------------------------------------------------------------------------- /compound-aragon-app/contracts/CErc20Interface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | contract CErc20Interface { 4 | 5 | address public underlying; 6 | 7 | function balanceOf(address owner) external returns (uint256); 8 | 9 | function exchangeRateStored() public view returns (uint256); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /compound-protocol/script/ganache-coverage: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | dir=`dirname $0` 4 | proj_root="$dir/.." 5 | 6 | node "$proj_root/node_modules/ethereumjs-testrpc-sc/build/cli.node.js" \ 7 | --port 8555 \ 8 | --gasLimit 17592186044415 \ 9 | --gasPrice 1 \ 10 | --allowUnlimitedContractSize \ 11 | $@ 12 | -------------------------------------------------------------------------------- /compound-protocol/.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | port: 8555, 3 | norpc: true, 4 | testCommand: process.env['TEST_COMMAND'] || 'NETWORK=coverage scripts/test', 5 | skipFiles: ['FormalMoneyMarket.sol', 'test_contracts'].concat( 6 | process.env['SKIP_UNITROLLER'] ? ['Unitroller.sol'] : []), 7 | deepSkip: true 8 | }; 9 | -------------------------------------------------------------------------------- /compound-protocol/spec/formal/CompoundIgnoreExtCalls.spclnk: -------------------------------------------------------------------------------- 1 | { 2 | "contracts": [ 3 | { 4 | "name": "CErc20Certora", 5 | "file": "spec/formal/contracts/CErc20Certora.sol", 6 | "address": "123" 7 | } 8 | ], 9 | 10 | "cvlsToCheck": { 11 | "CErc20Certora": ["spec/formal/general.cvl"] 12 | } 13 | } -------------------------------------------------------------------------------- /compound-protocol/test/contracts/MathHelpers.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | contract MathHelpers { 4 | 5 | /* 6 | * @dev Creates a number like 15e16 as a uint256 from scientific(15, 16). 7 | */ 8 | function scientific(uint val, uint expTen) pure internal returns (uint) { 9 | return val * ( 10 ** expTen ); 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Contract/Maximillion.ts: -------------------------------------------------------------------------------- 1 | import {Contract} from '../Contract'; 2 | import {Callable, Sendable} from '../Invokation'; 3 | 4 | interface MaximillionMethods { 5 | cEther(): Callable 6 | repayBehalf(string): Sendable 7 | } 8 | 9 | export interface Maximillion extends Contract { 10 | methods: MaximillionMethods 11 | } 12 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Contract/PriceOracleProxy.ts: -------------------------------------------------------------------------------- 1 | import {Contract} from '../Contract'; 2 | import {Callable, Sendable} from '../Invokation'; 3 | 4 | interface PriceOracleProxyMethods { 5 | getUnderlyingPrice(asset: string): Callable 6 | } 7 | 8 | export interface PriceOracleProxy extends Contract { 9 | methods: PriceOracleProxyMethods 10 | } 11 | -------------------------------------------------------------------------------- /compound-aragon-app/contracts/misc/MockCErc20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "../CErc20Interface.sol"; 4 | 5 | contract MockCErc20 is CErc20Interface { 6 | 7 | constructor(address _underlying) public { 8 | underlying = _underlying; 9 | } 10 | 11 | function mint(uint mintAmount) external returns (uint) { 12 | return 0; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /compound-aragon-app/app/src/abi/proxy-deposit-event.json: -------------------------------------------------------------------------------- 1 | { 2 | "anonymous": false, 3 | "inputs": [ 4 | { 5 | "indexed": false, 6 | "name": "sender", 7 | "type": "address" 8 | }, 9 | { 10 | "indexed": false, 11 | "name": "value", 12 | "type": "uint256" 13 | } 14 | ], 15 | "name": "ProxyDeposit", 16 | "type": "event" 17 | } 18 | -------------------------------------------------------------------------------- /compound-protocol/scenario/scen/set_price_oracle.scen: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env yarn run repl -s 2 | -- Sets the price oracle to given 3 | 4 | Gate ($price_oracle) (Throw "price_oracle argument required but not given") 5 | PriceOracle Set Standard $price_oracle "Rinkeby Price Oracle" 6 | PriceOracleProxy Deploy (Unitroller Address) (PriceOracle Address) (Address cETH) 7 | Comptroller SetPriceOracle (PriceOracleProxy Address) 8 | -------------------------------------------------------------------------------- /compound-aragon-app/app/src/lib/rx-error-operators.js: -------------------------------------------------------------------------------- 1 | import {catchError} from "rxjs/operators"; 2 | import {of} from "rxjs"; 3 | 4 | const onErrorReturnDefault = (errorContext, defaultReturnValue) => 5 | catchError(error => { 6 | console.error(`Script error fetching ${errorContext}: ${error}`) 7 | return of(defaultReturnValue) 8 | }) 9 | 10 | export { 11 | onErrorReturnDefault 12 | } -------------------------------------------------------------------------------- /compound-protocol/scenario/script/generate_parser: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 4 | scenario_dir="$(cd $dir/.. && pwd)" 5 | 6 | "$scenario_dir/node_modules/.bin/pegjs" \ 7 | --plugin "$scenario_dir/node_modules/ts-pegjs" \ 8 | -o "$scenario_dir/src/Parser.ts" \ 9 | --cache \ 10 | --allowed-start-rules tests,step,macros \ 11 | "$scenario_dir/Grammar.pegjs" 12 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Action.ts: -------------------------------------------------------------------------------- 1 | import {Invokation} from './Invokation'; 2 | 3 | export class Action { 4 | log: string; 5 | invokation: Invokation; 6 | 7 | constructor(log: string, invokation: Invokation) { 8 | this.log = log; 9 | this.invokation = invokation; 10 | } 11 | 12 | toString() { 13 | return `Action: log=${this.log}, result=${this.invokation.toString()}`; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Contract/Unitroller.ts: -------------------------------------------------------------------------------- 1 | import {Contract} from '../Contract'; 2 | import {Callable, Sendable} from '../Invokation'; 3 | 4 | interface UnitrollerMethods { 5 | admin(): Callable 6 | _setPendingImplementation(string): Sendable 7 | comptrollerImplementation(): Callable 8 | } 9 | 10 | export interface Unitroller extends Contract { 11 | methods: UnitrollerMethods 12 | } 13 | -------------------------------------------------------------------------------- /compound-protocol/scenario/scen/dev/tokens.scen: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env yarn run repl -s 2 | -- Deploys Underlying Tokens 3 | 4 | Gate (Erc20 ZRX Address) (Erc20 Deploy Standard ZRX "0x") 5 | Gate (Erc20 BAT Address) (Erc20 Deploy NonStandard BAT "Basic Attention Token") 6 | Gate (Erc20 DAI Address) (Erc20 Deploy Standard DAI "Dai") 7 | Gate (Erc20 REP Address) (Erc20 Deploy Standard REP "Augur") 8 | Gate (Erc20 USDC Address) (Erc20 Deploy Standard USDC "USD Coin" 6) -------------------------------------------------------------------------------- /compound-aragon-app/app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import {AragonApi} from '@aragon/api-react' 4 | import {Main} from '@aragon/ui' 5 | import App from './App' 6 | import reducer from "./app-state-reducer"; 7 | 8 | ReactDOM.render( 9 | 10 |
11 | 12 |
13 |
, 14 | document.getElementById('root') 15 | ) 16 | -------------------------------------------------------------------------------- /compound-aragon-app/app/src/assets/supply-icon.svg: -------------------------------------------------------------------------------- 1 | icono -------------------------------------------------------------------------------- /compound-protocol/scenario/script/tsc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eo pipefail 4 | 5 | dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 6 | scenario_dir="$(cd $dir/.. && pwd)" 7 | 8 | if [ ! -d "$scenario_dir/node_modules" ]; then 9 | echo "Getting scenario packages..." 10 | cd "$scenario_dir" && yarn 11 | fi 12 | 13 | echo "Building Scenario Runner..." 14 | cd "$scenario_dir" && node "$scenario_dir/node_modules/.bin/tsc" ${TSC_ARGS-"--skipLibCheck"} 15 | -------------------------------------------------------------------------------- /compound-aragon-app/app/src/lib/abi-utils.js: -------------------------------------------------------------------------------- 1 | 2 | const makeAbiFunctionConstant = (functionName, originalAbi) => { 3 | const abiFunction = originalAbi.filter(abiFunction => abiFunction.name === functionName)[0] 4 | abiFunction.constant = "true" 5 | 6 | const modifiedAbi = originalAbi.filter(abiFunction => abiFunction.name !== functionName).slice(0) 7 | modifiedAbi.push(abiFunction) 8 | 9 | return modifiedAbi 10 | } 11 | 12 | export { 13 | makeAbiFunctionConstant 14 | } 15 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Contract/ComptrollerImpl.ts: -------------------------------------------------------------------------------- 1 | import {Contract} from '../Contract'; 2 | import {Callable, Sendable} from '../Invokation'; 3 | import {encodedNumber} from '../Encoding'; 4 | 5 | interface ComptrollerImplMethods { 6 | _become(comptroller: string, priceOracle: string, maxAssets: encodedNumber, closeFactor: encodedNumber, reinitializing: boolean): Sendable 7 | } 8 | 9 | export interface ComptrollerImpl extends Contract { 10 | methods: ComptrollerImplMethods 11 | } 12 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Contract/InterestRateModel.ts: -------------------------------------------------------------------------------- 1 | import {Contract} from '../Contract'; 2 | import {Callable, Sendable} from '../Invokation'; 3 | import {encodedNumber} from '../Encoding'; 4 | 5 | interface InterestRateModelMethods { 6 | getBorrowRate(cash: encodedNumber, borrows: encodedNumber, reserves: encodedNumber): Callable<{0: number, 1: number}> 7 | } 8 | 9 | export interface InterestRateModel extends Contract { 10 | methods: InterestRateModelMethods 11 | name: string 12 | } 13 | -------------------------------------------------------------------------------- /compound-protocol/test/contracts/ERC20Basic.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | 4 | /** 5 | * @title ERC20Basic 6 | * @dev Simpler version of ERC20 interface 7 | * See https://github.com/ethereum/EIPs/issues/179 8 | */ 9 | contract ERC20Basic { 10 | function totalSupply() public view returns (uint256); 11 | function balanceOf(address who) public view returns (uint256); 12 | function transfer(address to, uint256 value) public returns (bool); 13 | event Transfer(address indexed from, address indexed to, uint256 value); 14 | } -------------------------------------------------------------------------------- /compound-aragon-app/app/public/meta/details.md: -------------------------------------------------------------------------------- 1 | The Compound Agent app is used to lend a specified ERC20 token to the Compound protocol to accrue interest. It requires 2 | an Agent instance be installed which is used for interacting with the Compound protocol. The Agent used is shown in the 3 | app's settings tab. The ERC20 token that is lendable to Compound can be changed using the CLI. 4 | 5 | ## Features 6 | - Supply the specified ERC20 token to Compound. 7 | - Redeem the specified ERC20 token from Compound. 8 | - Transfer ETH and ERC20 tokens to the Agent. -------------------------------------------------------------------------------- /compound-protocol/scenario/script/webpack: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 4 | scenario_dir="$(cd $dir/.. && pwd)" 5 | 6 | cd "$scenario_dir" && 7 | mkdir -p ./build && 8 | ./node_modules/.bin/webpack \ 9 | --mode production \ 10 | --config ./webpack.config.js \ 11 | --entry ./src/Web.ts \ 12 | --module-bind 'ts=ts-loader' \ 13 | --module-bind 'exports-loader?parse' \ 14 | --resolve-extensions ".ts,.js" \ 15 | --output-library-target window \ 16 | --output ./build/scenario.js 17 | -------------------------------------------------------------------------------- /compound-protocol/.gitignore: -------------------------------------------------------------------------------- 1 | allFiredEvents 2 | build 3 | build_ 4 | node_modules 5 | .env 6 | coverage/ 7 | coverage.json 8 | coverageEnv/ 9 | formulas/ 10 | networks/test.json 11 | networks/test-abi.json 12 | networks/coverage.json 13 | networks/coverage-abi.json 14 | networks/development.json 15 | networks/development-abi.json 16 | networks/*-contracts.json 17 | networks/*-history 18 | networks/*-settings.json 19 | outputs/ 20 | Reports/ 21 | scTopics 22 | *.DS_Store 23 | test-results.xml 24 | .tsbuilt 25 | yarn-error.log 26 | scenario/build/webpack.js -------------------------------------------------------------------------------- /compound-protocol/test/Utils/JS.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function dfn(val, def) { 4 | return isFinite(val) ? val : def; 5 | } 6 | 7 | function last(elems) { 8 | return Array.isArray(elems) ? elems[elems.length - 1] : elems; 9 | } 10 | 11 | function lookup(obj, path = []) { 12 | return Array.isArray(path) ? path.reduce((a, k) => a[k], obj) : obj[path]; 13 | } 14 | 15 | function select(obj, keys = []) { 16 | return keys.reduce((a, k) => (a[k] = obj[k], a), {}) 17 | } 18 | 19 | module.exports = { 20 | dfn, 21 | last, 22 | lookup, 23 | select 24 | }; 25 | -------------------------------------------------------------------------------- /compound-aragon-app/app/src/lib/retry-every.js: -------------------------------------------------------------------------------- 1 | 2 | const retryEvery = (callback, initialRetryTimer = 1000, increaseFactor = 5) => { 3 | const attempt = (retryTimer = initialRetryTimer) => { 4 | // eslint-disable-next-line standard/no-callback-literal 5 | callback(() => { 6 | console.error(`Retrying in ${retryTimer / 1000}s...`) 7 | 8 | // Exponentially backoff attempts 9 | setTimeout(() => attempt(retryTimer * increaseFactor), retryTimer) 10 | }) 11 | } 12 | attempt() 13 | } 14 | 15 | export default retryEvery -------------------------------------------------------------------------------- /compound-protocol/spec/formal/contracts/CEtherCertora.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "../../../contracts/CEther.sol"; 4 | 5 | contract CEtherCertora is CEther { 6 | constructor(ComptrollerInterface comptroller_, 7 | InterestRateModel interestRateModel_, 8 | uint initialExchangeRateMantissa_, 9 | string memory name_, 10 | string memory symbol_, 11 | uint decimals_) public CEther(comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_) { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Assert.ts: -------------------------------------------------------------------------------- 1 | export interface Assert { 2 | fail(x: any, y: any, reason: string) 3 | equal(x: any, y: any, reason: string) 4 | deepEqual(x: any, y: any, reason: string) 5 | } 6 | 7 | export const throwAssert: Assert = { 8 | fail: (x, y, reason) => { 9 | throw new Error(reason) 10 | }, 11 | equal: (x, y, reason) => { 12 | if (x != y) { 13 | throw new Error(reason); 14 | } 15 | }, 16 | deepEqual: (x, y, reason) => { 17 | if (JSON.stringify(x) != JSON.stringify(y)) { 18 | throw new Error(reason); 19 | } 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /compound-protocol/spec/scenario/ReEntry.scen: -------------------------------------------------------------------------------- 1 | -- Mint Tests 2 | 3 | Test "ReEntry Mint" 4 | NewComptroller 5 | Erc20 Deploy ReEntrant PHREAK PHREAK "transferFrom" "mint(uint256)" "0" 6 | InterestRateModel Deploy Fixed Std 0.0001 7 | CToken Deploy cPHREAK cPHREAK (Erc20 PHREAK Address) (Comptroller Address) (InterestRateModel Std Address) 1e9 8 8 | Comptroller SupportMarket cPHREAK 9 | Prep Geoff Some PHREAK cPHREAK 10 | AllowFailures 11 | Mint Geoff 50e18 cPHREAK 12 | Assert Revert "revert re-entered" 13 | Assert Equal (Erc20 cPHREAK TokenBalance Geoff) Zero 14 | -------------------------------------------------------------------------------- /compound-protocol/test/contracts/ERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "./ERC20Basic.sol"; 4 | 5 | /** 6 | * @title ERC20 interface 7 | * @dev see https://github.com/ethereum/EIPs/issues/20 8 | */ 9 | contract ERC20 is ERC20Basic { 10 | function allowance(address owner, address spender) public view returns (uint256); 11 | function transferFrom(address from, address to, uint256 value) public returns (bool); 12 | function approve(address spender, uint256 value) public returns (bool); 13 | 14 | event Approval(address indexed owner, address indexed spender, uint256 value); 15 | } -------------------------------------------------------------------------------- /compound-aragon-app/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Compound", 3 | "author": "EmpowerTheDao", 4 | "description": "Lend money to accrue interest", 5 | "details_url": "public/meta/details.md", 6 | "source_url": "https://github.com/empowerthedao/compound-aragon-app", 7 | "icons": [{ 8 | "src": "public/meta/icon.svg", 9 | "sizes": "56x56" 10 | }], 11 | "screenshots": [ 12 | { "src": "public/meta/screenshot-1.png" }, 13 | { "src": "public/meta/screenshot-2.png" }, 14 | { "src": "public/meta/screenshot-3.png" } 15 | ], 16 | "start_url": "index.html", 17 | "script": "script.js" 18 | } 19 | -------------------------------------------------------------------------------- /compound-protocol/script/utility/truffle_solidity_caching: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # two patches to make mocha testing faster 3 | # include SKIP_SOLIDITY=true scripts/test 4 | # if you change solidity files, you must rm .tmp ( where the compiled solidity cache lives ) 5 | # before -- 6 | # time { scripts/test; } 7 | # ... 8 | # real 1m46.113s 9 | # user 2m2.104s 10 | # sys 0m5.769s 11 | # after -- 12 | # time { SKIP_SOLIDITY=true scripts/test; } 13 | # ... 14 | # real 0m15.301s 15 | # user 0m16.168s 16 | # sys 0m1.945s 17 | # 18 | patch -p0 < "scripts/utility/truffle-cli.patch" 19 | patch -p0 < "scripts/utility/solidity_skip.patch" -------------------------------------------------------------------------------- /compound-protocol/test/contracts/FixedPriceOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "../PriceOracle.sol"; 4 | 5 | contract FixedPriceOracle is PriceOracle { 6 | uint public price; 7 | bool public constant isPriceOracle = true; 8 | 9 | constructor(uint _price) public { 10 | price = _price; 11 | } 12 | 13 | function getUnderlyingPrice(CToken cToken) public view returns (uint) { 14 | cToken; 15 | return price; 16 | } 17 | 18 | function assetPrices(address asset) public view returns (uint) { 19 | asset; 20 | return price; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /compound-protocol/test/contracts/ComptrollerBorked.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "../Comptroller.sol"; 4 | import "../PriceOracle.sol"; 5 | 6 | contract ComptrollerBorked { 7 | 8 | function _become(Unitroller unitroller, PriceOracle _oracle, uint _closeFactorMantissa, uint _maxAssets, bool _reinitializing) public { 9 | _oracle; 10 | _closeFactorMantissa; 11 | _maxAssets; 12 | _reinitializing; 13 | 14 | require(msg.sender == unitroller.admin(), "only unitroller admin can change brains"); 15 | unitroller._acceptImplementation(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /compound-protocol/test/contracts/ERC20BasicNS.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | /** 4 | * @title ERC20BasicNS (Non-Standard) 5 | * @dev Version of ERC20 with no return values for `transfer` and `transferFrom` 6 | * See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca 7 | */ 8 | contract ERC20BasicNS { 9 | function totalSupply() public view returns (uint256); 10 | function balanceOf(address who) public view returns (uint256); 11 | function transfer(address to, uint256 value) public; 12 | event Transfer(address indexed from, address indexed to, uint256 value); 13 | } -------------------------------------------------------------------------------- /compound-aragon-app/app/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "modules": false, 7 | "targets": { 8 | "browsers": [ 9 | "> 1%", 10 | "last 3 versions", 11 | "ie >= 9", 12 | "ios >= 8", 13 | "android >= 4.2" 14 | ] 15 | }, 16 | "useBuiltIns": false 17 | } 18 | ] 19 | ], 20 | "plugins": [ 21 | "@babel/plugin-proposal-class-properties", 22 | [ 23 | "styled-components", 24 | { 25 | "displayName": true 26 | } 27 | ] 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /compound-protocol/scenario/scen/verify_contracts.scen: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env yarn run repl -s 2 | 3 | Unitroller Verify "$api_key" 4 | ComptrollerImpl StdComptroller Verify "$api_key" 5 | CToken cZRX Verify "$api_key" 6 | CToken cBAT Verify "$api_key" 7 | CToken cDAI Verify "$api_key" 8 | CToken cREP Verify "$api_key" 9 | CToken cETH Verify "$api_key" 10 | CToken cUSDC Verify "$api_key" 11 | InterestRateModel Base0bps_Slope2000bps Verify "$api_key" 12 | InterestRateModel Base500bps_Slope1200bps Verify "$api_key" 13 | InterestRateModel Base200bps_Slope3000bps Verify "$api_key" 14 | PriceOracleProxy Verify "$api_key" 15 | Maximillion Verify "$api_key" -------------------------------------------------------------------------------- /compound-protocol/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mhart/alpine-node:11.10.1 2 | 3 | RUN apk update && apk add --no-cache --virtual build-dependencies git python g++ make 4 | RUN yarn global add truffle@5.0.1 5 | RUN yarn global add ganache-cli@6.4.1 6 | 7 | RUN mkdir -p /deploy/money-market 8 | WORKDIR /deploy/money-market 9 | 10 | # First add deps 11 | ADD ./package.json /deploy/money-market/ 12 | ADD ./yarn.lock /deploy/money-market/ 13 | RUN yarn 14 | 15 | # Then rest of code and build 16 | ADD . /deploy/money-market 17 | 18 | RUN truffle compile 19 | 20 | RUN apk del build-dependencies 21 | RUN yarn cache clean 22 | 23 | CMD while :; do sleep 2073600; done 24 | -------------------------------------------------------------------------------- /compound-protocol/script/coverage: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | dir=`dirname $0` 4 | proj_root="$dir/.." 5 | 6 | [[ ! -d ./.tsbuilt || -z $NO_TSC ]] && "$proj_root/scenario/script/tsc" 7 | case $1 in 8 | "mocha") 9 | export TEST_COMMAND="NETWORK=coverage SCENARIOS="skipall" scripts/test" 10 | export SKIP_UNITROLLER="" 11 | ;; 12 | "scenario") 13 | export TEST_COMMAND="NETWORK=coverage scripts/test test/ScenarioTest.js" 14 | export SKIP_UNITROLLER="true" 15 | ;; 16 | *) 17 | ;; 18 | esac 19 | 20 | echo "TEST_COMMAND: $TEST_COMMAND" 21 | echo "SKIP_UNITROLLER: $SKIP_UNITROLLER" 22 | 23 | "$proj_root/node_modules/.bin/solidity-coverage" 24 | -------------------------------------------------------------------------------- /compound-protocol/spec/formal/contracts/MaximillionCertora.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "../../../contracts/Maximillion.sol"; 4 | 5 | contract MaximillionCertora is Maximillion { 6 | constructor(CEther cEther_) public Maximillion(cEther_) {} 7 | 8 | function borrowBalance(address account) external returns (uint) { 9 | return cEther.borrowBalanceCurrent(account); 10 | } 11 | 12 | function etherBalance(address account) external returns (uint) { 13 | return account.balance; 14 | } 15 | 16 | function repayBehalf(address borrower) public payable { 17 | return super.repayBehalf(borrower); 18 | } 19 | } -------------------------------------------------------------------------------- /compound-protocol/contracts/PriceOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "./CToken.sol"; 4 | 5 | interface PriceOracle { 6 | /** 7 | * @notice Indicator that this is a PriceOracle contract (for inspection) 8 | */ 9 | function isPriceOracle() external pure returns (bool); 10 | 11 | /** 12 | * @notice Get the underlying price of a cToken asset 13 | * @param cToken The cToken to get the underlying price of 14 | * @return The underlying asset price mantissa (scaled by 1e18). 15 | * Zero means the price is unavailable. 16 | */ 17 | function getUnderlyingPrice(CToken cToken) external view returns (uint); 18 | } 19 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Runner.ts: -------------------------------------------------------------------------------- 1 | import {World} from './World'; 2 | import {parse} from './Parser'; 3 | import {expandEvent, Macros} from './Macro'; 4 | import {processEvents} from './CoreEvent' 5 | 6 | export async function runCommand(world: World, command: string, macros: Macros): Promise { 7 | const trimmedCommand = command.trim(); 8 | 9 | const event = parse(trimmedCommand, {startRule: 'step'}); 10 | 11 | if (event === null) { 12 | return world; 13 | } else { 14 | world.printer.printLine(`Command: ${trimmedCommand}`); 15 | 16 | let expanded = expandEvent(macros, event); 17 | 18 | return processEvents(world, expanded); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /compound-aragon-app/contracts/misc/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.4; 2 | 3 | 4 | contract Migrations { 5 | address public owner; 6 | uint public lastCompletedMigration; 7 | 8 | modifier restricted() { 9 | if (msg.sender == owner) 10 | _; 11 | } 12 | 13 | constructor() public { 14 | owner = msg.sender; 15 | } 16 | 17 | function setCompleted(uint completed) public restricted { 18 | lastCompletedMigration = completed; 19 | } 20 | 21 | function upgrade(address newAddress) public restricted { 22 | Migrations upgraded = Migrations(newAddress); 23 | upgraded.setCompleted(lastCompletedMigration); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /compound-aragon-app/app/src/components/balances/side-panel-input/token-selector/AddressBadge.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import {useNetwork} from '@aragon/api-react' 4 | import {IdentityBadge} from '@aragon/ui' 5 | 6 | const AddressBadge = ({entity}) => { 7 | const network = useNetwork() 8 | 9 | return ( 10 | 17 | ) 18 | } 19 | 20 | AddressBadge.propTypes = { 21 | entity: PropTypes.string.isRequired, 22 | } 23 | 24 | export default AddressBadge 25 | -------------------------------------------------------------------------------- /compound-protocol/scenario/scen/dev/accrue.scen: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env yarn run repl -s 2 | 3 | -- Performs a bunch of accrue interest events in a row 4 | CToken (Default $ctoken (Address cZRX)) AccrueInterest 5 | CToken (Default $ctoken (Address cZRX)) AccrueInterest 6 | CToken (Default $ctoken (Address cZRX)) AccrueInterest 7 | CToken (Default $ctoken (Address cZRX)) AccrueInterest 8 | CToken (Default $ctoken (Address cZRX)) AccrueInterest 9 | CToken (Default $ctoken (Address cZRX)) AccrueInterest 10 | CToken (Default $ctoken (Address cZRX)) AccrueInterest 11 | CToken (Default $ctoken (Address cZRX)) AccrueInterest 12 | CToken (Default $ctoken (Address cZRX)) AccrueInterest 13 | CToken (Default $ctoken (Address cZRX)) AccrueInterest -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Invariant/SuccessInvariant.ts: -------------------------------------------------------------------------------- 1 | import {Invariant} from '../Invariant'; 2 | import {fail, World} from '../World'; 3 | import {getCoreValue} from '../CoreValue'; 4 | import {Value} from '../Value'; 5 | import {Event} from '../Event'; 6 | 7 | export class SuccessInvariant implements Invariant { 8 | held = false; 9 | 10 | constructor() {} 11 | 12 | async checker(world: World): Promise { 13 | if (world.lastInvokation && !world.lastInvokation.success()) { 14 | fail(world, `Success invariant broken! Expected successful execution, but had error ${world.lastInvokation.toString()}`); 15 | } 16 | } 17 | 18 | toString() { 19 | return `SuccessInvariant`; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /compound-protocol/test/contracts/ComptrollerHarness.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "../Comptroller.sol"; 4 | import "../PriceOracle.sol"; 5 | 6 | contract ComptrollerHarness is Comptroller { 7 | constructor() Comptroller() public {} 8 | 9 | function getHypotheticalAccountLiquidity( 10 | address account, 11 | address cTokenModify, 12 | uint redeemTokens, 13 | uint borrowAmount) public view returns (uint, uint, uint) { 14 | (Error err, uint liquidity, uint shortfall) = 15 | super.getHypotheticalAccountLiquidityInternal(account, CToken(cTokenModify), redeemTokens, borrowAmount); 16 | return (uint(err), liquidity, shortfall); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /compound-protocol/scenario/webpack.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const webpack = require('webpack'); 3 | 4 | module.exports = { 5 | stats: 'verbose', 6 | devtool: 'source-map', 7 | externals: { 8 | file: '{}', 9 | fs: '{}', 10 | tls: '{}', 11 | net: '{}', 12 | xmlhttprequest: '{}', 13 | 'truffle-flattener': '{}', 14 | 'request': '{}' 15 | }, 16 | optimization: { 17 | minimize: true 18 | }, 19 | plugins: [ 20 | new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), 21 | new webpack.DefinePlugin({ 22 | 'process.env': { 23 | // This has effect on the react lib size 24 | 'NODE_ENV': JSON.stringify('production'), 25 | } 26 | }) 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /compound-protocol/test/contracts/ERC20NS.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "./ERC20BasicNS.sol"; 4 | 5 | /** 6 | * @title ERC20 interface (non-standard) 7 | * @dev Version of ERC20 with no return values for `transfer` and `transferFrom` 8 | * See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca 9 | */ 10 | contract ERC20NS is ERC20BasicNS { 11 | function allowance(address owner, address spender) public view returns (uint256); 12 | function transferFrom(address from, address to, uint256 value) public; 13 | function approve(address spender, uint256 value) public returns (bool); 14 | 15 | event Approval(address indexed owner, address indexed spender, uint256 value); 16 | } -------------------------------------------------------------------------------- /compound-aragon-app/test/helpers/helpers.js: -------------------------------------------------------------------------------- 1 | const assertRevert = async (receiptPromise, reason) => { 2 | try { 3 | await receiptPromise 4 | } catch (error) { 5 | if (reason) { 6 | assert.include(error.message, reason, "Incorrect revert reason") 7 | } 8 | return 9 | } 10 | assert.fail(`Expected a revert for reason: ${reason}`) 11 | } 12 | 13 | const getLog = (receipt, logName, argName) => { 14 | const log = receipt.logs.find(({ event }) => event === logName) 15 | return log ? log.args[argName] : null 16 | } 17 | 18 | const deployedContract = receipt => getLog(receipt, "NewAppProxy", "proxy") 19 | 20 | module.exports = { 21 | assertRevert, 22 | getLog, 23 | deployedContract 24 | } 25 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Formatter.ts: -------------------------------------------------------------------------------- 1 | import {Event} from './Event'; 2 | 3 | // Effectively the opposite of parse 4 | export function formatEvent(event: Event, outter=true): string { 5 | if (Array.isArray(event)) { 6 | if (event.length === 2 && typeof event[0] === "string" && (event[0]).toLowerCase() === "exactly") { 7 | return event[1].toString(); 8 | } 9 | 10 | let mapped = event.map(e => formatEvent(e, false)); 11 | let joined = mapped.join(' '); 12 | 13 | if (outter) { 14 | return joined; 15 | } else { 16 | return `(${joined})`; 17 | } 18 | } else { 19 | return event; 20 | } 21 | } 22 | 23 | export function formatError(err: any) { 24 | return JSON.stringify(err); // yeah... for now 25 | } 26 | -------------------------------------------------------------------------------- /compound-protocol/aragon-app-scripts/giveAcc0Dai.js: -------------------------------------------------------------------------------- 1 | const DeploymentInfo = require('../networks/development') 2 | const FaucetToken = artifacts.require('FaucetToken') 3 | const BN = require('bn.js') 4 | 5 | module.exports = async () => { 6 | 7 | try { 8 | const receiver = (await web3.eth.getAccounts())[0] 9 | const daiWithDecimals = (daiValue) => new BN(daiValue).mul(new BN(10).pow(new BN(18))) 10 | 11 | const dai = await FaucetToken.at(DeploymentInfo.Contracts.DAI) 12 | await dai.allocateTo(receiver, daiWithDecimals(10000)) 13 | 14 | console.log(`Dai balance of ${receiver}: ${(await dai.balanceOf(receiver)).toString()}`) 15 | 16 | } catch (error) { 17 | console.log(error) 18 | } 19 | 20 | process.exit() 21 | } 22 | -------------------------------------------------------------------------------- /compound-protocol/aragon-app-scripts/mineBlocks.js: -------------------------------------------------------------------------------- 1 | const Promise = require('bluebird') 2 | 3 | const MINE_BLOCK_COUNT = 100 4 | 5 | const mineBlock = Promise.promisify(function(done) { 6 | web3.currentProvider.send({ 7 | 'jsonrpc': "2.0", 8 | "method": "evm_mine", 9 | 'params': []}, done) 10 | } 11 | ) 12 | 13 | const mineBlocks = async (numOfBlocks) => { 14 | for (let i = 0; i < numOfBlocks; i++) { 15 | await mineBlock() 16 | } 17 | } 18 | 19 | module.exports = async () => { 20 | 21 | try { 22 | 23 | await mineBlocks(MINE_BLOCK_COUNT) 24 | 25 | console.log(`Mined: ${MINE_BLOCK_COUNT} blocks`) 26 | 27 | } catch (error) { 28 | console.log(error) 29 | } 30 | 31 | process.exit() 32 | } 33 | -------------------------------------------------------------------------------- /compound-protocol/test/contracts/DSValueHarness.sol: -------------------------------------------------------------------------------- 1 | // Abstract contract for the full DSValue standard 2 | // -- 3 | pragma solidity ^0.5.8; 4 | 5 | contract DSValueHarness { 6 | bool public has; 7 | bytes32 public val; 8 | 9 | constructor(bytes32 initVal) public { 10 | if (initVal != 0) { 11 | has = true; 12 | val = initVal; 13 | } 14 | } 15 | 16 | function peek() public view returns (bytes32, bool) { 17 | return (val, has); 18 | } 19 | 20 | function read() public view returns (bytes32) { 21 | return val; 22 | } 23 | 24 | function set(bytes32 _val) public { 25 | val = _val; 26 | has = true; 27 | } 28 | 29 | function unset() public { 30 | has = false; 31 | } 32 | } -------------------------------------------------------------------------------- /compound-protocol/test/contracts/EIP20NonCompliantHarness.sol: -------------------------------------------------------------------------------- 1 | /* 2 | Implements `transfer` and `transferForm` with 64-bit return values, just to be 3 | especially non-compliant and stress safe token. 4 | .*/ 5 | 6 | pragma solidity ^0.5.8; 7 | 8 | contract EIP20NonCompliantHarness { 9 | bool garbage; 10 | 11 | function transfer(address _to, uint256 _value) public returns (uint, uint) { 12 | _to; 13 | _value; // supress unused variable warning 14 | garbage = false; 15 | 16 | return (1, 2); 17 | } 18 | 19 | function transferFrom(address _from, address _to, uint256 _value) public returns (uint, uint) { 20 | _from; 21 | _to; 22 | _value; // supress unused variable warning 23 | garbage = false; 24 | 25 | return (1, 2); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /compound-aragon-app/app/src/lib/token-utils.js: -------------------------------------------------------------------------------- 1 | import { ETHER_TOKEN_VERIFIED_BY_SYMBOL } from "./verified-tokens" 2 | import { ETHER_TOKEN_FAKE_ADDRESS } from "./shared-constants" 3 | import { tokenIconUrl } from "@aragon/ui" 4 | 5 | const mainnetAddressForSymbol = (symbol) => { 6 | return ETHER_TOKEN_VERIFIED_BY_SYMBOL.get(symbol) 7 | } 8 | 9 | const iconSourceUrl = (network, address, symbol) => { 10 | if (!network) return "" 11 | 12 | if (address === ETHER_TOKEN_FAKE_ADDRESS) { 13 | return "https://raw.githubusercontent.com/trustwallet/tokens/master/blockchains/ethereum/info/logo.png" 14 | } else if (network.type === 'main') { 15 | return tokenIconUrl(address) 16 | } else { 17 | return tokenIconUrl(mainnetAddressForSymbol(symbol)) 18 | } 19 | } 20 | 21 | export { 22 | iconSourceUrl 23 | } -------------------------------------------------------------------------------- /compound-protocol/contracts/SimplePriceOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "./PriceOracle.sol"; 4 | import "./CErc20.sol"; 5 | 6 | contract SimplePriceOracle is PriceOracle { 7 | mapping(address => uint) prices; 8 | bool public constant isPriceOracle = true; 9 | 10 | function getUnderlyingPrice(CToken cToken) public view returns (uint) { 11 | return prices[address(CErc20(address(cToken)).underlying())]; 12 | } 13 | 14 | function setUnderlyingPrice(CToken cToken, uint underlyingPriceMantissa) public { 15 | prices[address(CErc20(address(cToken)).underlying())] = underlyingPriceMantissa; 16 | } 17 | 18 | // v1 price oracle interface for use as backing of proxy 19 | function assetPrices(address asset) external view returns (uint) { 20 | return prices[asset]; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /compound-aragon-app/app/src/lib/format-utils.js: -------------------------------------------------------------------------------- 1 | import { round } from './math-utils' 2 | 3 | export function formatDecimals(value, digits) { 4 | try { 5 | return value.toLocaleString('latn', { 6 | style: 'decimal', 7 | maximumFractionDigits: digits, 8 | }) 9 | } catch (err) { 10 | if (err.name === 'RangeError') { 11 | // Fallback to Number.prototype.toString() 12 | // if the language tag is not supported. 13 | return value.toString() 14 | } 15 | throw err 16 | } 17 | } 18 | 19 | export function formatTokenAmount( 20 | amount, 21 | isIncoming, 22 | decimals = 0, 23 | displaySign = false, 24 | { rounding = 2 } = {} 25 | ) { 26 | return ( 27 | (displaySign ? (isIncoming ? '+' : '-') : '') + 28 | formatDecimals(round(amount / Math.pow(10, decimals), rounding), 18) 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /compound-protocol/spec/formal/contracts/CTokenCollateral.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "../../../contracts/CErc20.sol"; 4 | import "../../../contracts/EIP20Interface.sol"; 5 | 6 | contract CTokenCollateral is CErc20 { 7 | constructor(address underlying_, 8 | ComptrollerInterface comptroller_, 9 | InterestRateModel interestRateModel_, 10 | uint initialExchangeRateMantissa_, 11 | string memory name_, 12 | string memory symbol_, 13 | uint decimals_) public CErc20(underlying_, comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_) { 14 | } 15 | 16 | function getCashOf(address account) public view returns (uint) { 17 | return EIP20Interface(underlying).balanceOf(account); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /compound-aragon-app/app/src/hooks/tabs.js: -------------------------------------------------------------------------------- 1 | import {useState, useCallback} from 'react' 2 | import Settings from "../components/settings/Settings"; 3 | 4 | export function useTabs() { 5 | 6 | const tabs = [ 7 | { 8 | index: 0, 9 | id: 'SUPPLY', 10 | tabName: 'Supply', 11 | }, 12 | { 13 | index: 1, 14 | id: 'SETTINGS', 15 | tabName: 'Settings', 16 | } 17 | ] 18 | 19 | const [tabBarSelected, setTabBarSelected] = useState(tabs[0]) 20 | 21 | const names = tabs.map(tab => tab.tabName) 22 | const selected = tabs.findIndex(item => item.id === tabBarSelected.id) 23 | 24 | const selectTab = useCallback((tabIndex) => { 25 | setTabBarSelected(tabs[tabIndex]) 26 | }, [tabBarSelected]) 27 | 28 | return { names, tabBarSelected, selectTab, selected } 29 | } -------------------------------------------------------------------------------- /compound-protocol/test/contracts/CEvil.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "./CErc20Scenario.sol"; 4 | 5 | contract CEvil is CErc20Scenario { 6 | constructor(address underlying_, 7 | ComptrollerInterface comptroller_, 8 | InterestRateModel interestRateModel_, 9 | uint initialExchangeRateMantissa, 10 | string memory name_, 11 | string memory symbol_, 12 | uint decimals_) 13 | CErc20Scenario( 14 | underlying_, 15 | comptroller_, 16 | interestRateModel_, 17 | initialExchangeRateMantissa, 18 | name_, 19 | symbol_, 20 | decimals_) public {} 21 | 22 | function evilSeize(CToken treasure, address liquidator, address borrower, uint seizeTokens) public returns (uint) { 23 | return treasure.seize(liquidator, borrower, seizeTokens); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /compound-protocol/test/contracts/EchoTypesComptroller.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "../ComptrollerStorage.sol"; 4 | import "../Unitroller.sol"; 5 | 6 | contract EchoTypesComptroller is UnitrollerAdminStorage { 7 | 8 | function stringy(string memory s) public pure returns(string memory) { 9 | return s; 10 | } 11 | 12 | function addresses(address a) public pure returns(address) { 13 | return a; 14 | } 15 | 16 | function booly(bool b) public pure returns(bool) { 17 | return b; 18 | } 19 | 20 | function listOInts(uint[] memory u) public pure returns(uint[] memory) { 21 | return u; 22 | } 23 | 24 | function reverty() public pure { 25 | require(false, "gotcha sucka"); 26 | } 27 | 28 | function becomeBrains(address payable unitroller) public { 29 | Unitroller(unitroller)._acceptImplementation(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /compound-protocol/script/utility/solidity_skip.patch: -------------------------------------------------------------------------------- 1 | --- node_modules/truffle/build/cli.bundled.js 2019-01-25 11:51:01.000000000 -0800 2 | +++ node_modules/truffle/build/cli.bundled_patched.js 2019-01-25 16:23:18.000000000 -0800 3 | @@ -579995,10 +579995,13 @@ 4 | }).then(function(paths) { 5 | dependency_paths = paths; 6 | 7 | - testContracts = sol_tests.map(function(test_file_path) { 8 | - var built_name = "./" + path.basename(test_file_path); 9 | - return test_resolver.require(built_name); 10 | - }); 11 | + if (!process.env.SKIP_SOLIDITY) { 12 | + console.log("preparing solidity tests") 13 | + testContracts = sol_tests.map(function(test_file_path) { 14 | + var built_name = "./" + path.basename(test_file_path); 15 | + return test_resolver.require(built_name); 16 | + }); 17 | + } 18 | 19 | runner = new TestRunner(config); 20 | 21 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Contract/Erc20.ts: -------------------------------------------------------------------------------- 1 | import {Contract} from '../Contract'; 2 | import {Callable, Sendable} from '../Invokation'; 3 | import {encodedNumber} from '../Encoding'; 4 | 5 | interface Erc20Methods { 6 | name(): Callable 7 | symbol(): Callable 8 | decimals(): Callable 9 | totalSupply(): Callable 10 | balanceOf(string): Callable 11 | allowance(owner: string, spender: string): Callable 12 | approve(address: string, amount: encodedNumber): Sendable 13 | allocateTo(address: string, amount: encodedNumber): Sendable 14 | transfer(address: string, amount: encodedNumber): Sendable 15 | transferFrom(owner: string, spender: string, amount: encodedNumber): Sendable 16 | setFail(fail: boolean): Sendable 17 | } 18 | 19 | export interface Erc20 extends Contract { 20 | methods: Erc20Methods 21 | name: string 22 | } 23 | -------------------------------------------------------------------------------- /compound-protocol/scenario/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "compound-money-market", 3 | "version": "0.2.1", 4 | "description": "The Compound Money Market", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "./script/webpack" 8 | }, 9 | "repository": "git@github.com:compound-finance/money-market.git", 10 | "author": "Compound Finance", 11 | "license": "UNLICENSED", 12 | "devDependencies": { 13 | "request": "^2.88.0", 14 | "solparse": "^2.2.8", 15 | "ts-loader": "^5.3.3", 16 | "ts-pegjs": "^0.2.2", 17 | "typescript": "^3.3.3", 18 | "webpack": "^4.29.6", 19 | "webpack-bundle-analyzer": "^3.1.0", 20 | "webpack-cli": "^3.3.0" 21 | }, 22 | "dependencies": { 23 | "bignumber.js": "8.0.1", 24 | "ethers": "^4.0.0-beta.1", 25 | "immutable": "^4.0.0-rc.12", 26 | "truffle-flattener": "^1.3.0", 27 | "truffle-hdwallet-provider": "1.0.5", 28 | "web3": "1.0.0-beta.37" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Completer.ts: -------------------------------------------------------------------------------- 1 | import {World} from './World'; 2 | import {Macros} from './Macro'; 3 | 4 | // TODO: Get smarter about storing actions as data 5 | const actions: string[] = [ 6 | "Read", 7 | "Assert", 8 | "FastForward", 9 | "Inspect", 10 | "Debug", 11 | "From", 12 | "Invariant", 13 | "Comptroller", 14 | "cToken", 15 | "Erc20", 16 | ]; 17 | 18 | function caseInsensitiveSort(a: string, b: string): number { 19 | let A = a.toUpperCase(); 20 | let B = b.toUpperCase(); 21 | 22 | if (A < B) { 23 | return -1; 24 | } else if (A > B) { 25 | return 1; 26 | } else { 27 | return 0; 28 | } 29 | } 30 | 31 | export function complete(world: World, macros: Macros, line: string) { 32 | let allActions = actions.concat(Object.keys(macros)).sort(caseInsensitiveSort); 33 | const hits = allActions.filter((c) => c.toLowerCase().startsWith(line.toLowerCase())); 34 | 35 | return [hits, line]; 36 | } 37 | -------------------------------------------------------------------------------- /compound-aragon-app/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "extends": [ 7 | "standard", 8 | "standard-react", 9 | "prettier", 10 | "prettier/react" 11 | ], 12 | "parser": "babel-eslint", 13 | "parserOptions": { 14 | "ecmaVersion": 6, 15 | "ecmaFeatures": { 16 | "jsx": true 17 | }, 18 | "sourceType": "module" 19 | }, 20 | "plugins": ["prettier", "react", "react-hooks"], 21 | "rules": { 22 | "valid-jsdoc": "error", 23 | "react/prop-types": 0, 24 | "prettier/prettier": [ 25 | "error", 26 | { 27 | "singleQuote": true, 28 | "semi": false, 29 | "trailingComma": "es5", 30 | "bracketSpacing": true, 31 | "jsxBracketSameLine": false 32 | } 33 | ], 34 | "linebreak-style": ["error", "unix"], 35 | "react-hooks/rules-of-hooks": "error", 36 | "react-hooks/exhaustive-deps": "warn" 37 | } 38 | } -------------------------------------------------------------------------------- /compound-protocol/networks/README.md: -------------------------------------------------------------------------------- 1 | ## Network Configuration 2 | 3 | This folder contains the configuration for given networks (e.g. `rinkeby.json` is the configuration for the Rinkeby test-net). These configuration files are meant to be used to configure external applications (like dApps) and thus contain a base set of information that may be useful (such as the address the Comptroller and a list of cToken markets). These configuration files are auto-generated when doing local development. 4 | 5 | Structure 6 | --------- 7 | 8 | ```json 9 | { 10 | "Contracts": { 11 | "MoneyMarket": "0x{address}", 12 | "Migrations": "0x{address}", 13 | "PriceOracle": "0x{address}", 14 | "InterestRateModel": "0x{address}" 15 | }, 16 | "Tokens": { 17 | "{SYM}": { 18 | "name": "{Full Name}", 19 | "symbol": "{SYM}", 20 | "decimals": 18, 21 | "address": "0x{address}", 22 | "supported": true 23 | } 24 | } 25 | } 26 | ``` -------------------------------------------------------------------------------- /compound-aragon-app/.soliumrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solium:all", 3 | "plugins": [ 4 | "security" 5 | ], 6 | "rules": { 7 | "imports-on-top": ["error"], 8 | "variable-declarations": ["error"], 9 | "array-declarations": ["error"], 10 | "operator-whitespace": ["error"], 11 | "lbrace": ["error"], 12 | "mixedcase": ["warning"], 13 | "camelcase": ["error"], 14 | "uppercase": ["warning"], 15 | "no-empty-blocks": ["error"], 16 | "no-unused-vars": ["error"], 17 | "quotes": ["error"], 18 | "indentation": ["error"], 19 | "whitespace": ["error"], 20 | "deprecated-suicide": ["error"], 21 | "arg-overflow": ["error", 8], 22 | "pragma-on-top": ["error"], 23 | "security/enforce-explicit-visibility": ["error"], 24 | "consequent": 0, 25 | "error-reason": ["warning"], 26 | "function-order": [ 27 | "error", 28 | { 29 | "ignore": { 30 | "functions": ["initialize"] 31 | } 32 | } 33 | ] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Compound Aragon App 2 | 3 | An Aragon app using the [Aragon Agent](https://github.com/aragon/aragon-apps/tree/master/apps/agent) enabling a DAO to 4 | interact with the [Compound protocol](https://compound.finance/). 5 | 6 | The current functionality allows a single cERC20 compound token to be enabled within the app. This token can then be supplied/lent 7 | to with the corresponding ERC20 token from the DAO to accrue interest. 8 | 9 | #### :white_check_mark: Security review status: Audited 10 | The Solidity code in this repo has been audited, see [the report](https://github.com/empowerthedao/compound-aragon-app/tree/master/audit). 11 | 12 | ## Deployment Instructions 13 | 14 | [Instructions for local deployment](https://github.com/empowerthedao/compound-aragon-app/blob/master/local-install.md) 15 | for development purposes. 16 | 17 | [Instructions for live net installation](https://github.com/empowerthedao/compound-aragon-app/blob/master/compound-aragon-app/fresh-install.md) 18 | for end user usage. 19 | -------------------------------------------------------------------------------- /compound-aragon-app/contracts/lib/AddressArrayUtils.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | 4 | library AddressArrayUtils { 5 | 6 | function deleteItem(address[] storage self, address item) internal returns (bool) { 7 | uint256 length = self.length; 8 | for (uint256 i = 0; i < length; i++) { 9 | if (self[i] == item) { 10 | uint256 newLength = length - 1; 11 | if (i != newLength) { 12 | self[i] = self[newLength]; 13 | } 14 | 15 | delete self[newLength]; 16 | self.length = newLength; 17 | 18 | return true; 19 | } 20 | } 21 | return false; 22 | } 23 | 24 | function contains(address[] storage self, address item) internal returns (bool) { 25 | for (uint256 i = 0; i < self.length; i++) { 26 | if (self[i] == item) { 27 | return true; 28 | } 29 | } 30 | return false; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /compound-protocol/test/contracts/FaucetToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "./StandardToken.sol"; 4 | 5 | /** 6 | * @title The Compound Faucet Test Token 7 | * @author Compound 8 | * @notice A simple test token that lets anyone get more of it. 9 | */ 10 | contract FaucetToken is StandardToken { 11 | string public name; 12 | string public symbol; 13 | uint8 public decimals; 14 | 15 | constructor(uint256 _initialAmount, string memory _tokenName, uint8 _decimalUnits, string memory _tokenSymbol) public { 16 | totalSupply_ = _initialAmount; 17 | balances[msg.sender] = _initialAmount; 18 | name = _tokenName; 19 | symbol = _tokenSymbol; 20 | decimals = _decimalUnits; 21 | } 22 | 23 | /** 24 | * @dev Arbitrarily adds tokens to any account 25 | */ 26 | function allocateTo(address _owner, uint256 value) public { 27 | balances[_owner] += value; 28 | totalSupply_ += value; 29 | emit Transfer(address(this), _owner, value); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /compound-protocol/test/contracts/FaucetNonStandardToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "./NonStandardToken.sol"; 4 | 5 | /** 6 | * @title The Compound Faucet Test Token 7 | * @author Compound 8 | * @notice A simple test token that lets anyone get more of it. 9 | */ 10 | contract FaucetNonStandardToken is NonStandardToken { 11 | string public name; 12 | string public symbol; 13 | uint8 public decimals; 14 | 15 | constructor(uint256 _initialAmount, string memory _tokenName, uint8 _decimalUnits, string memory _tokenSymbol) public { 16 | totalSupply_ = _initialAmount; 17 | balances[msg.sender] = _initialAmount; 18 | name = _tokenName; 19 | symbol = _tokenSymbol; 20 | decimals = _decimalUnits; 21 | } 22 | 23 | /** 24 | * @dev Arbitrarily adds tokens to any account 25 | */ 26 | function allocateTo(address _owner, uint256 value) public { 27 | balances[_owner] += value; 28 | totalSupply_ += value; 29 | emit Transfer(address(this), _owner, value); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Value/UserValue.ts: -------------------------------------------------------------------------------- 1 | import {Event} from '../Event'; 2 | import {World} from '../World'; 3 | import { 4 | getAddressV 5 | } from '../CoreValue'; 6 | import {Arg, Fetcher, getFetcherValue} from '../Command'; 7 | import { 8 | AddressV, 9 | Value 10 | } from '../Value'; 11 | 12 | async function getUserAddress(world: World, user: string): Promise { 13 | return new AddressV(user); 14 | } 15 | 16 | export function userFetchers() { 17 | return [ 18 | new Fetcher<{account: AddressV}, AddressV>(` 19 | #### Address 20 | 21 | * "User Address" - Returns address of user 22 | * E.g. "User Geoff Address" - Returns Geoff's address 23 | `, 24 | "Address", 25 | [ 26 | new Arg("account", getAddressV) 27 | ], 28 | async (world, {account}) => account, 29 | {namePos: 1} 30 | ) 31 | ]; 32 | } 33 | 34 | export async function getUserValue(world: World, event: Event): Promise { 35 | return await getFetcherValue("User", userFetchers(), world, event); 36 | } 37 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/HistoricReadline.ts: -------------------------------------------------------------------------------- 1 | import * as readline from 'readline'; 2 | import * as fs from 'fs'; 3 | import {readFile} from './File'; 4 | 5 | let readlineAny = readline; 6 | 7 | export async function createInterface(options): Promise { 8 | let history: string[] = await readFile(options['path'], [], (x) => x.split("\n")); 9 | let cleanHistory = history.filter((x) => !!x).reverse(); 10 | 11 | readlineAny.kHistorySize = Math.max(readlineAny.kHistorySize, options['maxLength']); 12 | 13 | let rl = readline.createInterface(options); 14 | let rlAny = rl; 15 | 16 | let oldAddHistory = rlAny._addHistory; 17 | 18 | rlAny._addHistory = function() { 19 | let last = rlAny.history[0]; 20 | let line = oldAddHistory.call(rl); 21 | 22 | // TODO: Should this be sync? 23 | if (line.length > 0 && line != last) { 24 | fs.appendFileSync(options['path'], `${line}\n`); 25 | } 26 | 27 | // TODO: Truncate file? 28 | 29 | return line; 30 | } 31 | 32 | rlAny.history.push.apply(rlAny.history, cleanHistory); 33 | 34 | return rl; 35 | } 36 | -------------------------------------------------------------------------------- /compound-protocol/script/verify: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | dir=`dirname $0` 6 | proj_root="$dir/.." 7 | contracts="`(cd $proj_root && pwd)`/contracts" 8 | emv_jar="$proj_root/scripts/certora/emv.jar" 9 | 10 | command -v java >/dev/null 2>&1 || { echo "Error: java is not installed." >&2; exit 1; } 11 | command -v z3 >/dev/null 2>&1 || { echo "Error: z3 is not installed." >&2; exit 1; } 12 | 13 | function verify_spec { 14 | [ -e "$1" ] || (echo "spec file not found: $1" && exit 1) 15 | 16 | set -x 17 | java -jar "$emv_jar" -spec "$1" -theory INT -s z3 -graphDrawLimit 0 -path $contracts 18 | set +x 19 | } 20 | 21 | if [ "$CI" ]; then 22 | all_specs=($(circleci tests glob "$proj_root/spec/formal/*.spclnk" | circleci tests split --split-by=timings)) 23 | echo XXX disabled in CI for now... && exit 0 24 | else 25 | if [[ $# -eq 0 ]] ; then 26 | all_specs=($proj_root/spec/formal/*.spclnk) 27 | else 28 | all_specs=($@) 29 | fi 30 | fi 31 | 32 | echo "running specs ${all_specs[@]}" 33 | 34 | for spec in "${all_specs[@]}"; do 35 | verify_spec "$spec" 36 | done 37 | -------------------------------------------------------------------------------- /compound-aragon-app/app/src/components/supply/Supply.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import styled from "styled-components"; 3 | import CompoundTokenDetails from "./CompoundTokenDetails"; 4 | import Balances from "../balances/Balances"; 5 | 6 | const Supply = ({supplyState, handleTransfer, compactMode, isSyncing}) => { 7 | 8 | const {balances, compoundTokens, tokens} = supplyState 9 | 10 | const compoundToken = compoundTokens && compoundTokens.length > 0 ? compoundTokens[0] : {} 11 | 12 | return ( 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ) 21 | } 22 | 23 | const Container = styled.div` 24 | display: flex; 25 | flex-direction: column; 26 | ` 27 | 28 | const SpacedBlock = styled.div` 29 | margin-top: 30px; 30 | &:first-child { 31 | margin-top: 0; 32 | } 33 | ` 34 | 35 | export default Supply 36 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/File.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | 4 | export function getNetworkPath(basePath: string | null, network: string, name: string, extension: string | null='json'): string { 5 | if (!basePath) { 6 | throw new Error(`Cannot read file when missing base path`); 7 | } 8 | 9 | return path.join(basePath, 'networks', `${network}${name}${extension ? `.${extension}` : ''}`); 10 | } 11 | 12 | export async function readFile(file: string, def: T, fn: (data: string) => T): Promise { 13 | return new Promise((resolve, reject) => { 14 | fs.access(file, fs.constants.F_OK, (err) => { 15 | if (err) { 16 | resolve(def); 17 | } else { 18 | fs.readFile(file, 'utf8', (err, data) => { 19 | return err ? reject(err) : resolve(fn(data)); 20 | }); 21 | } 22 | }); 23 | }); 24 | } 25 | 26 | export async function writeFile(file: string, data: string): Promise { 27 | return new Promise((resolve, reject) => { 28 | fs.writeFile(file, data, (err) => { 29 | return err ? reject(err) : resolve(); 30 | }); 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Accounts.ts: -------------------------------------------------------------------------------- 1 | import {World} from './World'; 2 | import {Map} from 'immutable'; 3 | 4 | export const accountMap = { 5 | "default": 0, 6 | "root": 0, 7 | "admin": 0, 8 | "first": 0, 9 | 10 | "bank": 1, 11 | "second": 1, 12 | 13 | "geoff": 2, 14 | "third": 2, 15 | 16 | "torrey": 3, 17 | "fourth": 3, 18 | 19 | "robert": 4, 20 | "fifth": 4, 21 | 22 | "coburn": 5, 23 | "sixth": 5, 24 | 25 | "jared": 6, 26 | "seventh": 6 27 | }; 28 | 29 | export interface Account { 30 | name: string 31 | address: string 32 | } 33 | 34 | export type Accounts = Map 35 | 36 | export function accountAliases(index: number): string[] { 37 | return Object.entries(accountMap).filter(([k,v]) => v === index).map(([k,v]) => k); 38 | } 39 | 40 | export function loadAccounts(accounts: string[]): Accounts { 41 | return Object.entries(accountMap).reduce((acc, [name, index]) => { 42 | if (accounts[index]) { 43 | return acc.set(name, { name: name, address: accounts[index] }); 44 | } else { 45 | return acc; 46 | } 47 | }, >Map({})); 48 | } 49 | -------------------------------------------------------------------------------- /compound-aragon-app/app/src/components/supply/CompoundTokenDetails.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import {Split} from '@aragon/ui' 3 | import {useNetwork} from "@aragon/api-react"; 4 | import SupplyDetails from "./SupplyDetails"; 5 | import SupplyActivity from "./SupplyActivity"; 6 | 7 | const CompoundTokenDetails = ({compoundToken, network, tokens, isSyncing}) => { 8 | 9 | return ( 10 |
11 | } 17 | secondary={ 18 | } 24 | /> 25 |
26 | 27 | ) 28 | } 29 | 30 | export default props => { 31 | const network = useNetwork() 32 | return 33 | } 34 | -------------------------------------------------------------------------------- /compound-aragon-app/test/helpers/rpc.js: -------------------------------------------------------------------------------- 1 | export default class RPC { 2 | 3 | constructor(web3) { 4 | this.web3 = web3 5 | } 6 | 7 | sendAsync(method, arg) { 8 | const req = { 9 | jsonrpc: "2.0", 10 | method: method, 11 | id: new Date().getTime() 12 | } 13 | 14 | if (arg) req.params = arg 15 | 16 | return new Promise((resolve, reject) => { 17 | return this.web3.currentProvider.send(req, (err, result) => { // Note 'send' has been changed from 'sendAsync' to support web3.js 1.0 syntax 18 | if (err) { 19 | reject(err) 20 | } else if (result && result.error) { 21 | reject(new Error("RPC Error: " + (result.error.message || result.error))) 22 | } else { 23 | resolve(result) 24 | } 25 | }) 26 | }) 27 | } 28 | 29 | snapshot() { 30 | return this.sendAsync("evm_snapshot") 31 | .then(res => res.result) 32 | } 33 | 34 | revert(snapshotId) { 35 | return this.sendAsync("evm_revert", [snapshotId]) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Invariant/StaticInvariant.ts: -------------------------------------------------------------------------------- 1 | import {Invariant} from '../Invariant'; 2 | import {fail, World} from '../World'; 3 | import {getCoreValue} from '../CoreValue'; 4 | import {Value} from '../Value'; 5 | import {Event} from '../Event'; 6 | import {formatEvent} from '../Formatter'; 7 | 8 | export class StaticInvariant implements Invariant { 9 | condition: Event; 10 | value: Value; 11 | held = false; 12 | 13 | constructor(condition: Event, value: Value) { 14 | this.condition = condition; 15 | this.value = value; 16 | } 17 | 18 | async getCurrentValue(world: World): Promise { 19 | return await getCoreValue(world, this.condition); 20 | }; 21 | 22 | async checker(world: World): Promise { 23 | const currentValue = await this.getCurrentValue(world); 24 | 25 | if (!this.value.compareTo(world, currentValue)) { 26 | fail(world, `Static invariant broken! Expected ${this.toString()} to remain static value \`${this.value}\` but became \`${currentValue}\``); 27 | } 28 | } 29 | 30 | toString() { 31 | return `StaticInvariant: condition=${formatEvent(this.condition)}, value=${this.value.toString()}`; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /compound-protocol/spec/scenario/SetComptroller.scen: -------------------------------------------------------------------------------- 1 | -- Sets for `_setComptroller` Admin Function 2 | 3 | Test "Set Comptroller" 4 | NewComptroller 5 | NewCToken ZRX cZRX 6 | Assert Equal (CToken cZRX Comptroller) (Unitroller Address) 7 | ComptrollerImpl Deploy Scenario NewComptroller 8 | From Root (CToken cZRX SetComptroller (ComptrollerImpl NewComptroller Address)) 9 | -- TODO: Fix log assertion 10 | -- Assert Log "NewComptroller" ("oldComptroller" (Unitroller Address)) ("newComptroller" (ComptrollerImpl NewComptroller Address)) 11 | Assert Equal (CToken cZRX Comptroller) (ComptrollerImpl NewComptroller Address) 12 | 13 | Test "Fail when is not a comptroller" 14 | NewComptroller 15 | NewCToken ZRX cZRX 16 | Invariant Remains (CToken cZRX Comptroller) (Unitroller Address) 17 | AllowFailures 18 | From Root (CToken cZRX SetComptroller (PriceOracle Address)) 19 | Assert Revert 20 | 21 | Test "Fail to set comptroller as not admin" 22 | NewComptroller 23 | NewCToken ZRX cZRX 24 | AllowFailures 25 | From Geoff (CToken cZRX SetComptroller (PriceOracle Address)) 26 | Assert Failure UNAUTHORIZED SET_COMPTROLLER_OWNER_CHECK 27 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Expectation/RemainsExpectation.ts: -------------------------------------------------------------------------------- 1 | import {Expectation} from '../Expectation'; 2 | import {fail, World} from '../World'; 3 | import {getCoreValue} from '../CoreValue'; 4 | import {Value} from '../Value'; 5 | import {Event} from '../Event'; 6 | import {formatEvent} from '../Formatter'; 7 | 8 | export class RemainsExpectation implements Expectation { 9 | condition: Event; 10 | value: Value; 11 | 12 | constructor(condition: Event, value: Value) { 13 | this.condition = condition; 14 | this.value = value; 15 | } 16 | 17 | async getCurrentValue(world: World): Promise { 18 | return await getCoreValue(world, this.condition); 19 | }; 20 | 21 | async checker(world: World, initialCheck: boolean=false): Promise { 22 | const currentValue = await this.getCurrentValue(world); 23 | 24 | if (!this.value.compareTo(world, currentValue)) { 25 | fail(world, `${this.toString()} failed as value ${initialCheck ? 'started as' : 'became'} \`${currentValue.toString()}\``); 26 | } 27 | } 28 | 29 | toString() { 30 | return `RemainsExpectation: condition=${formatEvent(this.condition)}, value=${this.value.toString()}`; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Value/UnitrollerValue.ts: -------------------------------------------------------------------------------- 1 | import {Event} from '../Event'; 2 | import {World} from '../World'; 3 | import {Unitroller} from '../Contract/Unitroller'; 4 | import { 5 | getAddressV 6 | } from '../CoreValue'; 7 | import { 8 | AddressV, 9 | Value 10 | } from '../Value'; 11 | import {Arg, Fetcher, getFetcherValue} from '../Command'; 12 | import {getUnitroller} from '../ContractLookup'; 13 | 14 | export async function getUnitrollerAddress(world: World, unitroller: Unitroller): Promise { 15 | return new AddressV(unitroller._address); 16 | } 17 | 18 | export function unitrollerFetchers() { 19 | return [ 20 | new Fetcher<{unitroller: Unitroller}, AddressV>(` 21 | #### Address 22 | 23 | * "Unitroller Address" - Returns address of unitroller 24 | `, 25 | "Address", 26 | [new Arg("unitroller", getUnitroller, {implicit: true})], 27 | (world, {unitroller}) => getUnitrollerAddress(world, unitroller) 28 | ) 29 | ]; 30 | } 31 | 32 | export async function getUnitrollerValue(world: World, event: Event): Promise { 33 | return await getFetcherValue("Unitroller", unitrollerFetchers(), world, event); 34 | } 35 | -------------------------------------------------------------------------------- /compound-aragon-app/contracts/misc/TestERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "openzeppelin-solidity/contracts/token/ERC20/MintableToken.sol"; 4 | import "openzeppelin-solidity/contracts/token/ERC20/DetailedERC20.sol"; 5 | 6 | contract TestERC20 is MintableToken, DetailedERC20("Basic Attention Token", "BAT", 18) { 7 | 8 | constructor() public { 9 | mint(msg.sender, 1000 * (10 ** 18)); 10 | } 11 | 12 | // Uncomment the below functions and delete the inherited DetailedERC20 contract to test the nonconformant ERC20 DAI token. 13 | 14 | // /** 15 | // * @dev Returns the name of the token. 16 | // */ 17 | // function name() public view returns (bytes32) { 18 | // return 0x44616920537461626c65636f696e2076312e3000000000000000000000000000; 19 | // } 20 | // 21 | // /** 22 | // * @dev Returns the symbol of the token, usually a shorter version of the 23 | // * name. 24 | // */ 25 | // function symbol() public view returns (bytes32) { 26 | // return 0x4441490000000000000000000000000000000000000000000000000000000000; 27 | // } 28 | // 29 | // function decimals() public view returns (uint8) { 30 | // return 18; 31 | // } 32 | } 33 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Value/MaximillionValue.ts: -------------------------------------------------------------------------------- 1 | import {Event} from '../Event'; 2 | import {World} from '../World'; 3 | import {Maximillion} from '../Contract/Maximillion'; 4 | import { 5 | getAddressV 6 | } from '../CoreValue'; 7 | import { 8 | AddressV, 9 | Value 10 | } from '../Value'; 11 | import {Arg, Fetcher, getFetcherValue} from '../Command'; 12 | import {getMaximillion} from '../ContractLookup'; 13 | 14 | export async function getMaximillionAddress(world: World, maximillion: Maximillion): Promise { 15 | return new AddressV(maximillion._address); 16 | } 17 | 18 | export function maximillionFetchers() { 19 | return [ 20 | new Fetcher<{maximillion: Maximillion}, AddressV>(` 21 | #### Address 22 | 23 | * "Maximillion Address" - Returns address of maximillion 24 | `, 25 | "Address", 26 | [new Arg("maximillion", getMaximillion, {implicit: true})], 27 | (world, {maximillion}) => getMaximillionAddress(world, maximillion) 28 | ) 29 | ]; 30 | } 31 | 32 | export async function getMaximillionValue(world: World, event: Event): Promise { 33 | return await getFetcherValue("Maximillion", maximillionFetchers(), world, event); 34 | } 35 | -------------------------------------------------------------------------------- /compound-aragon-app/app/src/hooks/redeem-panel.js: -------------------------------------------------------------------------------- 1 | import {useCallback} from 'react' 2 | import {useApi} from "@aragon/api-react"; 3 | import {balanceOfUnderlyingTokens$} from "../web3/CompoundData"; 4 | import {map} from "rxjs/operators"; 5 | import {fromDecimals} from "../lib/math-utils"; 6 | import {useAppState} from "@aragon/api-react"; 7 | 8 | export function useGetMaxRedeemable() { 9 | const api = useApi() 10 | const {compoundTokens, tokens} = useAppState() 11 | 12 | return useCallback((maxRedeemableCallback) => { 13 | 14 | const compoundToken = compoundTokens[0] || {} 15 | const {tokenAddress, underlyingToken: underlyingTokenAddress} = compoundToken 16 | 17 | const {decimals: underlyingTokenDecimals} = tokens.find(token => token.address === underlyingTokenAddress) 18 | 19 | balanceOfUnderlyingTokens$(api, tokenAddress).pipe( 20 | map(maxRedeemable => fromDecimals(maxRedeemable.toString(), underlyingTokenDecimals))) 21 | .subscribe(maxRedeemable => maxRedeemableCallback(maxRedeemable)) 22 | 23 | }, [api, compoundTokens]) 24 | } 25 | 26 | export function useRedeemState() { 27 | 28 | return { 29 | getMaxRedeemable: useGetMaxRedeemable() 30 | } 31 | } -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Web.ts: -------------------------------------------------------------------------------- 1 | import {parse} from './Parser'; 2 | import {IWeb3, World, initWorld} from './World'; 3 | import {throwAssert} from './Assert'; 4 | import {CallbackPrinter} from './Printer'; 5 | import {runCommand} from './Runner'; 6 | import {loadContractData, parseNetworkFile} from './Networks'; 7 | 8 | export async function webWorld(web3: IWeb3, networksData: string, networksABIData: string, printerCallback: (message: any) => void): Promise { 9 | let printer = new CallbackPrinter(printerCallback); 10 | let accounts = [web3.currentProvider.address]; 11 | let network = 'rinkeby'; // TODO: Get from web3 12 | 13 | let world = await initWorld(throwAssert, printer, web3, null, network, accounts, null); 14 | 15 | let networks = parseNetworkFile(networksData); 16 | let networksABI = parseNetworkFile(networksABIData); 17 | 18 | [world,] = await loadContractData(world, networks, networksABI); 19 | // world = loadInvokationOpts(world); 20 | // world = loadVerbose(world); 21 | // world = loadDryRun(world); 22 | // world = await loadSettings(world); 23 | 24 | return world; 25 | } 26 | 27 | export async function webParse(world: World, line: string): Promise { 28 | return runCommand(world, line, {}); 29 | } 30 | -------------------------------------------------------------------------------- /compound-protocol/test/contracts/InterestRateModelHarness.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "../InterestRateModel.sol"; 4 | 5 | /** 6 | * @title An Interest Rate Model for tests that can be instructed to return a failure instead of doing a calculation 7 | * @author Compound 8 | */ 9 | contract InterestRateModelHarness is InterestRateModel { 10 | bool public constant isInterestRateModel = true; 11 | uint public constant opaqueBorrowFailureCode = 20; 12 | bool public failBorrowRate; 13 | uint public borrowRate; 14 | 15 | constructor(uint borrowRate_) public { 16 | borrowRate = borrowRate_; 17 | } 18 | 19 | function setFailBorrowRate(bool failBorrowRate_) public { 20 | failBorrowRate = failBorrowRate_; 21 | } 22 | 23 | function setBorrowRate(uint borrowRate_) public { 24 | borrowRate = borrowRate_; 25 | } 26 | 27 | function getBorrowRate(uint _cash, uint _borrows, uint _reserves) public view returns (uint, uint) { 28 | _cash; // unused 29 | _borrows; // unused 30 | _reserves; // unused 31 | 32 | if (failBorrowRate) { 33 | return (opaqueBorrowFailureCode, 0); 34 | } 35 | return (0, borrowRate); 36 | } 37 | } -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Contract/PriceOracle.ts: -------------------------------------------------------------------------------- 1 | import {Contract} from '../Contract'; 2 | import {Callable, Sendable} from '../Invokation'; 3 | import {encodedNumber} from '../Encoding'; 4 | 5 | interface PriceOracleMethods { 6 | assetPrices(asset: string): Callable 7 | setUnderlyingPrice(cToken: string, amount: encodedNumber): Sendable 8 | 9 | // Anchor Price Oracle 10 | getPrice(asset: string): Callable 11 | readers(asset: string): Callable 12 | anchorAdmin(): Callable 13 | pendingAnchorAdmin(): Callable 14 | poster(): Callable 15 | maxSwing(): Callable 16 | anchors(asset: string): Callable<{0: number, 1: number}> 17 | pendingAnchors(asset: string): Callable 18 | _setPendingAnchor(asset: string, price: encodedNumber): Sendable 19 | _setPaused(paused: boolean): Sendable 20 | _setPendingAnchorAdmin(string): Sendable 21 | _acceptAnchorAdmin(): Sendable 22 | setPrice(asset: string, price: encodedNumber): Sendable 23 | setPrices(assets: string[], prices: encodedNumber[]): Sendable 24 | } 25 | 26 | export interface PriceOracle extends Contract { 27 | methods: PriceOracleMethods 28 | name: string 29 | } 30 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Invariant/RemainsInvariant.ts: -------------------------------------------------------------------------------- 1 | import {Invariant} from '../Invariant'; 2 | import {fail, World} from '../World'; 3 | import {getCoreValue} from '../CoreValue'; 4 | import {Value} from '../Value'; 5 | import {Event} from '../Event'; 6 | import {formatEvent} from '../Formatter'; 7 | 8 | export class RemainsInvariant implements Invariant { 9 | condition: Event; 10 | value: Value; 11 | held = false; 12 | 13 | constructor(condition: Event, value: Value) { 14 | this.condition = condition; 15 | this.value = value; 16 | } 17 | 18 | async getCurrentValue(world: World): Promise { 19 | return await getCoreValue(world, this.condition); 20 | }; 21 | 22 | async checker(world: World, initialCheck: boolean=false): Promise { 23 | const currentValue = await this.getCurrentValue(world); 24 | 25 | if (!this.value.compareTo(world, currentValue)) { 26 | fail(world, `Static invariant broken! Expected ${this.toString()} to remain static value \`${this.value}\` but ${initialCheck ? 'started as' : 'became'} \`${currentValue}\``); 27 | } 28 | } 29 | 30 | toString() { 31 | return `RemainsInvariant: condition=${formatEvent(this.condition)}, value=${this.value.toString()}`; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /compound-protocol/spec/scenario/CTokenAdmin.scen: -------------------------------------------------------------------------------- 1 | 2 | Test "Set admin" 3 | NewComptroller 4 | NewCToken ZRX cZRX 5 | Assert Equal (CToken cZRX Admin) (Address Root) 6 | Assert Equal (CToken cZRX PendingAdmin) (Address Zero) 7 | From Root (CToken cZRX SetPendingAdmin Geoff) 8 | Assert Equal (CToken cZRX Admin) (Address Root) 9 | Assert Equal (CToken cZRX PendingAdmin) (Address Geoff) 10 | From Geoff (CToken cZRX AcceptAdmin) 11 | Assert Equal (CToken cZRX Admin) (Address Geoff) 12 | Assert Equal (CToken cZRX PendingAdmin) (Address Zero) 13 | 14 | Test "Fail to set pending admin" 15 | NewComptroller 16 | NewCToken ZRX cZRX 17 | Invariant Remains (CToken cZRX Admin) (Address Root) 18 | Invariant Remains (CToken cZRX PendingAdmin) (Address Zero) 19 | AllowFailures 20 | From Geoff (CToken cZRX SetPendingAdmin Geoff) 21 | Assert Failure UNAUTHORIZED SET_PENDING_ADMIN_OWNER_CHECK 22 | 23 | Test "Fail to accept admin" 24 | NewComptroller 25 | NewCToken ZRX cZRX 26 | Invariant Remains (CToken cZRX Admin) (Address Root) 27 | Invariant Remains (CToken cZRX PendingAdmin) (Address Zero) 28 | AllowFailures 29 | From Geoff (CToken cZRX AcceptAdmin) 30 | Assert Failure UNAUTHORIZED ACCEPT_ADMIN_PENDING_ADMIN_CHECK 31 | -------------------------------------------------------------------------------- /compound-aragon-app/app/public/meta/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /compound-protocol/contracts/ReentrancyGuard.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | /** 4 | * @title Helps contracts guard against reentrancy attacks. 5 | * @author Remco Bloemen , Eenae 6 | * @dev If you mark a function `nonReentrant`, you should also 7 | * mark it `external`. 8 | */ 9 | contract ReentrancyGuard { 10 | /// @dev counter to allow mutex lock with only one SSTORE operation 11 | uint256 private _guardCounter; 12 | 13 | constructor () internal { 14 | // The counter starts at one to prevent changing it from zero to a non-zero 15 | // value, which is a more expensive operation. 16 | _guardCounter = 1; 17 | } 18 | 19 | /** 20 | * @dev Prevents a contract from calling itself, directly or indirectly. 21 | * Calling a `nonReentrant` function from another `nonReentrant` 22 | * function is not supported. It is possible to prevent this from happening 23 | * by making the `nonReentrant` function external, and make it call a 24 | * `private` function that does the actual work. 25 | */ 26 | modifier nonReentrant() { 27 | _guardCounter += 1; 28 | uint256 localCounter = _guardCounter; 29 | _; 30 | require(localCounter == _guardCounter, "re-entered"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Value/ComptrollerImplValue.ts: -------------------------------------------------------------------------------- 1 | import {Event} from '../Event'; 2 | import {World} from '../World'; 3 | import {ComptrollerImpl} from '../Contract/ComptrollerImpl'; 4 | import { 5 | getAddressV 6 | } from '../CoreValue'; 7 | import { 8 | AddressV, 9 | Value 10 | } from '../Value'; 11 | import {Arg, Fetcher, getFetcherValue} from '../Command'; 12 | import {getComptrollerImpl} from '../ContractLookup'; 13 | 14 | export async function getComptrollerImplAddress(world: World, comptrollerImpl: ComptrollerImpl): Promise { 15 | return new AddressV(comptrollerImpl._address); 16 | } 17 | 18 | export function comptrollerImplFetchers() { 19 | return [ 20 | new Fetcher<{comptrollerImpl: ComptrollerImpl}, AddressV>(` 21 | #### Address 22 | 23 | * "ComptrollerImpl Address" - Returns address of comptroller implementation 24 | `, 25 | "Address", 26 | [new Arg("comptrollerImpl", getComptrollerImpl)], 27 | (world, {comptrollerImpl}) => getComptrollerImplAddress(world, comptrollerImpl), 28 | {namePos: 1} 29 | ) 30 | ]; 31 | } 32 | 33 | export async function getComptrollerImplValue(world: World, event: Event): Promise { 34 | return await getFetcherValue("ComptrollerImpl", comptrollerImplFetchers(), world, event); 35 | } 36 | -------------------------------------------------------------------------------- /compound-protocol/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "compound-money-market", 3 | "version": "0.2.1", 4 | "description": "The Compound Money Market", 5 | "main": "index.js", 6 | "scripts": { 7 | "console": "./script/console", 8 | "coverage": "./script/coverage", 9 | "deploy": "./scenario/script/repl -s ./scenario/scen/deploy.scen -e dai_price=0.0001,dai_cf=0.5,eth_cf=0.5", 10 | "lint": "./script/lint", 11 | "repl": "./scenario/script/repl", 12 | "scenario": "./script/scenario", 13 | "test": "./script/test" 14 | }, 15 | "repository": "git@github.com:compound-finance/money-market-presidio.git", 16 | "author": "Compound Finance", 17 | "license": "UNLICENSED", 18 | "devDependencies": { 19 | "bignumber.js": "8.0.1", 20 | "ganache-cli": "^6.3.0", 21 | "immutable": "^4.0.0-rc.12", 22 | "mocha-junit-reporter": "^1.18.0", 23 | "mocha-multi-reporters": "^1.1.7", 24 | "request": "^2.88.0", 25 | "solc": "0.5.8", 26 | "solidity-coverage": "^0.6.0-beta.3", 27 | "solparse": "^2.2.8", 28 | "truffle": "5.0.4", 29 | "truffle-flattener": "^1.3.0", 30 | "web3": "1.0.0-beta.37" 31 | }, 32 | "dependencies": { 33 | "bluebird": "^3.5.5", 34 | "ethlint": "^1.2.2", 35 | "truffle-core": "5.0.4", 36 | "truffle-hdwallet-provider": "1.0.5" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /compound-protocol/spec/formal/maximillion.cvl: -------------------------------------------------------------------------------- 1 | methods { 2 | borrowBalance(address) returns uint256 3 | etherBalance(address) returns uint256 4 | 5 | repayBehalf(address) returns uint256 6 | } 7 | 8 | repayBehalf(uint256 repayAmount, address borrower) 9 | description "Break repayBehalf" { 10 | env e0; havoc e0; 11 | env e1; havoc e1; 12 | env e2; havoc e2; 13 | 14 | static_require e1.block.number == e0.block.number; 15 | static_require e2.block.number >= e1.block.number; 16 | 17 | static_require e0.msg.value == 0; 18 | uint256 borrowed = sinvoke borrowBalance(e0, borrower); 19 | 20 | static_require repayAmount == e1.msg.value; 21 | invoke repayBehalf(e1, borrower); 22 | bool repayReverted = lastReverted; 23 | 24 | static_require e2.msg.value == 0; 25 | uint256 borrows = sinvoke borrowBalance(e2, borrower); 26 | uint256 balance = sinvoke etherBalance(e2, e1.msg.sender); 27 | 28 | static_assert (!repayReverted => 29 | ((repayAmount >= borrowed) => (balance >= repayAmount - borrowed))), "Mismatch in refund"; 30 | static_assert (!repayReverted => 31 | ((repayAmount >= borrowed) => (borrows == 0)) && 32 | ((repayAmount < borrowed) => (borrows == borrowed - repayAmount))), "Mismatch in borrows repaid"; 33 | } -------------------------------------------------------------------------------- /compound-aragon-app/arapp.json: -------------------------------------------------------------------------------- 1 | { 2 | "roles": [ 3 | { 4 | "name": "Set the agent address", 5 | "id": "SET_AGENT_ROLE", 6 | "params": [] 7 | }, 8 | { 9 | "name": "Transfer agent tokens", 10 | "id": "TRANSFER_ROLE", 11 | "params": [] 12 | }, 13 | { 14 | "name": "Change Compound Tokens", 15 | "id": "MODIFY_CTOKENS_ROLE", 16 | "params": [] 17 | }, 18 | { 19 | "name": "Supply tokens", 20 | "id": "SUPPLY_ROLE", 21 | "params": [] 22 | }, 23 | { 24 | "name": "Redeem tokens", 25 | "id": "REDEEM_ROLE", 26 | "params": [] 27 | } 28 | ], 29 | "environments": { 30 | "default": { 31 | "network": "development", 32 | "appName": "compound.open.aragonpm.eth" 33 | }, 34 | "rinkeby": { 35 | "registry": "0x98df287b6c145399aaa709692c8d308357bc085d", 36 | "appName": "compound.open.aragonpm.eth", 37 | "wsRPC": "wss://rinkeby.eth.aragon.network/ws", 38 | "network": "rinkeby" 39 | }, 40 | "mainnet": { 41 | "registry": "0x314159265dd8dbb310642f98f50c066173c1259b", 42 | "appName": "compound.open.aragonpm.eth", 43 | "wsRPC": "wss://mainnet.eth.aragon.network/ws", 44 | "network": "mainnet" 45 | } 46 | }, 47 | "path": "contracts/Compound.sol" 48 | } 49 | -------------------------------------------------------------------------------- /compound-protocol/script/test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | dir=`dirname $0` 4 | proj_root="$dir/.." 5 | test_root="$dir/../test" 6 | contracts_root="$dir/../contracts" 7 | network=${NETWORK:-test} 8 | verbose=${verbose:-} 9 | 10 | function cleanup { 11 | mv "$contracts_root/test" "$test_root/contracts" 12 | } 13 | 14 | trap cleanup EXIT 15 | 16 | mv "$test_root/contracts" "$contracts_root/test" 17 | 18 | if [ "$CI" -a ! "$1" ]; then 19 | set -x 20 | set -- "$@" $(circleci tests glob "test/**/*Test.js" "test/**/*Test.sol" | circleci tests split --split-by=timings) 21 | fi 22 | 23 | # Coverage clones our project but not node_modules, so let's hop 24 | # up one directory. 25 | if [ ! -d "$proj_root/node_modules" -a -d "$proj_root/../node_modules" ]; then 26 | proj_root="$proj_root/.." 27 | fi 28 | 29 | # Compile scenario runner 30 | [[ ! -d ./.tsbuilt || -z $NO_TSC ]] && "$proj_root/scenario/script/tsc" 31 | [[ -z $no_compile ]] && solc --combined-json bin,abi --optimize contracts/*.sol contracts/**/*.sol --allow-paths ./contracts,./contracts/test > networks/${network}-contracts.json 32 | 33 | node --stack_size=10000 "$proj_root/node_modules/truffle/build/cli.bundled.js" compile --network "$network" 34 | proj_root="$proj_root" verbose="$verbose" node --stack_size=10000 "$proj_root/node_modules/truffle/build/cli.bundled.js" test --network "$network" $@ 35 | -------------------------------------------------------------------------------- /compound-protocol/test/contracts/CErc20Scenario.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "../CErc20.sol"; 4 | import "./ComptrollerScenario.sol"; 5 | 6 | contract CErc20Scenario is CErc20 { 7 | constructor(address underlying_, 8 | ComptrollerInterface comptroller_, 9 | InterestRateModel interestRateModel_, 10 | uint initialExchangeRateMantissa, 11 | string memory name_, 12 | string memory symbol_, 13 | uint decimals_) 14 | CErc20( 15 | underlying_, 16 | comptroller_, 17 | interestRateModel_, 18 | initialExchangeRateMantissa, 19 | name_, 20 | symbol_, 21 | decimals_) public {} 22 | 23 | function setTotalBorrows(uint totalBorrows_) public { 24 | totalBorrows = totalBorrows_; 25 | } 26 | 27 | function setTotalReserves(uint totalReserves_) public { 28 | totalReserves = totalReserves_; 29 | } 30 | 31 | /** 32 | * @dev Function to simply retrieve block number 33 | * This exists mainly for inheriting test contracts to stub this result. 34 | */ 35 | function getBlockNumber() internal view returns (uint) { 36 | ComptrollerScenario comptrollerScenario = ComptrollerScenario(address(comptroller)); 37 | 38 | return comptrollerScenario.blockNumber(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /compound-protocol/script/utility/setup-local: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Simple script to set-up development, makes plenty of assumptions, so use 4 | # with caution. 5 | 6 | set -eo pipefail 7 | 8 | script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 9 | proj_root="$script_dir/../.." 10 | 11 | environment="development" 12 | copy_dirs="$1" 13 | 14 | IFS=';' read -ra copy_dirs_arr <<< "$copy_dirs" 15 | for copy_dir_rel in "${copy_dirs_arr[@]}"; do 16 | copy_dir=$proj_root/../$copy_dir_rel 17 | [ ! -d "$copy_dir" ] && (echo "cannot find directory: $copy_dir" && exit 1) 18 | done 19 | 20 | [ -f "networks/$environment.json" ] && rm "networks/$environment.json" 21 | [ -f "networks/$environment-abi.json" ] && rm "networks/$environment-abi.json" 22 | 23 | scripts/blockchain/deploy-tokens "$environment" # deploys some basic faucet tokens 24 | scripts/blockchain/oracle-deploy "$environment" # deploys a price oracle 25 | scripts/blockchain/oracle-set-token-prices "$environment" # sets configured prices 26 | scripts/blockchain/deploy "$environment" # deploys money market to local ganache 27 | scripts/blockchain/set-oracle "$environment" # sets oracle 28 | scripts/blockchain/support-markets "$environment" # adds money market support for the tokens added above 29 | 30 | for copy_dir_rel in "${copy_dirs_arr[@]}"; do 31 | copy_dir=$proj_root/../$copy_dir_rel 32 | cp $proj_root/networks/"$environment"* "$copy_dir/networks" 33 | done -------------------------------------------------------------------------------- /compound-aragon-app/app/src/components/supply/side-panel-input/TransferPanel.js: -------------------------------------------------------------------------------- 1 | import React, {useMemo, useState} from 'react' 2 | import styled from 'styled-components' 3 | import {Tabs} from '@aragon/ui' 4 | import SupplyInput from "./SupplyInput"; 5 | import RedeemInput from "./RedeemInput"; 6 | 7 | const SupplyPanel = ({handleSupply, handleRedeem, opened, redeemPanelState}) => { 8 | 9 | const [screenIndex, setScreenIndex] = useState(0) 10 | 11 | useMemo(() => { 12 | if (!opened) { 13 | setScreenIndex(0) 14 | } 15 | }, [opened]) 16 | 17 | return ( 18 |
19 | 20 | 25 | 26 | 27 | {screenIndex === 0 && ( 28 | 31 | )} 32 | {screenIndex === 1 && ( 33 | 37 | )} 38 |
39 | ) 40 | } 41 | 42 | const TabBarWrapper = styled.div` 43 | margin: 0 -30px 30px; 44 | ` 45 | 46 | export default SupplyPanel 47 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Value/InterestRateModelValue.ts: -------------------------------------------------------------------------------- 1 | import {Event} from '../Event'; 2 | import {World} from '../World'; 3 | import {InterestRateModel} from '../Contract/InterestRateModel'; 4 | import { 5 | getAddressV 6 | } from '../CoreValue'; 7 | import { 8 | AddressV, 9 | NumberV, 10 | Value} from '../Value'; 11 | import {Arg, Fetcher, getFetcherValue} from '../Command'; 12 | import {getInterestRateModel} from '../ContractLookup'; 13 | 14 | export async function getInterestRateModelAddress(world: World, interestRateModel: InterestRateModel): Promise { 15 | return new AddressV(interestRateModel._address); 16 | } 17 | 18 | export function interestRateModelFetchers() { 19 | return [ 20 | new Fetcher<{interestRateModel: InterestRateModel}, AddressV>(` 21 | #### Address 22 | 23 | * " Address" - Gets the address of the global price oracle 24 | * E.g. "InterestRateModel MyInterestRateModel Address" 25 | `, 26 | "Address", 27 | [ 28 | new Arg("interestRateModel", getInterestRateModel) 29 | ], 30 | (world, {interestRateModel}) => getInterestRateModelAddress(world, interestRateModel), 31 | {namePos: 1} 32 | ) 33 | ]; 34 | } 35 | 36 | export async function getInterestRateModelValue(world: World, event: Event): Promise { 37 | return await getFetcherValue("InterestRateModel", interestRateModelFetchers(), world, event); 38 | } 39 | -------------------------------------------------------------------------------- /compound-protocol/script/utility/truffle-cli.patch: -------------------------------------------------------------------------------- 1 | --- node_modules/truffle/build/cli.bundled.js 2019-01-25 12:07:21.000000000 -0800 2 | +++ node_modules/truffle/build/cli.bundled_patched.js 2019-01-25 11:51:01.000000000 -0800 3 | @@ -579247,6 +579247,7 @@ 4 | var OS = __webpack_require__(27); 5 | var dir = __webpack_require__(1136); 6 | var temp = __webpack_require__(2056); 7 | + var path = __webpack_require__(1); 8 | var Config = __webpack_require__(48); 9 | var Artifactor = __webpack_require__(1147); 10 | var Develop = __webpack_require__(2103); 11 | @@ -579291,7 +579292,27 @@ 12 | return file.match(config.test_file_extension_regexp) != null; 13 | }); 14 | 15 | - temp.mkdir("test-", function(err, temporaryDirectory) { 16 | + function getTestTempDir(cb) { 17 | + console.log('using cache'); 18 | + var tempDir = path.join(config.working_directory, '.tmp'); 19 | + var testDir = path.join(tempDir, 'test'); 20 | + 21 | + try { 22 | + if (!fs.existsSync(tempDir)) { 23 | + fs.mkdirSync(tempDir); 24 | + } 25 | + 26 | + if (!fs.existsSync(testDir)) { 27 | + fs.mkdirSync(testDir); 28 | + } 29 | + 30 | + cb(null, testDir); 31 | + } catch (err) { 32 | + cb(err); 33 | + } 34 | + } 35 | + 36 | + getTestTempDir(function(err, temporaryDirectory) { 37 | if (err) return done(err); 38 | 39 | function cleanup() { 40 | -------------------------------------------------------------------------------- /compound-protocol/spec/scenario/admin/setMarketPriceOracle.scen.old: -------------------------------------------------------------------------------- 1 | 2 | -- Tests for MoneyMarket's `_setMarketPriceOracle(address,address)` function 3 | 4 | Macro ListAsset asset 5 | SupportMarket asset (FixedPrice 1.0) (FixedRate 0.5 0.75) SimplePolicyHook 6 | Assert Success 7 | 8 | Test "is initially unset" 9 | AddToken Ether 10 | Assert Equal (GetMarketPriceOracle Ether) (Exactly 0) 11 | 12 | Test "fails if not called by admin" 13 | AddToken Ether 14 | Invariant Static (GetMarketPriceOracle Ether) 15 | From Geoff (SetMarketPriceOracle Ether Simple) 16 | Assert Failure UNAUTHORIZED SET_ORACLE_OWNER_CHECK 17 | 18 | Test "fails if market not listed" 19 | AddToken Ether 20 | Invariant Static (GetMarketPriceOracle Ether) 21 | SetMarketPriceOracle Ether Simple 22 | Assert Failure MARKET_NOT_LISTED SET_ORACLE_MARKET_NOT_LISTED 23 | 24 | Test "reverts if new address is not really an oracle" 25 | AddToken Ether 26 | ListAsset Ether 27 | Invariant Static (GetMarketPriceOracle Ether) 28 | SetMarketPriceOracle Ether NotPriceOracle 29 | Assert Revert 30 | 31 | Test "successfully set by admin" 32 | AddToken Ether 33 | ListAsset Ether 34 | SetMarketPriceOracle Ether Simple 35 | Assert Success 36 | Assert Equal (GetMarketPriceOracle Ether) LastContract 37 | Assert Log "NewMarketOracle" \ 38 | ("asset" (Address Ether)) ("oldPriceOracle" Anything) ("newPriceOracle" (LastContract)) 39 | -------------------------------------------------------------------------------- /compound-aragon-app/app/src/lib/web3-utils.js: -------------------------------------------------------------------------------- 1 | import { toChecksumAddress, hexToNumber } from 'web3-utils' 2 | 3 | // Check address equality without checksums 4 | export function addressesEqual(first, second) { 5 | first = first && toChecksumAddress(first) 6 | second = second && toChecksumAddress(second) 7 | return first === second 8 | } 9 | 10 | export const addressPattern = '(0x)?[0-9a-fA-F]{40}' 11 | 12 | /** 13 | * Shorten an Ethereum address. `charsLength` allows to change the number of 14 | * characters on both sides of the ellipsis. 15 | * 16 | * Examples: 17 | * shortenAddress('0x19731977931271') // 0x1973…1271 18 | * shortenAddress('0x19731977931271', 2) // 0x19…71 19 | * shortenAddress('0x197319') // 0x197319 (already short enough) 20 | * 21 | * @param {string} address The address to shorten 22 | * @param {number} [charsLength=4] The number of characters to change on both sides of the ellipsis 23 | * @returns {string} The shortened address 24 | */ 25 | export function shortenAddress(address, charsLength = 4) { 26 | const prefixLength = 2 // "0x" 27 | if (!address) { 28 | return '' 29 | } 30 | if (address.length < charsLength * 2 + prefixLength) { 31 | return address 32 | } 33 | return address.slice(0, charsLength + prefixLength) + '…' + address.slice(-charsLength) 34 | } 35 | 36 | // Re-export some web3-utils functions 37 | export { isAddress, toChecksumAddress, toUtf8, soliditySha3, toWei } from 'web3-utils' 38 | -------------------------------------------------------------------------------- /compound-protocol/test/contracts/CEtherScenario.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "../CEther.sol"; 4 | import "./ComptrollerScenario.sol"; 5 | 6 | contract CEtherScenario is CEther { 7 | uint reserveFactor; 8 | 9 | constructor(string memory name_, 10 | string memory symbol_, 11 | uint8 decimals_, 12 | ComptrollerInterface comptroller_, 13 | InterestRateModel interestRateModel_, 14 | uint initialExchangeRateMantissa) 15 | CEther(comptroller_, 16 | interestRateModel_, 17 | initialExchangeRateMantissa, 18 | name_, 19 | symbol_, 20 | decimals_) public { 21 | } 22 | 23 | function setTotalBorrows(uint totalBorrows_) public { 24 | totalBorrows = totalBorrows_; 25 | } 26 | 27 | function setTotalReserves(uint totalReserves_) public { 28 | totalReserves = totalReserves_; 29 | } 30 | 31 | function donate() public payable { 32 | // no-op 33 | } 34 | 35 | /** 36 | * @dev Function to simply retrieve block number 37 | * This exists mainly for inheriting test contracts to stub this result. 38 | */ 39 | function getBlockNumber() internal view returns (uint) { 40 | ComptrollerScenario comptrollerScenario = ComptrollerScenario(address(comptroller)); 41 | 42 | return comptrollerScenario.blockNumber(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /compound-aragon-app/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "compound-frontend", 3 | "version": "1.0.0", 4 | "main": "src/index.js", 5 | "dependencies": { 6 | "@aragon/api": "2.0.0-beta.7", 7 | "@aragon/api-react": "2.0.0-beta.6", 8 | "@aragon/ui": "1.0.0-alpha.32", 9 | "@babel/polyfill": "^7.2.5", 10 | "bn.js": "^5.0.0", 11 | "date-fns": "2.0.0-alpha.29", 12 | "ethers": "^4.0.38", 13 | "lodash.throttle": "^4.1.1", 14 | "prop-types": "^15.7.2", 15 | "react": "^16.8.4", 16 | "react-dom": "^16.8.4", 17 | "rxjs": "^6.2.1", 18 | "styled-components": "^4.1.3", 19 | "web3-utils": "^1.2.1" 20 | }, 21 | "devDependencies": { 22 | "@babel/core": "^7.5.5", 23 | "@babel/plugin-proposal-class-properties": "^7.5.5", 24 | "@babel/preset-env": "^7.5.5", 25 | "copyfiles": "^2.1.0", 26 | "parcel-bundler": "^1.12.3" 27 | }, 28 | "scripts": { 29 | "build": "npm run sync-assets && npm run build:app && npm run build:script", 30 | "build:app": "parcel build index.html -d ../dist/ --public-url \".\" --no-cache", 31 | "build:script": "parcel build src/script.js --out-dir ../dist/ --no-cache", 32 | "watch:script": "parcel watch src/script.js --out-dir ../dist/ --no-hmr", 33 | "devserver": "parcel serve index.html -p 8001 --out-dir ../dist/ --no-cache", 34 | "start": "npm run sync-assets && npm run build:script -- --no-minify && npm run devserver", 35 | "sync-assets": "copy-aragon-ui-assets ../dist && copyfiles './public/**/*' ../dist" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /compound-protocol/test/contracts/BasicToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | 4 | import "./ERC20Basic.sol"; 5 | import "./SafeMath.sol"; 6 | 7 | 8 | /** 9 | * @title Basic token 10 | * @dev Basic version of StandardToken, with no allowances. 11 | */ 12 | contract BasicToken is ERC20Basic { 13 | using SafeMath for uint256; 14 | 15 | mapping(address => uint256) balances; 16 | 17 | uint256 totalSupply_; 18 | 19 | /** 20 | * @dev Total number of tokens in existence 21 | */ 22 | function totalSupply() public view returns (uint256) { 23 | return totalSupply_; 24 | } 25 | 26 | /** 27 | * @dev Transfer token for a specified address 28 | * @param _to The address to transfer to. 29 | * @param _value The amount to be transferred. 30 | */ 31 | function transfer(address _to, uint256 _value) public returns (bool) { 32 | require(_to != address(0)); 33 | require(_value <= balances[msg.sender]); 34 | 35 | balances[msg.sender] = balances[msg.sender].sub(_value); 36 | balances[_to] = balances[_to].add(_value); 37 | emit Transfer(msg.sender, _to, _value); 38 | return true; 39 | } 40 | 41 | /** 42 | * @dev Gets the balance of the specified address. 43 | * @param _owner The address to query the the balance of. 44 | * @return An uint256 representing the amount owned by the passed address. 45 | */ 46 | function balanceOf(address _owner) public view returns (uint256) { 47 | return balances[_owner]; 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /compound-protocol/test/contracts/FalseMarkerMethodInterestRateModel.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "../InterestRateModel.sol"; 4 | 5 | /** 6 | * @title Implements the interest rate model marker function but returns false 7 | * @author Compound 8 | */ 9 | contract FalseMarkerMethodInterestRateModel is InterestRateModel { 10 | /** 11 | * For exhaustive testing, this contract implements the marker function but returns false instead of the intended true 12 | */ 13 | bool public constant isInterestRateModel = false; 14 | 15 | uint borrowRate; 16 | 17 | constructor(uint borrowRate_) public { 18 | borrowRate = borrowRate_; 19 | } 20 | 21 | /** 22 | * @notice Gets the current borrow interest rate based on the given asset, total cash and total borrows 23 | * @dev The return value should be scaled by 1e18, thus a return value of 24 | * `(true, 1000000000000)` implies an interest rate of 0.000001% *per block*. 25 | * @param _cash The total cash of the asset in the CToken 26 | * @param _borrows The total borrows of the asset in the CToken 27 | * @param _reserves The total reserves of the asset in the CToken 28 | * @return Success or failure and the borrow interest rate per block scaled by 1e18 29 | */ 30 | function getBorrowRate(uint _cash, uint _borrows, uint _reserves) view public returns (uint, uint) { 31 | _cash; // unused 32 | _borrows; // unused 33 | _reserves; // unused 34 | return (0, borrowRate); 35 | } 36 | } -------------------------------------------------------------------------------- /compound-protocol/contracts/ComptrollerStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "./CToken.sol"; 4 | import "./PriceOracle.sol"; 5 | 6 | contract UnitrollerAdminStorage { 7 | /** 8 | * @notice Administrator for this contract 9 | */ 10 | address public admin; 11 | 12 | /** 13 | * @notice Pending administrator for this contract 14 | */ 15 | address public pendingAdmin; 16 | 17 | /** 18 | * @notice Active brains of Unitroller 19 | */ 20 | address public comptrollerImplementation; 21 | 22 | /** 23 | * @notice Pending brains of Unitroller 24 | */ 25 | address public pendingComptrollerImplementation; 26 | } 27 | 28 | contract ComptrollerV1Storage is UnitrollerAdminStorage { 29 | 30 | /** 31 | * @notice Oracle which gives the price of any given asset 32 | */ 33 | PriceOracle public oracle; 34 | 35 | /** 36 | * @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow 37 | */ 38 | uint public closeFactorMantissa; 39 | 40 | /** 41 | * @notice Multiplier representing the discount on collateral that a liquidator receives 42 | */ 43 | uint public liquidationIncentiveMantissa; 44 | 45 | /** 46 | * @notice Max number of assets a single account can participate in (borrow or use as collateral) 47 | */ 48 | uint public maxAssets; 49 | 50 | /** 51 | * @notice Per-account mapping of "assets you are in", capped by maxAssets 52 | */ 53 | mapping(address => CToken[]) public accountAssets; 54 | 55 | } 56 | -------------------------------------------------------------------------------- /compound-protocol/spec/scenario/admin/setMarketPolicyHook.scen.old: -------------------------------------------------------------------------------- 1 | 2 | -- Tests for MoneyMarket's `_setMarketPolicyHook(address,address)` function 3 | 4 | Macro ListAsset asset 5 | SupportMarket asset (FixedPrice 1.0) (FixedRate 0.5 0.75) SimplePolicyHook 6 | Assert Success 7 | 8 | Test "emits log on failure due to non-admin caller" 9 | AddToken Ether 10 | Invariant Static (GetMarketPolicyHook Ether) 11 | From Geoff (SetMarketPolicyHook Ether SimplePolicyHook) 12 | Assert Failure UNAUTHORIZED SET_POLICY_HOOK_OWNER_CHECK 13 | 14 | Test "emits log on failure due to not-listed market" 15 | AddToken Ether 16 | Invariant Static (GetMarketPolicyHook Ether) 17 | SetMarketPolicyHook Ether SimplePolicyHook 18 | Assert Failure MARKET_NOT_LISTED SET_POLICY_HOOK_MARKET_NOT_LISTED 19 | 20 | Test "emits log on failure due to being a bad policy hook" 21 | AddToken Ether 22 | ListAsset Ether 23 | Invariant Static (GetMarketPolicyHook Ether) 24 | SetMarketPolicyHook Ether FakePolicyHook 25 | Assert Failure BAD_INPUT SET_POLICY_HOOK_IS_NOT_POLICY_HOOK 26 | 27 | Test "reverts due to not being a policy hook" 28 | AddToken Ether 29 | ListAsset Ether 30 | Invariant Static (GetMarketPolicyHook Ether) 31 | SetMarketPolicyHook Ether NotPolicyHook 32 | Assert Revert 33 | 34 | Test "successfully sets policy hook" 35 | AddToken Ether 36 | ListAsset Ether 37 | SetMarketPolicyHook Ether SimplePolicyHook 38 | Assert Success 39 | Assert Equal (GetMarketPolicyHook Ether) LastContract 40 | Assert Log "PolicyHookChanged" \ 41 | ("asset" (Address Ether)) ("oldPolicyHook" Anything) ("newPolicyHook" (LastContract)) 42 | -------------------------------------------------------------------------------- /compound-protocol/contracts/InterestRateModel.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | /** 4 | * @title The Compound InterestRateModel Interface 5 | * @author Compound 6 | * @notice Any interest rate model should derive from this contract. 7 | * @dev These functions are specifically not marked `pure` as implementations of this 8 | * contract may read from storage variables. 9 | */ 10 | interface InterestRateModel { 11 | /** 12 | * @notice Gets the current borrow interest rate based on the given asset, total cash, total borrows 13 | * and total reserves. 14 | * @dev The return value should be scaled by 1e18, thus a return value of 15 | * `(true, 1000000000000)` implies an interest rate of 0.000001 or 0.0001% *per block*. 16 | * @param cash The total cash of the underlying asset in the CToken 17 | * @param borrows The total borrows of the underlying asset in the CToken 18 | * @param reserves The total reserves of the underlying asset in the CToken 19 | * @return Success or failure and the borrow interest rate per block scaled by 10e18 20 | */ 21 | function getBorrowRate(uint cash, uint borrows, uint reserves) external view returns (uint, uint); 22 | 23 | /** 24 | * @notice Marker function used for light validation when updating the interest rate model of a market 25 | * @dev Marker function used for light validation when updating the interest rate model of a market. Implementations should simply return true. 26 | * @return Success or failure 27 | */ 28 | function isInterestRateModel() external view returns (bool); 29 | } -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Encoding.ts: -------------------------------------------------------------------------------- 1 | import BigNumber from 'bignumber.js'; 2 | import { utils, ethers } from 'ethers'; 3 | 4 | const smallEnoughNumber = new BigNumber('100000000'); 5 | 6 | export type encodedNumber = number | utils.BigNumber; 7 | 8 | // Returns the mantissa of an Exp with given floating value 9 | export function getExpMantissa(float: number): encodedNumber { 10 | // Workaround from https://github.com/ethereum/web3.js/issues/1920 11 | const str = Math.floor(float * 1.0e18).toString(); 12 | 13 | return toEncodableNum(str); 14 | } 15 | 16 | export function toEncodableNum(amountArgRaw: string | encodedNumber): encodedNumber { 17 | let bigNumber; 18 | if (amountArgRaw instanceof BigNumber) { 19 | bigNumber = amountArgRaw; 20 | } else { 21 | bigNumber = new BigNumber(amountArgRaw.toString()); 22 | } 23 | 24 | if (bigNumber.lt(smallEnoughNumber)) { 25 | // The Ethers abi encoder can handle regular numbers (including with fractional part) 26 | // and its own internal big number class which is different from BigNumber.js published on npm (and can't accept 27 | // fractional parts.) 28 | // If the input is not huge, we just use a number, otherwise we try to use the Ethers class. 29 | 30 | return Number(amountArgRaw); 31 | } else { 32 | // bigNumberify (and the result class) only accept integers as digits, so we do .toFixed() to convert, for example, 1e4 to 10000. 33 | // Rather than doing toFixed(0) and silently truncating a fractional part, we'll let it through and get an error. 34 | // that case 35 | return utils.bigNumberify(bigNumber.toFixed()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /compound-protocol/test/contracts/SafeMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | 4 | /** 5 | * @title SafeMath 6 | * @dev Math operations with safety checks that throw on error 7 | */ 8 | library SafeMath { 9 | 10 | /** 11 | * @dev Multiplies two numbers, throws on overflow. 12 | */ 13 | function mul(uint256 a, uint256 b) internal pure returns (uint256 c) { 14 | // Gas optimization: this is cheaper than asserting 'a' not being zero, but the 15 | // benefit is lost if 'b' is also tested. 16 | // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 17 | if (a == 0) { 18 | return 0; 19 | } 20 | 21 | c = a * b; 22 | assert(c / a == b); 23 | return c; 24 | } 25 | 26 | /** 27 | * @dev Integer division of two numbers, truncating the quotient. 28 | */ 29 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 30 | // assert(b > 0); // Solidity automatically throws when dividing by 0 31 | // uint256 c = a / b; 32 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 33 | return a / b; 34 | } 35 | 36 | /** 37 | * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). 38 | */ 39 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 40 | assert(b <= a); 41 | return a - b; 42 | } 43 | 44 | /** 45 | * @dev Adds two numbers, throws on overflow. 46 | */ 47 | function add(uint256 a, uint256 b) internal pure returns (uint256 c) { 48 | c = a + b; 49 | assert(c >= a); 50 | return c; 51 | } 52 | } -------------------------------------------------------------------------------- /compound-protocol/test/contracts/BasicTokenNS.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | 4 | import "./ERC20BasicNS.sol"; 5 | import "./SafeMath.sol"; 6 | 7 | /** 8 | * @title Basic token (Non-Standard) 9 | * @dev Basic version of NonStandardToken, with no allowances. 10 | * @dev Version of ERC20 with no return values for `transfer` and `transferFrom` 11 | * See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca 12 | */ 13 | contract BasicTokenNS is ERC20BasicNS { 14 | using SafeMath for uint256; 15 | 16 | mapping(address => uint256) balances; 17 | 18 | uint256 totalSupply_; 19 | 20 | /** 21 | * @dev Total number of tokens in existence 22 | */ 23 | function totalSupply() public view returns (uint256) { 24 | return totalSupply_; 25 | } 26 | 27 | /** 28 | * @dev Transfer token for a specified address 29 | * @param _to The address to transfer to. 30 | * @param _value The amount to be transferred. 31 | */ 32 | function transfer(address _to, uint256 _value) public { 33 | require(_to != address(0)); 34 | require(_value <= balances[msg.sender]); 35 | 36 | balances[msg.sender] = balances[msg.sender].sub(_value); 37 | balances[_to] = balances[_to].add(_value); 38 | emit Transfer(msg.sender, _to, _value); 39 | } 40 | 41 | /** 42 | * @dev Gets the balance of the specified address. 43 | * @param _owner The address to query the the balance of. 44 | * @return An uint256 representing the amount owned by the passed address. 45 | */ 46 | function balanceOf(address _owner) public view returns (uint256) { 47 | return balances[_owner]; 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /compound-protocol/test/contracts/EvilToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "./StandardToken.sol"; 4 | 5 | /** 6 | * @title The Compound Evil Test Token 7 | * @author Compound 8 | * @notice A simple test token that fails certain operations 9 | */ 10 | contract EvilToken is StandardToken { 11 | string public name; 12 | string public symbol; 13 | uint8 public decimals; 14 | bool public fail; 15 | 16 | constructor(uint256 _initialAmount, string memory _tokenName, uint8 _decimalUnits, string memory _tokenSymbol) public { 17 | totalSupply_ = _initialAmount; 18 | balances[msg.sender] = _initialAmount; 19 | name = _tokenName; 20 | symbol = _tokenSymbol; 21 | decimals = _decimalUnits; 22 | fail = true; 23 | } 24 | 25 | function setFail(bool _fail) public { 26 | fail = _fail; 27 | } 28 | 29 | /** 30 | * @dev Arbitrarily adds tokens to any account 31 | */ 32 | function allocateTo(address _owner, uint256 value) public { 33 | balances[_owner] += value; 34 | totalSupply_ += value; 35 | emit Transfer(address(this), _owner, value); 36 | } 37 | 38 | /** 39 | * @dev Fail to transfer 40 | */ 41 | function transfer(address to, uint256 value) public returns (bool) { 42 | if (fail) { 43 | return false; 44 | } 45 | 46 | return super.transfer(to, value); 47 | } 48 | 49 | /** 50 | * @dev Fail to transfer from 51 | */ 52 | function transferFrom(address from, address to, uint256 value) public returns (bool) { 53 | if (fail) { 54 | return false; 55 | } 56 | 57 | return super.transferFrom(from, to, value); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /compound-protocol/test/contracts/ComptrollerScenario.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "../Comptroller.sol"; 4 | import "../PriceOracle.sol"; 5 | 6 | contract ComptrollerScenario is Comptroller { 7 | uint public blockNumber; 8 | 9 | constructor() Comptroller() public {} 10 | 11 | function membershipLength(CToken cToken) public view returns (uint) { 12 | return accountAssets[address(cToken)].length; 13 | } 14 | 15 | function fastForward(uint blocks) public returns (uint) { 16 | blockNumber += blocks; 17 | 18 | return blockNumber; 19 | } 20 | 21 | function setBlockNumber(uint number) public { 22 | blockNumber = number; 23 | } 24 | 25 | function _become(Unitroller unitroller, PriceOracle _oracle, uint _closeFactorMantissa, uint _maxAssets, bool reinitializing) public { 26 | super._become(unitroller, _oracle, _closeFactorMantissa, _maxAssets, reinitializing); 27 | 28 | if (!reinitializing) { 29 | ComptrollerScenario freshBrainedComptroller = ComptrollerScenario(address(unitroller)); 30 | 31 | freshBrainedComptroller.setBlockNumber(100000); 32 | } 33 | } 34 | 35 | function getHypotheticalAccountLiquidity( 36 | address account, 37 | address cTokenModify, 38 | uint redeemTokens, 39 | uint borrowAmount) public view returns (uint, uint, uint) { 40 | (Error err, uint liquidity, uint shortfall) = 41 | super.getHypotheticalAccountLiquidityInternal(account, CToken(cTokenModify), redeemTokens, borrowAmount); 42 | return (uint(err), liquidity, shortfall); 43 | } 44 | 45 | function unlist(CToken cToken) public { 46 | markets[address(cToken)].isListed = false; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /compound-protocol/test/contracts/AnchorPriceOracleHarness.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "../AnchorPriceOracle.sol"; 4 | 5 | contract AnchorPriceOracleHarness is AnchorPriceOracle { 6 | mapping (address => uint256) updateCount; 7 | 8 | constructor(address poster) public AnchorPriceOracle(poster) {} 9 | 10 | function numSetPriceCalls(address asset) public view returns (uint256) { 11 | return updateCount[asset]; 12 | } 13 | 14 | function harnessClearStoredPrice(address asset) public { 15 | _assetPrices[asset] = Exp({mantissa: 0}); 16 | } 17 | 18 | function setPriceStorageInternal(address asset, uint priceMantissa) internal { 19 | _assetPrices[asset] = Exp({mantissa: priceMantissa}); 20 | updateCount[asset] += 1; 21 | } 22 | 23 | function harnessCapToMax(uint anchorPriceMantissa, uint priceMantissa) view public returns (uint, bool, uint) { 24 | (MathError err, bool wasCapped, Exp memory newPrice) = capToMax(Exp({mantissa: anchorPriceMantissa}), Exp({mantissa: priceMantissa})); 25 | return (uint(err), wasCapped, newPrice.mantissa); 26 | } 27 | 28 | function harnessCalculateSwing(uint anchorPriceMantissa, uint priceMantissa) pure public returns (uint, uint) { 29 | (MathError err, Exp memory swing) = calculateSwing(Exp({mantissa: anchorPriceMantissa}), Exp({mantissa: priceMantissa})); 30 | return (uint(err), swing.mantissa); 31 | } 32 | 33 | function harnessSetMaxSwing(uint newMaxSwingMantissa) public { 34 | maxSwing = Exp({mantissa: newMaxSwingMantissa}); 35 | } 36 | 37 | function harnessSetAnchor(address asset, uint anchorPriceMantissa, uint anchorPeriod) public { 38 | anchors[asset] = Anchor({period: anchorPeriod, priceMantissa: anchorPriceMantissa}); 39 | } 40 | } -------------------------------------------------------------------------------- /compound-aragon-app/app/src/components/supply/side-panel-input/SupplyInput.js: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react' 2 | import {Info, Field, TextInput, Button} from '@aragon/ui' 3 | import styled from "styled-components"; 4 | 5 | // TODO: Add slider for specifying amount 6 | const SupplyInput = ({handleSupply}) => { 7 | 8 | const [amount, setAmount] = useState("") 9 | 10 | const handleSubmit = (event) => { 11 | event.preventDefault() 12 | handleSupply(amount) 13 | } 14 | 15 | return ( 16 |
17 | 18 | 19 | 20 | setAmount(event.target.value)}/> 26 | 27 | 28 | 30 | Supply Tokens 31 | 32 | 33 | 34 | This action will supply the specified amount of Tokens to the Compound App. They will earn interest over time. 35 | 36 | 37 |
38 | ) 39 | } 40 | 41 | const DepositContainer = styled.div` 42 | display:flex; 43 | flex-direction: column; 44 | padding-top: 1px; 45 | ` 46 | const FieldStyled = styled(Field)` 47 | margin-bottom: 20px; 48 | ` 49 | const ButtonStyled = styled(Button)` 50 | margin-top: 10px; 51 | margin-bottom: 30px; 52 | ` 53 | 54 | export default SupplyInput -------------------------------------------------------------------------------- /compound-protocol/spec/scenario/Borrow.scen: -------------------------------------------------------------------------------- 1 | 2 | -- Waiting on Comptroller actually checking market entered 3 | Test "Borrow some BAT fails when BAT not entered" 4 | NewComptroller price:1.0 5 | NewCToken ZRX cZRX 6 | NewCToken BAT cBAT 7 | Support cZRX collateralFactor:0.5 8 | Support cBAT collateralFactor:0.5 9 | Prep Geoff Some ZRX cZRX 10 | Mint Geoff 100e18 cZRX 11 | EnterMarkets Geoff cZRX 12 | Invariant Static (CToken cZRX ExchangeRateStored) 13 | AllowFailures 14 | Borrow Geoff 1e18 cBAT 15 | Assert Failure COMPTROLLER_REJECTION BORROW_COMPTROLLER_REJECTION MARKET_NOT_ENTERED 16 | 17 | Test "Borrow some BAT fails when no BAT available" 18 | NewComptroller price:1.0 19 | NewCToken ZRX cZRX 20 | NewCToken BAT cBAT 21 | Support cZRX collateralFactor:0.5 22 | Support cBAT collateralFactor:0.5 23 | Prep Geoff Some ZRX cZRX 24 | Mint Geoff 100e18 cZRX 25 | EnterMarkets Geoff cZRX cBAT 26 | Invariant Static (CToken cZRX ExchangeRateStored) 27 | AllowFailures 28 | Borrow Geoff 1e18 cBAT 29 | Assert Failure TOKEN_INSUFFICIENT_CASH BORROW_CASH_NOT_AVAILABLE 30 | 31 | Test "Borrow some BAT from Excess Cash" 32 | Invariant Success 33 | NewComptroller price:1.0 34 | NewCToken ZRX cZRX 35 | NewCToken BAT cBAT 36 | Give cBAT 10e18 BAT -- Faucet some bat to borrow 37 | Support cZRX collateralFactor:0.5 38 | Support cBAT collateralFactor:0.5 39 | Prep Geoff Some ZRX cZRX 40 | Mint Geoff 100e18 cZRX 41 | EnterMarkets Geoff cZRX cBAT 42 | Borrow Geoff 1e18 cBAT 43 | EnterMarkets Geoff cZRX cBAT 44 | Assert Equal (cToken cBAT BorrowBalance Geoff) (Exactly 1e18) 45 | Assert Equal (Erc20 BAT TokenBalance Geoff) (Exactly 1e18) 46 | Assert Equal (Erc20 BAT TokenBalance cBAT) (Exactly 9e18) 47 | -------------------------------------------------------------------------------- /compound-protocol/spec/scenario/Seize.scen: -------------------------------------------------------------------------------- 1 | 2 | Test "Fail to seize calling directly" 3 | NewComptroller 4 | ListedCToken ZRX cZRX initialExchangeRate:1e9 5 | ListedCToken BAT cBAT initialExchangeRate:1e9 6 | Prep Geoff Some ZRX cZRX 7 | Mint Geoff 50e18 cZRX 8 | Invariant Remains (Erc20 cZRX TokenBalance Geoff) 50e9 9 | AllowFailures 10 | Seize 1e9 cZRX caller:Geoff liquidator:Geoff borrower:Torrey 11 | -- The caller must be from another cToken market, thus this fails 12 | Assert Failure COMPTROLLER_REJECTION LIQUIDATE_SEIZE_COMPTROLLER_REJECTION MARKET_NOT_LISTED 13 | 14 | Test "Not able to seize tokens with a malicious unlisted cToken" 15 | NewComptroller 16 | ListedCToken ZRX cZRX initialExchangeRate:1e9 17 | NewCToken EVL cEVL initialExchangeRate:1e9 cTokenType:CEvil 18 | Prep Geoff Some ZRX cZRX 19 | Mint Geoff 50e18 cZRX 20 | Invariant Remains (Erc20 cZRX TokenBalance Geoff) 50e9 21 | Invariant Static (Erc20 cZRX TokenBalance Geoff) 22 | Invariant Static (Erc20 cZRX TokenBalance Torrey) 23 | AllowFailures 24 | EvilSeize cEVL 1e9 cZRX seizer:Geoff seizee:Torrey 25 | -- The caller must be from another cToken market, thus this fails 26 | Assert Failure COMPTROLLER_REJECTION LIQUIDATE_SEIZE_COMPTROLLER_REJECTION MARKET_NOT_LISTED 27 | 28 | Test "Able to seize tokens with a malicious listed cToken" 29 | NewComptroller 30 | ListedCToken ZRX cZRX initialExchangeRate:1e9 31 | ListedCToken EVL cEVL initialExchangeRate:1e9 cTokenType:CEvil 32 | Prep Geoff Some ZRX cZRX 33 | Mint Geoff 50e18 cZRX 34 | Assert Equal (Erc20 cZRX TokenBalance Geoff) 50e9 35 | Expect Changes (Erc20 cZRX TokenBalance Geoff) -1e9 36 | Expect Changes (Erc20 cZRX TokenBalance Torrey) +1e9 37 | EvilSeize cEVL 1e9 cZRX seizer:Torrey seizee:Geoff 38 | -------------------------------------------------------------------------------- /compound-aragon-app/app/src/components/balances/side-panel-input/TransferPanel.js: -------------------------------------------------------------------------------- 1 | import React, {useMemo, useState} from 'react' 2 | import styled from 'styled-components' 3 | import {Tabs} from '@aragon/ui' 4 | 5 | import Deposit from './Deposit' 6 | import Withdraw from './Withdraw' 7 | 8 | const TransferPanel = ({handleDeposit, handleWithdraw, opened, transferPanelsState}) => { 9 | 10 | const [screenIndex, setScreenIndex] = useState(0) 11 | 12 | const {tokens, balances, checkConnectedAccountBalance} = transferPanelsState 13 | 14 | const oneOrMoreTokensWithBalance = tokens.filter(token => token.amount > 0).length > 0 15 | 16 | useMemo(() => { 17 | if (!opened) { 18 | setScreenIndex(0) 19 | } 20 | }, [opened]) 21 | 22 | return ( 23 |
24 | 25 | oneOrMoreTokensWithBalance ? setScreenIndex(id) : setScreenIndex(0)} 29 | /> 30 | 31 | 32 | {screenIndex === 0 && ( 33 | 38 | )} 39 | {screenIndex === 1 && ( 40 | 45 | )} 46 |
47 | ) 48 | } 49 | 50 | const TabBarWrapper = styled.div` 51 | margin: 0 -30px 30px; 52 | ` 53 | 54 | export default TransferPanel 55 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Expectation/ChangesExpectation.ts: -------------------------------------------------------------------------------- 1 | import {Expectation} from '../Expectation'; 2 | import {fail, World} from '../World'; 3 | import {getCoreValue} from '../CoreValue'; 4 | import {Value, NumberV} from '../Value'; 5 | import {Event} from '../Event'; 6 | import {formatEvent} from '../Formatter'; 7 | import {BigNumber} from 'bignumber.js'; 8 | 9 | function asNumberV(v: Value): NumberV { 10 | if (v instanceof NumberV) { 11 | return v; 12 | } else { 13 | throw new Error(`Expected NumberV for ChangesExpectation, got ${v.toString()}`); 14 | } 15 | } 16 | 17 | export class ChangesExpectation implements Expectation { 18 | condition: Event; 19 | originalValue: NumberV; 20 | delta: NumberV; 21 | expected: NumberV; 22 | 23 | constructor(condition: Event, originalValue: Value, delta: Value) { 24 | this.condition = condition; 25 | this.originalValue = asNumberV(originalValue); 26 | this.delta = asNumberV(delta); 27 | this.expected = this.originalValue.add(this.delta); 28 | } 29 | 30 | async getCurrentValue(world: World): Promise { 31 | return await getCoreValue(world, this.condition); 32 | }; 33 | 34 | async checker(world: World, initialCheck: boolean=false): Promise { 35 | const currentValue = asNumberV(await this.getCurrentValue(world)); 36 | 37 | if (!currentValue.compareTo(world, this.expected)) { 38 | const trueDelta = currentValue.sub(this.originalValue); 39 | 40 | fail(world, `${this.toString()} instead had value \`${currentValue.toString()}\` (true delta: ${trueDelta.toString()})`); 41 | } 42 | } 43 | 44 | toString() { 45 | return `ChangesExpectation: condition=${formatEvent(this.condition)}, originalValue=${this.originalValue.toString()}, delta=${this.delta.toString()}, expected=${this.expected.toString()}`; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /compound-aragon-app/app/src/hooks/side-panels.js: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react' 2 | import {useCallback} from 'react' 3 | 4 | export function useSidePanel() { 5 | 6 | const defaultSidePanel = {id: "", title: ""} 7 | 8 | const [currentSidePanel, setCurrentSidePanel] = useState(defaultSidePanel) 9 | const [visible, setVisible] = useState(false) 10 | const [opened, setOpened] = useState(false) 11 | 12 | const sidePanels = { 13 | DEFAULT: defaultSidePanel, 14 | SUPPLY: { 15 | id: 'SUPPLY', 16 | title: 'Supply Tokens' 17 | }, 18 | TRANSFER: { 19 | id: 'TRANSFER', 20 | title: 'New Agent Transfer' 21 | }, 22 | CHANGE_AGENT: { 23 | id: 'CHANGE_AGENT', 24 | title: 'Change the Agent' 25 | } 26 | } 27 | 28 | const requestOpen = useCallback((sidePanel) => { 29 | setCurrentSidePanel(sidePanel) 30 | setVisible(true) 31 | }, [setVisible, currentSidePanel]) 32 | 33 | const endTransition = useCallback( 34 | opened => { 35 | if (opened) { 36 | setOpened(true) 37 | } else { 38 | setOpened(false) 39 | setCurrentSidePanel(sidePanels.DEFAULT) 40 | } 41 | }, 42 | [setOpened, currentSidePanel] 43 | ) 44 | 45 | const requestClose = useCallback(() => { 46 | setVisible(false) 47 | }, [setVisible]) 48 | 49 | const openPanelActions = { 50 | supply: () => requestOpen(sidePanels.SUPPLY), 51 | transfer: () => requestOpen(sidePanels.TRANSFER), 52 | changeAgent: () => requestOpen(sidePanels.CHANGE_AGENT) 53 | } 54 | 55 | return { currentSidePanel, opened, visible, openPanelActions, requestOpen, endTransition, requestClose } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /compound-aragon-app/app/src/components/balances/side-panel-input/token-selector/TokenSelectorInstance.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import styled from "styled-components" 3 | import { GU } from "@aragon/ui" 4 | import { useNetwork } from "@aragon/api-react" 5 | import { ETHER_TOKEN_FAKE_ADDRESS } from "../../../../lib/shared-constants" 6 | import { addressesEqual } from "../../../../lib/web3-utils" 7 | import AddressBadge from "./AddressBadge" 8 | import { iconSourceUrl } from "../../../../lib/token-utils" 9 | 10 | const TokenSelectorInstance = ({ address, name, symbol, showIcon = true }) => { 11 | const network = useNetwork() 12 | 13 | // const iconSource = iconSourceUrl(network, address, symbol) 14 | const iconSource = `https://chasing-coins.com/coin/logo/${symbol}` 15 | 16 | return ( 17 |
18 | {showIcon ? ( 19 | 20 | ) : ( 21 | 22 | )} 23 | {symbol && {symbol}} 24 | {name && ({name})} 25 | {!addressesEqual(address, ETHER_TOKEN_FAKE_ADDRESS) && ( 26 | 27 | )} 28 |
29 | ) 30 | } 31 | 32 | const Main = styled.div` 33 | display: flex; 34 | align-items: center; 35 | ` 36 | 37 | const Icon = styled.img.attrs({ alt: "", width: "16", height: "16" })` 38 | margin-right: ${1 * GU}px; 39 | ` 40 | 41 | const IconSpacer = styled.div` 42 | width: ${3 * GU}px; 43 | ` 44 | 45 | const TokenName = styled.span` 46 | max-width: 110px; 47 | margin-right: ${1 * GU}px; 48 | overflow: hidden; 49 | text-overflow: ellipsis; 50 | ` 51 | 52 | const TokenSymbol = styled.span` 53 | margin-right: ${1 * GU}px; 54 | ` 55 | 56 | export default TokenSelectorInstance 57 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Value/PriceOracleValue.ts: -------------------------------------------------------------------------------- 1 | import {Event} from '../Event'; 2 | import {World} from '../World'; 3 | import {PriceOracle} from '../Contract/PriceOracle'; 4 | import { 5 | getAddressV 6 | } from '../CoreValue'; 7 | import { 8 | AddressV, 9 | NumberV, 10 | Value} from '../Value'; 11 | import {Arg, Fetcher, getFetcherValue} from '../Command'; 12 | import {getPriceOracle} from '../ContractLookup'; 13 | 14 | async function getPrice(world: World, priceOracle: PriceOracle, asset: string): Promise { 15 | return new NumberV(await priceOracle.methods.assetPrices(asset).call()); 16 | } 17 | 18 | export async function getPriceOracleAddress(world: World, priceOracle: PriceOracle): Promise { 19 | return new AddressV(priceOracle._address); 20 | } 21 | 22 | export function priceOracleFetchers() { 23 | return [ 24 | new Fetcher<{priceOracle: PriceOracle}, AddressV>(` 25 | #### Address 26 | 27 | * "Address" - Gets the address of the global price oracle 28 | `, 29 | "Address", 30 | [ 31 | new Arg("priceOracle", getPriceOracle, {implicit: true}) 32 | ], 33 | (world, {priceOracle}) => getPriceOracleAddress(world, priceOracle) 34 | ), 35 | new Fetcher<{priceOracle: PriceOracle, asset: AddressV}, NumberV>(` 36 | #### Price 37 | 38 | * "Price asset:
" - Gets the price of the given asset 39 | `, 40 | "Price", 41 | [ 42 | new Arg("priceOracle", getPriceOracle, {implicit: true}), 43 | new Arg("asset", getAddressV,) 44 | ], 45 | (world, {priceOracle, asset}) => getPrice(world, priceOracle, asset.val) 46 | ) 47 | ]; 48 | } 49 | 50 | export async function getPriceOracleValue(world: World, event: Event): Promise { 51 | return await getFetcherValue("PriceOracle", priceOracleFetchers(), world, event); 52 | } 53 | -------------------------------------------------------------------------------- /compound-protocol/contracts/Maximillion.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "./CEther.sol"; 4 | 5 | /** 6 | * @title Compound's Maximillion Contract 7 | * @author Compound 8 | */ 9 | contract Maximillion { 10 | /** 11 | * @notice The default cEther market to repay in 12 | */ 13 | CEther public cEther; 14 | 15 | /** 16 | * @notice Construct a Maximillion to repay max in a CEther market 17 | */ 18 | constructor(CEther cEther_) public { 19 | cEther = cEther_; 20 | } 21 | 22 | /** 23 | * @notice msg.sender sends Ether to repay an account's borrow in the cEther market 24 | * @dev The provided Ether is applied towards the borrow balance, any excess is refunded 25 | * @param borrower The address of the borrower account to repay on behalf of 26 | * @return The initial borrows before the repay 27 | */ 28 | function repayBehalf(address borrower) public payable { 29 | return repayBehalfExplicit(borrower, cEther); 30 | } 31 | 32 | /** 33 | * @notice msg.sender sends Ether to repay an account's borrow in a cEther market 34 | * @dev The provided Ether is applied towards the borrow balance, any excess is refunded 35 | * @param borrower The address of the borrower account to repay on behalf of 36 | * @param cEther_ The address of the cEther contract to repay in 37 | * @return The initial borrows before the repay 38 | */ 39 | function repayBehalfExplicit(address borrower, CEther cEther_) public payable { 40 | uint received = msg.value; 41 | uint borrows = cEther_.borrowBalanceCurrent(borrower); 42 | if (received > borrows) { 43 | cEther_.repayBorrowBehalf.value(borrows)(borrower); 44 | msg.sender.transfer(received - borrows); 45 | } else { 46 | cEther_.repayBorrowBehalf.value(received)(borrower); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Builder/UnitrollerBuilder.ts: -------------------------------------------------------------------------------- 1 | import {Event} from '../Event'; 2 | import {addAction, World} from '../World'; 3 | import {Unitroller} from '../Contract/Unitroller'; 4 | import {Invokation} from '../Invokation'; 5 | import {Arg, Fetcher, getFetcherValue} from '../Command'; 6 | import {storeAndSaveContract} from '../Networks'; 7 | import {getContract} from '../Contract'; 8 | 9 | const UnitrollerContract = getContract("Unitroller"); 10 | 11 | export interface UnitrollerData { 12 | invokation: Invokation, 13 | description: string, 14 | address?: string 15 | } 16 | 17 | export async function buildUnitroller(world: World, from: string, event: Event): Promise<{world: World, unitroller: Unitroller, unitrollerData: UnitrollerData}> { 18 | const fetchers = [ 19 | new Fetcher<{}, UnitrollerData>(` 20 | #### Unitroller 21 | 22 | * "" - The Upgradable Comptroller 23 | * E.g. "Unitroller Deploy" 24 | `, 25 | "Unitroller", 26 | [], 27 | async (world, {}) => { 28 | return { 29 | invokation: await UnitrollerContract.deploy(world, from, []), 30 | description: "Unitroller" 31 | }; 32 | }, 33 | {catchall: true} 34 | ) 35 | ]; 36 | 37 | let unitrollerData = await getFetcherValue("DeployUnitroller", fetchers, world, event); 38 | let invokation = unitrollerData.invokation; 39 | delete unitrollerData.invokation; 40 | 41 | if (invokation.error) { 42 | throw invokation.error; 43 | } 44 | const unitroller = invokation.value!; 45 | unitrollerData.address = unitroller._address; 46 | 47 | world = await storeAndSaveContract( 48 | world, 49 | unitroller, 50 | 'Unitroller', 51 | invokation, 52 | [ 53 | { index: ['Unitroller'], data: unitrollerData } 54 | ] 55 | ); 56 | 57 | return {world, unitroller, unitrollerData}; 58 | } 59 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Contract/Comptroller.ts: -------------------------------------------------------------------------------- 1 | import {Contract} from '../Contract'; 2 | import {Callable, Sendable} from '../Invokation'; 3 | import {encodedNumber} from '../Encoding'; 4 | 5 | interface ComptrollerMethods { 6 | getAccountLiquidity(string): Callable<{0: number, 1: number, 2: number}> 7 | getHypotheticalAccountLiquidity(account: string, asset: string, redeemTokens: encodedNumber, borrowAmount: encodedNumber): Callable<{0: number, 1: number, 2: number}> 8 | membershipLength(string): Callable 9 | checkMembership(user: string, cToken: string): Callable 10 | getAssetsIn(string): Callable 11 | admin(): Callable 12 | oracle(): Callable 13 | maxAssets(): Callable 14 | liquidationIncentiveMantissa(): Callable 15 | closeFactorMantissa(): Callable 16 | blockNumber(): Callable 17 | collateralFactor(string): Callable 18 | markets(string): Callable<{0: boolean, 1: number}> 19 | _setMintPaused(bool): Sendable 20 | _setMaxAssets(encodedNumber): Sendable 21 | _setLiquidationIncentive(encodedNumber): Sendable 22 | _supportMarket(string): Sendable 23 | _setPriceOracle(string): Sendable 24 | _setCollateralFactor(string, encodedNumber): Sendable 25 | _setCloseFactor(encodedNumber): Sendable 26 | enterMarkets(markets: string[]): Sendable 27 | exitMarket(market: string): Sendable 28 | fastForward(encodedNumber): Sendable 29 | _setPendingImplementation(string): Sendable 30 | comptrollerImplementation(): Callable 31 | unlist(string): Sendable 32 | admin(): Callable 33 | pendingAdmin(): Callable 34 | _setPendingAdmin(string): Sendable 35 | _acceptAdmin(): Sendable 36 | } 37 | 38 | export interface Comptroller extends Contract { 39 | methods: ComptrollerMethods 40 | } 41 | -------------------------------------------------------------------------------- /compound-protocol/spec/formal/contracts/UnderlyingModelNonStandard.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | import "../../../contracts/EIP20NonStandardInterface.sol"; 4 | 5 | import "./SimulationInterface.sol"; 6 | 7 | contract UnderlyingModelNonStandard is EIP20NonStandardInterface, SimulationInterface { 8 | uint256 _totalSupply; 9 | mapping (address => uint256) balances; 10 | mapping (address => mapping (address => uint256)) allowances; 11 | 12 | function totalSupply() external view returns (uint256) { 13 | return _totalSupply; 14 | } 15 | 16 | /** 17 | * @notice Gets the balance of the specified address 18 | * @param owner The address from which the balance will be retrieved 19 | * @return The balance 20 | */ 21 | function balanceOf(address owner) external view returns (uint256 balance) { 22 | balance = balances[owner]; 23 | } 24 | 25 | function transfer(address dst, uint256 amount) external { 26 | address src = msg.sender; 27 | require (balances[src]>=amount); 28 | require (balances[dst]+amount>=balances[dst]); 29 | 30 | balances[src] -= amount; 31 | balances[dst] += amount; 32 | } 33 | 34 | function transferFrom(address src, address dst, uint256 amount) external { 35 | require (allowances[src][msg.sender] >= amount); 36 | require (balances[src]>=amount); 37 | require (balances[dst]+amount>=balances[dst]); 38 | 39 | allowances[src][msg.sender] -= amount; 40 | balances[src] -= amount; 41 | balances[dst] += amount; 42 | } 43 | 44 | function approve(address spender, uint256 amount) external returns (bool success) { 45 | allowances[msg.sender][spender] = amount; 46 | } 47 | 48 | function allowance(address owner, address spender) external view returns (uint256 remaining) { 49 | remaining = allowances[owner][spender]; 50 | } 51 | 52 | function dummy() external { 53 | return; 54 | } 55 | } -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Value/PriceOracleProxyValue.ts: -------------------------------------------------------------------------------- 1 | import {Event} from '../Event'; 2 | import {World} from '../World'; 3 | import {PriceOracleProxy} from '../Contract/PriceOracleProxy'; 4 | import { 5 | getAddressV 6 | } from '../CoreValue'; 7 | import { 8 | AddressV, 9 | NumberV, 10 | Value} from '../Value'; 11 | import {Arg, Fetcher, getFetcherValue} from '../Command'; 12 | import {getPriceOracleProxy} from '../ContractLookup'; 13 | 14 | export async function getPriceOracleProxyAddress(world: World, priceOracleProxy: PriceOracleProxy): Promise { 15 | return new AddressV(priceOracleProxy._address); 16 | } 17 | 18 | async function getPrice(world: World, priceOracleProxy: PriceOracleProxy, asset: string): Promise { 19 | return new NumberV(await priceOracleProxy.methods.getUnderlyingPrice(asset).call()); 20 | } 21 | 22 | export function priceOracleProxyFetchers() { 23 | return [ 24 | new Fetcher<{priceOracleProxy: PriceOracleProxy}, AddressV>(` 25 | #### Address 26 | 27 | * "Address" - Gets the address of the global price oracle 28 | `, 29 | "Address", 30 | [ 31 | new Arg("priceOracleProxy", getPriceOracleProxy, {implicit: true}) 32 | ], 33 | (world, {priceOracleProxy}) => getPriceOracleProxyAddress(world, priceOracleProxy) 34 | ), 35 | new Fetcher<{priceOracle: PriceOracleProxy, asset: AddressV}, NumberV>(` 36 | #### Price 37 | 38 | * "Price asset:
" - Gets the price of the given asset 39 | `, 40 | "Price", 41 | [ 42 | new Arg("priceOracle", getPriceOracleProxy, {implicit: true}), 43 | new Arg("asset", getAddressV) 44 | ], 45 | (world, {priceOracle, asset}) => getPrice(world, priceOracle, asset.val) 46 | ) 47 | ]; 48 | } 49 | 50 | export async function getPriceOracleProxyValue(world: World, event: Event): Promise { 51 | return await getFetcherValue("PriceOracle", priceOracleProxyFetchers(), world, event); 52 | } 53 | -------------------------------------------------------------------------------- /compound-protocol/test/Errors.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /* 4 | * This module loads Error and FailureInfo enum from ErrorReporter.sol. 5 | */ 6 | 7 | const path = require('path'); 8 | const solparse = require('solparse'); 9 | 10 | const errorReporterPath = path.join(__dirname, '..', 'contracts', 'ErrorReporter.sol'); 11 | const contents = solparse.parseFile(errorReporterPath); 12 | const [ 13 | ComptrollerErrorReporter, 14 | TokenErrorReporter, 15 | OracleErrorReporter 16 | ] = contents.body.filter(k => k.type === 'ContractStatement'); 17 | 18 | function invert(object) { 19 | return Object.entries(object).reduce((obj, [key, value]) => ({ ...obj, [value]: key }), {}); 20 | } 21 | 22 | function parse(reporter) { 23 | const ErrorInv = reporter.body.find(k => k.name == 'Error').members; 24 | const FailureInfoInv = reporter.body.find(k => k.name == 'FailureInfo').members; 25 | const Error = invert(ErrorInv); 26 | const FailureInfo = invert(FailureInfoInv); 27 | return {Error, FailureInfo, ErrorInv, FailureInfoInv}; 28 | } 29 | 30 | const carefulMathPath = path.join(__dirname, '..', 'contracts', 'CarefulMath.sol'); 31 | const CarefulMath = solparse.parseFile(carefulMathPath).body.find(k => k.type === 'ContractStatement'); 32 | const MathErrorInv = CarefulMath.body.find(k => k.name == 'MathError').members; 33 | const MathError = invert(MathErrorInv); 34 | 35 | const whitePaperModelPath = path.join(__dirname, '..', 'contracts', 'WhitePaperInterestRateModel.sol'); 36 | const whitePaperModel = solparse.parseFile(whitePaperModelPath).body.find(k => k.type === 'ContractStatement'); 37 | const IRErrorInv = whitePaperModel.body.find(k => k.name == 'IRError').members; 38 | const IRError = invert(IRErrorInv); 39 | 40 | module.exports = { 41 | ComptrollerErr: parse(ComptrollerErrorReporter), 42 | OracleErr: parse(OracleErrorReporter), 43 | TokenErr: parse(TokenErrorReporter), 44 | IRErr: { 45 | Error: IRError, 46 | ErrorInv: IRErrorInv 47 | }, 48 | MathErr: { 49 | Error: MathError, 50 | ErrorInv: MathErrorInv 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Builder/PriceOracleProxyBuilder.ts: -------------------------------------------------------------------------------- 1 | import {Event} from '../Event'; 2 | import {addAction, World} from '../World'; 3 | import {PriceOracleProxy} from '../Contract/PriceOracleProxy'; 4 | import {Invokation} from '../Invokation'; 5 | import {Arg, Fetcher, getFetcherValue} from '../Command'; 6 | import {storeAndSaveContract} from '../Networks'; 7 | import {getContract} from '../Contract'; 8 | import {getAddressV} from '../CoreValue'; 9 | import {AddressV} from '../Value'; 10 | 11 | const PriceOracleProxyContract = getContract("PriceOracleProxy"); 12 | 13 | export async function buildPriceOracleProxy(world: World, from: string, event: Event): Promise<{world: World, priceOracleProxy: PriceOracleProxy, invokation: Invokation}> { 14 | const fetchers = [ 15 | new Fetcher<{comptroller: AddressV, priceOracle: AddressV, cEther: AddressV}, Invokation>(` 16 | #### Price Oracle Proxy 17 | 18 | * "" - The Price Oracle which proxies to a backing oracle 19 | * E.g. "PriceOracleProxy Deploy (Unitroller Address) (PriceOracle Address (CToken cETH Address))" 20 | `, 21 | "PriceOracleProxy", 22 | [ 23 | new Arg("comptroller", getAddressV), 24 | new Arg("priceOracle", getAddressV), 25 | new Arg("cEther", getAddressV) 26 | ], 27 | (world, {comptroller, priceOracle, cEther}) => { 28 | return PriceOracleProxyContract.deploy(world, from, [comptroller.val, priceOracle.val, cEther.val]); 29 | }, 30 | {catchall: true} 31 | ) 32 | ]; 33 | 34 | let invokation = await getFetcherValue>("DeployPriceOracleProxy", fetchers, world, event); 35 | 36 | if (invokation.error) { 37 | throw invokation.error; 38 | } 39 | const priceOracleProxy = invokation.value!; 40 | 41 | world = await storeAndSaveContract( 42 | world, 43 | priceOracleProxy, 44 | 'PriceOracleProxy', 45 | invokation, 46 | [] 47 | ); 48 | 49 | return {world, priceOracleProxy, invokation}; 50 | } 51 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Event/TrxEvent.ts: -------------------------------------------------------------------------------- 1 | import {World} from '../World'; 2 | import {Event} from '../Event'; 3 | import {processCoreEvent} from '../CoreEvent'; 4 | import { 5 | EventV, 6 | NumberV 7 | } from '../Value'; 8 | import { 9 | getEventV, 10 | getNumberV 11 | } from '../CoreValue'; 12 | import {Arg, Command, processCommandEvent} from '../Command'; 13 | import {encodedNumber} from '../Encoding'; 14 | 15 | async function setTrxValue(world: World, value: encodedNumber): Promise { 16 | return world.update('trxInvokationOpts', (t) => t.set('value', value)); 17 | } 18 | 19 | async function setTrxGasPrice(world: World, gasPrice: encodedNumber): Promise { 20 | return world.update('trxInvokationOpts', (t) => t.set('gasPrice', gasPrice.toString())); 21 | } 22 | 23 | export function trxCommands() { 24 | return [ 25 | new Command<{amount: NumberV, event: EventV}>(` 26 | #### Value 27 | 28 | * "Value " - Runs event with a set amount for any transactions 29 | * E.g. "Value 1.0e18 (CToken cEth Mint 1.0e18)" 30 | `, 31 | "Value", 32 | [ 33 | new Arg("amount", getNumberV), 34 | new Arg("event", getEventV) 35 | ], 36 | async (world, from, {amount, event}) => processCoreEvent(await setTrxValue(world, amount.encode()), event.val, from) 37 | ), 38 | new Command<{gasPrice: NumberV, event: EventV}>(` 39 | #### GasPrice 40 | 41 | * "GasPrice " - Runs event with a given gas price 42 | * E.g. "GasPrice 0 (CToken cEth Mint 1.0e18)" 43 | `, 44 | "GasPrice", 45 | [ 46 | new Arg("gasPrice", getNumberV), 47 | new Arg("event", getEventV) 48 | ], 49 | async (world, from, {gasPrice, event}) => processCoreEvent(await setTrxGasPrice(world, gasPrice.encode()), event.val, from) 50 | ) 51 | ]; 52 | } 53 | 54 | export async function processTrxEvent(world: World, event: Event, from: string | null): Promise { 55 | return await processCommandEvent("Trx", trxCommands(), world, event, from); 56 | } 57 | -------------------------------------------------------------------------------- /compound-aragon-app/app/src/web3/ExternalContracts.js: -------------------------------------------------------------------------------- 1 | import AgentAbi from '../abi/agent-abi' 2 | import ProxyDepositEvent from '../abi/proxy-deposit-event' 3 | import ERC20Abi from '../abi/erc20-abi' 4 | import ERC20DaiAbi from '../abi/erc20-dai-abi' 5 | import CERC20Abi from '../abi/cerc20-abi' 6 | import {of} from 'rxjs' 7 | import {concatMap, map, mergeMap, toArray} from 'rxjs/operators' 8 | import { convertToDaiErc20Abi, makeAbiFunctionConstant } from "../lib/abi-utils" 9 | import { ETHER_TOKEN_VERIFIED_BY_SYMBOL } from "../lib/verified-tokens" 10 | 11 | // Currently the AragonApi provides no option to "call" non-constant functions on external contracts so we modify 12 | // the CERC20Abi so we can "call" the non-constant balanceOfUnderlying function. 13 | const modifiedCERC20Abi = makeAbiFunctionConstant('balanceOfUnderlying', CERC20Abi) 14 | 15 | const agentAddress$ = api => api.call('agent') 16 | 17 | const compoundTokenAddresses$ = api => api.call('getEnabledCErc20s') 18 | 19 | const agentApp$ = (api) => { 20 | const agentProxyDepositAbi = AgentAbi.concat([ProxyDepositEvent]) 21 | return agentAddress$(api).pipe( 22 | map(agentAddress => api.external(agentAddress, agentProxyDepositAbi))) 23 | } 24 | 25 | const tokenContract$ = (api, tokenAddress) => { 26 | if (tokenAddress.toLowerCase() === ETHER_TOKEN_VERIFIED_BY_SYMBOL.get("DAI")) { 27 | return of(api.external(tokenAddress, ERC20DaiAbi)) 28 | } else { 29 | return of(api.external(tokenAddress, ERC20Abi)) 30 | } 31 | } 32 | 33 | const compoundToken$ = (api, compoundTokenAddress) => of(api.external(compoundTokenAddress, modifiedCERC20Abi)) 34 | 35 | const allCompoundTokens$ = (api) => 36 | compoundTokenAddresses$(api).pipe( 37 | concatMap(address => address), 38 | mergeMap(compoundTokenAddress => compoundToken$(api, compoundTokenAddress)), 39 | map(compoundToken => ({contract: compoundToken})), 40 | toArray()) 41 | 42 | export { 43 | agentAddress$, 44 | compoundTokenAddresses$, 45 | agentApp$, 46 | tokenContract$, 47 | compoundToken$, 48 | allCompoundTokens$ 49 | } -------------------------------------------------------------------------------- /compound-protocol/spec/scenario/BorrowEth.scen: -------------------------------------------------------------------------------- 1 | 2 | Test "Borrow some Eth fails when Eth not entered" 3 | NewComptroller price:1.0 4 | ListedCToken ZRX cZRX 5 | ListedEtherToken cETH initialExchangeRate:0.005e9 6 | SetCollateralFactor cZRX collateralFactor:0.5 7 | SetCollateralFactor cETH collateralFactor:0.5 8 | Prep Geoff Some ZRX cZRX 9 | Mint Geoff 100e18 cZRX 10 | EnterMarkets Geoff cZRX 11 | AllowFailures 12 | Invariant Static (CToken cZRX ExchangeRateStored) 13 | Invariant Static (CToken cETH ExchangeRateStored) 14 | Invariant Static (Comptroller Liquidity Geoff) 15 | Invariant Static (EtherBalance Geoff) 16 | BorrowEth Geoff 1e18 cETH 17 | Assert Failure COMPTROLLER_REJECTION BORROW_COMPTROLLER_REJECTION MARKET_NOT_ENTERED 18 | 19 | Test "Borrow some ETH fails when no ETH available" 20 | NewComptroller price:1.0 21 | ListedCToken ZRX cZRX 22 | ListedEtherToken cETH initialExchangeRate:0.005e9 23 | SetCollateralFactor cZRX collateralFactor:0.5 24 | SetCollateralFactor cETH collateralFactor:0.5 25 | Prep Geoff Some ZRX cZRX 26 | Mint Geoff 100e18 cZRX 27 | EnterMarkets Geoff cZRX cETH 28 | AllowFailures 29 | Invariant Static (CToken cZRX ExchangeRateStored) 30 | Invariant Static (CToken cETH ExchangeRateStored) 31 | Invariant Static (Comptroller Liquidity Geoff) 32 | Invariant Static (EtherBalance Geoff) 33 | BorrowEth Geoff 1e18 cETH 34 | Assert Failure TOKEN_INSUFFICIENT_CASH BORROW_CASH_NOT_AVAILABLE 35 | 36 | Test "Borrow some ETH from excess cash" 37 | NewComptroller price:1.0 38 | ListedCToken ZRX cZRX 39 | ListedEtherToken cETH initialExchangeRate:0.005e9 40 | SetCollateralFactor cZRX collateralFactor:0.5 41 | SetCollateralFactor cETH collateralFactor:0.5 42 | Donate cETH 0.003e18 43 | Prep Geoff Some ZRX cZRX 44 | Mint Geoff 1e18 cZRX 45 | EnterMarkets Geoff cZRX cETH 46 | Expect Changes (EtherBalance Geoff) +0.001e18 47 | BorrowEth Geoff 0.001e18 cETH 48 | Assert Equal (EtherBalance cETH) 0.002e18 49 | Assert Equal (Comptroller Liquidity Geoff) 4.99e17 50 | -------------------------------------------------------------------------------- /compound-protocol/scenario/script/repl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | dir=`dirname $0` 6 | tsc_root="$dir/.." 7 | proj_root="$dir/../.." 8 | networks_root="$dir/../../networks" 9 | test_root="$dir/../../test" 10 | contracts_root="$dir/../../contracts" 11 | network=${NETWORK:-development} 12 | script="$SCRIPT" 13 | verbose="$VERBOSE" 14 | dry_run="$DRY_RUN" 15 | no_tsc="$NO_TSC" 16 | 17 | usage() { echo "$0 usage:" && grep ".)\ #" $0; exit 0; } 18 | while getopts ":hdn:e:s:vt" arg; do 19 | case $arg in 20 | c) # Don't compile 21 | no_compile="true" 22 | ;; 23 | d) # Dry run 24 | dry_run="true" 25 | ;; 26 | e) # Add variables for script (key=value,key2=value2) 27 | env_vars="$OPTARG" 28 | ;; 29 | n) # Specify network 30 | network=$OPTARG 31 | ;; 32 | s) # Specify a script to run 33 | script=$OPTARG 34 | [ ! -f "$script" ] \ 35 | && echo "Cannot find script $script" \ 36 | && exit 1 37 | ;; 38 | t) # Don't build TSC 39 | no_tsc="true" 40 | ;; 41 | 42 | v) # Verbose 43 | verbose="true" 44 | ;; 45 | 46 | h | *) # Display help. 47 | usage 48 | exit 0 49 | ;; 50 | esac 51 | done 52 | 53 | if [ $network = "test" -o $network = "development" -o -n "$add_test_contracts" ]; then 54 | function cleanup { 55 | mv "$contracts_root/test" "$test_root/contracts" 56 | } 57 | 58 | trap cleanup EXIT 59 | 60 | mv "$test_root/contracts" "$contracts_root/test" 61 | 62 | # Compile with test contracts 63 | [[ -z $no_compile ]] && solc --combined-json bin,abi --optimize contracts/*.sol contracts/**/*.sol --allow-paths ./contracts,./contracts/test > networks/${network}-contracts.json 64 | else 65 | # Compile without test contracts 66 | [[ -z $no_compile ]] && solc --combined-json bin,abi --optimize contracts/*.sol > networks/${network}-contracts.json 67 | fi 68 | 69 | [[ ! -d ./.tsbuilt || -z $no_tsc ]] && "$dir/tsc" 70 | 71 | 72 | proj_root="$proj_root" env_vars="$env_vars" dry_run="$dry_run" script="$script" network="$network" verbose="$verbose" node --stack_size=10000 "$proj_root/node_modules/truffle/build/cli.bundled.js" exec --network "$network" "$tsc_root/.tsbuilt/repl.js" 73 | -------------------------------------------------------------------------------- /compound-protocol/spec/scenario/Withdraw.scen.old: -------------------------------------------------------------------------------- 1 | -- Withdraw Tests 2 | 3 | Test "Supply Ether 5 then Withdraw MAX in the same block" 4 | AddToken Ether -- baseline sanity check for withdraw max 5 | SupportMarket Ether (FixedPrice 1.0) (FixedRate 0.5 0.75) SimplePolicyHook 6 | Approve Geoff Ether "6.0e18" 7 | Faucet Geoff Ether "6.0e18" 8 | Supply Geoff Ether "5.0e18" 9 | Assert Success 10 | Assert Equal (SupplyBalance Geoff Ether) (Exactly "5.0e18") 11 | Assert Equal (BorrowBalance Geoff Ether) (Exactly "0e18") 12 | Assert Equal (MaxBorrow Geoff) (Exactly "2.5e18") 13 | Withdraw Geoff Ether "MAX" 14 | Assert Success 15 | Assert Equal (SupplyBalance Geoff Ether) (Exactly "0.0e18") 16 | Assert Equal (TokenBalance Geoff Ether) (Exactly "6e18") 17 | 18 | Test "Supply Ether 5 then Withdraw MAX (6) after accruing some interest" 19 | AddToken Ether 20 | SupportMarket Ether (FixedPrice 1.0) (FixedRate 0.5 0.75) SimplePolicyHook 21 | Approve Geoff Ether "6.0e18" 22 | Faucet Geoff Ether "6.0e18" 23 | Supply Geoff Ether "5.0e18" -- We need more Ether in the system to simulate protocol gaining borrow interest to pay Geoff 24 | Approve Torrey Ether "10.0e18" 25 | Faucet Torrey Ether "10.0e18" 26 | Supply Torrey Ether "10.0e18" 27 | Assert Success 28 | Assert Equal (SupplyBalance Geoff Ether) (Exactly "5.0e18") 29 | FastForward 2 Blocks 30 | Assert Equal (SupplyBalance Geoff Ether) (Exactly "10.0e18") 31 | Withdraw Geoff Ether "MAX" 32 | Assert Success 33 | Assert Equal (SupplyBalance Geoff Ether) (Exactly "0.0e18") 34 | Assert Equal (TokenBalance Geoff Ether) (Exactly "11e18") 35 | 36 | Test "Withdraw Ether 1 when contract paused" 37 | AddToken Ether 38 | SupportMarket Ether (FixedPrice 1.0) (FixedRate 0.5 0.75) SimplePolicyHook 39 | Approve Geoff Ether "1.0e18" 40 | Faucet Geoff Ether "1.0e18" 41 | Supply Geoff Ether "1.0e18" 42 | Assert Success 43 | Assert Equal (SupplyBalance Geoff Ether) (Exactly "1.0e18") 44 | PolicyHook Ether (SetPaused True) 45 | Withdraw Geoff Ether "1.0e18" 46 | Assert Failure COMPTROLLER_REJECTION WITHDRAW_COMPTROLLER_REJECTION 1 47 | Assert Equal (SupplyBalance Geoff Ether) (Exactly "1e18") 48 | Assert Equal (TokenBalance Geoff Ether) (Exactly "0e18") 49 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Builder/MaximillionBuilder.ts: -------------------------------------------------------------------------------- 1 | import {Event} from '../Event'; 2 | import {addAction, World} from '../World'; 3 | import {Maximillion} from '../Contract/Maximillion'; 4 | import {Invokation} from '../Invokation'; 5 | import {Arg, Fetcher, getFetcherValue} from '../Command'; 6 | import {storeAndSaveContract} from '../Networks'; 7 | import {getContract} from '../Contract'; 8 | import {getAddressV} from '../CoreValue'; 9 | import {AddressV} from '../Value'; 10 | 11 | const MaximillionContract = getContract("Maximillion"); 12 | 13 | export interface MaximillionData { 14 | invokation: Invokation, 15 | description: string, 16 | cEtherAddress: string, 17 | address?: string 18 | } 19 | 20 | export async function buildMaximillion(world: World, from: string, event: Event): Promise<{world: World, maximillion: Maximillion, maximillionData: MaximillionData}> { 21 | const fetchers = [ 22 | new Fetcher<{cEther: AddressV}, MaximillionData>(` 23 | #### Maximillion 24 | 25 | * "" - Maximum Eth Repays Contract 26 | * E.g. "Maximillion Deploy" 27 | `, 28 | "Maximillion", 29 | [ 30 | new Arg("cEther", getAddressV) 31 | ], 32 | async (world, {cEther}) => { 33 | return { 34 | invokation: await MaximillionContract.deploy(world, from, [cEther.val]), 35 | description: "Maximillion", 36 | cEtherAddress: cEther.val 37 | }; 38 | }, 39 | {catchall: true} 40 | ) 41 | ]; 42 | 43 | let maximillionData = await getFetcherValue("DeployMaximillion", fetchers, world, event); 44 | let invokation = maximillionData.invokation; 45 | delete maximillionData.invokation; 46 | 47 | if (invokation.error) { 48 | throw invokation.error; 49 | } 50 | const maximillion = invokation.value!; 51 | maximillionData.address = maximillion._address; 52 | 53 | world = await storeAndSaveContract( 54 | world, 55 | maximillion, 56 | 'Maximillion', 57 | invokation, 58 | [ 59 | { index: ['Maximillion'], data: maximillionData } 60 | ] 61 | ); 62 | 63 | return {world, maximillion, maximillionData}; 64 | } 65 | -------------------------------------------------------------------------------- /compound-protocol/spec/formal/_setMarketPriceOracle.cvl: -------------------------------------------------------------------------------- 1 | methods { 2 | admin() returns address 3 | markets(address) returns bool,uint256,uint256,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256 4 | _setMarketPriceOracle(address,address) returns uint256 5 | } 6 | 7 | _setMarketPriceOracle(uint result, address asset, address reqPriceOracle) 8 | description "Failed to set market oracle for $asset with result $result (result=$result, asset=$asset, reqPriceOracle=$reqPriceOracle)" { 9 | // N.B. The implementation calls `priceOracle.assetPrices(address(0))` 10 | // which needs to not revert for the call to succeed. 11 | 12 | address currAdmin; 13 | bool currListed; 14 | address currPriceOracle; 15 | address nextPriceOracle; 16 | 17 | // Free Variables 18 | env e0; havoc e0; 19 | env e1; havoc e1; 20 | env e2; havoc e2; 21 | 22 | // Strict ordering 23 | static_require e1.block > e0.block; 24 | static_require e2.block > e1.block; 25 | 26 | // Capture curr values 27 | uint currAdmin = sinvoke admin(e0); 28 | currListed, _, _, currPriceOracle, _, _, _, _, _, _, _, _ = sinvoke markets(e0, asset); 29 | 30 | // Invoke set market oracle 31 | uint256 resultT = sinvoke _setMarketPriceOracle(e1, asset, reqPriceOracle); 32 | static_require result == resultT; 33 | 34 | // Get next value 35 | _, _, _, nextPriceOracle, _, _, _, _, _, _, _, _ = sinvoke markets(e2, asset); 36 | 37 | // Guarantee return values 38 | static_assert( 39 | result == 0 || 40 | result == 1 || 41 | result == 10 42 | ); 43 | 44 | // Success case updates market 45 | static_assert(result == 0 <=> ( 46 | e1.msg.sender == currAdmin && 47 | nextPriceOracle == reqPriceOracle && 48 | currListed 49 | )); 50 | 51 | // Unauthorized case 52 | static_assert(result == 1 <=> ( 53 | e1.msg.sender != currAdmin && 54 | nextPriceOracle == currPriceOracle 55 | )); 56 | 57 | // Unlisted case 58 | static_assert(result == 10 <=> ( 59 | e1.msg.sender == currAdmin && 60 | nextPriceOracle == currPriceOracle && 61 | !currListed 62 | )); 63 | } -------------------------------------------------------------------------------- /compound-aragon-app/app/src/components/balances/side-panel-input/token-selector/TokenSelector.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { DropDown, Field, TextInput, Text} from '@aragon/ui' 3 | import { useNetwork } from '@aragon/api-react' 4 | import TokenSelectorInstance from './TokenSelectorInstance' 5 | 6 | const INITIAL_STATE = { 7 | customToken: { 8 | address: '', 9 | value: '', 10 | }, 11 | } 12 | 13 | class TokenSelector extends React.Component { 14 | 15 | static defaultProps = { 16 | onSelectToken: () => {}, 17 | onOtherInput: () => {}, 18 | tokens: [], 19 | label: 'Token', 20 | labelCustomToken: 'Token address', 21 | selectedIndex: 0, 22 | } 23 | 24 | state = { 25 | ...INITIAL_STATE, 26 | } 27 | 28 | getItems() { 29 | return [...this.getTokenItems(), Other...] 30 | } 31 | 32 | getTokenItems() { 33 | return this.props.tokens.map(({ address, name, symbol, verified }) => ( 34 | 40 | )) 41 | } 42 | 43 | render() { 44 | const { label, labelCustomToken, selectedIndex, onSelectToken, onOtherInput } = this.props 45 | const items = this.getItems() 46 | const showCustomToken = selectedIndex === items.length - 1 47 | return ( 48 | 49 | 50 | onSelectToken(index)} 55 | required 56 | wide 57 | /> 58 | 59 | 60 | {showCustomToken && ( 61 | 62 | onOtherInput(event.target.value)} 64 | required 65 | wide 66 | /> 67 | 68 | )} 69 | 70 | ) 71 | } 72 | } 73 | 74 | export default props => { 75 | const network = useNetwork() 76 | return 77 | } 78 | -------------------------------------------------------------------------------- /compound-protocol/test/Tokens/setComptrollerTest.js: -------------------------------------------------------------------------------- 1 | const {call, send} = require('../Utils/MochaTruffle'); 2 | const { 3 | makeComptroller, 4 | makeCToken 5 | } = require('../Utils/Compound'); 6 | 7 | contract('CToken', function([root, ...accounts]) { 8 | let cToken, oldComptroller, newComptroller; 9 | before(async () => { 10 | cToken = await makeCToken(); 11 | oldComptroller = cToken.comptroller; 12 | newComptroller = await makeComptroller(); 13 | assert.notEqual(newComptroller._address, oldComptroller._address, 'setup failed'); 14 | }); 15 | 16 | describe('_setComptroller', async () => { 17 | it("should fail if called by non-admin", async () => { 18 | assert.hasTokenFailure( 19 | await send(cToken, '_setComptroller', [newComptroller._address], {from: accounts[0]}), 20 | 'UNAUTHORIZED', 21 | 'SET_COMPTROLLER_OWNER_CHECK' 22 | ); 23 | assert.equal(await call(cToken, 'comptroller'), oldComptroller._address); 24 | }); 25 | 26 | it("reverts if passed a contract that doesn't implement isComptroller", async () => { 27 | await assert.revert(send(cToken, '_setComptroller', [cToken.underlying._address])); 28 | assert.equal(await call(cToken, 'comptroller'), oldComptroller._address); 29 | }); 30 | 31 | it("reverts if passed a contract that implements isComptroller as false", async () => { 32 | // extremely unlikely to occur, of course, but let's be exhaustive 33 | const badComptroller = await makeComptroller({kind: 'false-marker'}); 34 | await assert.revert(send(cToken, '_setComptroller', [badComptroller._address]), "revert marker method returned false"); 35 | assert.equal(await call(cToken, 'comptroller'), oldComptroller._address); 36 | }); 37 | 38 | it("updates comptroller and emits log on success", async () => { 39 | const result = await send(cToken, '_setComptroller', [newComptroller._address]); 40 | assert.success(result); 41 | assert.hasLog(result, 'NewComptroller', { 42 | oldComptroller: oldComptroller._address, 43 | newComptroller: newComptroller._address 44 | }); 45 | assert.equal(await call(cToken, 'comptroller'), newComptroller._address); 46 | }); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Utils.ts: -------------------------------------------------------------------------------- 1 | import {Event} from './Event'; 2 | import {World} from './World'; 3 | 4 | // Wraps the element in an array, if it was not already an array 5 | // If array is null or undefined, return the empty array 6 | export function mustArray(arg: T[] | T): T[] { 7 | if (Array.isArray(arg)) { 8 | return arg; 9 | } else { 10 | if (arg === null || arg === undefined) { 11 | return []; 12 | } else { 13 | return [arg]; 14 | } 15 | } 16 | } 17 | 18 | // Asserts that the array must be given length and if so returns it, otherwise 19 | // it will raise an error 20 | export function mustLen(arg: any[] | any, len: number, maxLen?: number): any[] { 21 | if (!Array.isArray(arg)) { 22 | throw `Expected array of length ${len}, got ${arg}`; 23 | } else if (maxLen === undefined && arg.length !== len) { 24 | throw `Expected array of length ${len}, got length ${arg.length} (${arg})`; 25 | } else if (maxLen !== undefined && (arg.length < len || arg.length > maxLen)) { 26 | throw `Expected array of length ${len}-${maxLen}, got length ${arg.length} (${arg})`; 27 | } else { 28 | return arg; 29 | } 30 | } 31 | 32 | export function mustString(arg: Event): string { 33 | if (typeof(arg) === "string") { 34 | return arg; 35 | } 36 | 37 | throw new Error(`Expected string argument, got ${arg.toString()}`); 38 | } 39 | 40 | // Web3 doesn't have a function ABI parser.. not sure why.. but we build a simple encoder 41 | // that accepts "fun(uint256,uint256)" and params and returns the encoded value. 42 | export function encodeABI(world: World, fnABI: string, fnParams: string[]): string { 43 | const regex = /(\w+)\(([\w,]+)\)/; 44 | const res = regex.exec(fnABI); 45 | if (!res) { 46 | throw new Error(`Expected ABI signature, got: ${fnABI}`); 47 | } 48 | const [_, fnName, fnInputs] = <[string, string, string]>res; 49 | const jsonInterface = { 50 | name: fnName, 51 | inputs: fnInputs.split(',').map((i) => ({name: '', type: i})) 52 | }; 53 | return world.web3.eth.abi.encodeFunctionCall(jsonInterface, fnParams); 54 | } 55 | 56 | export function sleep(timeout: number): Promise { 57 | return new Promise((resolve, reject) => { 58 | setTimeout(() => { 59 | resolve(); 60 | }, timeout); 61 | }); 62 | } 63 | -------------------------------------------------------------------------------- /compound-aragon-app/app/src/hooks/transfer-panels.js: -------------------------------------------------------------------------------- 1 | import { useCallback } from "react" 2 | import { useApi, useAragonApi, useAppState } from "@aragon/api-react" 3 | import { tokenContract$ } from "../web3/ExternalContracts" 4 | import { mergeMap, map } from "rxjs/operators" 5 | import { toDecimals } from "../lib/math-utils" 6 | import { zip } from "rxjs" 7 | import BN from "bn.js" 8 | import { isAddress } from "../lib/web3-utils" 9 | import { ETH_DECIMALS, ETHER_TOKEN_FAKE_ADDRESS } from "../lib/shared-constants" 10 | 11 | const checkBalanceAvailable = (api, account, token, amount, setBalanceAvailable) => { 12 | 13 | if (!isAddress(token)) { 14 | setBalanceAvailable(true) 15 | return 16 | } 17 | 18 | if (token === ETHER_TOKEN_FAKE_ADDRESS) { 19 | api.web3Eth("getBalance", account).pipe( 20 | map(userBalance => { 21 | const adjustedAmount = toDecimals(amount.toString(), ETH_DECIMALS) 22 | const adjustedAmountBn = new BN(adjustedAmount) 23 | return adjustedAmountBn.lte(new BN(userBalance)) 24 | }) 25 | ).subscribe(setBalanceAvailable) 26 | } else { 27 | const adjustedAmountBn$ = token => token.decimals().pipe( 28 | map(decimals => toDecimals(amount.toString(), parseInt(decimals))), 29 | map(adjustedAmount => new BN(adjustedAmount))) 30 | 31 | tokenContract$(api, token).pipe( 32 | mergeMap(token => zip(adjustedAmountBn$(token), token.balanceOf(account))), 33 | map(([adjustedAmountBn, userBalance]) => adjustedAmountBn.lte(new BN(userBalance))) 34 | ).subscribe(setBalanceAvailable) 35 | } 36 | } 37 | 38 | const useCheckConnectedAccountBalance = () => { 39 | const api = useApi() 40 | const { connectedAccount } = useAragonApi() 41 | 42 | return useCallback((token, amount, setBalanceAvailable) => { 43 | checkBalanceAvailable(api, connectedAccount, token, amount, setBalanceAvailable) 44 | }, [api, connectedAccount]) 45 | } 46 | 47 | const useTransferState = () => { 48 | 49 | const { tokens, balances } = useAppState() 50 | 51 | return { 52 | tokens, 53 | balances, 54 | checkConnectedAccountBalance: useCheckConnectedAccountBalance() 55 | } 56 | } 57 | 58 | export { 59 | useTransferState 60 | } -------------------------------------------------------------------------------- /compound-aragon-app/app/src/components/GenericInputPanel.js: -------------------------------------------------------------------------------- 1 | import React, {useState} from "react" 2 | import {Info, TextInput, Button, Field} from "@aragon/ui" 3 | import styled from 'styled-components' 4 | 5 | const PanelContainer = styled.div` 6 | display: flex; 7 | flex-direction: column; 8 | ` 9 | const InfoContainer = styled(Info.Action)` 10 | margin-top: 30px; 11 | ` 12 | 13 | const InputField = ({id, inputFieldLabel, inputFieldType, onChange}) => { 14 | 15 | const handleChange = event => { 16 | const text = event.target.value; 17 | onChange(id, text); 18 | } 19 | 20 | return ( 21 | 22 | 24 | 25 | ) 26 | } 27 | 28 | // inputFieldList must represent the arguments to handleSubmit and id's must be in the order of the arguments 29 | const GenericInputPanel = ({actionTitle, actionDescription, inputFieldList, submitLabel, handleSubmit}) => { 30 | 31 | const [inputFieldData, setInputFieldData] = useState({}) 32 | 33 | const handleFieldChange = (fieldId, value) => { 34 | setInputFieldData({...inputFieldData, [fieldId]: value}) 35 | } 36 | 37 | const inputFields = inputFieldList.map(inputField => ( 38 | 44 | )); 45 | 46 | 47 | const sortedInputFieldData = () => Object.fromEntries( 48 | Object.entries(inputFieldData).sort( (a,b) => a[0] - b[0] ) 49 | ) 50 | 51 | return ( 52 | 53 | 54 | {inputFields} 55 | 56 | 61 | 62 | 63 | {actionDescription} 64 | 65 | 66 | 67 | ) 68 | } 69 | 70 | export default GenericInputPanel -------------------------------------------------------------------------------- /compound-protocol/test/Tokens/transferTest.js: -------------------------------------------------------------------------------- 1 | const {call, send} = require('../Utils/MochaTruffle'); 2 | const {makeCToken} = require('../Utils/Compound'); 3 | 4 | contract('CToken', function ([root, ...accounts]) { 5 | describe('transfer', () => { 6 | it("cannot transfer from a zero balance", async () => { 7 | const cToken = await makeCToken({supportMarket: true}); 8 | assert.equal(await call(cToken, 'balanceOf', [root]), 0); 9 | assert.hasTokenFailure( 10 | await send(cToken, 'transfer', [accounts[0], 100]), 11 | 'MATH_ERROR', 12 | 'TRANSFER_NOT_ENOUGH' 13 | ); 14 | }); 15 | 16 | it("transfers 50 tokens", async () => { 17 | const cToken = await makeCToken({supportMarket: true}); 18 | await send(cToken, 'harnessSetBalance', [root, 100]); 19 | assert.equal(await call(cToken, 'balanceOf', [root]), 100); 20 | await send(cToken, 'transfer', [accounts[0], 50]); 21 | assert.equal(await call(cToken, 'balanceOf', [root]), 50); 22 | assert.equal(await call(cToken, 'balanceOf', [accounts[0]]), 50); 23 | }); 24 | 25 | it("doesn't transfer when src == dst", async () => { 26 | const cToken = await makeCToken({supportMarket: true}); 27 | await send(cToken, 'harnessSetBalance', [root, 100]); 28 | assert.equal(await call(cToken, 'balanceOf', [root]), 100); 29 | assert.hasTokenFailure( 30 | await send(cToken, 'transfer', [root, 50]), 31 | 'BAD_INPUT', 32 | 'TRANSFER_NOT_ALLOWED' 33 | ); 34 | }); 35 | 36 | it("rejects transfer when not allowed and reverts if not verified", async () => { 37 | const cToken = await makeCToken({comptrollerOpts: {kind: 'bool'}}); 38 | await send(cToken, 'harnessSetBalance', [root, 100]); 39 | assert.equal(await call(cToken, 'balanceOf', [root]), 100); 40 | 41 | await send(cToken.comptroller, 'setTransferAllowed', [false]) 42 | assert.hasTrollReject( 43 | await send(cToken, 'transfer', [root, 50]), 44 | 'TRANSFER_COMPTROLLER_REJECTION' 45 | ); 46 | 47 | await send(cToken.comptroller, 'setTransferAllowed', [true]) 48 | await send(cToken.comptroller, 'setTransferVerify', [false]) 49 | await assert.revert(send(cToken, 'transfer', [accounts[0], 50]), "revert transferVerify rejected transfer"); 50 | }); 51 | }); 52 | }); -------------------------------------------------------------------------------- /compound-aragon-app/app/src/web3/CompoundData.js: -------------------------------------------------------------------------------- 1 | import { agentAddress$, compoundToken$, compoundTokenAddresses$, tokenContract$ } from "./ExternalContracts" 2 | import {zip} from "rxjs" 3 | import {concatMap, mergeMap, toArray, map} from "rxjs/operators"; 4 | import {onErrorReturnDefault} from "../lib/rx-error-operators"; 5 | 6 | const balanceOfUnderlyingTokens$ = (api, compoundTokenAddress) => 7 | zip(agentAddress$(api), compoundToken$(api, compoundTokenAddress)).pipe( 8 | mergeMap(([agentAddress, compoundToken]) => compoundToken.balanceOfUnderlying(agentAddress))) 9 | 10 | const balanceOfToken$ = (api, tokenAddress) => 11 | zip(agentAddress$(api), tokenContract$(api, tokenAddress)).pipe( 12 | mergeMap(([agentAddress, token]) => token.balanceOf(agentAddress))) 13 | 14 | const compoundTokenDetails$ = (api, compoundTokenAddress, state) => { 15 | 16 | const tokenObject = (tokenAddress, tokenName, tokenSymbol, underlyingToken, supplyRatePerBlock, balanceOfUnderlying, exchangeRateStored) => ({ 17 | ...((state || {}).compoundTokens || []) 18 | .find(compoundToken => compoundToken.tokenAddress === compoundTokenAddress) || {}, 19 | tokenAddress, 20 | tokenName, 21 | tokenSymbol, 22 | underlyingToken, 23 | supplyRatePerBlock, 24 | balanceOfUnderlying, 25 | exchangeRateStored 26 | }) 27 | 28 | return compoundToken$(api, compoundTokenAddress).pipe( 29 | mergeMap(token => zip(token.name(), token.symbol(), token.supplyRatePerBlock(), 30 | balanceOfUnderlyingTokens$(api, compoundTokenAddress), token.underlying(), token.exchangeRateStored())), 31 | map(([name, symbol, supplyRatePerBlock, balanceOfUnderlying, underlyingToken, exchangeRateStored]) => 32 | tokenObject(compoundTokenAddress, name, symbol, underlyingToken, supplyRatePerBlock, balanceOfUnderlying, exchangeRateStored))) 33 | } 34 | 35 | const compoundTokensDetails$ = (api, state) => 36 | compoundTokenAddresses$(api).pipe( 37 | concatMap(address => address), 38 | mergeMap(compoundTokenAddress => compoundTokenDetails$(api, compoundTokenAddress, state)), 39 | toArray(), 40 | onErrorReturnDefault('compoundTokensDetails', [])) 41 | 42 | export { 43 | compoundTokensDetails$, 44 | balanceOfUnderlyingTokens$, 45 | balanceOfToken$ 46 | } -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Settings.ts: -------------------------------------------------------------------------------- 1 | import {getNetworkPath, readFile, writeFile} from './File'; 2 | 3 | export class Settings { 4 | basePath: string | null 5 | network: string | null 6 | aliases: {[name: string]: string} 7 | from: string | undefined 8 | 9 | constructor(basePath: string | null, network: string | null, aliases: {[name: string]: string}, from?: string) { 10 | this.basePath = basePath; 11 | this.network = network; 12 | this.aliases = aliases; 13 | this.from = from; 14 | } 15 | 16 | static deserialize(basePath: string, network: string, data: string): Settings { 17 | const {aliases} = JSON.parse(data); 18 | 19 | return new Settings(basePath, network, aliases); 20 | } 21 | 22 | serialize(): string { 23 | return JSON.stringify({ 24 | aliases: this.aliases 25 | }); 26 | } 27 | 28 | static default(basePath: string | null, network: string | null): Settings { 29 | return new Settings(basePath, network, {}); 30 | } 31 | 32 | static getFilePath(basePath: string | null, network: string): string { 33 | return getNetworkPath(basePath, network, '-settings'); 34 | } 35 | 36 | static load(basePath: string, network: string): Promise { 37 | return readFile(Settings.getFilePath(basePath, network), Settings.default(basePath, network), (data) => Settings.deserialize(basePath, network, data)); 38 | } 39 | 40 | async save(): Promise { 41 | if (this.network) { 42 | return await writeFile(Settings.getFilePath(this.basePath, this.network), this.serialize()); 43 | } 44 | } 45 | 46 | lookupAlias(address: string): string { 47 | let entry = Object.entries(this.aliases).find(([key, value]) => { 48 | return value === address; 49 | }); 50 | 51 | if (entry) { 52 | return entry[0]; 53 | } else { 54 | return address; 55 | } 56 | } 57 | 58 | lookupAliases(address: string): string[] { 59 | let entries = Object.entries(this.aliases).filter(([key, value]) => { 60 | return value === address; 61 | }); 62 | 63 | return entries.map(([key, _value]) => key); 64 | } 65 | 66 | findAlias(name: string): string | null { 67 | const alias = Object.entries(this.aliases).find(([alias, addr]) => alias.toLowerCase() === name.toLowerCase()); 68 | 69 | if (alias) { 70 | return alias[1]; 71 | } else { 72 | return null; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /compound-aragon-app/test/helpers/ChainSetup.js: -------------------------------------------------------------------------------- 1 | import RPC from "./rpc"; 2 | 3 | const DAOFactory = artifacts.require('DAOFactory') 4 | const EVMScriptRegistryFactory = artifacts.require('EVMScriptRegistryFactory') 5 | const ACL = artifacts.require('ACL') 6 | const Kernel = artifacts.require('Kernel') 7 | 8 | class Snapshot { 9 | 10 | constructor(web3) { 11 | this.rpc = new RPC(web3) 12 | } 13 | 14 | async saveSnapshot() { 15 | this.currentSnapshotId = await this.rpc.snapshot() 16 | } 17 | 18 | async restoreSnapshot() { 19 | await this.rpc.revert(this.currentSnapshotId) 20 | } 21 | } 22 | 23 | class DaoDeployment { 24 | 25 | constructor(rootAddress) { 26 | this.rootAddress = rootAddress 27 | } 28 | 29 | async deploy() { 30 | await this.createStatelessContracts() 31 | await this.createDaoProxyContractsAndPermission() 32 | } 33 | 34 | async createStatelessContracts() { 35 | this.kernelBase = await Kernel.new(true) 36 | this.aclBase = await ACL.new() 37 | this.evmScriptRegistryFactory = await EVMScriptRegistryFactory.new() 38 | this.daoFactory = await DAOFactory.new(this.kernelBase.address, this.aclBase.address, this.evmScriptRegistryFactory.address) 39 | } 40 | 41 | async createDaoProxyContractsAndPermission() { 42 | const newKernelReceipt = await this.daoFactory.newDAO(this.rootAddress) 43 | this.kernel = await Kernel.at(newKernelReceipt.logs.filter(log => log.event === 'DeployDAO')[0].args.dao) 44 | this.acl = await ACL.at(await this.kernel.acl()) 45 | 46 | const APP_MANAGER_ROLE = await this.kernelBase.APP_MANAGER_ROLE() 47 | await this.acl.createPermission(this.rootAddress, this.kernel.address, APP_MANAGER_ROLE, this.rootAddress, {from: this.rootAddress}) 48 | } 49 | } 50 | 51 | class TemplateAgentChainSetup { 52 | 53 | constructor(snapshot, daoDeployment) { 54 | this.snapshot = snapshot 55 | this.daoDeployment = daoDeployment 56 | } 57 | 58 | async before(rootAddress) { 59 | await this.daoDeployment.deploy(rootAddress) 60 | } 61 | 62 | async beforeEach() { 63 | await this.snapshot.saveSnapshot() 64 | } 65 | 66 | async afterEach() { 67 | await this.snapshot.restoreSnapshot() 68 | } 69 | } 70 | 71 | export { 72 | Snapshot, 73 | DaoDeployment, 74 | TemplateAgentChainSetup 75 | } -------------------------------------------------------------------------------- /compound-aragon-app/app/src/app-state-reducer.js: -------------------------------------------------------------------------------- 1 | import BN from "bn.js" 2 | import { ETHER_TOKEN_FAKE_ADDRESS } from "./lib/shared-constants" 3 | import { utils } from "ethers" 4 | import { ETHER_TOKEN_VERIFIED_BY_SYMBOL } from "./lib/verified-tokens" 5 | 6 | const compareBalancesByEthAndSymbol = (tokenA, tokenB) => { 7 | if (tokenA.address === ETHER_TOKEN_FAKE_ADDRESS) { 8 | return -1 9 | } 10 | if (tokenB.address === ETHER_TOKEN_FAKE_ADDRESS) { 11 | return 1 12 | } 13 | return tokenA.symbol.localeCompare(tokenB.symbol) 14 | } 15 | 16 | const reducer = state => { 17 | 18 | const { balances } = state || {} 19 | 20 | if (balances) { 21 | balances 22 | .filter(balance => balance.address.toLowerCase() === ETHER_TOKEN_VERIFIED_BY_SYMBOL.get("DAI")) 23 | .map(balance => { 24 | balance.symbol = utils.parseBytes32String(balance.symbol) 25 | balance.name = utils.parseBytes32String(balance.name) 26 | return balance 27 | }) 28 | } 29 | 30 | const convertedBalances = balances ? balances 31 | .map(balance => { 32 | return ({ 33 | ...balance, 34 | amount: new BN(balance.amount), 35 | decimals: new BN(balance.decimals), 36 | 37 | // Note that numbers in `numData` are not safe for accurate 38 | // computations (but are useful for making divisions easier). 39 | numData: { 40 | amount: parseInt(balance.amount, 10), 41 | decimals: parseInt(balance.decimals, 10) 42 | } 43 | }) 44 | }) 45 | .sort(compareBalancesByEthAndSymbol) 46 | : [] 47 | 48 | const tokens = convertedBalances.map( 49 | ({ address, name, symbol, numData: { amount, decimals }, verified }) => ({ 50 | address, 51 | amount, 52 | decimals, 53 | name, 54 | symbol, 55 | verified 56 | }) 57 | ) 58 | 59 | const tokensWithValue = convertedBalances.filter(balance => !balance.amount.isZero()) 60 | 61 | return { 62 | ...state, 63 | // balances: convertedBalances, 64 | balances: convertedBalances.filter(balance => !balance.amount.isZero() || (balance.symbol === "ETH" && tokensWithValue.length === 0)), 65 | tokens 66 | } 67 | } 68 | 69 | export default reducer -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Contract/CToken.ts: -------------------------------------------------------------------------------- 1 | import {Contract} from '../Contract'; 2 | import {Callable, Sendable} from '../Invokation'; 3 | import {encodedNumber} from '../Encoding'; 4 | 5 | interface CTokenMethods { 6 | balanceOfUnderlying(string): Callable 7 | borrowBalanceCurrent(string): Callable 8 | borrowBalanceStored(string): Callable 9 | totalBorrows(): Callable 10 | totalBorrowsCurrent(): Callable 11 | totalReserves(): Callable 12 | reserveFactorMantissa(): Callable 13 | comptroller(): Callable 14 | exchangeRateStored(): Sendable 15 | exchangeRateCurrent(): Callable 16 | accrueInterest(): Sendable 17 | mint(): Sendable 18 | mint(encodedNumber): Sendable 19 | redeem(encodedNumber): Sendable 20 | redeemUnderlying(encodedNumber): Sendable 21 | borrow(encodedNumber): Sendable 22 | repayBorrow(): Sendable 23 | repayBorrow(encodedNumber): Sendable 24 | repayBorrowBehalf(string): Sendable 25 | repayBorrowBehalf(string, encodedNumber): Sendable 26 | liquidateBorrow(borrower: string, cTokenCollateral: string): Sendable 27 | liquidateBorrow(borrower: string, repayAmount: encodedNumber, cTokenCollateral: string): Sendable 28 | seize(liquidator: string, borrower: string, seizeTokens: encodedNumber): Sendable 29 | evilSeize(treasure: string, liquidator: string, borrower: string, seizeTokens: encodedNumber): Sendable 30 | _reduceReserves(encodedNumber): Sendable 31 | _setReserveFactor(encodedNumber): Sendable 32 | _setInterestRateModel(string): Sendable 33 | _setComptroller(string): Sendable 34 | underlying(): Callable 35 | interestRateModel(): Callable 36 | borrowRatePerBlock(): Callable 37 | donate(): Sendable 38 | admin(): Callable 39 | pendingAdmin(): Callable 40 | _setPendingAdmin(string): Sendable 41 | _acceptAdmin(): Sendable 42 | } 43 | 44 | interface CTokenScenarioMethods extends CTokenMethods { 45 | setTotalBorrows(encodedNumber): Sendable 46 | setTotalReserves(encodedNumber): Sendable 47 | } 48 | 49 | export interface CToken extends Contract { 50 | methods: CTokenMethods 51 | name: string 52 | } 53 | 54 | export interface CTokenScenario extends Contract { 55 | methods: CTokenScenarioMethods 56 | name: string 57 | } 58 | -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Help.ts: -------------------------------------------------------------------------------- 1 | import {Event} from './Event'; 2 | import {Expression} from './Command'; 3 | import {mustString} from './Utils'; 4 | import {Printer} from './Printer'; 5 | 6 | export function printHelp(printer: Printer, event: Event, expressions: Expression[], path: string[]=[]) { 7 | if (event.length === 0) { 8 | let banner; 9 | 10 | if (path.length === 0) { 11 | banner = ( 12 | ` 13 | ## Compound Command Runner 14 | 15 | The Compound Command Runner makes it easy to interact with Compound. You can input simple commands 16 | and it will construct Web3 calls to pull data or generate transactions. A list of available commands 17 | is included below. To dig further into a command run \`Help \`, such as \`Help From\` or for 18 | sub-commands run \`Help CToken\` or \`Help CToken Mint\`. 19 | `).trim(); 20 | } else { 21 | if (expressions.length > 0) { 22 | banner = `### ${path.join(" ")} Sub-Commands`; 23 | } 24 | } 25 | 26 | if (!!banner) { 27 | printer.printMarkdown(banner); 28 | } 29 | 30 | expressions.forEach((expression) => { 31 | printer.printMarkdown(`\n${expression.doc}`); 32 | if (expression.subExpressions.length > 0) { 33 | printer.printMarkdown(`For more information, run: \`Help ${path} ${expression.name}\``); 34 | } 35 | }); 36 | } else { 37 | const [first, ...rest] = event; 38 | const expressionName = mustString(first); 39 | 40 | let expression = expressions.find((expression) => expression.name.toLowerCase() === expressionName.toLowerCase()); 41 | 42 | if (expression) { 43 | if (rest.length === 0) { 44 | printer.printMarkdown(`${expression.doc}`); 45 | } 46 | 47 | printHelp(printer, rest, expression.subExpressions, path.concat(expression.name)); 48 | } else { 49 | let matchingExpressions = expressions.filter((expression) => expression.name.toLowerCase().startsWith(expressionName.toLowerCase())); 50 | 51 | if (matchingExpressions.length === 0) { 52 | printer.printLine(`\nError: cannot find help docs for ${path.concat(first).join(" ")}`); 53 | } else { 54 | if (rest.length === 0) { 55 | matchingExpressions.forEach((expression) => { 56 | printer.printMarkdown(`${expression.doc}`); 57 | }); 58 | } else { 59 | printer.printLine(`\nError: cannot find help docs for ${path.concat(event).join(" ")}`); 60 | } 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /compound-aragon-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "compound", 3 | "version": "1.0.0", 4 | "description": "", 5 | "dependencies": { 6 | "@aragon/apps-agent": "^2.0.0", 7 | "@aragon/apps-shared-minime": "^1.0.0", 8 | "@aragon/apps-token-manager": "2.0.0", 9 | "@aragon/apps-voting": "2.0.0", 10 | "@aragon/os": "^4.0.1", 11 | "abort-controller": "^3.0.0", 12 | "openzeppelin-solidity": "^1.12.0" 13 | }, 14 | "devDependencies": { 15 | "@aragon/cli": "6.3.0", 16 | "@aragon/test-helpers": "^1.2.2", 17 | "@babel/core": "^7.5.5", 18 | "@babel/polyfill": "^7.4.4", 19 | "@babel/preset-env": "^7.5.5", 20 | "@babel/register": "^7.0.0", 21 | "cross-env": "^5.2.0", 22 | "eslint": "^5.13.0", 23 | "eslint-config-prettier": "^4.0.0", 24 | "eslint-config-standard": "^12.0.0", 25 | "eslint-config-standard-react": "^7.0.2", 26 | "eslint-plugin-import": "^2.16.0", 27 | "eslint-plugin-node": "^8.0.1", 28 | "eslint-plugin-prettier": "^3.0.1", 29 | "eslint-plugin-promise": "^4.0.1", 30 | "eslint-plugin-react": "^7.12.4", 31 | "eslint-plugin-standard": "^4.0.0", 32 | "eslint-plugin-react-hooks": "^1.6.1", 33 | "ethlint": "^1.2.3", 34 | "prettier": "^1.16.4", 35 | "solidity-coverage": "^0.5.11" 36 | }, 37 | "scripts": { 38 | "prepublishOnly": "aragon contracts compile", 39 | "start": "npm run start:ipfs", 40 | "start:ipfs": "aragon run", 41 | "start:http": "aragon run --http localhost:8001 --http-served-from ./dist", 42 | "start:ipfs:template": "npm run start:ipfs -- --template Template --template-init @ARAGON_ENS", 43 | "start:http:template": "npm run start:http -- --template Template --template-init @ARAGON_ENS 0x5060915CC8cCbf6e43be46b3999A169A47171CA0", 44 | "prepare": "cd app && npm install && cd ..", 45 | "start:app": "cd app && npm start && cd ..", 46 | "test": "cross-env TRUFFLE_TEST=true npm run ganache-cli:test", 47 | "compile": "aragon contracts compile", 48 | "build": "cd app && npm run build && cd ..", 49 | "publish:patch": "aragon apm publish patch", 50 | "publish:minor": "aragon apm publish minor", 51 | "publish:major": "aragon apm publish major", 52 | "versions": "aragon apm versions", 53 | "lint": "eslint . & solium --dir ./contracts", 54 | "lint:fix": "eslint . --fix & solium --dir ./contracts --fix", 55 | "coverage": "cross-env SOLIDITY_COVERAGE=true npm run ganache-cli:test", 56 | "ganache-cli:test": "sh ./node_modules/@aragon/test-helpers/ganache-cli.sh" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /compound-protocol/spec/scenario/Supply.scen.old: -------------------------------------------------------------------------------- 1 | -- Supply Tests 2 | 3 | Test "Geoff supplies Ether and we check 2 future balances and then supply again" 4 | AddToken Ether 5 | SupportMarket Ether (FixedPrice 1.0) (FixedRate 0.5 0.75) SimplePolicyHook 6 | Approve Geoff Ether "10.0e18" 7 | Faucet Geoff Ether "10.0e18" 8 | Supply Geoff Ether "3e18" 9 | Assert Success 10 | FastForward 2 Blocks 11 | Assert Equal (SupplyBalance Geoff Ether) (Exactly "6.0e18") -- 3 * ( 1 + 2 * .5 ) 12 | FastForward 2 Blocks 13 | Assert Equal (SupplyBalance Geoff Ether) (Exactly "9.0e18") -- 3 * ( 1 + 4 * .5 ) 14 | Supply Geoff Ether "1e18" 15 | Assert Equal (SupplyBalance Geoff Ether) (Exactly "10.0e18") -- 3 * ( 1 + 4 * .5 ) + 1 16 | FastForward 2 Blocks 17 | Assert Equal (SupplyBalance Geoff Ether) (Exactly "20.0e18") -- 10 * ( 1 + 2 * .5 ) 18 | 19 | Test "Geoff supplies Ether, Torrey supplies Ether and then Geoff supplies more Ether" 20 | AddToken Ether 21 | SupportMarket Ether (FixedPrice 1.0) (FixedRate 0.5 0.75) SimplePolicyHook 22 | Approve Geoff Ether "10.0e18" 23 | Faucet Geoff Ether "10.0e18" 24 | Approve Torrey Ether "5.0e18" 25 | Faucet Torrey Ether "5.0e18" 26 | Supply Geoff Ether "1e18" 27 | Assert Success 28 | FastForward 2 Blocks 29 | Assert Equal (SupplyBalance Geoff Ether) (Exactly "2.0e18") 30 | Supply Torrey Ether "3e18" 31 | Assert Success 32 | FastForward 2 Blocks 33 | Assert Equal (SupplyBalance Torrey Ether) (Exactly "6.0e18") 34 | Assert Equal (SupplyBalance Geoff Ether) (Exactly "4.0e18") 35 | Supply Geoff Ether "1e18" 36 | Assert Equal (SupplyBalance Geoff Ether) (Exactly "5.0e18") 37 | 38 | Test "Can't supply an 'initial' asset" 39 | AddToken Dragon 40 | Approve Geoff Dragon "10.0e18" 41 | Faucet Geoff Dragon "10.0e18" 42 | Supply Geoff Dragon "1e18" 43 | Assert Failure MARKET_NOT_LISTED SUPPLY_MARKET_NOT_LISTED 44 | 45 | Test "Can't supply when contract is paused" 46 | AddToken Ether 47 | SupportMarket Ether (FixedPrice 1.0) (FixedRate 0.5 0.75) SimplePolicyHook 48 | Approve Geoff Ether 1.0e18 49 | Faucet Geoff Ether 0.4e18 50 | PolicyHook Ether (SetPaused True) 51 | Supply Geoff Ether 0.3e18 52 | Assert Failure COMPTROLLER_REJECTION SUPPLY_COMPTROLLER_REJECTION 1 53 | Assert Equal (SupplyBalance Geoff Ether) (Exactly "0e18") 54 | 55 | Test "With always paused policy hook, can't supply when contract is paused" 56 | AddToken Ether 57 | SupportMarket Ether (FixedPrice 1.0) (FixedRate 0.5 0.75) AlwaysPausedPolicyHook 58 | Supply Geoff Ether 0.3e18 59 | Assert Failure COMPTROLLER_REJECTION SUPPLY_COMPTROLLER_REJECTION 99 60 | -------------------------------------------------------------------------------- /compound-protocol/contracts/CarefulMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.8; 2 | 3 | /** 4 | * @title Careful Math 5 | * @author Compound 6 | * @notice Derived from OpenZeppelin's SafeMath library 7 | * https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol 8 | */ 9 | contract CarefulMath { 10 | 11 | /** 12 | * @dev Possible error codes that we can return 13 | */ 14 | enum MathError { 15 | NO_ERROR, 16 | DIVISION_BY_ZERO, 17 | INTEGER_OVERFLOW, 18 | INTEGER_UNDERFLOW 19 | } 20 | 21 | /** 22 | * @dev Multiplies two numbers, returns an error on overflow. 23 | */ 24 | function mulUInt(uint a, uint b) internal pure returns (MathError, uint) { 25 | if (a == 0) { 26 | return (MathError.NO_ERROR, 0); 27 | } 28 | 29 | uint c = a * b; 30 | 31 | if (c / a != b) { 32 | return (MathError.INTEGER_OVERFLOW, 0); 33 | } else { 34 | return (MathError.NO_ERROR, c); 35 | } 36 | } 37 | 38 | /** 39 | * @dev Integer division of two numbers, truncating the quotient. 40 | */ 41 | function divUInt(uint a, uint b) internal pure returns (MathError, uint) { 42 | if (b == 0) { 43 | return (MathError.DIVISION_BY_ZERO, 0); 44 | } 45 | 46 | return (MathError.NO_ERROR, a / b); 47 | } 48 | 49 | /** 50 | * @dev Subtracts two numbers, returns an error on overflow (i.e. if subtrahend is greater than minuend). 51 | */ 52 | function subUInt(uint a, uint b) internal pure returns (MathError, uint) { 53 | if (b <= a) { 54 | return (MathError.NO_ERROR, a - b); 55 | } else { 56 | return (MathError.INTEGER_UNDERFLOW, 0); 57 | } 58 | } 59 | 60 | /** 61 | * @dev Adds two numbers, returns an error on overflow. 62 | */ 63 | function addUInt(uint a, uint b) internal pure returns (MathError, uint) { 64 | uint c = a + b; 65 | 66 | if (c >= a) { 67 | return (MathError.NO_ERROR, c); 68 | } else { 69 | return (MathError.INTEGER_OVERFLOW, 0); 70 | } 71 | } 72 | 73 | /** 74 | * @dev add a and b and then subtract c 75 | */ 76 | function addThenSubUInt(uint a, uint b, uint c) internal pure returns (MathError, uint) { 77 | (MathError err0, uint sum) = addUInt(a, b); 78 | 79 | if (err0 != MathError.NO_ERROR) { 80 | return (err0, 0); 81 | } 82 | 83 | return subUInt(sum, c); 84 | } 85 | } -------------------------------------------------------------------------------- /compound-protocol/scenario/src/Event/ExpectationEvent.ts: -------------------------------------------------------------------------------- 1 | import {Event} from '../Event'; 2 | import {addExpectation, World} from '../World'; 3 | import { 4 | EventV, 5 | NumberV, 6 | Value 7 | } from '../Value'; 8 | import { 9 | getCoreValue, 10 | getEventV, 11 | getNumberV 12 | } from '../CoreValue'; 13 | import {Invariant} from '../Invariant'; 14 | import {ChangesExpectation} from '../Expectation/ChangesExpectation'; 15 | import {RemainsExpectation} from '../Expectation/RemainsExpectation'; 16 | import {formatEvent} from '../Formatter'; 17 | import {Arg, View, processCommandEvent} from '../Command'; 18 | 19 | async function changesExpectation(world: World, condition: Event, delta: Value): Promise { 20 | const value = await getCoreValue(world, condition); 21 | const expectation = new ChangesExpectation(condition, value, delta); 22 | 23 | return addExpectation(world, expectation); 24 | } 25 | 26 | async function remainsExpectation(world: World, condition: Event, value: Value): Promise { 27 | const expectation = new RemainsExpectation(condition, value); 28 | 29 | // Immediately check value matches 30 | await expectation.checker(world, true); 31 | 32 | return addExpectation(world, expectation); 33 | } 34 | 35 | export function expectationCommands() { 36 | return [ 37 | new View<{condition: EventV, delta: NumberV}>(` 38 | #### Changes 39 | 40 | * "Changes amount:" - Expects that given value changes by amount 41 | * E.g ."Expect Changes (CToken cZRX UnderlyingBalance Geoff) +10e18" 42 | `, 43 | "Changes", 44 | [ 45 | new Arg("condition", getEventV), 46 | new Arg("delta", getNumberV) 47 | ], 48 | (world, {condition, delta}) => changesExpectation(world, condition.val, delta) 49 | ), 50 | new View<{condition: EventV, value: Value}>(` 51 | #### Remains 52 | 53 | * "Expect Remains " - Ensures that the given condition starts at and remains a given value 54 | * E.g ."Expect Remains (CToken cZRX UnderlyingBalance Geoff) (Exactly 0)" 55 | `, 56 | "Remains", 57 | [ 58 | new Arg("condition", getEventV), 59 | new Arg("value", getCoreValue) 60 | ], 61 | (world, {condition, value}) => remainsExpectation(world, condition.val, value) 62 | ) 63 | ]; 64 | } 65 | 66 | export async function processExpectationEvent(world: World, event: Event, from: string | null): Promise { 67 | return await processCommandEvent("Expectation", expectationCommands(), world, event, from); 68 | } 69 | -------------------------------------------------------------------------------- /compound-aragon-app/app/src/hooks/app-logic.js: -------------------------------------------------------------------------------- 1 | import {deposit, supplyToken, redeemToken, setAgent, withdraw} from "../web3/CompoundContract"; 2 | import {useApi, useAppState} from "@aragon/api-react"; 3 | import {useCallback} from 'react' 4 | import {useSidePanel} from "./side-panels"; 5 | import {useTabs} from "./tabs"; 6 | import {useSupplyState} from "./supply"; 7 | import {useRedeemState} from "./redeem-panel"; 8 | import {useTransferState} from "./transfer-panels"; 9 | 10 | const useSetAgentAddress = (onDone) => { 11 | const api = useApi() 12 | 13 | return useCallback(address => { 14 | setAgent(api, address) 15 | onDone() 16 | }, [api, onDone]) 17 | } 18 | 19 | const useDeposit = (onDone) => { 20 | const api = useApi() 21 | 22 | return useCallback((token, amount, decimals) => { 23 | deposit(api, token, amount, decimals) 24 | onDone() 25 | }, [api, onDone]) 26 | } 27 | 28 | const useWithdraw = (onDone) => { 29 | const api = useApi() 30 | 31 | return useCallback((token, recipient, amount, decimals) => { 32 | withdraw(api, token, recipient, amount, decimals) 33 | onDone() 34 | }, [api, onDone]) 35 | } 36 | 37 | const useSupply = (onDone) => { 38 | const api = useApi() 39 | 40 | return useCallback((amount) => { 41 | supplyToken(api, amount) 42 | onDone() 43 | }, [api]) 44 | } 45 | 46 | const useRedeem = (onDone) => { 47 | const api = useApi() 48 | 49 | return useCallback((amount, redeemAll) => { 50 | redeemToken(api, amount, redeemAll) 51 | onDone() 52 | }, [api]) 53 | } 54 | 55 | export function useAppLogic() { 56 | const { 57 | isSyncing, 58 | appAddress, 59 | agentAddress, 60 | } = useAppState() 61 | 62 | const supplyState = useSupplyState() 63 | const redeemPanelState = useRedeemState() 64 | const transferPanelsState = useTransferState() 65 | const settings = {appAddress, agentAddress} 66 | 67 | const sidePanel = useSidePanel() 68 | const tabs = useTabs() 69 | 70 | const actions = { 71 | setAgentAddress: useSetAgentAddress(sidePanel.requestClose), 72 | deposit: useDeposit(sidePanel.requestClose), 73 | withdraw: useWithdraw(sidePanel.requestClose), 74 | supply: useSupply(sidePanel.requestClose), 75 | redeem: useRedeem(sidePanel.requestClose) 76 | } 77 | 78 | return { 79 | isSyncing, 80 | actions, 81 | sidePanel, 82 | tabs, 83 | supplyState, 84 | settings, 85 | redeemPanelState, 86 | transferPanelsState 87 | } 88 | } --------------------------------------------------------------------------------