├── .nvmrc ├── .dockerignore ├── .github ├── PULL_REQUEST_TEMPLATE.md ├── CODEOWNERS ├── workflows │ ├── lint.yaml │ ├── board.yaml │ ├── check-payment-code.yml │ └── deploy.yaml ├── issue-close-app.yml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── lock.yml ├── app ├── containers │ ├── Cream │ │ ├── reducer.js │ │ ├── Loadable.js │ │ ├── constants.js │ │ └── actions.js │ ├── Vaults │ │ ├── futureMigrationWhitelist.json │ │ ├── hackedEmergencyWhitelist.json │ │ ├── apyOverrides.js │ │ ├── Loadable.js │ │ ├── blacklist.json │ │ ├── hooks.js │ │ ├── migrationWhitelist.example │ │ ├── retiredWhitelist.json │ │ └── selectors.js │ ├── App │ │ ├── updates.json │ │ ├── constants.js │ │ ├── tests │ │ │ ├── __snapshots__ │ │ │ │ └── index.test.js.snap │ │ │ └── index.test.js │ │ └── actions.js │ ├── DrizzleProvider │ │ ├── context.js │ │ ├── actions.js │ │ ├── constants.js │ │ ├── hooks.js │ │ └── index.js │ ├── ModalProvider │ │ ├── context.js │ │ └── hooks.js │ ├── ConnectionProvider │ │ ├── context.js │ │ ├── constants.js │ │ ├── selectors.js │ │ ├── actions.js │ │ ├── reducer.js │ │ └── hooks.js │ ├── DevMode │ │ ├── constants.js │ │ ├── actions.js │ │ ├── selectors.js │ │ ├── saga.js │ │ ├── index.js │ │ └── reducer.js │ ├── LiteVaults │ │ ├── constants.js │ │ ├── Loadable.js │ │ ├── actions.js │ │ └── hooks.js │ ├── LanguageProvider │ │ ├── constants.js │ │ ├── actions.js │ │ ├── tests │ │ │ ├── selectors.test.js │ │ │ ├── actions.test.js │ │ │ ├── reducer.test.js │ │ │ └── index.test.js │ │ ├── selectors.js │ │ ├── reducer.js │ │ └── index.js │ ├── Cover │ │ ├── Loadable.js │ │ ├── selectors.js │ │ ├── constants.js │ │ ├── reducer.js │ │ ├── actions.js │ │ └── index.js │ ├── Main │ │ ├── Loadable.js │ │ └── index.js │ ├── Splash │ │ ├── Loadable.js │ │ ├── Splash.stories.js │ │ └── index.js │ ├── Dashboard │ │ ├── Loadable.js │ │ └── index.js │ ├── LiteCover │ │ ├── Loadable.js │ │ ├── constants.js │ │ ├── selectors.js │ │ ├── actions.js │ │ ├── reducer.js │ │ └── index.js │ ├── NotFoundPage │ │ ├── Loadable.js │ │ ├── tests │ │ │ ├── __snapshots__ │ │ │ │ └── index.test.js.snap │ │ │ └── index.test.js │ │ ├── messages.js │ │ └── index.js │ ├── ThemeProvider │ │ ├── constants.js │ │ ├── actions.js │ │ ├── selectors.js │ │ ├── saga.js │ │ ├── toggle.js │ │ ├── index.js │ │ └── reducer.js │ ├── Zapper │ │ ├── constants.js │ │ ├── selectors.js │ │ ├── reducer.js │ │ └── actions.js │ └── PasswordProtector │ │ └── index.js ├── images │ ├── Splash │ │ ├── B.png │ │ ├── smiley.png │ │ ├── hero-bg-jpg.jpg │ │ ├── hero-bg-png.png │ │ ├── triangle.svg │ │ ├── rectangle.svg │ │ ├── square.svg │ │ ├── red-rectangle.svg │ │ ├── yellow-square.svg │ │ ├── yellow-arc.svg │ │ ├── cirlce.svg │ │ ├── arc.svg │ │ ├── green-circle.svg │ │ ├── blue-arc.svg │ │ └── b.svg │ ├── favicon.ico │ ├── hero-bg.png │ ├── y-vaults.png │ ├── background.jpg │ ├── focused-defi.png │ ├── icon-512x512.png │ ├── navbar-mobile-bg.png │ ├── security-graphic.png │ ├── twitter-logo.svg │ ├── discord-logo.svg │ ├── medium-logo.svg │ ├── github-logo.svg │ └── lazy-ape-logo.svg ├── components │ ├── Table │ │ └── context.js │ ├── Icon │ │ ├── arrowRight.svg │ │ ├── arrowDownAlt.svg │ │ ├── arrowUpAlt.svg │ │ ├── close.svg │ │ ├── shine.svg │ │ ├── chevronRight.svg │ │ ├── chevronLeft.svg │ │ ├── stats.svg │ │ ├── info.svg │ │ ├── arrowDown.svg │ │ ├── externalLink.svg │ │ ├── externalLinkBlack.svg │ │ ├── copy.svg │ │ ├── clock.svg │ │ ├── index.js │ │ └── bluePill.svg │ ├── Navbar │ │ └── Navbar.stories.js │ ├── Label │ │ └── index.js │ ├── Vault │ │ ├── columns.js │ │ ├── amplifyColumns.js │ │ └── columnsDev.js │ ├── SplashScreen │ │ ├── Hero │ │ │ └── shapes │ │ │ │ └── Triangle.js │ │ ├── index.js │ │ └── Security │ │ │ └── index.js │ ├── AnimatedNumber │ │ └── index.js │ ├── SectionHeader │ │ └── index.js │ ├── CreamCard │ │ └── index.js │ ├── TailwindButton │ │ └── index.js │ ├── Notify │ │ ├── Notify.stories.js │ │ └── GlobalNotifyStyles.js │ ├── VaultsHeaderDev │ │ └── index.js │ ├── Header │ │ └── index.js │ ├── BackscratchersHeaders │ │ └── index.js │ ├── CoverProtocolHeader │ │ └── index.js │ ├── Text │ │ └── index.js │ ├── Input │ │ └── index.js │ ├── VaultTop │ │ └── index.js │ ├── VaultTopDev │ │ └── index.js │ ├── Box │ │ └── index.js │ ├── CoverProtocol │ │ └── index.js │ ├── TokenIcon │ │ └── index.js │ ├── Button │ │ └── index.js │ ├── BlueOutlineCard │ │ └── index.js │ ├── LearnMoreCard │ │ └── LearnMoreCard.stories.js │ ├── BackLink │ │ └── index.js │ ├── InfoCard │ │ └── index.js │ ├── VaultControls │ │ └── SvgArrow.js │ ├── CreamProgressBar │ │ └── index.js │ ├── ButtonFilledRed │ │ └── index.js │ ├── IconButton │ │ └── index.js │ ├── Modal │ │ └── index.js │ ├── VaultsNavLinks │ │ └── index.js │ ├── LiteHeader │ │ └── index.js │ ├── TabbedNavigation │ │ └── index.js │ ├── AddVault │ │ └── index.js │ └── ConnectButton │ │ └── index.js ├── fonts │ ├── OpenSans-Bold.ttf │ ├── OpenSans-Italic.ttf │ ├── OpenSans-Light.ttf │ ├── OpenSans-Regular.ttf │ ├── OpenSans-SemiBold.ttf │ ├── OpenSans-BoldItalic.ttf │ ├── OpenSans-ExtraBold.ttf │ ├── OpenSans-LightItalic.ttf │ ├── OpenSans-ExtraBoldItalic.ttf │ └── OpenSans-SemiBoldItalic.ttf ├── utils │ ├── history.js │ ├── constants.js │ ├── loadable.js │ ├── setDecimals.js │ ├── nFormat.js │ ├── checkStore.js │ ├── drizzle.js │ ├── table.js │ ├── tests │ │ └── checkStore.test.js │ ├── permit.js │ ├── reducerInjectors.js │ ├── injectReducer.js │ └── request.js ├── drizzle │ └── store │ │ ├── contracts │ │ ├── constants.js │ │ ├── contractsSelectors.js │ │ ├── contractsSubscriptionsReducer.js │ │ └── contractsActions.js │ │ ├── rootSaga.js │ │ ├── mergeOptions.js │ │ ├── blocks │ │ └── blocksReducer.js │ │ ├── drizzleStatus │ │ └── drizzleStatusReducer.js │ │ ├── defaultOptions.js │ │ ├── web3 │ │ ├── constants.js │ │ └── web3Reducer.js │ │ ├── transactions │ │ ├── transactionStackReducer.js │ │ └── transactionsReducer.js │ │ ├── contractStateUtils.js │ │ ├── index.js │ │ └── drizzle-middleware.js ├── middleware │ └── websocket │ │ ├── constants.js │ │ ├── selectors.js │ │ ├── actions.js │ │ ├── index.js │ │ └── connection.js ├── wdyr.js ├── translations │ └── en.json ├── abi │ ├── triCryptoVaultMigrator.json │ ├── v2EthZapAbi.json │ ├── zapv23crv.json │ ├── zapViper.json │ ├── minimalErc20.json │ └── minimalVault.json ├── reducers.js ├── tests │ ├── i18n.test.js │ └── store.test.js ├── index.html └── i18n.js ├── internals ├── generators │ ├── language │ │ ├── translations-json.hbs │ │ ├── app-locale.hbs │ │ ├── add-locale-data.hbs │ │ ├── polyfill-intl-locale.hbs │ │ ├── intl-locale-data.hbs │ │ ├── translation-messages.hbs │ │ └── format-translation-messages.hbs │ ├── container │ │ ├── constants.js.hbs │ │ ├── saga.js.hbs │ │ ├── actions.js.hbs │ │ ├── selectors.test.js.hbs │ │ ├── actions.test.js.hbs │ │ ├── messages.js.hbs │ │ ├── saga.test.js.hbs │ │ ├── reducer.js.hbs │ │ ├── selectors.js.hbs │ │ ├── reducer.test.js.hbs │ │ └── test.js.hbs │ ├── component │ │ ├── loadable.js.hbs │ │ ├── messages.js.hbs │ │ ├── index.js.hbs │ │ └── test.js.hbs │ └── utils │ │ └── componentExists.js ├── mocks │ ├── cssModule.js │ └── image.js ├── scripts │ ├── helpers │ │ ├── get-npm-config.js │ │ ├── xmark.js │ │ ├── checkmark.js │ │ └── progress.js │ ├── npmcheckversion.js │ └── analyze.js ├── testing │ └── test-bundler.js └── webpack │ └── webpack.dev.babel.js ├── server ├── argv.js ├── port.js ├── middlewares │ ├── frontendMiddleware.js │ ├── addProdMiddlewares.js │ └── addDevMiddlewares.js └── logger.js ├── commitlint.config.js ├── .storybook ├── preview-head.html ├── Layout.js ├── main.js └── preview.js ├── .prettierignore ├── .prettierrc ├── Dockerfile ├── .stylelintrc ├── postcss.config.js ├── .editorconfig ├── .gitignore ├── .travis.yml ├── .env.sample ├── jsconfig.json ├── run_mainnet_fork.sh ├── docker-compose.yml ├── jest.config.js ├── babel.config.js ├── LICENSE.md └── appveyor.yml /.nvmrc: -------------------------------------------------------------------------------- 1 | v14.16.0 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/containers/Cream/reducer.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/containers/Vaults/futureMigrationWhitelist.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /app/containers/Vaults/hackedEmergencyWhitelist.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /internals/generators/language/translations-json.hbs: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /internals/mocks/cssModule.js: -------------------------------------------------------------------------------- 1 | module.exports = 'CSS_MODULE'; 2 | -------------------------------------------------------------------------------- /internals/mocks/image.js: -------------------------------------------------------------------------------- 1 | module.exports = 'IMAGE_MOCK'; 2 | -------------------------------------------------------------------------------- /internals/generators/language/app-locale.hbs: -------------------------------------------------------------------------------- 1 | $1 '{{language}}', 2 | -------------------------------------------------------------------------------- /server/argv.js: -------------------------------------------------------------------------------- 1 | module.exports = require('minimist')(process.argv.slice(2)); 2 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ['@commitlint/config-conventional'] }; 2 | -------------------------------------------------------------------------------- /internals/generators/language/add-locale-data.hbs: -------------------------------------------------------------------------------- 1 | $1addLocaleData({{language}}LocaleData); 2 | -------------------------------------------------------------------------------- /app/containers/App/updates.json: -------------------------------------------------------------------------------- 1 | { 2 | "lastSplashPageUpdate": "2021-03-01T20:00:34.646Z" 3 | } 4 | -------------------------------------------------------------------------------- /app/images/Splash/B.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yearn/yearn-finance/HEAD/app/images/Splash/B.png -------------------------------------------------------------------------------- /app/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yearn/yearn-finance/HEAD/app/images/favicon.ico -------------------------------------------------------------------------------- /app/images/hero-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yearn/yearn-finance/HEAD/app/images/hero-bg.png -------------------------------------------------------------------------------- /app/images/y-vaults.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yearn/yearn-finance/HEAD/app/images/y-vaults.png -------------------------------------------------------------------------------- /app/images/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yearn/yearn-finance/HEAD/app/images/background.jpg -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # default reviewer for all pull requests 2 | * @x48-crypto @nymmrx @dudesahn @xgambitox 3 | -------------------------------------------------------------------------------- /app/components/Table/context.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | export default createContext({}); 3 | -------------------------------------------------------------------------------- /app/fonts/OpenSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yearn/yearn-finance/HEAD/app/fonts/OpenSans-Bold.ttf -------------------------------------------------------------------------------- /app/fonts/OpenSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yearn/yearn-finance/HEAD/app/fonts/OpenSans-Italic.ttf -------------------------------------------------------------------------------- /app/fonts/OpenSans-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yearn/yearn-finance/HEAD/app/fonts/OpenSans-Light.ttf -------------------------------------------------------------------------------- /app/images/Splash/smiley.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yearn/yearn-finance/HEAD/app/images/Splash/smiley.png -------------------------------------------------------------------------------- /app/images/focused-defi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yearn/yearn-finance/HEAD/app/images/focused-defi.png -------------------------------------------------------------------------------- /app/images/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yearn/yearn-finance/HEAD/app/images/icon-512x512.png -------------------------------------------------------------------------------- /app/fonts/OpenSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yearn/yearn-finance/HEAD/app/fonts/OpenSans-Regular.ttf -------------------------------------------------------------------------------- /app/fonts/OpenSans-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yearn/yearn-finance/HEAD/app/fonts/OpenSans-SemiBold.ttf -------------------------------------------------------------------------------- /app/images/navbar-mobile-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yearn/yearn-finance/HEAD/app/images/navbar-mobile-bg.png -------------------------------------------------------------------------------- /app/images/security-graphic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yearn/yearn-finance/HEAD/app/images/security-graphic.png -------------------------------------------------------------------------------- /internals/generators/language/polyfill-intl-locale.hbs: -------------------------------------------------------------------------------- 1 | $1 import('intl/locale-data/jsonp/{{language}}.js'), 2 | -------------------------------------------------------------------------------- /app/containers/DrizzleProvider/context.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | export default createContext({}); 3 | -------------------------------------------------------------------------------- /app/containers/ModalProvider/context.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | export default createContext({}); 3 | -------------------------------------------------------------------------------- /app/fonts/OpenSans-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yearn/yearn-finance/HEAD/app/fonts/OpenSans-BoldItalic.ttf -------------------------------------------------------------------------------- /app/fonts/OpenSans-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yearn/yearn-finance/HEAD/app/fonts/OpenSans-ExtraBold.ttf -------------------------------------------------------------------------------- /app/fonts/OpenSans-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yearn/yearn-finance/HEAD/app/fonts/OpenSans-LightItalic.ttf -------------------------------------------------------------------------------- /app/images/Splash/hero-bg-jpg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yearn/yearn-finance/HEAD/app/images/Splash/hero-bg-jpg.jpg -------------------------------------------------------------------------------- /app/images/Splash/hero-bg-png.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yearn/yearn-finance/HEAD/app/images/Splash/hero-bg-png.png -------------------------------------------------------------------------------- /app/containers/ConnectionProvider/context.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | export default createContext({}); 3 | -------------------------------------------------------------------------------- /app/fonts/OpenSans-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yearn/yearn-finance/HEAD/app/fonts/OpenSans-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /app/fonts/OpenSans-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yearn/yearn-finance/HEAD/app/fonts/OpenSans-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /server/port.js: -------------------------------------------------------------------------------- 1 | const argv = require('./argv'); 2 | 3 | module.exports = parseInt(argv.port || process.env.PORT || '3000', 10); 4 | -------------------------------------------------------------------------------- /internals/generators/language/intl-locale-data.hbs: -------------------------------------------------------------------------------- 1 | $&const {{language}}LocaleData = require('react-intl/locale-data/{{language}}'); 2 | -------------------------------------------------------------------------------- /.storybook/preview-head.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | build/ 2 | node_modules/ 3 | internals/generators/ 4 | internals/scripts/ 5 | package-lock.json 6 | yarn.lock 7 | package.json 8 | -------------------------------------------------------------------------------- /app/utils/history.js: -------------------------------------------------------------------------------- 1 | import { createBrowserHistory } from 'history'; 2 | const history = createBrowserHistory(); 3 | export default history; 4 | -------------------------------------------------------------------------------- /internals/generators/language/translation-messages.hbs: -------------------------------------------------------------------------------- 1 | $1const {{language}}TranslationMessages = require('./translations/{{language}}.json'); 2 | -------------------------------------------------------------------------------- /internals/scripts/helpers/get-npm-config.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | module.exports = JSON.parse(fs.readFileSync('package.json', 'utf8')); 4 | -------------------------------------------------------------------------------- /.storybook/Layout.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Layout = ({ children }) => { 4 | return children; 5 | }; 6 | 7 | export default Layout; 8 | -------------------------------------------------------------------------------- /internals/testing/test-bundler.js: -------------------------------------------------------------------------------- 1 | // needed for regenerator-runtime 2 | // (ES7 generator support is required by redux-saga) 3 | import '@babel/polyfill'; 4 | -------------------------------------------------------------------------------- /internals/generators/language/format-translation-messages.hbs: -------------------------------------------------------------------------------- 1 | $1 {{language}}: formatTranslationMessages('{{language}}', {{language}}TranslationMessages), 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "trailingComma": "all" 8 | } 9 | -------------------------------------------------------------------------------- /app/containers/DevMode/constants.js: -------------------------------------------------------------------------------- 1 | export const TOGGLE_DEV_MODE = 'app/DevMode/TOGGLE_DEV_MODE'; 2 | export const UNLOCK_DEV_MODE = 'app/DevMode/UNLOCK_DEV_MODE'; 3 | -------------------------------------------------------------------------------- /app/containers/LiteVaults/constants.js: -------------------------------------------------------------------------------- 1 | export const VAULTS_LOADED = 'VAULTS_LOADED'; 2 | export const USER_VAULT_STATISTICS_LOADED = 'USER_VAULT_STATISTICS_LOADED '; 3 | -------------------------------------------------------------------------------- /app/containers/LanguageProvider/constants.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * LanguageProvider constants 4 | * 5 | */ 6 | 7 | export const CHANGE_LOCALE = 'app/LanguageToggle/CHANGE_LOCALE'; 8 | -------------------------------------------------------------------------------- /app/drizzle/store/contracts/constants.js: -------------------------------------------------------------------------------- 1 | export const EVENT_FIRED = 'EVENT_FIRED'; 2 | export const EVENT_CHANGED = 'EVENT_CHANGED'; 3 | export const EVENT_ERROR = 'EVENT_ERROR'; 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:erbium 2 | 3 | RUN mkdir -p /app/yearn-finance 4 | WORKDIR /app/yearn-finance 5 | ADD . /app/yearn-finance 6 | RUN yarn install 7 | 8 | ENTRYPOINT ["yarn", "dev"] 9 | -------------------------------------------------------------------------------- /app/containers/ModalProvider/hooks.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import context from './context'; 3 | 4 | export function useModal() { 5 | return useContext(context); 6 | } 7 | -------------------------------------------------------------------------------- /app/containers/Vaults/apyOverrides.js: -------------------------------------------------------------------------------- 1 | import { USDC_V2_VAULT_ADDRESS, APY_NEW } from 'containers/Vaults/constants'; 2 | 3 | export default { 4 | [USDC_V2_VAULT_ADDRESS]: APY_NEW, 5 | }; 6 | -------------------------------------------------------------------------------- /internals/generators/container/constants.js.hbs: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * {{ properCase name }} constants 4 | * 5 | */ 6 | 7 | export const DEFAULT_ACTION = 'app/{{ properCase name }}/DEFAULT_ACTION'; 8 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "processors": ["stylelint-processor-styled-components"], 3 | "extends": [ 4 | "stylelint-config-recommended", 5 | "stylelint-config-styled-components" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /app/containers/Cover/Loadable.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Asynchronously loads the component for HomePage 3 | */ 4 | 5 | import loadable from 'utils/loadable'; 6 | 7 | export default loadable(() => import('./index')); 8 | -------------------------------------------------------------------------------- /app/containers/Cream/Loadable.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Asynchronously loads the component for HomePage 3 | */ 4 | 5 | import loadable from 'utils/loadable'; 6 | 7 | export default loadable(() => import('./index')); 8 | -------------------------------------------------------------------------------- /app/containers/Main/Loadable.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Asynchronously loads the component for HomePage 3 | */ 4 | 5 | import loadable from 'utils/loadable'; 6 | 7 | export default loadable(() => import('./index')); 8 | -------------------------------------------------------------------------------- /app/containers/Splash/Loadable.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Asynchronously loads the component for HomePage 3 | */ 4 | 5 | import loadable from 'utils/loadable'; 6 | 7 | export default loadable(() => import('./index')); 8 | -------------------------------------------------------------------------------- /app/containers/Vaults/Loadable.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Asynchronously loads the component for HomePage 3 | */ 4 | 5 | import loadable from 'utils/loadable'; 6 | 7 | export default loadable(() => import('./index')); 8 | -------------------------------------------------------------------------------- /app/containers/Dashboard/Loadable.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Asynchronously loads the component for HomePage 3 | */ 4 | 5 | import loadable from 'utils/loadable'; 6 | 7 | export default loadable(() => import('./index')); 8 | -------------------------------------------------------------------------------- /app/containers/LiteCover/Loadable.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Asynchronously loads the component for HomePage 3 | */ 4 | 5 | import loadable from 'utils/loadable'; 6 | 7 | export default loadable(() => import('./index')); 8 | -------------------------------------------------------------------------------- /app/containers/LiteVaults/Loadable.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Asynchronously loads the component for HomePage 3 | */ 4 | 5 | import loadable from 'utils/loadable'; 6 | 7 | export default loadable(() => import('./index')); 8 | -------------------------------------------------------------------------------- /app/containers/ConnectionProvider/constants.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Connection constants 4 | * 5 | */ 6 | export const CONNECTION_CONNECTED = 'CONNECTION_CONNECTED'; 7 | export const ACCOUNT_UPDATED = 'ACCOUNT_UPDATED'; 8 | -------------------------------------------------------------------------------- /app/containers/NotFoundPage/Loadable.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Asynchronously loads the component for NotFoundPage 3 | */ 4 | 5 | import loadable from 'utils/loadable'; 6 | 7 | export default loadable(() => import('./index')); 8 | -------------------------------------------------------------------------------- /app/utils/constants.js: -------------------------------------------------------------------------------- 1 | export const RESTART_ON_REMOUNT = '@@saga-injector/restart-on-remount'; 2 | export const DAEMON = '@@saga-injector/daemon'; 3 | export const ONCE_TILL_UNMOUNT = '@@saga-injector/once-till-unmount'; 4 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | // eslint-disable-next-line 4 | require('tailwindcss')('./app/tailwind.config.js'), 5 | // eslint-disable-next-line 6 | require('autoprefixer'), 7 | ], 8 | }; 9 | -------------------------------------------------------------------------------- /app/containers/Vaults/blacklist.json: -------------------------------------------------------------------------------- 1 | [ 2 | "0x39546945695DCb1c037C836925B355262f551f55", 3 | "0x28a5b95C101df3Ded0C0d9074DB80C438774B6a9", 4 | "0x3D27705c64213A5DcD9D26880c1BcFa72d5b6B0E", 5 | "0x80bbeE2fa460dA291e796B9045e93d19eF948C6A" 6 | ] 7 | -------------------------------------------------------------------------------- /internals/generators/component/loadable.js.hbs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Asynchronously loads the component for {{ properCase name }} 4 | * 5 | */ 6 | 7 | import loadable from 'utils/loadable'; 8 | 9 | export default loadable(() => import('./index')); 10 | -------------------------------------------------------------------------------- /internals/generators/container/saga.js.hbs: -------------------------------------------------------------------------------- 1 | // import { take, call, put, select } from 'redux-saga/effects'; 2 | 3 | // Individual exports for testing 4 | export default function* {{ camelCase name }}Saga() { 5 | // See example in containers/HomePage/saga.js 6 | } 7 | -------------------------------------------------------------------------------- /app/components/Icon/arrowRight.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/drizzle/store/contracts/contractsSelectors.js: -------------------------------------------------------------------------------- 1 | import { createSelector } from 'reselect'; 2 | 3 | const selectContracts = (state) => state.subscriptions; 4 | 5 | export const selectContractsSubscriptions = () => 6 | createSelector(selectContracts, (substate) => substate); 7 | -------------------------------------------------------------------------------- /app/drizzle/store/rootSaga.js: -------------------------------------------------------------------------------- 1 | import blocksSaga from './blocks/blocksSaga'; 2 | import contractsSaga from './contracts/contractsSaga'; 3 | import drizzleStatusSaga from './drizzleStatus/drizzleStatusSaga'; 4 | 5 | export default [blocksSaga, contractsSaga, drizzleStatusSaga]; 6 | -------------------------------------------------------------------------------- /internals/generators/container/actions.js.hbs: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * {{ properCase name }} actions 4 | * 5 | */ 6 | 7 | import { DEFAULT_ACTION } from './constants'; 8 | 9 | export function defaultAction() { 10 | return { 11 | type: DEFAULT_ACTION, 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /app/containers/ConnectionProvider/selectors.js: -------------------------------------------------------------------------------- 1 | import { createSelector } from 'reselect'; 2 | 3 | const selectConnection = (state) => state.connection; 4 | 5 | export const selectAccount = () => 6 | createSelector(selectConnection, (substate) => substate && substate.account); 7 | -------------------------------------------------------------------------------- /app/images/Splash/triangle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /internals/scripts/helpers/xmark.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | 3 | /** 4 | * Adds mark cross symbol 5 | */ 6 | function addXMark(callback) { 7 | process.stdout.write(chalk.red(' ✘')); 8 | if (callback) callback(); 9 | } 10 | 11 | module.exports = addXMark; 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | insert_final_newline = true 9 | indent_style = space 10 | indent_size = 2 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /app/drizzle/store/mergeOptions.js: -------------------------------------------------------------------------------- 1 | import merge from 'deepmerge'; 2 | const isPlainObject = require('is-plain-object'); 3 | 4 | export default function (defaultOptions, newOptions) { 5 | return merge(defaultOptions, newOptions, { 6 | isMergeableObject: isPlainObject, 7 | }); 8 | } 9 | -------------------------------------------------------------------------------- /app/images/Splash/rectangle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/images/Splash/square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/containers/NotFoundPage/tests/__snapshots__/index.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` should render and match the snapshot 1`] = ` 4 |

5 | 6 | This is the NotFoundPage container! 7 | 8 |

9 | `; 10 | -------------------------------------------------------------------------------- /app/middleware/websocket/constants.js: -------------------------------------------------------------------------------- 1 | export const WEBSOCKET_CONNECT = 'WEBSOCKET_CONNECT'; 2 | export const WEBSOCKET_CONNECTED = 'WEBSOCKET_CONNECTED'; 3 | export const WEBSOCKET_DISCONNECTED = 'WEBSOCKET_DISCONNECTED'; 4 | export const WEBSOCKET_MESSAGE_RECEIVED = 'WEBSOCKET_MESSAGE_RECEIVED'; 5 | -------------------------------------------------------------------------------- /internals/generators/container/selectors.test.js.hbs: -------------------------------------------------------------------------------- 1 | // import { select{{ properCase name }}Domain } from '../selectors'; 2 | 3 | describe('select{{ properCase name }}Domain', () => { 4 | it('Expect to have unit tests specified', () => { 5 | expect(true).toEqual(false); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /internals/scripts/helpers/checkmark.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | 3 | /** 4 | * Adds mark check symbol 5 | */ 6 | function addCheckMark(callback) { 7 | process.stdout.write(chalk.green(' ✓')); 8 | if (callback) callback(); 9 | } 10 | 11 | module.exports = addCheckMark; 12 | -------------------------------------------------------------------------------- /app/drizzle/store/blocks/blocksReducer.js: -------------------------------------------------------------------------------- 1 | const initialState = {}; 2 | 3 | const blocksReducer = (state = initialState, action) => { 4 | if (action.type === 'BLOCK_PROCESSING') { 5 | return action.block; 6 | } 7 | 8 | return state; 9 | }; 10 | 11 | export default blocksReducer; 12 | -------------------------------------------------------------------------------- /app/images/Splash/red-rectangle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/images/Splash/yellow-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/wdyr.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | if (process.env.NODE_ENV === 'development') { 4 | // eslint-disable-next-line 5 | const whyDidYouRender = require('@welldone-software/why-did-you-render'); 6 | whyDidYouRender(React, { 7 | trackAllPureComponents: false, 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /app/components/Navbar/Navbar.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Navbar } from '.'; 4 | 5 | export default { 6 | title: 'V2/Navbar', 7 | component: Navbar, 8 | }; 9 | 10 | const Template = (args) => ; 11 | 12 | export const Default = Template.bind({}); 13 | -------------------------------------------------------------------------------- /app/containers/Splash/Splash.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Splash } from '.'; 4 | 5 | export default { 6 | title: 'V2/Splash', 7 | component: Splash, 8 | }; 9 | 10 | const Template = (args) => ; 11 | 12 | export const Default = Template.bind({}); 13 | -------------------------------------------------------------------------------- /app/images/Splash/yellow-arc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/components/Icon/arrowDownAlt.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/components/Icon/arrowUpAlt.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/containers/DevMode/actions.js: -------------------------------------------------------------------------------- 1 | import { TOGGLE_DEV_MODE, UNLOCK_DEV_MODE } from './constants'; 2 | 3 | export function toggleDevMode() { 4 | return { 5 | type: TOGGLE_DEV_MODE, 6 | }; 7 | } 8 | 9 | export function unlockDevMode() { 10 | return { 11 | type: UNLOCK_DEV_MODE, 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /app/containers/LanguageProvider/actions.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * LanguageProvider actions 4 | * 5 | */ 6 | 7 | import { CHANGE_LOCALE } from './constants'; 8 | 9 | export function changeLocale(languageLocale) { 10 | return { 11 | type: CHANGE_LOCALE, 12 | locale: languageLocale, 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /app/images/Splash/cirlce.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/translations/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "splash.enterButton": "YOLO", 3 | "dashboard.products": "products", 4 | "dashboard.stats": "stats", 5 | "dashboard.gov": "gov", 6 | "dashboard.labs": "labs", 7 | "dashboard.community": "community", 8 | "dashboard.docs": "docs", 9 | "account.connect": "connect" 10 | } 11 | -------------------------------------------------------------------------------- /app/containers/ThemeProvider/constants.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Theme constants 4 | * 5 | */ 6 | export const TOGGLE_DARK_MODE = 'app/ThemeToggle/TOGGLE_DARK_MODE'; 7 | export const SET_THEME_MODE = 'app/ThemeToggle/SET_MODE'; 8 | 9 | export const DARK_MODE = 'DARK_MODE'; 10 | export const LIGHT_MODE = 'LIGHT_MODE'; 11 | -------------------------------------------------------------------------------- /app/components/Icon/close.svg: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /app/images/Splash/arc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Don't check auto-generated stuff into git 2 | coverage 3 | build 4 | node_modules 5 | stats.json 6 | .env 7 | package-lock.json 8 | 9 | # Cruft 10 | .DS_Store 11 | npm-debug.log 12 | .idea 13 | # Outputed css 14 | app/assets/styles.css 15 | 16 | ## brownie 17 | __pycache__ 18 | 19 | # yearn-mainnet-fork repo 20 | yearn-mainnet-fork 21 | -------------------------------------------------------------------------------- /app/containers/App/constants.js: -------------------------------------------------------------------------------- 1 | export const VAULTS_LOADED = 'VAULTS_LOADED'; 2 | export const APP_READY = 'APP_READY'; 3 | export const APP_INITIALIZED = 'APP_INITIALIZED'; 4 | 5 | export const DRIZZLE_ADD_CONTRACTS = 'DRIZZLE_ADD_CONTRACTS'; 6 | 7 | export const ROUTE_CHANGED = 'ROUTE_CHANGED'; 8 | export const SPLASH_PAGE_VISITED = 'SPLASH_PAGE_VISITED'; 9 | -------------------------------------------------------------------------------- /app/containers/LiteVaults/actions.js: -------------------------------------------------------------------------------- 1 | import * as c from './constants'; 2 | 3 | export function userVaultStatisticsLoaded(vaults) { 4 | return { 5 | type: c.USER_VAULT_STATISTICS_LOADED, 6 | vaults, 7 | }; 8 | } 9 | 10 | export function vaultsLoaded(vaults) { 11 | return { 12 | type: c.VAULTS_LOADED, 13 | vaults, 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /app/images/Splash/green-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/abi/triCryptoVaultMigrator.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "stateMutability": "nonpayable", 4 | "type": "constructor", 5 | "inputs": [], 6 | "outputs": [] 7 | }, 8 | { 9 | "stateMutability": "nonpayable", 10 | "type": "function", 11 | "name": "migrate_to_new_vault", 12 | "inputs": [], 13 | "outputs": [], 14 | "gas": 59336 15 | } 16 | ] 17 | -------------------------------------------------------------------------------- /app/middleware/websocket/selectors.js: -------------------------------------------------------------------------------- 1 | import { createSelector } from 'reselect'; 2 | 3 | const selectApp = (state) => state.websocket; 4 | 5 | export const selectTransactions = () => 6 | createSelector(selectApp, (substate) => substate.transactions); 7 | 8 | export const selectContractsState = () => 9 | createSelector(selectApp, (substate) => substate.contractsState); 10 | -------------------------------------------------------------------------------- /app/containers/Cover/selectors.js: -------------------------------------------------------------------------------- 1 | import { createSelector } from 'reselect'; 2 | 3 | const selectCover = (state) => state.cover; 4 | 5 | export const selectProtocols = () => 6 | createSelector(selectCover, (substate) => substate && substate.protocols); 7 | 8 | export const selectPoolData = () => 9 | createSelector(selectCover, (substate) => substate && substate.poolData); 10 | -------------------------------------------------------------------------------- /app/containers/LiteCover/constants.js: -------------------------------------------------------------------------------- 1 | import BigNumber from 'bignumber.js'; 2 | 3 | export const INITIALIZE_COVER = 'INITIALIZE_COVER'; 4 | export const COVER_DATA_LOADED = 'COVER_DATA_LOADED'; 5 | export const BUY_COVER = 'BUY_COVER'; 6 | export const DAI_ADDRESS = '0x6B175474E89094C44Da98b954EedeAC495271d0F'; 7 | export const MAX_UINT256 = new BigNumber(2).pow(256).minus(1).toFixed(0); 8 | -------------------------------------------------------------------------------- /app/utils/loadable.js: -------------------------------------------------------------------------------- 1 | import React, { lazy, Suspense } from 'react'; 2 | 3 | const loadable = (importFunc, { fallback = null } = { fallback: null }) => { 4 | const LazyComponent = lazy(importFunc); 5 | 6 | return (props) => ( 7 | 8 | 9 | 10 | ); 11 | }; 12 | 13 | export default loadable; 14 | -------------------------------------------------------------------------------- /app/utils/setDecimals.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Set minimum decimals to show a vault 3 | */ 4 | export default function setDecimals(decimals) { 5 | let minDecimals = decimals; 6 | if (decimals > 6) { 7 | minDecimals = decimals - 4; 8 | } else if (decimals >= 4) { 9 | minDecimals = decimals - 2; 10 | } else { 11 | minDecimals = 0; 12 | } 13 | return minDecimals; 14 | } 15 | -------------------------------------------------------------------------------- /app/containers/LanguageProvider/tests/selectors.test.js: -------------------------------------------------------------------------------- 1 | import { selectLanguage } from '../selectors'; 2 | 3 | describe('selectLanguage', () => { 4 | it('should select the global state', () => { 5 | const globalState = {}; 6 | const mockedState = { 7 | language: globalState, 8 | }; 9 | expect(selectLanguage(mockedState)).toEqual(globalState); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /app/containers/LiteCover/selectors.js: -------------------------------------------------------------------------------- 1 | import { createSelector } from 'reselect'; 2 | 3 | const selectCover = (state) => state.cover; 4 | 5 | export const selectProtocols = () => 6 | createSelector(selectCover, (substate) => substate && substate.protocols); 7 | 8 | export const selectPoolData = () => 9 | createSelector(selectCover, (substate) => substate && substate.poolData); 10 | -------------------------------------------------------------------------------- /internals/scripts/npmcheckversion.js: -------------------------------------------------------------------------------- 1 | const { exec } = require('child_process'); 2 | exec('npm -v', (err, stdout) => { 3 | if (err) throw err; 4 | if (parseFloat(stdout) < 5) { 5 | // NOTE: This can happen if you have a dependency which lists an old version of npm in its own dependencies. 6 | throw new Error(`[ERROR] You need npm version @>=5 but you have ${stdout}`); 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /app/components/Label/index.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Label = styled.label` 4 | color: white; 5 | font-size: ${(props) => (props.fontSize ? `${props.fontSize}px` : 16)}; 6 | margin-bottom: ${(props) => (props.marginBottom ? props.marginBottom : 0)}; 7 | margin-top: ${(props) => (props.marginTop ? props.marginTop : 0)}; 8 | `; 9 | 10 | export default Label; 11 | -------------------------------------------------------------------------------- /app/containers/ThemeProvider/actions.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Theme actions 4 | * 5 | */ 6 | 7 | import { TOGGLE_DARK_MODE, SET_THEME_MODE } from './constants'; 8 | 9 | export function toggleDarkMode() { 10 | return { 11 | type: TOGGLE_DARK_MODE, 12 | }; 13 | } 14 | 15 | export function setThemeMode(mode) { 16 | return { 17 | type: SET_THEME_MODE, 18 | mode, 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /app/containers/Cover/constants.js: -------------------------------------------------------------------------------- 1 | import BigNumber from 'bignumber.js'; 2 | 3 | export const INITIALIZE_COVER = 'INITIALIZE_COVER'; 4 | export const COVER_DATA_LOADED = 'COVER_DATA_LOADED'; 5 | export const BUY_COVER = 'BUY_COVER'; 6 | export const SELL_COVER = 'SELL_COVER'; 7 | export const DAI_ADDRESS = '0x6B175474E89094C44Da98b954EedeAC495271d0F'; 8 | export const MAX_UINT256 = new BigNumber(2).pow(256).minus(1).toFixed(0); 9 | -------------------------------------------------------------------------------- /app/containers/App/tests/__snapshots__/index.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` should render and match the snapshot 1`] = ` 4 |
5 | 6 | 11 | 14 | 15 | 16 |
17 | `; 18 | -------------------------------------------------------------------------------- /app/containers/DrizzleProvider/actions.js: -------------------------------------------------------------------------------- 1 | import { DRIZZLE_ADD_CONTRACTS, ADD_WATCHED_CONTRACTS } from './constants'; 2 | 3 | export function addContracts(contracts, clear) { 4 | return { 5 | type: DRIZZLE_ADD_CONTRACTS, 6 | contracts, 7 | clear, 8 | }; 9 | } 10 | 11 | export function addWatchedContracts(addresses) { 12 | return { 13 | type: ADD_WATCHED_CONTRACTS, 14 | addresses, 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /app/containers/ThemeProvider/selectors.js: -------------------------------------------------------------------------------- 1 | import { createSelector } from 'reselect'; 2 | import { initialState } from './reducer'; 3 | 4 | /** 5 | * Direct selector to the themeToggle state domain 6 | */ 7 | const selectTheme = (state) => state.theme || initialState; 8 | 9 | const selectDarkMode = () => 10 | createSelector(selectTheme, (substate) => substate.darkMode); 11 | 12 | export { selectTheme, selectDarkMode }; 13 | -------------------------------------------------------------------------------- /app/containers/NotFoundPage/messages.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NotFoundPage Messages 3 | * 4 | * This contains all the text for the NotFoundPage container. 5 | */ 6 | import { defineMessages } from 'react-intl'; 7 | 8 | export const scope = 'app.containers.NotFoundPage'; 9 | 10 | export default defineMessages({ 11 | header: { 12 | id: `${scope}.header`, 13 | defaultMessage: 'This is the NotFoundPage container!', 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /app/images/Splash/blue-arc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/components/Vault/columns.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export default styled.div` 4 | display: grid; 5 | grid-template-columns: ${({ gridTemplate }) => 6 | gridTemplate || '210px 110px 160px 140px 200px 1fr'}; 7 | width: 100%; 8 | align-items: center; 9 | > div { 10 | white-space: nowrap; 11 | text-overflow: ellipsis; 12 | &:first-of-type { 13 | margin-left: 15px; 14 | } 15 | } 16 | `; 17 | -------------------------------------------------------------------------------- /app/drizzle/store/drizzleStatus/drizzleStatusReducer.js: -------------------------------------------------------------------------------- 1 | const initialState = { 2 | initialized: false, 3 | }; 4 | 5 | const drizzleStatusReducer = (state = initialState, action) => { 6 | /* 7 | * Drizzle Status 8 | */ 9 | 10 | if (action.type === 'DRIZZLE_INITIALIZED') { 11 | return { 12 | ...state, 13 | initialized: true, 14 | }; 15 | } 16 | return state; 17 | }; 18 | 19 | export default drizzleStatusReducer; 20 | -------------------------------------------------------------------------------- /app/components/Vault/amplifyColumns.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export default styled.div` 4 | display: grid; 5 | grid-template-columns: ${({ gridTemplate }) => 6 | gridTemplate || '210px 110px 160px 140px 200px 1fr'}; 7 | width: 100%; 8 | align-items: center; 9 | > div { 10 | white-space: nowrap; 11 | text-overflow: ellipsis; 12 | &:first-of-type { 13 | margin-left: 15px; 14 | } 15 | } 16 | `; 17 | -------------------------------------------------------------------------------- /app/containers/ConnectionProvider/actions.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Connection actions 4 | * 5 | */ 6 | 7 | import { CONNECTION_CONNECTED, ACCOUNT_UPDATED } from './constants'; 8 | 9 | export function connectionConnected() { 10 | return { 11 | type: CONNECTION_CONNECTED, 12 | }; 13 | } 14 | 15 | export function accountUpdated(account, localWeb3) { 16 | return { 17 | type: ACCOUNT_UPDATED, 18 | account, 19 | localWeb3, 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /internals/generators/container/actions.test.js.hbs: -------------------------------------------------------------------------------- 1 | import { defaultAction } from '../actions'; 2 | import { DEFAULT_ACTION } from '../constants'; 3 | 4 | describe('{{ properCase name }} actions', () => { 5 | describe('Default Action', () => { 6 | it('has a type of DEFAULT_ACTION', () => { 7 | const expected = { 8 | type: DEFAULT_ACTION, 9 | }; 10 | expect(defaultAction()).toEqual(expected); 11 | }); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /app/containers/NotFoundPage/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * NotFoundPage 3 | * 4 | * This is the page we show when the user visits a url that doesn't have a route 5 | * 6 | */ 7 | 8 | import React from 'react'; 9 | import { FormattedMessage } from 'react-intl'; 10 | 11 | import messages from './messages'; 12 | 13 | export default function NotFound() { 14 | return ( 15 |

16 | 17 |

18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | commits: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Check out github repository 17 | uses: actions/checkout@v2 18 | with: 19 | fetch-depth: 0 20 | 21 | - name: Run commitlint 22 | uses: wagoid/commitlint-github-action@v2 23 | -------------------------------------------------------------------------------- /.github/workflows/board.yaml: -------------------------------------------------------------------------------- 1 | name: Project Board 2 | 3 | on: 4 | issues: 5 | types: [opened] 6 | 7 | jobs: 8 | project: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Create or Update Project Card 13 | uses: peter-evans/create-or-update-project-card@v1 14 | with: 15 | token: ${{ secrets.PAT }} 16 | project-location: iearn-finance 17 | project-name: Web 18 | column-name: Back log 19 | -------------------------------------------------------------------------------- /.github/workflows/check-payment-code.yml: -------------------------------------------------------------------------------- 1 | name: 'check-payment-code-in-diff' 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | check_payment_code_in_diff: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v1 9 | - uses: patcito/check-keywords-in-diff@v0.2.63 10 | with: 11 | branch: develop 12 | notify_issue: true 13 | title: payment related code 14 | token: ${{ secrets.GITHUB_TOKEN }} 15 | -------------------------------------------------------------------------------- /app/containers/App/tests/index.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ShallowRenderer from 'react-test-renderer/shallow'; 3 | 4 | import App from '../index'; 5 | 6 | const renderer = new ShallowRenderer(); 7 | 8 | describe('', () => { 9 | it('should render and match the snapshot', () => { 10 | renderer.render(); 11 | const renderedOutput = renderer.getRenderOutput(); 12 | expect(renderedOutput).toMatchSnapshot(); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /app/containers/LiteCover/actions.js: -------------------------------------------------------------------------------- 1 | import { COVER_DATA_LOADED, INITIALIZE_COVER, BUY_COVER } from './constants'; 2 | 3 | export function initializeCover() { 4 | return { 5 | type: INITIALIZE_COVER, 6 | }; 7 | } 8 | 9 | export function coverDataLoaded(payload) { 10 | return { 11 | type: COVER_DATA_LOADED, 12 | payload, 13 | }; 14 | } 15 | 16 | export function buyCover(payload) { 17 | return { 18 | type: BUY_COVER, 19 | payload, 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /internals/generators/component/messages.js.hbs: -------------------------------------------------------------------------------- 1 | /* 2 | * {{ properCase name }} Messages 3 | * 4 | * This contains all the text for the {{ properCase name }} component. 5 | */ 6 | 7 | import { defineMessages } from 'react-intl'; 8 | 9 | export const scope = 'app.components.{{ properCase name }}'; 10 | 11 | export default defineMessages({ 12 | header: { 13 | id: `${scope}.header`, 14 | defaultMessage: 'This is the {{ properCase name }} component!', 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /internals/generators/container/messages.js.hbs: -------------------------------------------------------------------------------- 1 | /* 2 | * {{ properCase name }} Messages 3 | * 4 | * This contains all the text for the {{ properCase name }} container. 5 | */ 6 | 7 | import { defineMessages } from 'react-intl'; 8 | 9 | export const scope = 'app.containers.{{ properCase name }}'; 10 | 11 | export default defineMessages({ 12 | header: { 13 | id: `${scope}.header`, 14 | defaultMessage: 'This is the {{ properCase name }} container!', 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /app/containers/LanguageProvider/tests/actions.test.js: -------------------------------------------------------------------------------- 1 | import { changeLocale } from '../actions'; 2 | 3 | import { CHANGE_LOCALE } from '../constants'; 4 | 5 | describe('LanguageProvider actions', () => { 6 | describe('Change Local Action', () => { 7 | it('has a type of CHANGE_LOCALE', () => { 8 | const expected = { 9 | type: CHANGE_LOCALE, 10 | locale: 'de', 11 | }; 12 | expect(changeLocale('de')).toEqual(expected); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /internals/generators/container/saga.test.js.hbs: -------------------------------------------------------------------------------- 1 | /** 2 | * Test sagas 3 | */ 4 | 5 | /* eslint-disable redux-saga/yield-effects */ 6 | // import { take, call, put, select } from 'redux-saga/effects'; 7 | // import {{ camelCase name }}Saga from '../saga'; 8 | 9 | // const generator = {{ camelCase name }}Saga(); 10 | 11 | describe('{{ camelCase name }}Saga Saga', () => { 12 | it('Expect to have unit tests specified', () => { 13 | expect(true).toEqual(false); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /app/components/SplashScreen/Hero/shapes/Triangle.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const Triangle = () => ( 4 | 11 | 17 | 18 | ); 19 | -------------------------------------------------------------------------------- /app/drizzle/store/defaultOptions.js: -------------------------------------------------------------------------------- 1 | const defaultOptions = { 2 | web3: { 3 | // `block` no longer needed; 4 | // keeping for pre-v1.1.1 compatibility with drizzle-react. 5 | block: false, 6 | fallback: { 7 | type: 'ws', 8 | url: 'ws://127.0.0.1:8545', 9 | }, 10 | }, 11 | contracts: [], 12 | events: {}, 13 | polls: { 14 | blocks: 3000, 15 | }, 16 | syncAlways: false, 17 | networkWhitelist: [], 18 | }; 19 | 20 | export default defaultOptions; 21 | -------------------------------------------------------------------------------- /app/components/Icon/shine.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/images/Splash/b.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/components/AnimatedNumber/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AnimatedNumber from 'animated-number-react'; 3 | 4 | const defaultFormatter = (v) => 5 | v.toLocaleString('en', { 6 | minimumFractionDigits: 2, 7 | maximumFractionDigits: 2, 8 | }); 9 | 10 | export default function AnimatedNumberComponent({ 11 | value, 12 | formatter = defaultFormatter, 13 | }) { 14 | return ( 15 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /app/components/SectionHeader/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | const Wrapper = styled.div` 4 | font-family: 'Roboto'; 5 | font-style: normal; 6 | font-weight: 900; 7 | font-size: 36px; 8 | line-height: 40px; 9 | color: #ffffff; 10 | `; 11 | 12 | function SectionHeader(props) { 13 | const { children } = props; 14 | return {children}; 15 | } 16 | 17 | SectionHeader.whyDidYouRender = true; 18 | export default SectionHeader; 19 | -------------------------------------------------------------------------------- /app/containers/DrizzleProvider/constants.js: -------------------------------------------------------------------------------- 1 | export const DRIZZLE_ADD_CONTRACTS = 'DRIZZLE_ADD_CONTRACTS'; 2 | export const DRIZZLE_INITIALIZED = 'DRIZZLE_INITIALIZED'; 3 | export const TX_BROADCASTED = 'TX_BROADCASTED'; 4 | export const GOT_CONTRACT_VAR = 'GOT_CONTRACT_VAR'; 5 | export const DELETE_CONTRACT = 'DELETE_CONTRACT'; 6 | export const ADD_WATCHED_CONTRACTS = 'ADD_WATCHED_CONTRACTS'; 7 | export const CONTRACT_INITIALIZED = 'CONTRACT_INITIALIZED'; 8 | export const ETH_BALANCE_UPDATED = 'ETH_BALANCE_UPDATED'; 9 | -------------------------------------------------------------------------------- /app/containers/DevMode/selectors.js: -------------------------------------------------------------------------------- 1 | /* eslint no-unused-vars: 0 */ 2 | import { createSelector } from 'reselect'; 3 | import { initialState } from './reducer'; 4 | 5 | /** 6 | * Direct selector to the themeToggle state domain 7 | */ 8 | export const selectState = (state) => state.devMode || initialState; 9 | 10 | export const selectDevMode = () => 11 | createSelector(selectState, (substate) => true); 12 | 13 | export const selectDevModeUnlocked = () => 14 | createSelector(selectState, (substate) => true); 15 | -------------------------------------------------------------------------------- /app/containers/LanguageProvider/selectors.js: -------------------------------------------------------------------------------- 1 | import { createSelector } from 'reselect'; 2 | import { initialState } from './reducer'; 3 | 4 | /** 5 | * Direct selector to the languageToggle state domain 6 | */ 7 | const selectLanguage = (state) => state.language || initialState; 8 | 9 | /** 10 | * Select the language locale 11 | */ 12 | 13 | const makeSelectLocale = () => 14 | createSelector(selectLanguage, (languageState) => languageState.locale); 15 | 16 | export { selectLanguage, makeSelectLocale }; 17 | -------------------------------------------------------------------------------- /app/components/CreamCard/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | const StyledCard = styled.div` 5 | height: 160px; 6 | display: flex; 7 | flex-direction: column; 8 | justify-content: center; 9 | align-content: center; 10 | background-color: rgba(6, 87, 249, 0.3); 11 | color: #fff; 12 | align-items: center; 13 | border-radius: 10px; 14 | `; 15 | 16 | const CreamCard = ({ children }) => {children}; 17 | 18 | export default CreamCard; 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - 'node' 5 | - 'lts/*' 6 | 7 | script: 8 | - node ./internals/scripts/generate-templates-for-linting 9 | - npm test -- --maxWorkers=4 10 | - npm run build 11 | 12 | before_install: 13 | - export CHROME_BIN=chromium-browser 14 | - export DISPLAY=:99.0 15 | - sh -e /etc/init.d/xvfb start 16 | 17 | notifications: 18 | email: 19 | on_failure: change 20 | 21 | after_success: 'npm run coveralls' 22 | 23 | cache: 24 | directories: 25 | - node_modules 26 | -------------------------------------------------------------------------------- /app/containers/ThemeProvider/saga.js: -------------------------------------------------------------------------------- 1 | import { takeLatest, select } from 'redux-saga/effects'; 2 | import { TOGGLE_DARK_MODE, SET_THEME_MODE } from './constants'; 3 | import { selectDarkMode } from './selectors'; 4 | 5 | export function* setDarkMode() { 6 | const mode = JSON.parse(yield select(selectDarkMode())); 7 | localStorage.setItem('darkMode', mode); 8 | } 9 | 10 | export default function* watchers() { 11 | yield takeLatest(TOGGLE_DARK_MODE, setDarkMode); 12 | yield takeLatest(SET_THEME_MODE, setDarkMode); 13 | } 14 | -------------------------------------------------------------------------------- /.env.sample: -------------------------------------------------------------------------------- 1 | WEB3_PROVIDER_HTTPS=https://eth-mainnet.alchemyapi.io/v2/euSwyu6Yf-VQ3NJ32KHxDhHmTta7OvIe 2 | WEB3_PROVIDER_WSS=wss://eth-mainnet.ws.alchemyapi.io/v2/euSwyu6Yf-VQ3NJ32KHxDhHmTta7OvIe 3 | WEB3_INFURA_PROJECT_ID= 4 | BLOCKNATIVE_DAPP_ID=05e3003d-7df8-4034-9ecb-57b3e27a4de2 5 | ETHERSCAN_APIKEY=GEQXZDY67RZ4QHNU1A57QVPNDV3RP1RYH4 6 | ETHERSCAN_TOKEN= 7 | PORTIS_APIKEY=b2b7586f-2b1e-4c30-a7fb-c2d1533b153b 8 | FORTMATIC_APIKEY=pk_test_886ADCAB855632AA 9 | ZAPPER_APIKEY= 10 | BLOCK_SUBSCRIPTION=FALSE 11 | USE_LOCAL_RPC=FALSE 12 | -------------------------------------------------------------------------------- /app/components/Icon/chevronRight.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/components/TailwindButton/index.js: -------------------------------------------------------------------------------- 1 | import tw, { styled, theme, css } from 'twin.macro'; 2 | 3 | export const TailwindButton = styled.button(({ isSecondary }) => [ 4 | // updated 5 | tw`py-3 px-8 uppercase rounded border border-primary hover:bg-primary duration-200`, 6 | 7 | css` 8 | & { 9 | background-color: ${theme`colors.whiteAlt`}; 10 | } 11 | 12 | &:hover { 13 | font-size: 2rem; 14 | } 15 | `, 16 | 17 | isSecondary && tw`border-secondary hover:bg-secondary hover:text-white`, // new 18 | ]); 19 | -------------------------------------------------------------------------------- /app/components/Vault/columnsDev.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export default styled.div` 4 | display: grid; 5 | grid-template-columns: 370px 140px 160px 140px 1fr; 6 | width: 100%; 7 | align-items: center; 8 | font-family: 'Roboto'; 9 | font-weight: 900; 10 | font-size: 20px; 11 | > div { 12 | white-space: nowrap; 13 | text-overflow: ellipsis; 14 | &:not(:first-of-type) { 15 | margin-top: 8px; 16 | } 17 | &:first-of-type { 18 | margin-left: 15px; 19 | } 20 | } 21 | `; 22 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "react", 4 | "moduleResolution": "node", 5 | "target": "es6", 6 | "baseUrl": "./", 7 | "paths": { 8 | "containers/*": ["app/containers/*"], 9 | "components/*": ["app/components/*"], 10 | "abi/*": ["app/abi/*"], 11 | "utils/*": ["app/utils/*"], 12 | "fonts/*": ["app/fonts/*"], 13 | "middleware/*": ["app/middleware/*"], 14 | "translations/*": ["app/translations/*"] 15 | } 16 | }, 17 | "exclude": ["node_modules", "dist"] 18 | } 19 | -------------------------------------------------------------------------------- /app/containers/ConnectionProvider/reducer.js: -------------------------------------------------------------------------------- 1 | import produce from 'immer'; 2 | import { ACCOUNT_UPDATED } from 'containers/ConnectionProvider/constants'; 3 | 4 | // The initial state of the App 5 | export const initialState = {}; 6 | 7 | /* eslint-disable default-case, no-param-reassign */ 8 | const appReducer = (state = initialState, action) => 9 | produce(state, (draft) => { 10 | switch (action.type) { 11 | case ACCOUNT_UPDATED: 12 | draft.account = action.account; 13 | break; 14 | } 15 | }); 16 | 17 | export default appReducer; 18 | -------------------------------------------------------------------------------- /app/containers/NotFoundPage/tests/index.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-testing-library'; 3 | import { IntlProvider } from 'react-intl'; 4 | 5 | import NotFoundPage from '../index'; 6 | 7 | describe('', () => { 8 | it('should render and match the snapshot', () => { 9 | const { 10 | container: { firstChild }, 11 | } = render( 12 | 13 | 14 | , 15 | ); 16 | expect(firstChild).toMatchSnapshot(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /internals/generators/container/reducer.js.hbs: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * {{ properCase name }} reducer 4 | * 5 | */ 6 | import produce from 'immer'; 7 | import { DEFAULT_ACTION } from './constants'; 8 | 9 | export const initialState = {}; 10 | 11 | /* eslint-disable default-case, no-param-reassign */ 12 | const {{ camelCase name }}Reducer = (state = initialState, action) => 13 | produce(state, (/* draft */) => { 14 | switch (action.type) { 15 | case DEFAULT_ACTION: 16 | break; 17 | } 18 | }); 19 | 20 | export default {{ camelCase name }}Reducer; 21 | -------------------------------------------------------------------------------- /app/containers/Vaults/hooks.js: -------------------------------------------------------------------------------- 1 | import { matchPath } from 'react-router'; 2 | import { useSelector } from 'react-redux'; 3 | import { selectLocation } from 'containers/App/selectors'; 4 | 5 | export function useShowDevVaults() { 6 | const devMode = true; 7 | const location = useSelector(selectLocation()); 8 | const { pathname } = location; 9 | const routeIsDevelop = matchPath(pathname, { 10 | path: '/vaults/develop', 11 | exact: true, 12 | strict: false, 13 | }); 14 | const showDevVaults = routeIsDevelop && devMode; 15 | return showDevVaults; 16 | } 17 | -------------------------------------------------------------------------------- /app/components/Icon/chevronLeft.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/containers/Vaults/migrationWhitelist.example: -------------------------------------------------------------------------------- 1 | // Add to migrationWhitelist.json when enabled 2 | 3 | [ 4 | { 5 | "symbol": "DAI", 6 | "token": "0x6b175474e89094c44da98b954eedeac495271d0f", 7 | "vaultFrom": "0xACd43E627e64355f1861cEC6d3a6688B31a6F952", 8 | "vaultTo": "0x19D3364A399d251E894aC732651be8B0E4e85001" 9 | }, 10 | { 11 | "symbol": "USDC", 12 | "token": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", 13 | "vaultFrom": "0x597aD1e0c13Bfe8025993D9e79C69E1c0233522e", 14 | "vaultTo": "0x5f18C75AbDAe578b483E5F43f12a39cF75b973a9" 15 | }, 16 | ] -------------------------------------------------------------------------------- /app/drizzle/store/web3/constants.js: -------------------------------------------------------------------------------- 1 | export const WEB3_INITIALIZING = 'WEB3_INITIALIZING'; 2 | export const WEB3_INITIALIZED = 'WEB3_INITIALIZED'; 3 | export const WEB3_FAILED = 'WEB3_FAILED'; 4 | export const WEB3_USER_DENIED = 'WEB3_USER_DENIED'; 5 | 6 | export const NETWORK_ID_FETCHED = 'NETWORK_ID_FETCHED'; 7 | export const NETWORK_ID_FAILED = 'NETWORK_ID_FAILED'; 8 | export const NETWORK_MISMATCH = 'NETWORK_MISMATCH'; 9 | 10 | export const NETWORK_IDS = { 11 | mainnet: 1, 12 | ropsten: 3, 13 | rinkeby: 4, 14 | goerli: 5, 15 | kovan: 42, 16 | ganache: 5777, 17 | }; 18 | -------------------------------------------------------------------------------- /app/containers/Zapper/constants.js: -------------------------------------------------------------------------------- 1 | export const INIT_ZAPPER = 'INIT_ZAPPER'; 2 | export const ZAPPER_DATA_LOADED = 'ZAPPER_DATA_LOADED'; 3 | export const ZAP_IN = 'ZAP_IN'; 4 | export const ZAP_OUT = 'ZAP_OUT'; 5 | export const ZAP_IN_ERROR = 'ZAP_IN_ERROR'; 6 | export const ZAP_OUT_ERROR = 'ZAP_OUT_ERROR'; 7 | export const MIGRATE_PICKLE_GAUGE = 'MIGRATE_PICKLE_GAUGE'; 8 | 9 | export const ZAPPER_AFFILIATE_ADDRESS = 10 | '0xFEB4acf3df3cDEA7399794D0869ef76A6EfAff52'; 11 | export const ETH_ADDRESS = '0x0000000000000000000000000000000000000000'; 12 | export const DEFAULT_SLIPPAGE = '0.01'; 13 | -------------------------------------------------------------------------------- /app/utils/nFormat.js: -------------------------------------------------------------------------------- 1 | export const nFormat = (num) => { 2 | const format = [ 3 | { value: 1e18, symbol: 'E' }, 4 | { value: 1e15, symbol: 'P' }, 5 | { value: 1e12, symbol: 'T' }, 6 | { value: 1e9, symbol: 'B' }, 7 | { value: 1e6, symbol: 'M' }, 8 | { value: 1e3, symbol: 'k' }, 9 | { value: 1, symbol: '' }, 10 | ]; 11 | const formatIndex = format.findIndex((data) => num >= data.value); 12 | return ( 13 | (num / format[formatIndex === -1 ? 6 : formatIndex].value).toFixed(2) + 14 | format[formatIndex === -1 ? 6 : formatIndex].symbol 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /app/drizzle/store/transactions/transactionStackReducer.js: -------------------------------------------------------------------------------- 1 | const initialState = []; 2 | 3 | const transactionStackReducer = (state = initialState, action) => { 4 | if (action.type === 'PUSH_TO_TXSTACK') { 5 | return [...state, action.stackTempKey]; 6 | } 7 | 8 | if (action.type === 'POP_FROM_TXSTACK') { 9 | state.pop(); 10 | 11 | return [...state]; 12 | } 13 | 14 | if (action.type === 'TX_BROADCASTED') { 15 | state[action.stackId] = action.txHash; 16 | 17 | return [...state]; 18 | } 19 | 20 | return state; 21 | }; 22 | 23 | export default transactionStackReducer; 24 | -------------------------------------------------------------------------------- /run_mainnet_fork.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -e 4 | 5 | if [[ -z "${ETHERSCAN_TOKEN}" ]]; then 6 | echo "Please provide your ETHERSCAN_TOKEN as env variable." 7 | exit 1 8 | fi 9 | 10 | if [[ -z "${WEB3_INFURA_PROJECT_ID}" ]]; then 11 | echo "Please provide your WEB3_INFURA_PROJECT_ID as env variable." 12 | exit 1 13 | fi 14 | 15 | rm -fr yearn-mainnet-fork || true 16 | git clone https://github.com/yearn/yearn-mainnet-fork.git 17 | cd yearn-mainnet-fork 18 | docker build --build-arg ETHERSCAN_TOKEN --build-arg WEB3_INFURA_PROJECT_ID -t yearn-mainnet-fork . 19 | cd .. 20 | docker-compose up 21 | -------------------------------------------------------------------------------- /app/containers/Cream/constants.js: -------------------------------------------------------------------------------- 1 | export const COMPTROLLER_ADDRESS = '0xAB1c342C7bf5Ec5F02ADEA1c2270670bCa144CbB'; 2 | export const PRICE_ORACLE_ADDRESS = 3 | '0x6B96c414ce762578c3E7930da9114CffC88704Cb'; 4 | export const BLOCKS_PER_YEAR = 2102400; 5 | export const INITIALIZE_CREAM = 'INITIALIZE_CREAM'; 6 | export const CREAM_ENTER_MARKETS = 'CREAM_ENTER_MARKETS'; 7 | export const CREAM_SUPPLY = 'CREAM_SUPPLY'; 8 | export const CREAM_BORROW = 'CREAM_BORROW'; 9 | export const CREAM_REPAY = 'CREAM_REPAY'; 10 | export const CREAM_WITHDRAW = 'CREAM_WITHDRAW'; 11 | export const CREAM_APPROVE = 'CREAM_APPROVE'; 12 | -------------------------------------------------------------------------------- /server/middlewares/frontendMiddleware.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable global-require */ 2 | 3 | /** 4 | * Front-end middleware 5 | */ 6 | module.exports = (app, options) => { 7 | const isProd = process.env.NODE_ENV === 'production'; 8 | 9 | if (isProd) { 10 | const addProdMiddlewares = require('./addProdMiddlewares'); 11 | addProdMiddlewares(app, options); 12 | } else { 13 | const webpackConfig = require('../../internals/webpack/webpack.dev.babel'); 14 | const addDevMiddlewares = require('./addDevMiddlewares'); 15 | addDevMiddlewares(app, webpackConfig); 16 | } 17 | 18 | return app; 19 | }; 20 | -------------------------------------------------------------------------------- /app/containers/DevMode/saga.js: -------------------------------------------------------------------------------- 1 | import { takeLatest } from 'redux-saga/effects'; 2 | import { TOGGLE_DEV_MODE, UNLOCK_DEV_MODE } from './constants'; 3 | // import { selectDevMode } from './selectors'; 4 | 5 | function* toggleDevMode() { 6 | const mode = JSON.parse(true); 7 | localStorage.setItem('devMode', mode); 8 | } 9 | 10 | function* unlockDevMode() { 11 | console.log('Welcome dev ;)'); 12 | localStorage.setItem('devModeUnlocked', 'true'); 13 | } 14 | 15 | export default function* watchers() { 16 | yield takeLatest(TOGGLE_DEV_MODE, toggleDevMode); 17 | yield takeLatest(UNLOCK_DEV_MODE, unlockDevMode); 18 | } 19 | -------------------------------------------------------------------------------- /app/utils/checkStore.js: -------------------------------------------------------------------------------- 1 | import { conformsTo, isFunction, isObject } from 'lodash'; 2 | import invariant from 'invariant'; 3 | 4 | /** 5 | * Validate the shape of redux store 6 | */ 7 | export default function checkStore(store) { 8 | const shape = { 9 | dispatch: isFunction, 10 | subscribe: isFunction, 11 | getState: isFunction, 12 | replaceReducer: isFunction, 13 | runSaga: isFunction, 14 | injectedReducers: isObject, 15 | injectedSagas: isObject, 16 | }; 17 | invariant( 18 | conformsTo(store, shape), 19 | '(app/utils...) injectors: Expected a valid redux store', 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /app/containers/Cover/reducer.js: -------------------------------------------------------------------------------- 1 | import produce from 'immer'; 2 | import { COVER_DATA_LOADED } from './constants'; 3 | 4 | export const initialState = {}; 5 | 6 | /* eslint-disable default-case, no-param-reassign */ 7 | const appReducer = (state = initialState, action) => 8 | produce(state, (draft) => { 9 | const assignData = (val, key) => { 10 | draft[key] = val; 11 | }; 12 | switch (action.type) { 13 | case COVER_DATA_LOADED: { 14 | const coverData = action.payload; 15 | _.each(coverData, assignData); 16 | break; 17 | } 18 | } 19 | }); 20 | 21 | export default appReducer; 22 | -------------------------------------------------------------------------------- /app/containers/Cover/actions.js: -------------------------------------------------------------------------------- 1 | import { 2 | COVER_DATA_LOADED, 3 | INITIALIZE_COVER, 4 | BUY_COVER, 5 | SELL_COVER, 6 | } from './constants'; 7 | 8 | export function initializeCover() { 9 | return { 10 | type: INITIALIZE_COVER, 11 | }; 12 | } 13 | 14 | export function coverDataLoaded(payload) { 15 | return { 16 | type: COVER_DATA_LOADED, 17 | payload, 18 | }; 19 | } 20 | 21 | export function buyCover(payload) { 22 | return { 23 | type: BUY_COVER, 24 | payload, 25 | }; 26 | } 27 | 28 | export function sellCover(payload) { 29 | return { 30 | type: SELL_COVER, 31 | payload, 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /app/containers/LiteCover/reducer.js: -------------------------------------------------------------------------------- 1 | import produce from 'immer'; 2 | import { COVER_DATA_LOADED } from './constants'; 3 | 4 | export const initialState = {}; 5 | 6 | /* eslint-disable default-case, no-param-reassign */ 7 | const appReducer = (state = initialState, action) => 8 | produce(state, (draft) => { 9 | const assignData = (val, key) => { 10 | draft[key] = val; 11 | }; 12 | switch (action.type) { 13 | case COVER_DATA_LOADED: { 14 | const coverData = action.payload; 15 | _.each(coverData, assignData); 16 | break; 17 | } 18 | } 19 | }); 20 | 21 | export default appReducer; 22 | -------------------------------------------------------------------------------- /app/middleware/websocket/actions.js: -------------------------------------------------------------------------------- 1 | import * as constants from './constants'; 2 | 3 | export function websocketConnect() { 4 | return { 5 | type: constants.WEBSOCKET_CONNECT, 6 | }; 7 | } 8 | 9 | export function websocketConnected(connection) { 10 | return { 11 | type: constants.WEBSOCKET_CONNECTED, 12 | connection, 13 | }; 14 | } 15 | 16 | export function websocketMessageReceived(data) { 17 | return { 18 | type: constants.WEBSOCKET_MESSAGE_RECEIVED, 19 | data, 20 | }; 21 | } 22 | 23 | export function websocketDisconnected() { 24 | return { 25 | type: constants.WEBSOCKET_DISCONNECTED, 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /.github/issue-close-app.yml: -------------------------------------------------------------------------------- 1 | comment: This issue was automatically closed because it does not follow either one of our templates. Please open a new issue and fill out the template that appears instead of deleting it. If you're reporting an issue, it's especially important that you provide detailed steps for how to reproduce it. 2 | 3 | issueConfigs: 4 | - content: 5 | - Description 6 | - Steps to reproduce 7 | - Versions 8 | 9 | - content: 10 | - Is your feature request related to a problem 11 | - Describe the solution you'd like 12 | - Describe alternatives you've considered 13 | - Additional context 14 | -------------------------------------------------------------------------------- /app/containers/LiteVaults/hooks.js: -------------------------------------------------------------------------------- 1 | import { matchPath } from 'react-router'; 2 | // import { selectDevMode } from 'containers/DevMode/selectors'; 3 | import { useSelector } from 'react-redux'; 4 | import { selectLocation } from 'containers/App/selectors'; 5 | 6 | export function useShowDevVaults() { 7 | const devMode = true; 8 | const location = useSelector(selectLocation()); 9 | const { pathname } = location; 10 | const routeIsDevelop = matchPath(pathname, { 11 | path: '/vaults/develop', 12 | exact: true, 13 | strict: false, 14 | }); 15 | const showDevVaults = routeIsDevelop && devMode; 16 | return showDevVaults; 17 | } 18 | -------------------------------------------------------------------------------- /app/components/Notify/Notify.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Notify } from '.'; 4 | 5 | export default { 6 | title: 'V2/Notify', 7 | component: Notify, 8 | }; 9 | 10 | const Template = (args) => ; 11 | 12 | export const Default = Template.bind({}); 13 | 14 | Default.args = { 15 | // NOTE - Can't add a link to the a notification object 16 | // NOTE - Cannot even use a React.Node in the message field.. 17 | notificationObject: { 18 | eventCode: 'dbUpdate', 19 | type: 'pending', 20 | message: 'Updating the database with your information', 21 | // autoDismiss: 5000, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | --- 5 | 6 | **Is your feature request related to a problem? Please describe.** 7 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 8 | 9 | **Describe the solution you'd like** 10 | A clear and concise description of what you want to happen. 11 | 12 | **Describe alternatives you've considered** 13 | A clear and concise description of any alternative solutions or features you've considered. 14 | 15 | **Additional context** 16 | Add any other context or screenshots about the feature request here. 17 | -------------------------------------------------------------------------------- /app/utils/drizzle.js: -------------------------------------------------------------------------------- 1 | export function transformContractData(contractData) { 2 | const getContractData = (acc, val, key) => { 3 | const valKeys = _.keys(val); 4 | let newVal = ''; 5 | if ( 6 | valKeys.length && 7 | key !== 'address' && 8 | key !== 'metadata' && 9 | key !== 'tags' && 10 | key !== 'readMethods' && 11 | key !== 'writeMethods' 12 | ) { 13 | newVal = val[valKeys[0]]; 14 | acc[key] = _.get(newVal, 'value', ''); 15 | } 16 | return acc; 17 | }; 18 | 19 | const transformedContractData = _.reduce(contractData, getContractData, {}); 20 | return transformedContractData; 21 | } 22 | -------------------------------------------------------------------------------- /internals/generators/utils/componentExists.js: -------------------------------------------------------------------------------- 1 | /** 2 | * componentExists 3 | * 4 | * Check whether the given component exist in either the components or containers directory 5 | */ 6 | 7 | const fs = require('fs'); 8 | const path = require('path'); 9 | const pageComponents = fs.readdirSync( 10 | path.join(__dirname, '../../../app/components'), 11 | ); 12 | const pageContainers = fs.readdirSync( 13 | path.join(__dirname, '../../../app/containers'), 14 | ); 15 | const components = pageComponents.concat(pageContainers); 16 | 17 | function componentExists(comp) { 18 | return components.indexOf(comp) >= 0; 19 | } 20 | 21 | module.exports = componentExists; 22 | -------------------------------------------------------------------------------- /internals/scripts/helpers/progress.js: -------------------------------------------------------------------------------- 1 | const readline = require('readline'); 2 | 3 | /** 4 | * Adds an animated progress indicator 5 | * 6 | * @param {string} message The message to write next to the indicator 7 | * @param {number} [amountOfDots=3] The amount of dots you want to animate 8 | */ 9 | function animateProgress(message, amountOfDots = 3) { 10 | let i = 0; 11 | return setInterval(() => { 12 | readline.cursorTo(process.stdout, 0); 13 | i = (i + 1) % (amountOfDots + 1); 14 | const dots = new Array(i + 1).join('.'); 15 | process.stdout.write(message + dots); 16 | }, 500); 17 | } 18 | 19 | module.exports = animateProgress; 20 | -------------------------------------------------------------------------------- /app/containers/LanguageProvider/reducer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * LanguageProvider reducer 4 | * 5 | */ 6 | import produce from 'immer'; 7 | 8 | import { CHANGE_LOCALE } from './constants'; 9 | import { DEFAULT_LOCALE } from '../../i18n'; 10 | 11 | export const initialState = { 12 | locale: DEFAULT_LOCALE, 13 | }; 14 | 15 | /* eslint-disable default-case, no-param-reassign */ 16 | const languageProviderReducer = (state = initialState, action) => 17 | produce(state, (draft) => { 18 | switch (action.type) { 19 | case CHANGE_LOCALE: 20 | draft.locale = action.locale; 21 | break; 22 | } 23 | }); 24 | 25 | export default languageProviderReducer; 26 | -------------------------------------------------------------------------------- /app/containers/LanguageProvider/tests/reducer.test.js: -------------------------------------------------------------------------------- 1 | import languageProviderReducer from '../reducer'; 2 | import { CHANGE_LOCALE } from '../constants'; 3 | 4 | /* eslint-disable default-case, no-param-reassign */ 5 | describe('languageProviderReducer', () => { 6 | it('returns the initial state', () => { 7 | expect(languageProviderReducer(undefined, {})).toEqual({ 8 | locale: 'en', 9 | }); 10 | }); 11 | 12 | it('changes the locale', () => { 13 | expect( 14 | languageProviderReducer(undefined, { 15 | type: CHANGE_LOCALE, 16 | locale: 'de', 17 | }), 18 | ).toEqual({ 19 | locale: 'de', 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /internals/generators/container/selectors.js.hbs: -------------------------------------------------------------------------------- 1 | import { createSelector } from 'reselect'; 2 | import { initialState } from './reducer'; 3 | 4 | /** 5 | * Direct selector to the {{ camelCase name }} state domain 6 | */ 7 | 8 | const select{{ properCase name }}Domain = state => state.{{ camelCase name }} || initialState; 9 | 10 | /** 11 | * Other specific selectors 12 | */ 13 | 14 | /** 15 | * Default selector used by {{ properCase name }} 16 | */ 17 | 18 | const makeSelect{{ properCase name }} = () => 19 | createSelector(select{{ properCase name }}Domain, substate => substate); 20 | 21 | export default makeSelect{{ properCase name }}; 22 | export { select{{ properCase name }}Domain }; 23 | -------------------------------------------------------------------------------- /app/components/VaultsHeaderDev/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import ColumnListDev from 'components/Vault/columnsDev'; 4 | 5 | const ColumnHeader = styled.div` 6 | margin-bottom: 10px; 7 | padding-top: 20px; 8 | font-size: 17px; 9 | text-transform: uppercase; 10 | `; 11 | 12 | export default function VaultsHeaderDev() { 13 | return ( 14 | 15 | Asset 16 | Type 17 | Deposited 18 | Total Assets 19 | Available to deposit 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /app/containers/App/actions.js: -------------------------------------------------------------------------------- 1 | import * as c from './constants'; 2 | 3 | export function vaultsLoaded(vaults) { 4 | return { 5 | type: c.VAULTS_LOADED, 6 | vaults, 7 | }; 8 | } 9 | 10 | export function appReady(web3, drizzle, notify) { 11 | return { 12 | type: c.APP_READY, 13 | web3, 14 | drizzle, 15 | notify, 16 | }; 17 | } 18 | 19 | export function appInitialized() { 20 | return { 21 | type: c.APP_INITIALIZED, 22 | }; 23 | } 24 | 25 | export function routeChanged(route) { 26 | return { 27 | type: c.ROUTE_CHANGED, 28 | route, 29 | }; 30 | } 31 | 32 | export function splashPageVisited() { 33 | return { 34 | type: c.SPLASH_PAGE_VISITED, 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /app/components/Header/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // import styled from 'styled-components'; 3 | // import ThemeToggle from 'containers/ThemeProvider/toggle'; 4 | // import DevModeToggle from 'containers/DevMode'; 5 | import { Navbar } from 'components/Navbar'; 6 | 7 | // const Toggles = styled.div` 8 | // display: flex; 9 | // align-items: center; 10 | // margin-left: 20px; 11 | // > div { 12 | // margin-right: 20px; 13 | // } 14 | // `; 15 | 16 | // const StyledConnectButton = styled(ConnectButton)` 17 | // margin-right: 15px; 18 | // margin-top: 15px; 19 | // `; 20 | 21 | export default function Header() { 22 | return ( 23 | <> 24 | 25 | 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /app/components/Icon/stats.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/containers/ThemeProvider/toggle.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import { useDispatch, useSelector } from 'react-redux'; 4 | import { toggleDarkMode } from './actions'; 5 | import { selectDarkMode } from './selectors'; 6 | 7 | const Wrapper = styled.div``; 8 | 9 | export default function ConnectButton() { 10 | const darkMode = useSelector(selectDarkMode()); 11 | const dispatch = useDispatch(); 12 | const toggleMode = () => { 13 | dispatch(toggleDarkMode()); 14 | }; 15 | 16 | return ( 17 | 18 | 22 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | module.exports = { 3 | stories: [ 4 | '../app/components/**/*.stories.@(js|jsx|ts|tsx)', 5 | '../app/containers/**/*.stories.@(js|jsx|ts|tsx)', 6 | ], 7 | addons: ['@storybook/addon-links', '@storybook/addon-essentials'], 8 | webpackFinal: async (config) => { 9 | config.resolve.modules = [ 10 | ...config.resolve.modules, 11 | path.resolve(__dirname, '..', 'app'), 12 | path.resolve(__dirname, '..', 'node_modules'), 13 | ]; 14 | 15 | Object.assign(config, { 16 | node: { 17 | console: 'mock', 18 | fs: 'empty', 19 | net: 'empty', 20 | tls: 'empty', 21 | }, 22 | }); 23 | 24 | return config; 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /app/containers/Dashboard/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import { FormattedMessage } from 'react-intl'; 4 | import { Link } from 'react-router-dom'; 5 | 6 | const Wrapper = styled.div` 7 | top: 0; 8 | bottom: 0; 9 | left: 0; 10 | right: 0; 11 | position: absolute; 12 | display: flex; 13 | justify-content: center; 14 | align-items: center; 15 | flex-direction: column; 16 | `; 17 | 18 | export default function Dashboard() { 19 | return ( 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | 3 | networks: 4 | yearn-finance: 5 | 6 | services: 7 | yearn-finance-dev: 8 | build: . 9 | ports: 10 | - 3000:3000 11 | environment: 12 | - WEB3_PROVIDER_HTTPS 13 | - WEB3_PROVIDER_WSS 14 | - BLOCKNATIVE_DAPP_ID 15 | - ETHERSCAN_APIKEY 16 | - PORTIS_APIKEY 17 | - FORTMATIC_APIKEY 18 | - BLOCK_SUBSCRIPTION 19 | - USE_LOCAL_RPC=TRUE 20 | networks: 21 | - yearn-finance 22 | restart: always 23 | 24 | yearn-mainnet-fork: 25 | image: yearn-mainnet-fork 26 | ports: 27 | - 8545:8545 28 | environment: 29 | - WEB3_INFURA_PROJECT_ID 30 | - ETHERSCAN_TOKEN 31 | networks: 32 | - yearn-finance 33 | restart: always 34 | -------------------------------------------------------------------------------- /app/components/BackscratchersHeaders/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import AmplifyColumns from 'components/Vault/amplifyColumns'; 4 | 5 | const ColumnHeader = styled.div` 6 | margin-bottom: 10px; 7 | padding-top: 20px; 8 | font-size: 17px; 9 | text-transform: uppercase; 10 | `; 11 | 12 | export default function AmplifyHeaders() { 13 | return ( 14 | 15 | Asset 16 | Deposited 17 | Multiplier 18 | Growth 19 | Total assets 20 | Available to deposit 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /app/components/CoverProtocolHeader/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import SectionHeader from 'components/SectionHeader'; 4 | import BackLink from 'components/BackLink'; 5 | const Wrapper = styled.div``; 6 | const StyledBackLink = styled(BackLink)` 7 | margin-top: 30px; 8 | `; 9 | 10 | function CoverProtocolHeader(props) { 11 | const { protocol } = props; 12 | const name = _.get(protocol, 'protocolDisplayName'); 13 | 14 | return ( 15 | 16 | Buy and sell {name} cover 17 | Back to Cover 18 | 19 | ); 20 | } 21 | 22 | CoverProtocolHeader.whyDidYouRender = true; 23 | export default CoverProtocolHeader; 24 | -------------------------------------------------------------------------------- /app/components/Text/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import Box from 'components/Box'; 4 | 5 | const StyledBox = styled(Box)` 6 | word-wrap: break-word; 7 | white-space: normal; 8 | `; 9 | 10 | const Text = ({ children, bold, italic, large, small, center, ...props }) => { 11 | let fontSize = 2; 12 | if (small) { 13 | fontSize = 1; 14 | } 15 | if (large) { 16 | fontSize = 3; 17 | } 18 | return ( 19 | 26 | {children} 27 | 28 | ); 29 | }; 30 | 31 | export default Text; 32 | -------------------------------------------------------------------------------- /app/containers/ThemeProvider/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ThemeProvider } from 'styled-components'; 3 | import { useInjectSaga } from 'utils/injectSaga'; 4 | import { useInjectReducer } from 'utils/injectReducer'; 5 | import { useSelector } from 'react-redux'; 6 | import saga from './saga'; 7 | import reducer from './reducer'; 8 | import { lightTheme, darkTheme } from './themes'; 9 | import { selectDarkMode } from './selectors'; 10 | 11 | const Theme = (props) => { 12 | useInjectSaga({ key: 'theme', saga }); 13 | useInjectReducer({ key: 'theme', reducer }); 14 | const darkMode = useSelector(selectDarkMode()); 15 | const theme = darkMode ? darkTheme : lightTheme; 16 | return {props.children}; 17 | }; 18 | 19 | export default Theme; 20 | -------------------------------------------------------------------------------- /server/middlewares/addProdMiddlewares.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const express = require('express'); 3 | const compression = require('compression'); 4 | 5 | module.exports = function addProdMiddlewares(app, options) { 6 | const publicPath = options.publicPath || '/'; 7 | const outputPath = options.outputPath || path.resolve(process.cwd(), 'build'); 8 | 9 | // compression middleware compresses your server responses which makes them 10 | // smaller (applies also to assets). You can read more about that technique 11 | // and other good practices on official Express.js docs http://mxs.is/googmy 12 | app.use(compression()); 13 | app.use(publicPath, express.static(outputPath)); 14 | 15 | app.get('*', (req, res) => 16 | res.sendFile(path.resolve(outputPath, 'index.html')), 17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /app/components/Icon/info.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/utils/table.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import TokenIcon from 'components/TokenIcon'; 4 | 5 | const IconAndName = styled.div` 6 | display: flex; 7 | align-items: center; 8 | `; 9 | 10 | const StyledTokenIcon = styled(TokenIcon)` 11 | width: 30px; 12 | margin-right: 20px; 13 | `; 14 | 15 | const IconName = styled.div` 16 | overflow: hidden; 17 | padding-right: 10px; 18 | text-overflow: ellipsis; 19 | `; 20 | 21 | export const tokenTransform = (dontCare, asset) => { 22 | const { vaultAlias, address, token } = asset; 23 | const tokenContractAddress = token.address; 24 | return ( 25 | 26 | 27 | {vaultAlias || address} 28 | 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /app/containers/DrizzleProvider/hooks.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { getWriteMethods } from 'utils/contracts'; 3 | import context from './context'; 4 | 5 | export function useDrizzle() { 6 | const { drizzle } = useContext(context); 7 | return drizzle; 8 | } 9 | 10 | export function useContract(address) { 11 | const { drizzle } = useContext(context); 12 | if (!drizzle) { 13 | return {}; 14 | } 15 | return drizzle.contracts[address]; 16 | } 17 | 18 | export function useContractAbi(address) { 19 | const contract = useContract(address); 20 | if (!contract) { 21 | return []; 22 | } 23 | return contract.abi; 24 | } 25 | 26 | export function useGetWriteMethods(address) { 27 | const abi = useContractAbi(address); 28 | const writeMethods = getWriteMethods(abi); 29 | return writeMethods; 30 | } 31 | -------------------------------------------------------------------------------- /internals/generators/component/index.js.hbs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * {{ properCase name }} 4 | * 5 | */ 6 | 7 | {{#if memo}} 8 | import React, { memo } from 'react'; 9 | {{else}} 10 | import React from 'react'; 11 | {{/if}} 12 | // import PropTypes from 'prop-types'; 13 | // import styled from 'styled-components'; 14 | 15 | {{#if wantMessages}} 16 | import { FormattedMessage } from 'react-intl'; 17 | import messages from './messages'; 18 | {{/if}} 19 | 20 | function {{ properCase name }}() { 21 | return ( 22 |
23 | {{#if wantMessages}} 24 | 25 | {{/if}} 26 |
27 | ); 28 | } 29 | 30 | {{ properCase name }}.propTypes = {}; 31 | 32 | {{#if memo}} 33 | export default memo({{ properCase name }}); 34 | {{else}} 35 | export default {{ properCase name }}; 36 | {{/if}} 37 | -------------------------------------------------------------------------------- /app/containers/Zapper/selectors.js: -------------------------------------------------------------------------------- 1 | import { createSelector } from 'reselect'; 2 | 3 | const selectZapper = (state) => state.zapper; 4 | 5 | export const selectZapperTokens = () => 6 | createSelector(selectZapper, (substate) => substate.tokens); 7 | 8 | export const selectZapperVaults = () => 9 | createSelector(selectZapper, (substate) => substate.vaults); 10 | 11 | export const selectZapperPickleVaults = () => 12 | createSelector(selectZapper, (substate) => substate.pickleVaults); 13 | 14 | export const selectZapperBalances = () => 15 | createSelector(selectZapper, (substate) => substate.balances); 16 | 17 | export const selectZapperError = () => 18 | createSelector(selectZapper, (substate) => substate.error); 19 | 20 | export const selectZapperOutError = () => 21 | createSelector(selectZapper, (substate) => substate.errorOut); 22 | -------------------------------------------------------------------------------- /app/containers/DrizzleProvider/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Drizzle } from 'drizzle/store'; 3 | import { useInjectSaga } from 'utils/injectSaga'; 4 | import DrizzleContext from './context'; 5 | import saga from './saga'; 6 | const DrizzleProvider = (props) => { 7 | useInjectSaga({ key: 'drizzleSaga', saga }); 8 | const { children, store } = props; 9 | const customProvider = 10 | localStorage.getItem('WEB3_PROVIDER_HTTPS') || 11 | process.env.WEB3_PROVIDER_HTTPS; 12 | const options = { 13 | disableReduxDevTools: false, 14 | web3: { 15 | customProvider, 16 | }, 17 | }; 18 | const drizzle = new Drizzle(options, store); 19 | return ( 20 | 21 | {children} 22 | 23 | ); 24 | }; 25 | 26 | export default DrizzleProvider; 27 | -------------------------------------------------------------------------------- /app/middleware/websocket/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-nested-callbacks */ 2 | import * as constants from './constants'; 3 | import WebsocketConnection from './connection'; 4 | 5 | const socketMiddleware = (() => { 6 | let websocketConnection = null; 7 | 8 | return (store) => (next) => (action) => { 9 | switch (action.type) { 10 | case constants.WEBSOCKET_CONNECT: 11 | if (websocketConnection != null) { 12 | websocketConnection.close(); 13 | } 14 | websocketConnection = new WebsocketConnection(store.dispatch); 15 | break; 16 | default: 17 | } 18 | const newAction = action; 19 | if (action.type === 'DRIZZLE_ADD_CONTRACTS') { 20 | newAction.websocket = websocketConnection; 21 | } 22 | return next(newAction); 23 | }; 24 | })(); 25 | 26 | export default socketMiddleware; 27 | -------------------------------------------------------------------------------- /app/components/SplashScreen/index.js: -------------------------------------------------------------------------------- 1 | import 'twin.macro'; 2 | import React from 'react'; 3 | 4 | import { Backscratcher } from './Backscratcher'; 5 | // import { Hero } from './Hero'; 6 | // import { Products } from './Products'; 7 | // import { Reviews } from './Reviews'; 8 | // import { Security } from './Security'; 9 | 10 | export const SplashScreen = () => ; 11 | 12 | // ( 13 | //
14 | // 15 | // {/* */} 16 | // {/* TODO: Uncomment when designs are finalised :) */} 17 | // {/* 18 | // 19 | // */} 20 | // {/*

24 | // Version: {process.env.REACT_APP_VERSION} 25 | //

*/} 26 | //
27 | // ); 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | --- 5 | 6 | Before opening a new issue, please take a moment to review our [**community guidelines**](https://github.com/iearn-finance/yearn-finance/blob/develop/CONTRIBUTING.md) to make the contribution process easy and effective for everyone involved. 7 | 8 | ## Description 9 | 10 | A clear and concise description of what the bug is. 11 | 12 | ## Steps to reproduce 13 | 14 | Steps to reproduce the behavior: 15 | 16 | (Add link to a demo on https://jsfiddle.net or similar if possible) 17 | 18 | **Expected behavior** 19 | A clear and concise description of what you expected to happen. 20 | 21 | **Screenshots** 22 | If applicable, add screenshots to help explain your problem. 23 | 24 | ## Versions 25 | 26 | - Browser: 27 | - yearn-finance: 28 | - Node/NPM: 29 | -------------------------------------------------------------------------------- /app/reducers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Combine all reducers in this file and export the combined reducers. 3 | */ 4 | 5 | import { combineReducers } from 'redux'; 6 | import { connectRouter } from 'connected-react-router'; 7 | import { drizzleReducers } from 'drizzle/store'; 8 | import history from 'utils/history'; 9 | import languageProviderReducer from 'containers/LanguageProvider/reducer'; 10 | import appReducer from 'containers/App/reducer'; 11 | 12 | /** 13 | * Merges the main reducer with the router state and dynamically injected reducers 14 | */ 15 | export default function createReducer(injectedReducers = {}) { 16 | const rootReducer = combineReducers({ 17 | language: languageProviderReducer, 18 | router: connectRouter(history), 19 | app: appReducer, 20 | ...drizzleReducers, 21 | ...injectedReducers, 22 | }); 23 | 24 | return rootReducer; 25 | } 26 | -------------------------------------------------------------------------------- /app/components/Icon/arrowDown.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/components/Input/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | const Wrapper = styled.input` 5 | height: 30px; 6 | font-size: 14px; 7 | padding: 0px 7px; 8 | width: 350px; 9 | border: 2px solid ${(props) => props.theme.inputBorder}; 10 | border-radius: 23.5px; 11 | background-color: transparent; 12 | outline: none; 13 | color: ${(props) => props.theme.text}; 14 | &:focus { 15 | border-color: ${(props) => props.theme.yearnBlue}; 16 | box-shadow: 0 0 25pt 5pt ${(props) => props.theme.inputOutline}; 17 | } 18 | &:invalid:not(:focus) { 19 | box-shadow: none; 20 | } 21 | padding: 0px 13px; 22 | `; 23 | 24 | export default function Input(props) { 25 | const { children, ...restProps } = props; 26 | return ( 27 | 28 | {props.children} 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /app/tests/i18n.test.js: -------------------------------------------------------------------------------- 1 | import { formatTranslationMessages } from '../i18n'; 2 | 3 | jest.mock('../translations/en.json', () => ({ 4 | message1: 'default message', 5 | message2: 'default message 2', 6 | })); 7 | 8 | const esTranslationMessages = { 9 | message1: 'mensaje predeterminado', 10 | message2: '', 11 | }; 12 | 13 | describe('formatTranslationMessages', () => { 14 | it('should build only defaults when DEFAULT_LOCALE', () => { 15 | const result = formatTranslationMessages('en', { a: 'a' }); 16 | 17 | expect(result).toEqual({ a: 'a' }); 18 | }); 19 | 20 | it('should combine default locale and current locale when not DEFAULT_LOCALE', () => { 21 | const result = formatTranslationMessages('', esTranslationMessages); 22 | 23 | expect(result).toEqual({ 24 | message1: 'mensaje predeterminado', 25 | message2: 'default message 2', 26 | }); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /app/components/VaultTop/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | const Wrapper = styled.input` 5 | height: 30px; 6 | font-size: 14px; 7 | padding: 0px 7px; 8 | width: 350px; 9 | border: 2px solid ${(props) => props.theme.inputBorder}; 10 | border-radius: 23.5px; 11 | background-color: transparent; 12 | outline: none; 13 | color: ${(props) => props.theme.text}; 14 | &:focus { 15 | border-color: ${(props) => props.theme.yearnBlue}; 16 | box-shadow: 0 0 25pt 5pt ${(props) => props.theme.inputOutline}; 17 | } 18 | &:invalid:not(:focus) { 19 | box-shadow: none; 20 | } 21 | padding: 0px 13px; 22 | `; 23 | 24 | export default function ConnectButton(props) { 25 | const { children, ...restProps } = props; 26 | return ( 27 | 28 | {props.children} 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /app/drizzle/store/contractStateUtils.js: -------------------------------------------------------------------------------- 1 | export const getAbi = (contractEntry) => 2 | contractEntry.web3Contract 3 | ? contractEntry.web3Contract.options.jsonInterface 4 | : contractEntry.abi; 5 | 6 | export const isConstant = (x) => x.type === 'function' && x.constant === true; 7 | 8 | export const generateContractInitialState = (contractConfig) => { 9 | const constants = getAbi(contractConfig).filter(isConstant); 10 | const objectOfConstants = constants.reduce( 11 | (acc, x) => ({ ...acc, [x.name]: {} }), 12 | {}, 13 | ); 14 | return { 15 | initialized: false, 16 | synced: false, 17 | ...objectOfConstants, 18 | }; 19 | }; 20 | 21 | export const generateContractsInitialState = (options) => 22 | (options.contracts || []).reduce((state, contract) => { 23 | state[contract.contractName] = generateContractInitialState(contract); 24 | return state; 25 | }, {}); 26 | -------------------------------------------------------------------------------- /app/components/Icon/externalLink.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/components/VaultTopDev/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | const Wrapper = styled.input` 5 | height: 30px; 6 | font-size: 14px; 7 | padding: 0px 7px; 8 | width: 350px; 9 | border: 2px solid ${(props) => props.theme.inputBorder}; 10 | border-radius: 23.5px; 11 | background-color: transparent; 12 | outline: none; 13 | color: ${(props) => props.theme.text}; 14 | &:focus { 15 | border-color: ${(props) => props.theme.yearnBlue}; 16 | box-shadow: 0 0 25pt 5pt ${(props) => props.theme.inputOutline}; 17 | } 18 | &:invalid:not(:focus) { 19 | box-shadow: none; 20 | } 21 | padding: 0px 13px; 22 | `; 23 | 24 | export default function ConnectButton(props) { 25 | const { children, ...restProps } = props; 26 | return ( 27 | 28 | {props.children} 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /app/components/Icon/externalLinkBlack.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/components/Box/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import { 4 | space, 5 | color, 6 | typography, 7 | layout, 8 | flexbox, 9 | grid, 10 | background, 11 | border, 12 | position, 13 | shadow, 14 | compose, 15 | } from 'styled-system'; 16 | 17 | const StyledBox = styled('div')( 18 | compose( 19 | space, 20 | color, 21 | typography, 22 | layout, 23 | flexbox, 24 | grid, 25 | background, 26 | border, 27 | position, 28 | shadow, 29 | ), 30 | ); 31 | 32 | const Box = ({ center, children, ...props }) => ( 33 | 40 | {children} 41 | 42 | ); 43 | 44 | export default Box; 45 | -------------------------------------------------------------------------------- /app/components/CoverProtocol/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import CoverProtocolHeader from 'components/CoverProtocolHeader'; 4 | import CoverProtocolDetail from 'components/CoverProtocolDetail'; 5 | import { useSelector } from 'react-redux'; 6 | import { selectProtocols } from 'containers/Cover/selectors'; 7 | 8 | const Wrapper = styled.div` 9 | display: flex; 10 | flex-direction: column; 11 | `; 12 | 13 | function CoverProtocol(props) { 14 | const { protocolAddress } = props.match.params; 15 | 16 | const protocols = useSelector(selectProtocols()); 17 | const protocol = _.find(protocols, { protocolAddress }); 18 | return ( 19 | 20 | 21 | 22 | 23 | ); 24 | } 25 | 26 | CoverProtocol.whyDidYouRender = true; 27 | export default CoverProtocol; 28 | -------------------------------------------------------------------------------- /app/abi/v2EthZapAbi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "stateMutability": "nonpayable", 5 | "type": "constructor" 6 | }, 7 | { 8 | "inputs": [], 9 | "name": "depositETH", 10 | "outputs": [], 11 | "stateMutability": "payable", 12 | "type": "function" 13 | }, 14 | { 15 | "inputs": [], 16 | "name": "weth", 17 | "outputs": [ 18 | { 19 | "internalType": "address", 20 | "name": "", 21 | "type": "address" 22 | } 23 | ], 24 | "stateMutability": "view", 25 | "type": "function" 26 | }, 27 | { 28 | "inputs": [], 29 | "name": "yvWETH", 30 | "outputs": [ 31 | { 32 | "internalType": "address", 33 | "name": "", 34 | "type": "address" 35 | } 36 | ], 37 | "stateMutability": "view", 38 | "type": "function" 39 | }, 40 | { 41 | "stateMutability": "payable", 42 | "type": "receive" 43 | } 44 | ] 45 | -------------------------------------------------------------------------------- /app/components/TokenIcon/index.js: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from 'react'; 2 | import web3 from 'web3'; 3 | import ReactImageFallback from 'react-image-fallback'; 4 | 5 | const FallbackUrl = `https://i.imgur.com/7lETB36.png`; 6 | 7 | export default function TokenIcon(props) { 8 | const url = useMemo(() => { 9 | if (props) { 10 | if (props.icon) { 11 | return props.icon; 12 | } 13 | if (props.address) { 14 | const checksumAddress = web3.utils.toChecksumAddress(props.address); 15 | return `https://raw.githubusercontent.com/yearn/yearn-assets/master/icons/multichain-tokens/1/${checksumAddress}/logo-128.png`; 16 | } 17 | } 18 | return FallbackUrl; 19 | }, [props]); 20 | 21 | return ( 22 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /.github/lock.yml: -------------------------------------------------------------------------------- 1 | # Configuration for lock-threads - https://github.com/dessant/lock-threads 2 | 3 | # Number of days of inactivity before a closed issue or pull request is locked 4 | daysUntilLock: 30 5 | 6 | # Issues and pull requests with these labels will not be locked. Set to `[]` to disable 7 | exemptLabels: [] 8 | 9 | # Label to add before locking, such as `outdated`. Set to `false` to disable 10 | lockLabel: false 11 | 12 | # Comment to post before locking. Set to `false` to disable 13 | lockComment: > 14 | This thread has been automatically locked since there has not been 15 | any recent activity after it was closed. Please open a new issue for 16 | related bugs. 17 | 18 | # Limit to only `issues` or `pulls` 19 | # only: issues 20 | 21 | # Optionally, specify configuration settings just for `issues` or `pulls` 22 | # issues: 23 | # exemptLabels: 24 | # - help-wanted 25 | # lockLabel: outdated 26 | 27 | # pulls: 28 | # daysUntilLock: 30 29 | 30 | -------------------------------------------------------------------------------- /app/components/Button/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | const Wrapper = styled.button` 5 | margin: 7px 0px; 6 | padding: 5px; 7 | background-color: transparent; 8 | text-transform: uppercase; 9 | border-radius: 13px; 10 | padding: 10px 10px; 11 | border: 2px solid #2f80ed; 12 | color: #2f80ed; 13 | white-space: nowrap; 14 | &:focus { 15 | outline: 0 !important; 16 | } 17 | &:not(:disabled):hover { 18 | background-color: #2f80ed; 19 | color: #fff; 20 | } 21 | font-size: 17px; 22 | &:disabled { 23 | opacity: 0.5; 24 | } 25 | width: 200px; 26 | `; 27 | 28 | export default function Button(props) { 29 | const { onClick, disabled, className } = props; 30 | return ( 31 | 37 | {props.children} 38 | 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /app/abi/zapv23crv.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "stateMutability": "nonpayable", 4 | "type": "constructor", 5 | "inputs": [], 6 | "outputs": [] 7 | }, 8 | { 9 | "stateMutability": "nonpayable", 10 | "type": "function", 11 | "name": "zap", 12 | "inputs": [], 13 | "outputs": [], 14 | "gas": 40936 15 | }, 16 | { 17 | "stateMutability": "view", 18 | "type": "function", 19 | "name": "threeCrv", 20 | "inputs": [], 21 | "outputs": [{ "name": "", "type": "address" }], 22 | "gas": 2418 23 | }, 24 | { 25 | "stateMutability": "view", 26 | "type": "function", 27 | "name": "threeCrvVault", 28 | "inputs": [], 29 | "outputs": [{ "name": "", "type": "address" }], 30 | "gas": 2448 31 | }, 32 | { 33 | "stateMutability": "view", 34 | "type": "function", 35 | "name": "vecrvVault", 36 | "inputs": [], 37 | "outputs": [{ "name": "", "type": "address" }], 38 | "gas": 2478 39 | } 40 | ] 41 | -------------------------------------------------------------------------------- /app/components/BlueOutlineCard/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | const Wrapper = styled.div` 5 | border: 3px solid ${(props) => props.theme.vaultBorderActive}; 6 | border-radius: 10px; 7 | > div:nth-of-type(1) { 8 | padding: 20px; 9 | border-radius: 10px 10px 0px 0px; 10 | background-color: ${(props) => props.theme.vaultBackground}; 11 | } 12 | > div:nth-of-type(2) { 13 | background-color: ${(props) => props.theme.vaultBackgroundMiddle}; 14 | padding: 20px; 15 | } 16 | > div:nth-of-type(3) { 17 | background-color: ${(props) => props.theme.vaultBackground}; 18 | border-radius: 0px 0px 10px 10px; 19 | padding: 20px; 20 | } 21 | `; 22 | 23 | function BlueOutlineCard(props) { 24 | const { className, children } = props; 25 | return {children}; 26 | } 27 | 28 | BlueOutlineCard.whyDidYouRender = false; 29 | export default BlueOutlineCard; 30 | -------------------------------------------------------------------------------- /app/components/LearnMoreCard/LearnMoreCard.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import 'twin.macro'; 3 | 4 | import { LearnMoreCard } from '.'; 5 | 6 | export default { 7 | title: 'V2/LearnMoreCard', 8 | component: LearnMoreCard, 9 | }; 10 | 11 | const Template = (args) => ( 12 |
13 |

14 | Clear local storage key and refresh if you close it :) 15 |

16 | 17 |
18 | ); 19 | 20 | export const Default = Template.bind({}); 21 | 22 | Default.args = { 23 | infoTitle: 'What are staking rewards?', 24 | infoDescription: 25 | 'Earn passive income and participate in Yearn’s success. Through staking YFI in Yearn’s YFI vault you are entitled to a share of Yearn’s protocol earnings. Keep your YFI in the vault and let them grow your earnings as Yearn’s usage grows.', 26 | learnMoreText: 'Full Documentation', 27 | learnMoreHref: 'https://lmgtfy.com', 28 | }; 29 | -------------------------------------------------------------------------------- /app/containers/ConnectionProvider/hooks.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import context from './context'; 3 | 4 | export function useAccount() { 5 | const { account } = useContext(context); 6 | return account; 7 | } 8 | 9 | export function useWallet() { 10 | const { wallet } = useContext(context); 11 | return wallet; 12 | } 13 | 14 | export function useSelectWallet() { 15 | const { selectWallet } = useContext(context); 16 | return selectWallet; 17 | } 18 | 19 | export function useOnboard() { 20 | const { onboard } = useContext(context); 21 | return onboard; 22 | } 23 | 24 | export function useWeb3() { 25 | const { web3 } = useContext(context); 26 | return web3; 27 | } 28 | 29 | export function useNotify() { 30 | const { notify } = useContext(context); 31 | return notify; 32 | } 33 | 34 | export function useRequireConnection() { 35 | const { selectWallet, wallet } = useContext(context); 36 | if (!wallet.provider) { 37 | selectWallet(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /internals/scripts/analyze.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const shelljs = require('shelljs'); 4 | const chalk = require('chalk'); 5 | const animateProgress = require('./helpers/progress'); 6 | const addCheckMark = require('./helpers/checkmark'); 7 | 8 | const progress = animateProgress('Generating stats'); 9 | 10 | // Generate stats.json file with webpack 11 | shelljs.exec( 12 | 'webpack --config internals/webpack/webpack.prod.babel.js --profile --json > stats.json', 13 | addCheckMark.bind(null, callback), // Output a checkmark on completion 14 | ); 15 | 16 | // Called after webpack has finished generating the stats.json file 17 | function callback() { 18 | clearInterval(progress); 19 | process.stdout.write( 20 | `\n\nOpen ${ 21 | chalk.magenta('http://webpack.github.io/analyse/') 22 | } in your browser and upload the stats.json file!${ 23 | chalk.blue( 24 | `\n(Tip: ${ chalk.italic('CMD + double-click') } the link!)\n\n`, 25 | )}`, 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /app/components/BackLink/index.js: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import { compose } from 'redux'; 3 | import styled from 'styled-components'; 4 | import { Link } from 'react-router-dom'; 5 | import Icon from 'components/Icon'; 6 | 7 | const Wrapper = styled.div` 8 | font-family: 'Roboto'; 9 | font-style: normal; 10 | font-weight: 500; 11 | font-size: 16px; 12 | line-height: 16px; 13 | letter-spacing: 0.529412px; 14 | color: #ffffff; 15 | `; 16 | 17 | const StyledLink = styled(Link)` 18 | margin-left: 2px; 19 | text-decoration: none; 20 | display: flex; 21 | align-items: center; 22 | `; 23 | 24 | function BackLink(props) { 25 | const { to, children, className } = props; 26 | return ( 27 | 28 | 29 | 30 | {children} 31 | 32 | 33 | ); 34 | } 35 | 36 | BackLink.whyDidYouRender = true; 37 | export default compose(memo)(BackLink); 38 | -------------------------------------------------------------------------------- /app/components/InfoCard/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import AnimatedNumber from 'animated-number-react'; 4 | // import BigNumber from 'bignumber.js'; 5 | 6 | const Card = styled.div` 7 | background-color: ${(props) => props.theme.infoCardBackground}; 8 | padding: 32px; 9 | border-radius: 4px; 10 | color: ${(props) => props.theme.infoCardText}; 11 | text-align: center; 12 | `; 13 | 14 | const Label = styled.div` 15 | color: ${(props) => props.theme.infoCardLabel}; 16 | font-size: 16px; 17 | margin-bottom: 8px; 18 | `; 19 | 20 | const Value = styled.div` 21 | font-size: 24px; 22 | font-weight: 500; 23 | `; 24 | 25 | export default function InfoCard(props) { 26 | const { label, value, formatter } = props; 27 | return ( 28 | 29 | 30 | 31 | 32 | 33 | 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | collectCoverageFrom: [ 3 | 'app/**/*.{js,jsx}', 4 | '!app/**/*.test.{js,jsx}', 5 | '!app/*/RbGenerated*/*.{js,jsx}', 6 | '!app/app.js', 7 | '!app/global-styles.js', 8 | '!app/*/*/Loadable.{js,jsx}', 9 | ], 10 | coverageThreshold: { 11 | global: { 12 | statements: 98, 13 | branches: 91, 14 | functions: 98, 15 | lines: 98, 16 | }, 17 | }, 18 | moduleDirectories: ['node_modules', 'app'], 19 | moduleNameMapper: { 20 | '.*\\.(css|less|styl|scss|sass)$': '/internals/mocks/cssModule.js', 21 | '.*\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': 22 | '/internals/mocks/image.js', 23 | }, 24 | setupFilesAfterEnv: [ 25 | '/internals/testing/test-bundler.js', 26 | 'react-testing-library/cleanup-after-each', 27 | ], 28 | setupFiles: ['raf/polyfill'], 29 | testRegex: 'tests/.*\\.test\\.js$', 30 | snapshotSerializers: [], 31 | }; 32 | -------------------------------------------------------------------------------- /app/images/twitter-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | modules: false, 7 | }, 8 | ], 9 | '@babel/preset-react', 10 | ], 11 | plugins: [ 12 | 'babel-plugin-macros', 13 | 'styled-components', 14 | ['@babel/plugin-proposal-class-properties', { loose: true }], 15 | ['@babel/plugin-proposal-private-methods', { loose: true }], 16 | ['@babel/plugin-proposal-private-property-in-object', { loose: true }], 17 | '@babel/plugin-syntax-dynamic-import', 18 | ], 19 | env: { 20 | production: { 21 | only: ['app'], 22 | plugins: [ 23 | 'lodash', 24 | 'transform-react-remove-prop-types', 25 | '@babel/plugin-transform-react-inline-elements', 26 | '@babel/plugin-transform-react-constant-elements', 27 | ], 28 | }, 29 | test: { 30 | plugins: [ 31 | '@babel/plugin-transform-modules-commonjs', 32 | 'dynamic-import-node', 33 | ], 34 | }, 35 | }, 36 | }; 37 | -------------------------------------------------------------------------------- /app/components/VaultControls/SvgArrow.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | const SvgArrow = () => ( 3 | 10 | 16 | 17 | ); 18 | 19 | export default SvgArrow; 20 | -------------------------------------------------------------------------------- /app/utils/tests/checkStore.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Test injectors 3 | */ 4 | 5 | import checkStore from '../checkStore'; 6 | 7 | describe('checkStore', () => { 8 | let store; 9 | 10 | beforeEach(() => { 11 | store = { 12 | dispatch: () => {}, 13 | subscribe: () => {}, 14 | getState: () => {}, 15 | replaceReducer: () => {}, 16 | runSaga: () => {}, 17 | injectedReducers: {}, 18 | injectedSagas: {}, 19 | }; 20 | }); 21 | 22 | it('should not throw if passed valid store shape', () => { 23 | expect(() => checkStore(store)).not.toThrow(); 24 | }); 25 | 26 | it('should throw if passed invalid store shape', () => { 27 | expect(() => checkStore({})).toThrow(); 28 | expect(() => checkStore({ ...store, injectedSagas: null })).toThrow(); 29 | expect(() => checkStore({ ...store, injectedReducers: null })).toThrow(); 30 | expect(() => checkStore({ ...store, runSaga: null })).toThrow(); 31 | expect(() => checkStore({ ...store, replaceReducer: null })).toThrow(); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /internals/generators/container/reducer.test.js.hbs: -------------------------------------------------------------------------------- 1 | // import produce from 'immer'; 2 | import {{ camelCase name }}Reducer from '../reducer'; 3 | // import { someAction } from '../actions'; 4 | 5 | /* eslint-disable default-case, no-param-reassign */ 6 | describe('{{ camelCase name }}Reducer', () => { 7 | let state; 8 | beforeEach(() => { 9 | state = { 10 | // default state params here 11 | }; 12 | }); 13 | 14 | it('returns the initial state', () => { 15 | const expectedResult = state; 16 | expect({{ camelCase name }}Reducer(undefined, {})).toEqual(expectedResult); 17 | }); 18 | 19 | /** 20 | * Example state change comparison 21 | * 22 | * it('should handle the someAction action correctly', () => { 23 | * const expectedResult = produce(state, draft => { 24 | * draft.loading = true; 25 | * draft.error = false; 26 | * draft.userData.nested = false; 27 | * }); 28 | * 29 | * expect(appReducer(state, someAction())).toEqual(expectedResult); 30 | * }); 31 | */ 32 | }); 33 | -------------------------------------------------------------------------------- /app/components/CreamProgressBar/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { makeStyles, withStyles } from '@material-ui/core/styles'; 3 | import LinearProgress from '@material-ui/core/LinearProgress'; 4 | 5 | const LinearProgressBar = withStyles(() => ({ 6 | root: { 7 | height: 5, 8 | }, 9 | colorPrimary: { 10 | backgroundColor: 'rgba(0, 0, 0, 0.1)', 11 | }, 12 | bar: { 13 | borderRadius: 5, 14 | backgroundColor: ({ value }) => 15 | // eslint-disable-next-line no-nested-ternary 16 | value < 75 ? '#23d198' : value <= 100 ? '#FABF06' : '#EF1E02', 17 | }, 18 | }))(LinearProgress); 19 | 20 | const useStyles = makeStyles({ 21 | root: { 22 | width: '100%', 23 | flexGrow: 1, 24 | }, 25 | }); 26 | 27 | export default function CreamProgressBars({ value }) { 28 | const classes = useStyles(); 29 | 30 | return ( 31 |
32 | 100 ? 101 : value} 35 | /> 36 |
37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /app/containers/Zapper/reducer.js: -------------------------------------------------------------------------------- 1 | import produce from 'immer'; 2 | import { keyBy } from 'lodash'; 3 | import { ZAPPER_DATA_LOADED, ZAP_IN_ERROR, ZAP_OUT_ERROR } from './constants'; 4 | 5 | export const initialState = { 6 | tokens: {}, 7 | vaults: {}, 8 | balances: {}, 9 | error: null, 10 | errorOut: null, 11 | }; 12 | 13 | /* eslint-disable default-case, no-param-reassign */ 14 | const zapperReducer = (state = initialState, action) => 15 | produce(state, (draft) => { 16 | switch (action.type) { 17 | case ZAPPER_DATA_LOADED: { 18 | draft.tokens = keyBy(action.payload.tokens, 'address'); 19 | draft.vaults = keyBy(action.payload.vaults, 'address'); 20 | draft.balances = keyBy(action.payload.balances, 'address'); 21 | break; 22 | } 23 | case ZAP_IN_ERROR: { 24 | draft.error = action.payload; 25 | break; 26 | } 27 | case ZAP_OUT_ERROR: { 28 | draft.errorOut = action.payload; 29 | break; 30 | } 31 | } 32 | }); 33 | 34 | export default zapperReducer; 35 | -------------------------------------------------------------------------------- /app/components/Icon/copy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/containers/Zapper/actions.js: -------------------------------------------------------------------------------- 1 | import { 2 | INIT_ZAPPER, 3 | ZAPPER_DATA_LOADED, 4 | ZAP_IN, 5 | ZAP_IN_ERROR, 6 | ZAP_OUT_ERROR, 7 | ZAP_OUT, 8 | MIGRATE_PICKLE_GAUGE, 9 | } from './constants'; 10 | 11 | export function initializeZapper() { 12 | return { 13 | type: INIT_ZAPPER, 14 | }; 15 | } 16 | 17 | export function zapperDataLoaded(payload) { 18 | return { 19 | type: ZAPPER_DATA_LOADED, 20 | payload, 21 | }; 22 | } 23 | 24 | export function zapIn(payload) { 25 | return { 26 | type: ZAP_IN, 27 | payload, 28 | }; 29 | } 30 | 31 | export function zapOut(payload) { 32 | return { 33 | type: ZAP_OUT, 34 | payload, 35 | }; 36 | } 37 | 38 | export function zapInError(payload) { 39 | return { 40 | type: ZAP_IN_ERROR, 41 | payload, 42 | }; 43 | } 44 | 45 | export function zapOutError(payload) { 46 | return { 47 | type: ZAP_OUT_ERROR, 48 | payload, 49 | }; 50 | } 51 | 52 | export function migratePickleGauge(payload) { 53 | return { 54 | type: MIGRATE_PICKLE_GAUGE, 55 | payload, 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /app/containers/Splash/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import Button from 'components/Button'; 4 | import { FormattedMessage } from 'react-intl'; 5 | import { Link } from 'react-router-dom'; 6 | import { SplashScreen } from 'components/SplashScreen'; 7 | 8 | const Wrapper = styled.div` 9 | background-color: ${(props) => props.theme.yearnBlue}; 10 | top: 0; 11 | bottom: 0; 12 | left: 0; 13 | right: 0; 14 | position: absolute; 15 | display: flex; 16 | justify-content: center; 17 | align-items: center; 18 | flex-direction: column; 19 | `; 20 | 21 | const Logo = styled.div` 22 | color: ${({ theme }) => theme.white}; 23 | font-size: 44px; 24 | margin-bottom: 20px; 25 | `; 26 | 27 | export const Splash = () => ; 28 | 29 | export default function HomePage() { 30 | return ( 31 | 32 | Yearn 33 | 34 | 37 | 38 | 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /app/utils/permit.js: -------------------------------------------------------------------------------- 1 | export function createPermitMessageData( 2 | owner, 3 | spender, 4 | value, 5 | nonce, 6 | deadline, 7 | domainName, 8 | verifyingContract, 9 | ) { 10 | const permitData = { 11 | types: { 12 | EIP712Domain: [ 13 | { name: 'name', type: 'string' }, 14 | { name: 'version', type: 'string' }, 15 | { name: 'chainId', type: 'uint256' }, 16 | { name: 'verifyingContract', type: 'address' }, 17 | ], 18 | Permit: [ 19 | { name: 'owner', type: 'address' }, 20 | { name: 'spender', type: 'address' }, 21 | { name: 'value', type: 'uint256' }, 22 | { name: 'nonce', type: 'uint256' }, 23 | { name: 'deadline', type: 'uint256' }, 24 | ], 25 | }, 26 | domain: { 27 | name: domainName, 28 | version: '2', 29 | chainId: 1, 30 | verifyingContract, 31 | }, 32 | primaryType: 'Permit', 33 | message: { 34 | owner, 35 | spender, 36 | value, 37 | nonce, 38 | deadline, 39 | }, 40 | }; 41 | 42 | return JSON.stringify(permitData); 43 | } 44 | -------------------------------------------------------------------------------- /app/containers/ThemeProvider/reducer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Theme reducer 4 | * 5 | */ 6 | import produce from 'immer'; 7 | 8 | import { TOGGLE_DARK_MODE, SET_THEME_MODE, DARK_MODE } from './constants'; 9 | 10 | const defaultMode = DARK_MODE; 11 | const localStorageDarkModeStr = localStorage.getItem('darkMode'); 12 | const localStorageDarkMode = JSON.parse(localStorageDarkModeStr); 13 | const modeNotSet = localStorageDarkModeStr === null; 14 | 15 | let darkMode; 16 | if (modeNotSet) { 17 | darkMode = defaultMode === DARK_MODE; 18 | } else { 19 | darkMode = localStorageDarkMode; 20 | } 21 | 22 | export const initialState = { 23 | darkMode, 24 | }; 25 | 26 | /* eslint-disable default-case, no-param-reassign */ 27 | const languageProviderReducer = (state = initialState, action) => 28 | produce(state, (draft) => { 29 | switch (action.type) { 30 | case TOGGLE_DARK_MODE: 31 | draft.darkMode = !draft.darkMode; 32 | break; 33 | case SET_THEME_MODE: 34 | draft.darkMode = action.mode === DARK_MODE; 35 | break; 36 | } 37 | }); 38 | 39 | export default languageProviderReducer; 40 | -------------------------------------------------------------------------------- /app/containers/Vaults/retiredWhitelist.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "address": "0x29E240CFD7946BA20895a7a02eDb25C210f9f324", 4 | "vault": "aLINK v1", 5 | "apyTooltip": "This vault is no longer active and its strategy is unwinding.", 6 | "type": "v1" 7 | }, 8 | { 9 | "address": "0xe11ba472F74869176652C35D30dB89854b5ae84D", 10 | "apyTooltip": "This vault is no longer active. Please withdraw any funds.", 11 | "vault": "HEGIC v2", 12 | "type": "v2" 13 | }, 14 | { 15 | "address": "0xa5cA62D95D24A4a350983D5B8ac4EB8638887396", 16 | "apyTooltip": "This vault is no longer active. Please withdraw any funds.", 17 | "vault": "sUSD", 18 | "type": "v2" 19 | }, 20 | { 21 | "address": "0xE0db48B4F71752C4bEf16De1DBD042B82976b8C7", 22 | "vault": "mUSD", 23 | "apyTooltip": "This vault is no longer active and its strategy is unwinding.", 24 | "type": "v1" 25 | }, 26 | { 27 | "address": "0x6FAfCA7f49B4Fd9dC38117469cd31A1E5aec91F5", 28 | "vault": "USDM", 29 | "apyTooltip": "This vault is no longer active. Please withdraw any funds.", 30 | "type": "v2" 31 | } 32 | ] 33 | -------------------------------------------------------------------------------- /app/components/ButtonFilledRed/index.js: -------------------------------------------------------------------------------- 1 | // TODO: Remove this and refactor buttons 2 | 3 | import React from 'react'; 4 | 5 | import Button from '@material-ui/core/Button'; 6 | import { withStyles } from '@material-ui/core/styles'; 7 | import { useShowDevVaults } from 'containers/Vaults/hooks'; 8 | 9 | export default function ButtonFilled(props) { 10 | const { onClick, disabled, children, type, title, onSubmit } = props; 11 | const showDevVaults = useShowDevVaults(); 12 | 13 | const ColorButton = withStyles(() => ({ 14 | root: { 15 | fontFamily: 'Calibre Medium', 16 | fontSize: '20px', 17 | padding: '8px 20px 5px 20px', 18 | margin: '10px 0px', 19 | marginLeft: '20px', 20 | width: '200px', 21 | textTransform: showDevVaults ? 'inherit' : 'capitalize', 22 | }, 23 | }))(Button); 24 | 25 | return ( 26 | 35 | {children} 36 | 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /app/abi/zapViper.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "outputs": [], 4 | "inputs": [], 5 | "stateMutability": "nonpayable", 6 | "type": "constructor" 7 | }, 8 | { 9 | "name": "zap", 10 | "outputs": [], 11 | "inputs": [], 12 | "stateMutability": "nonpayable", 13 | "type": "function", 14 | "gas": 13629 15 | }, 16 | { 17 | "name": "threeCrv", 18 | "outputs": [ 19 | { 20 | "type": "address", 21 | "name": "" 22 | } 23 | ], 24 | "inputs": [], 25 | "stateMutability": "view", 26 | "type": "function", 27 | "gas": 1091 28 | }, 29 | { 30 | "name": "threeCrvVault", 31 | "outputs": [ 32 | { 33 | "type": "address", 34 | "name": "" 35 | } 36 | ], 37 | "inputs": [], 38 | "stateMutability": "view", 39 | "type": "function", 40 | "gas": 1121 41 | }, 42 | { 43 | "name": "vecrvVault", 44 | "outputs": [ 45 | { 46 | "type": "address", 47 | "name": "" 48 | } 49 | ], 50 | "inputs": [], 51 | "stateMutability": "view", 52 | "type": "function", 53 | "gas": 1151 54 | } 55 | ] 56 | -------------------------------------------------------------------------------- /app/containers/DevMode/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useInjectSaga } from 'utils/injectSaga'; 3 | import { useInjectReducer } from 'utils/injectReducer'; 4 | 5 | import styled from 'styled-components'; 6 | import { useDispatch } from 'react-redux'; 7 | import { toggleDevMode } from './actions'; 8 | import saga from './saga'; 9 | import reducer from './reducer'; 10 | 11 | const Wrapper = styled.div` 12 | display: ${(props) => (props.devModeUnlocked ? 'inherit' : 'none')}; 13 | position: absolute; 14 | z-index: 5; 15 | top: 60px; 16 | `; 17 | 18 | export default function DevModeToggle() { 19 | useInjectSaga({ key: 'devMode', saga }); 20 | useInjectReducer({ key: 'devMode', reducer }); 21 | 22 | const devMode = true; 23 | const devModeUnlocked = true; 24 | const dispatch = useDispatch(); 25 | const toggleMode = () => { 26 | dispatch(toggleDevMode()); 27 | }; 28 | 29 | return ( 30 | 31 | 35 | 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /app/components/IconButton/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import Icon from 'components/Icon'; 4 | 5 | const Wrapper = styled.button` 6 | background-color: ${(props) => props.theme.buttonBackground}; 7 | color: ${(props) => props.theme.buttonColor}; 8 | align-items: center; 9 | border-radius: 4px; 10 | height: 40px; 11 | line-height: 40px; 12 | padding: 0 12px; 13 | font-weight: 500; 14 | cursor: pointer; 15 | font-size: 14px; 16 | `; 17 | 18 | const StyledIcon = styled(Icon)` 19 | height: 1.1em; 20 | width: 1.1em; 21 | margin-right: 4px; 22 | `; 23 | 24 | const IconWrapper = styled.div` 25 | display: flex; 26 | align-items: center; 27 | `; 28 | 29 | export default function Button(props) { 30 | const { onClick, disabled, className, iconType } = props; 31 | return ( 32 | 38 | 39 | 40 |
{props.children}
41 |
42 |
43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Maximilian Stoiber 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/utils/reducerInjectors.js: -------------------------------------------------------------------------------- 1 | import invariant from 'invariant'; 2 | import { isEmpty, isFunction, isString } from 'lodash'; 3 | 4 | import checkStore from './checkStore'; 5 | import createReducer from '../reducers'; 6 | 7 | export function injectReducerFactory(store, isValid) { 8 | return function injectReducer(key, reducer) { 9 | if (!isValid) checkStore(store); 10 | 11 | invariant( 12 | isString(key) && !isEmpty(key) && isFunction(reducer), 13 | '(app/utils...) injectReducer: Expected `reducer` to be a reducer function', 14 | ); 15 | 16 | // Check `store.injectedReducers[key] === reducer` for hot reloading when a key is the same but a reducer is different 17 | if ( 18 | Reflect.has(store.injectedReducers, key) && 19 | store.injectedReducers[key] === reducer 20 | ) 21 | return; 22 | 23 | store.injectedReducers[key] = reducer; // eslint-disable-line no-param-reassign 24 | store.replaceReducer(createReducer(store.injectedReducers)); 25 | }; 26 | } 27 | 28 | export default function getInjectors(store) { 29 | checkStore(store); 30 | 31 | return { 32 | injectReducer: injectReducerFactory(store, true), 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /app/components/Modal/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Modal from '@material-ui/core/Modal'; 3 | import Backdrop from '@material-ui/core/Backdrop'; 4 | import Fade from '@material-ui/core/Fade'; 5 | import Container from '@material-ui/core/Container'; 6 | import Box from '@material-ui/core/Box'; 7 | import styled from 'styled-components'; 8 | 9 | const StyledModal = styled(Modal)` 10 | display: flex; 11 | align-items: center; 12 | justify-content: center; 13 | `; 14 | 15 | const StyledContainer = styled(Container)` 16 | &&& { 17 | outline: 0; 18 | } 19 | `; 20 | 21 | const StyledBox = styled(Box)` 22 | &&& { 23 | background: #fff; 24 | } 25 | `; 26 | 27 | export const BaseModal = ({ children, open, onClose }) => ( 28 | 37 | 38 | 39 | {children} 40 | 41 | 42 | 43 | ); 44 | 45 | export default BaseModal; 46 | -------------------------------------------------------------------------------- /app/images/discord-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/components/VaultsNavLinks/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import tw from 'twin.macro'; 4 | 5 | import { NavLink } from 'react-router-dom'; 6 | 7 | const Wrapper = styled.div``; 8 | 9 | const NavLinks = styled.nav` 10 | display: flex; 11 | justify-content: center; 12 | `; 13 | 14 | const StyledNavLink = styled(NavLink)` 15 | height: 100%; 16 | @media (min-width: 768px) { 17 | font-size: 24px; 18 | padding: 0px 15px; 19 | } 20 | display: inline-flex; 21 | text-decoration: none; 22 | align-items: center; 23 | text-transform: uppercase; 24 | color: #777; 25 | ${tw`text-xl px-4`}; 26 | 27 | &:hover { 28 | color: ${(props) => props.theme.text}; 29 | } 30 | &.active { 31 | color: ${(props) => props.theme.text}; 32 | } 33 | `; 34 | 35 | export default function VaultsNavLinks() { 36 | return ( 37 | 38 | 39 | 40 | Production 41 | 42 | 43 | Experimental 44 | 45 | 46 | 47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /app/components/LiteHeader/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import { NavLink } from 'react-router-dom'; 4 | 5 | const Wrapper = styled.div` 6 | padding-top: 30px; 7 | padding-bottom: 50px; 8 | `; 9 | 10 | const NavLinks = styled.nav` 11 | display: flex; 12 | justify-content: center; 13 | `; 14 | 15 | const StyledNavLink = styled(NavLink)` 16 | padding: 0px 15px; 17 | height: 100%; 18 | font-size: 24px; 19 | display: inline-flex; 20 | text-decoration: none; 21 | align-items: center; 22 | text-transform: uppercase; 23 | color: #777; 24 | &:hover { 25 | color: ${(props) => props.theme.text}; 26 | } 27 | &.active { 28 | color: ${(props) => props.theme.text}; 29 | } 30 | `; 31 | 32 | export default function VaultsNavLinks() { 33 | return ( 34 | 35 | 36 | 37 | Earn 38 | 39 | 40 | Borrow 41 | 42 | 43 | Insure 44 | 45 | 46 | 47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /server/middlewares/addDevMiddlewares.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const webpackDevMiddleware = require('webpack-dev-middleware'); 4 | const webpackHotMiddleware = require('webpack-hot-middleware'); 5 | 6 | function createWebpackMiddleware(compiler, publicPath) { 7 | return webpackDevMiddleware(compiler, { 8 | logLevel: 'warn', 9 | publicPath, 10 | silent: true, 11 | stats: 'errors-only', 12 | }); 13 | } 14 | 15 | module.exports = function addDevMiddlewares(app, webpackConfig) { 16 | const compiler = webpack(webpackConfig); 17 | const middleware = createWebpackMiddleware( 18 | compiler, 19 | webpackConfig.output.publicPath, 20 | ); 21 | 22 | app.use(middleware); 23 | app.use(webpackHotMiddleware(compiler)); 24 | 25 | // Since webpackDevMiddleware uses memory-fs internally to store build 26 | // artifacts, we use it instead 27 | const fs = middleware.fileSystem; 28 | 29 | app.get('*', (req, res) => { 30 | fs.readFile(path.join(compiler.outputPath, 'index.html'), (err, file) => { 31 | if (err) { 32 | res.sendStatus(404); 33 | } else { 34 | res.send(file.toString()); 35 | } 36 | }); 37 | }); 38 | }; 39 | -------------------------------------------------------------------------------- /app/containers/LiteCover/index.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import styled from 'styled-components'; 3 | import { useInjectSaga } from 'utils/injectSaga'; 4 | import { useInjectReducer } from 'utils/injectReducer'; 5 | import LiteCoverCards from 'components/LiteCoverCards'; 6 | import CoverProtocol from 'components/CoverProtocol'; 7 | import { Switch, Route } from 'react-router-dom'; 8 | import { useDispatch } from 'react-redux'; 9 | import { initializeCover } from './actions'; 10 | import reducer from './reducer'; 11 | import saga from './saga'; 12 | 13 | const Wrapper = styled.div` 14 | margin: 0 auto; 15 | max-width: 1200px; 16 | padding: 50px 40px; 17 | `; 18 | 19 | const Cover = () => { 20 | const dispatch = useDispatch(); 21 | useInjectSaga({ key: 'cover', saga }); 22 | useInjectReducer({ key: 'cover', reducer }); 23 | 24 | useEffect(() => { 25 | dispatch(initializeCover()); 26 | }, []); 27 | return ( 28 | 29 | 30 | 31 | 32 | 33 | 34 | ); 35 | }; 36 | 37 | Cover.whyDidYouRender = true; 38 | export default Cover; 39 | -------------------------------------------------------------------------------- /server/logger.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | const chalk = require('chalk'); 4 | const ip = require('ip'); 5 | 6 | const divider = chalk.gray('\n-----------------------------------'); 7 | 8 | /** 9 | * Logger middleware, you can customize it to make messages more personal 10 | */ 11 | const logger = { 12 | // Called whenever there's an error on the server we want to print 13 | error: (err) => { 14 | console.error(chalk.red(err)); 15 | }, 16 | 17 | // Called when express.js app starts on given port w/o errors 18 | appStarted: (port, host, tunnelStarted) => { 19 | console.log(`Server started ! ${chalk.green('✓')}`); 20 | 21 | // If the tunnel started, log that and the URL it's available at 22 | if (tunnelStarted) { 23 | console.log(`Tunnel initialised ${chalk.green('✓')}`); 24 | } 25 | 26 | console.log(` 27 | ${chalk.bold('Access URLs:')}${divider} 28 | Localhost: ${chalk.magenta(`http://${host}:${port}`)} 29 | LAN: ${ 30 | chalk.magenta(`http://${ip.address()}:${port}`) + 31 | (tunnelStarted ? `\n Proxy: ${chalk.magenta(tunnelStarted)}` : '') 32 | }${divider} 33 | ${chalk.blue(`Press ${chalk.italic('CTRL-C')} to stop`)} 34 | `); 35 | }, 36 | }; 37 | 38 | module.exports = logger; 39 | -------------------------------------------------------------------------------- /app/containers/DevMode/reducer.js: -------------------------------------------------------------------------------- 1 | import produce from 'immer'; 2 | 3 | import { TOGGLE_DEV_MODE, UNLOCK_DEV_MODE } from './constants'; 4 | 5 | const defaultDevMode = true; 6 | const localStorageDevModeStr = localStorage.getItem('devMode'); 7 | const localStorageDevMode = JSON.parse(localStorageDevModeStr); 8 | const localStorageDevModeUnlockedStr = localStorage.getItem('devModeUnlocked'); 9 | const localStorageDevModeUnlocked = JSON.parse(localStorageDevModeUnlockedStr); 10 | const modeNotSet = localStorageDevModeStr === null; 11 | 12 | let devMode; 13 | if (modeNotSet) { 14 | devMode = defaultDevMode; 15 | } else { 16 | devMode = localStorageDevMode; 17 | } 18 | 19 | const unlocked = localStorageDevModeUnlocked || false; 20 | 21 | export const initialState = { 22 | enabled: unlocked && devMode, 23 | unlocked, 24 | }; 25 | 26 | /* eslint-disable default-case, no-param-reassign */ 27 | export default function reducer(state = initialState, action) { 28 | return produce(state, (draft) => { 29 | switch (action.type) { 30 | case TOGGLE_DEV_MODE: 31 | draft.enabled = draft.unlocked && !draft.enabled; 32 | break; 33 | case UNLOCK_DEV_MODE: 34 | draft.unlocked = true; 35 | draft.enabled = true; 36 | } 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /app/containers/Cover/index.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import styled from 'styled-components'; 3 | import { useInjectSaga } from 'utils/injectSaga'; 4 | import { useInjectReducer } from 'utils/injectReducer'; 5 | import CoverCards from 'components/CoverCards'; 6 | import CoverProtocol from 'components/CoverProtocol'; 7 | import { Switch, Route } from 'react-router-dom'; 8 | import { useDispatch } from 'react-redux'; 9 | import { initializeCover } from './actions'; 10 | import reducer from './reducer'; 11 | import saga from './saga'; 12 | 13 | const Wrapper = styled.div` 14 | width: 100%; 15 | padding: 50px 20px; 16 | @media (min-width: 768px) { 17 | padding: 50px 40px; 18 | } 19 | `; 20 | 21 | const Cover = () => { 22 | const dispatch = useDispatch(); 23 | useInjectSaga({ key: 'cover', saga }); 24 | useInjectReducer({ key: 'cover', reducer }); 25 | 26 | useEffect(() => { 27 | dispatch(initializeCover()); 28 | }, []); 29 | return ( 30 | 31 | 32 | 33 | 34 | 35 | 36 | ); 37 | }; 38 | 39 | Cover.whyDidYouRender = true; 40 | export default Cover; 41 | -------------------------------------------------------------------------------- /app/abi/minimalErc20.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [ 5 | { 6 | "name": "_owner", 7 | "type": "address" 8 | } 9 | ], 10 | "name": "balanceOf", 11 | "outputs": [ 12 | { 13 | "name": "balance", 14 | "type": "uint256" 15 | } 16 | ], 17 | "payable": false, 18 | "stateMutability": "view", 19 | "type": "function" 20 | }, 21 | { 22 | "constant": true, 23 | "inputs": [], 24 | "name": "name", 25 | "outputs": [ 26 | { 27 | "name": "", 28 | "type": "string" 29 | } 30 | ], 31 | "payable": false, 32 | "stateMutability": "view", 33 | "type": "function" 34 | }, 35 | { 36 | "constant": true, 37 | "inputs": [], 38 | "name": "decimals", 39 | "outputs": [ 40 | { 41 | "name": "", 42 | "type": "uint256" 43 | } 44 | ], 45 | "payable": false, 46 | "stateMutability": "view", 47 | "type": "function" 48 | }, 49 | { 50 | "constant": true, 51 | "inputs": [], 52 | "name": "symbol", 53 | "outputs": [{ "internalType": "string", "name": "", "type": "string" }], 54 | "payable": false, 55 | "stateMutability": "view", 56 | "type": "function" 57 | } 58 | ] 59 | -------------------------------------------------------------------------------- /app/drizzle/store/contracts/contractsSubscriptionsReducer.js: -------------------------------------------------------------------------------- 1 | /* eslint no-param-reassign: 0 */ 2 | 3 | import produce from 'immer'; 4 | import { 5 | DRIZZLE_ADD_CONTRACTS, 6 | DELETE_CONTRACT, 7 | } from 'containers/DrizzleProvider/constants'; 8 | 9 | const initialState = []; 10 | 11 | const contractsReducer = (state = initialState, action) => 12 | // eslint-disable-next-line no-unused-vars 13 | produce(state, (draft) => { 14 | switch (action.type) { 15 | case DRIZZLE_ADD_CONTRACTS: { 16 | const { contracts } = action; 17 | state.push(...contracts); 18 | draft = state; 19 | break; 20 | } 21 | case DELETE_CONTRACT: { 22 | const { contractName: address } = action; 23 | const removeAddress = (subscription) => { 24 | const { addresses } = subscription; 25 | const newAddresses = _.pull(addresses, address); 26 | subscription.addresses = newAddresses; 27 | return subscription; 28 | }; 29 | const subscriptions = state; 30 | const newSubscriptions = _.map(subscriptions, removeAddress); 31 | draft = newSubscriptions; 32 | break; 33 | } 34 | default: 35 | break; 36 | } 37 | }); 38 | 39 | export default contractsReducer; 40 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # http://www.appveyor.com/docs/appveyor-yml 2 | 3 | # Set build version format here instead of in the admin panel 4 | version: '{build}' 5 | 6 | # Do not build on gh tags 7 | skip_tags: true 8 | 9 | # Test against these versions of Node.js 10 | environment: 11 | matrix: 12 | # Node versions to run 13 | - nodejs_version: 'Current' 14 | - nodejs_version: 'LTS' 15 | 16 | # Fix line endings in Windows. (runs before repo cloning) 17 | init: 18 | - git config --global core.autocrlf input 19 | 20 | # Install scripts--runs after repo cloning 21 | install: 22 | # Install the latest stable version of Node 23 | - ps: Install-Product node $env:nodejs_version 24 | - npm ci 25 | 26 | # Disable automatic builds 27 | build: off 28 | 29 | # Post-install test scripts 30 | test_script: 31 | # Output debugging info 32 | - node --version 33 | - node ./internals/scripts/generate-templates-for-linting 34 | # run tests and run build 35 | - npm run test 36 | - npm run build 37 | 38 | # Cache node_modules for faster builds 39 | cache: 40 | - node_modules -> package.json 41 | # remove, as appveyor doesn't support secure variables on pr builds 42 | # so `COVERALLS_REPO_TOKEN` cannot be set, without hard-coding in this file 43 | #on_success: 44 | #- npm run coveralls 45 | -------------------------------------------------------------------------------- /app/containers/LanguageProvider/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * LanguageProvider 4 | * 5 | * this component connects the redux state language locale to the 6 | * IntlProvider component and i18n messages (loaded from `app/translations`) 7 | */ 8 | 9 | import React from 'react'; 10 | import PropTypes from 'prop-types'; 11 | import { connect } from 'react-redux'; 12 | import { createSelector } from 'reselect'; 13 | import { IntlProvider } from 'react-intl'; 14 | 15 | import { makeSelectLocale } from './selectors'; 16 | 17 | export function LanguageProvider(props) { 18 | return ( 19 | 25 | {React.Children.only(props.children)} 26 | 27 | ); 28 | } 29 | 30 | LanguageProvider.propTypes = { 31 | locale: PropTypes.string, 32 | messages: PropTypes.object, 33 | children: PropTypes.element.isRequired, 34 | }; 35 | 36 | const mapStateToProps = createSelector(makeSelectLocale(), (locale) => ({ 37 | locale, 38 | })); 39 | 40 | function mapDispatchToProps(dispatch) { 41 | return { 42 | dispatch, 43 | }; 44 | } 45 | 46 | export default connect(mapStateToProps, mapDispatchToProps)(LanguageProvider); 47 | -------------------------------------------------------------------------------- /app/drizzle/store/contracts/contractsActions.js: -------------------------------------------------------------------------------- 1 | const INITIALIZING_CONTRACT = 'INITIALIZING_CONTRACT'; 2 | 3 | export function initializingContract(results) { 4 | return { 5 | type: INITIALIZING_CONTRACT, 6 | payload: results, 7 | }; 8 | } 9 | 10 | const INITIALIZED_CONTRACT = 'INITIALIZED_CONTRACT'; 11 | 12 | export function initializedContract(results) { 13 | return { 14 | type: INITIALIZED_CONTRACT, 15 | payload: results, 16 | }; 17 | } 18 | 19 | const GETTING_CONTRACT_VAR = 'GETTING_CONTRACT_VAR'; 20 | 21 | export function gettingContractVar(results) { 22 | return { 23 | type: GETTING_CONTRACT_VAR, 24 | payload: results, 25 | }; 26 | } 27 | 28 | const GOT_CONTRACT_VAR = 'GOT_CONTRACT_VAR'; 29 | 30 | export function gotContractVar(results) { 31 | return { 32 | type: GOT_CONTRACT_VAR, 33 | payload: results, 34 | }; 35 | } 36 | 37 | const DELETE_CONTRACT = 'DELETE_CONTRACT'; 38 | 39 | export function deleteContract(contractName) { 40 | return { 41 | type: DELETE_CONTRACT, 42 | contractName, 43 | }; 44 | } 45 | 46 | const PROCESS_ADDRESSES_TO_UPDATE = 'PROCESS_ADDRESSES_TO_UPDATE'; 47 | 48 | export function processAdressesToUpdate(contractAddress) { 49 | return { 50 | type: PROCESS_ADDRESSES_TO_UPDATE, 51 | contractAddress, 52 | }; 53 | } 54 | -------------------------------------------------------------------------------- /app/drizzle/store/web3/web3Reducer.js: -------------------------------------------------------------------------------- 1 | import * as Action from './constants'; 2 | 3 | const initialState = { 4 | status: '', 5 | }; 6 | 7 | const web3Reducer = (state = initialState, action) => { 8 | if (action.type === Action.WEB3_INITIALIZING) { 9 | return { 10 | ...state, 11 | status: 'initializing', 12 | }; 13 | } 14 | 15 | if (action.type === Action.WEB3_INITIALIZED) { 16 | return { 17 | ...state, 18 | status: 'initialized', 19 | }; 20 | } 21 | 22 | if (action.type === Action.WEB3_FAILED) { 23 | return { 24 | ...state, 25 | status: 'failed', 26 | }; 27 | } 28 | 29 | if (action.type === Action.WEB3_USER_DENIED) { 30 | return { 31 | ...state, 32 | status: 'UserDeniedAccess', 33 | }; 34 | } 35 | 36 | if (action.type === Action.NETWORK_ID_FETCHED) { 37 | return { 38 | ...state, 39 | networkId: action.networkId, 40 | }; 41 | } 42 | 43 | if (action.type === Action.NETWORK_ID_FAILED) { 44 | return { 45 | ...state, 46 | networkId: action.networkId, 47 | }; 48 | } 49 | if (action.type === Action.NETWORK_MISMATCH) { 50 | return { 51 | ...state, 52 | networkMismatch: true, 53 | }; 54 | } 55 | 56 | return state; 57 | }; 58 | 59 | export default web3Reducer; 60 | -------------------------------------------------------------------------------- /app/tests/store.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Test store addons 3 | */ 4 | 5 | import { browserHistory } from 'react-router-dom'; 6 | import configureStore from '../configureStore'; 7 | 8 | describe('configureStore', () => { 9 | let store; 10 | 11 | beforeAll(() => { 12 | store = configureStore({}, browserHistory); 13 | }); 14 | 15 | describe('injectedReducers', () => { 16 | it('should contain an object for reducers', () => { 17 | expect(typeof store.injectedReducers).toBe('object'); 18 | }); 19 | }); 20 | 21 | describe('injectedSagas', () => { 22 | it('should contain an object for sagas', () => { 23 | expect(typeof store.injectedSagas).toBe('object'); 24 | }); 25 | }); 26 | 27 | describe('runSaga', () => { 28 | it('should contain a hook for `sagaMiddleware.run`', () => { 29 | expect(typeof store.runSaga).toBe('function'); 30 | }); 31 | }); 32 | }); 33 | 34 | describe('configureStore params', () => { 35 | it('should call window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__', () => { 36 | /* eslint-disable no-underscore-dangle */ 37 | const compose = jest.fn(); 38 | window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ = () => compose; 39 | configureStore(undefined, browserHistory); 40 | expect(compose).toHaveBeenCalled(); 41 | /* eslint-enable */ 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /app/drizzle/store/transactions/transactionsReducer.js: -------------------------------------------------------------------------------- 1 | const initialState = {}; 2 | 3 | const transactionsReducer = (state = initialState, action) => { 4 | if (action.type === 'TX_BROADCASTED') { 5 | return { 6 | ...state, 7 | [action.txHash]: { 8 | status: 'pending', 9 | confirmations: [], 10 | }, 11 | }; 12 | } 13 | 14 | if (action.type === 'TX_CONFIRMAITON') { 15 | return { 16 | ...state, 17 | [action.txHash]: { 18 | ...state[action.txHash], 19 | confirmations: [ 20 | ...state[action.txHash].confirmations, 21 | action.confirmationReceipt, 22 | ], 23 | }, 24 | }; 25 | } 26 | 27 | if (action.type === 'TX_SUCCESSFUL') { 28 | return { 29 | ...state, 30 | [action.txHash]: { 31 | ...state[action.txHash], 32 | status: 'success', 33 | receipt: action.receipt, 34 | }, 35 | }; 36 | } 37 | 38 | if (action.type === 'TX_ERROR') { 39 | return { 40 | ...state, 41 | [action.stackTempKey]: { 42 | ...state[action.stackTempKey], 43 | status: 'error', 44 | error: { 45 | message: action.error && action.error.message, 46 | }, 47 | }, 48 | }; 49 | } 50 | 51 | return state; 52 | }; 53 | 54 | export default transactionsReducer; 55 | -------------------------------------------------------------------------------- /app/images/medium-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.storybook/preview.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { addDecorator } from '@storybook/react'; 3 | import { ConnectedRouter } from 'connected-react-router'; 4 | import Layout from './Layout'; 5 | import { Provider } from 'react-redux'; 6 | import ConnectionProvider from 'containers/ConnectionProvider'; 7 | import DrizzleProvider from 'containers/DrizzleProvider'; 8 | import LanguageProvider from 'containers/LanguageProvider'; 9 | import configureStore from '../app/configureStore'; 10 | import { translationMessages } from '../app/i18n'; 11 | import history from 'utils/history'; 12 | import { GlobalStyles } from 'twin.macro'; 13 | 14 | const initialState = {}; 15 | const store = configureStore(initialState, history); 16 | 17 | addDecorator((storyFn) => ( 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | {storyFn()} 26 | 27 | 28 | 29 | 30 | 31 | 32 | )); 33 | 34 | export const parameters = { 35 | layout: 'fullscreen', 36 | actions: { argTypesRegex: '^on[A-Z].*' }, 37 | }; 38 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yaml: -------------------------------------------------------------------------------- 1 | name: Deployment 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | sync: 8 | name: Deploy to IPFS 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout repo 13 | uses: actions/checkout@v2 14 | 15 | - name: Use Node 12.x 16 | uses: actions/setup-node@v1 17 | with: 18 | node-version: 12.x 19 | 20 | - name: Install deps (with cache) 21 | uses: bahmutov/npm-install@v1 22 | 23 | - name: Generate build artifact 24 | run: yarn build 25 | env: 26 | API_ENV: ${{ secrets.API_ENV }} 27 | BLOCKNATIVE_DAPP_ID: ${{ secrets.BLOCKNATIVE_DAPP_ID }} 28 | ETHERSCAN_APIKEY: ${{ secrets.ETHERSCAN_APIKEY }} 29 | FORTMATIC_APIKEY: ${{ secrets.FORTMATIC_APIKEY }} 30 | PORTIS_APIKEY: ${{ secrets.PORTIS_APIKEY }} 31 | WEB3_PROVIDER_HTTPS: ${{ secrets.WEB3_PROVIDER_HTTPS }} 32 | WEB3_PROVIDER_WSS: ${{ secrets.WEB3_PROVIDER_WSS }} 33 | ZAPPER_APIKEY: ${{ secrets.ZAPPER_APIKEY }} 34 | 35 | - name: Deploy to IPFS 36 | id: deployment 37 | uses: nymmrx/ipfs-deploy@master 38 | with: 39 | path: "./build" 40 | pin-name: Yearn Finance V2 41 | pinata-key: ${{ secrets.PINATA_KEY }} 42 | pinata-secret: ${{ secrets.PINATA_SECRET }} 43 | -------------------------------------------------------------------------------- /app/utils/injectReducer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import hoistNonReactStatics from 'hoist-non-react-statics'; 3 | import { ReactReduxContext } from 'react-redux'; 4 | 5 | import getInjectors from './reducerInjectors'; 6 | 7 | /** 8 | * Dynamically injects a reducer 9 | * 10 | * @param {string} key A key of the reducer 11 | * @param {function} reducer A reducer that will be injected 12 | * 13 | */ 14 | export default ({ key, reducer }) => (WrappedComponent) => { 15 | class ReducerInjector extends React.Component { 16 | static WrappedComponent = WrappedComponent; 17 | 18 | static contextType = ReactReduxContext; 19 | 20 | static displayName = `withReducer(${ 21 | WrappedComponent.displayName || WrappedComponent.name || 'Component' 22 | })`; 23 | 24 | constructor(props, context) { 25 | super(props, context); 26 | 27 | getInjectors(context.store).injectReducer(key, reducer); 28 | } 29 | 30 | render() { 31 | return ; 32 | } 33 | } 34 | 35 | return hoistNonReactStatics(ReducerInjector, WrappedComponent); 36 | }; 37 | 38 | const useInjectReducer = ({ key, reducer }) => { 39 | const context = React.useContext(ReactReduxContext); 40 | React.useEffect(() => { 41 | getInjectors(context.store).injectReducer(key, reducer); 42 | }, []); 43 | }; 44 | 45 | export { useInjectReducer }; 46 | -------------------------------------------------------------------------------- /app/abi/minimalVault.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [], 5 | "name": "balance", 6 | "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], 7 | "payable": false, 8 | "stateMutability": "view", 9 | "type": "function" 10 | }, 11 | { 12 | "constant": true, 13 | "inputs": [], 14 | "name": "getPricePerFullShare", 15 | "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], 16 | "payable": false, 17 | "stateMutability": "view", 18 | "type": "function" 19 | }, 20 | { 21 | "constant": true, 22 | "inputs": [], 23 | "name": "symbol", 24 | "outputs": [{ "internalType": "string", "name": "", "type": "string" }], 25 | "payable": false, 26 | "stateMutability": "view", 27 | "type": "function" 28 | }, 29 | { 30 | "constant": true, 31 | "inputs": [], 32 | "name": "name", 33 | "outputs": [ 34 | { 35 | "name": "", 36 | "type": "string" 37 | } 38 | ], 39 | "payable": false, 40 | "stateMutability": "view", 41 | "type": "function" 42 | }, 43 | { 44 | "constant": true, 45 | "inputs": [], 46 | "name": "decimals", 47 | "outputs": [ 48 | { 49 | "name": "", 50 | "type": "uint256" 51 | } 52 | ], 53 | "payable": false, 54 | "stateMutability": "view", 55 | "type": "function" 56 | } 57 | ] 58 | -------------------------------------------------------------------------------- /app/components/Icon/clock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/components/TabbedNavigation/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | const Wrapper = styled.div` 5 | width: 100%; 6 | height: 100%; 7 | `; 8 | 9 | const PageName = styled.div` 10 | font-family: gotham; 11 | font-weight: 500; 12 | font-size: 14px; 13 | color: ${(props) => (props.pageSelected ? '#ff5f00' : '#9b9b9b')}; 14 | display: inline-block; 15 | padding-left: 7px; 16 | padding-right: 7px; 17 | text-transform: uppercase; 18 | `; 19 | 20 | const PageNames = styled.div` 21 | padding-bottom: 26px; 22 | text-align: center; 23 | width: 100%; 24 | `; 25 | 26 | function Component(props) { 27 | const { pages } = props; 28 | const [selectedPageNbr, setSelectedPageNbr] = useState(0); 29 | const currentPage = pages[selectedPageNbr]; 30 | const CurrentPage = currentPage.element; 31 | 32 | const renderPageName = (page, idx) => { 33 | const { name } = page; 34 | const pageSelected = selectedPageNbr === idx; 35 | return ( 36 | setSelectedPageNbr(idx)} 38 | key={idx} 39 | pageSelected={pageSelected} 40 | > 41 | {name} 42 | 43 | ); 44 | }; 45 | const pageNames = _.map(pages, renderPageName); 46 | return ( 47 | 48 | {pageNames} 49 | 50 | 51 | ); 52 | } 53 | 54 | export default Component; 55 | -------------------------------------------------------------------------------- /app/components/SplashScreen/Security/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { css } from 'twin.macro'; 3 | import SecurityGraphic from '../../../images/security-graphic.png'; 4 | 5 | export const Security = () => ( 6 |
16 |
17 |

18 | Keeping your money safe is our priority 19 |

20 |
21 |

Audited and verified

22 |

Learn more about

23 |

Yearn protocol security

24 |
25 |
26 | {/* audit logo */} 27 |
28 | Logo 29 |
30 |
31 | Logo 32 |
33 |
34 | Logo 35 |
36 |
37 | Logo 38 |
39 |
40 |
41 |
42 | ); 43 | -------------------------------------------------------------------------------- /app/components/Notify/GlobalNotifyStyles.js: -------------------------------------------------------------------------------- 1 | import tw, { theme } from 'twin.macro'; 2 | import { createGlobalStyle } from 'styled-components'; 3 | 4 | export const GlobalNotifyStyles = createGlobalStyle` 5 | .bn-notify-custom { 6 | 7 | &.bn-notify-notification { 8 | ${tw`rounded-md`} 9 | } 10 | 11 | &.bn-notify-notification-status-icon { 12 | ${tw`fill-current stroke-current text-white`} 13 | } 14 | &.bn-notify-notification-info-meta-duration-time, &.bn-notify-notification-info-meta-timestamp { 15 | ${tw`text-sm`} 16 | } 17 | 18 | &.bn-notify-notification-info { 19 | ${tw`text-white font-sans text-base`} 20 | } 21 | 22 | &.bn-notify-notification-pending { 23 | background: ${theme('background.yearn.gradient.blue')}; 24 | border: 2px solid; 25 | ${tw`shadow-yearn-blue border-yearn-blue`}; 26 | } 27 | &.bn-notify-notification-success { 28 | background: ${theme('background.yearn.gradient.green')}; 29 | border: 2px solid; 30 | ${tw`shadow-yearn-green border-yearn-green`}; 31 | } 32 | &.bn-notify-notification-error { 33 | background: ${theme('background.yearn.gradient.red')}; 34 | border: 2px solid; 35 | ${tw`shadow-yearn-red border-yearn-red`}; 36 | } 37 | &.bn-notify-notification-hint { 38 | background: ${theme('background.yearn.gradient.yellow')}; 39 | border: 2px solid; 40 | ${tw`shadow-yearn-yellow border-yearn-yellow`}; 41 | } 42 | } 43 | `; 44 | -------------------------------------------------------------------------------- /app/utils/request.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Parses the JSON returned by a network request 3 | * 4 | * @param {object} response A response from a network request 5 | * 6 | * @return {object} The parsed JSON from the request 7 | */ 8 | function parseJSON(response) { 9 | if (response.status === 204 || response.status === 205) { 10 | return null; 11 | } 12 | return response.json(); 13 | } 14 | 15 | /** 16 | * Checks if a network request came back fine, and throws an error if not 17 | * 18 | * @param {object} response A response from a network request 19 | * 20 | * @return {object|undefined} Returns either the response, or throws an error 21 | */ 22 | async function checkStatus(response) { 23 | if (response.status >= 200 && response.status < 300) { 24 | return response; 25 | } 26 | 27 | const data = await response.json(); 28 | 29 | if (data.error) { 30 | const responseError = new Error(data.message); 31 | responseError.response = response; 32 | throw error; 33 | } 34 | 35 | const error = new Error(response.statusText); 36 | error.response = response; 37 | throw error; 38 | } 39 | 40 | /** 41 | * Requests a URL, returning a promise 42 | * 43 | * @param {string} url The URL we want to request 44 | * @param {object} [options] The options we want to pass to "fetch" 45 | * 46 | * @return {object} The response data 47 | */ 48 | export default function request(url, options) { 49 | return fetch(url, options).then(checkStatus).then(parseJSON); 50 | } 51 | -------------------------------------------------------------------------------- /app/containers/Vaults/selectors.js: -------------------------------------------------------------------------------- 1 | import { createSelector } from 'reselect'; 2 | import { keyBy } from 'lodash'; 3 | import { selectContractsByTag } from 'containers/App/selectors'; 4 | import migrationWhitelist from 'containers/Vaults/migrationWhitelist.json'; 5 | import { TRICRYPTO_VAULT } from './constants'; 6 | 7 | export const selectMigrationData = createSelector( 8 | selectContractsByTag('trustedMigratorVaults'), 9 | (trustedMigratorVaults) => { 10 | const trustedMigratorVaultsByAddress = keyBy( 11 | trustedMigratorVaults, 12 | 'address', 13 | ); 14 | const migrationData = migrationWhitelist.map((migration) => ({ 15 | ...trustedMigratorVaultsByAddress[migration.vaultFrom], 16 | ...migration, 17 | })); 18 | 19 | const migrationDataByVault = keyBy(migrationData, 'vaultFrom'); 20 | return migrationDataByVault; 21 | }, 22 | ); 23 | 24 | export const selectTriCryptoMigrationData = createSelector( 25 | selectContractsByTag('triCryptoVaultMigratorVaults'), 26 | (triCryptoVaultMigratorVaults) => { 27 | const triCryptoVaultMigratorVaultsByAddress = keyBy( 28 | triCryptoVaultMigratorVaults, 29 | 'address', 30 | ); 31 | const migrationData = migrationWhitelist.find( 32 | (migration) => migration.vaultFrom === TRICRYPTO_VAULT, 33 | ); 34 | const triCryptoMigrationData = { 35 | ...triCryptoVaultMigratorVaultsByAddress[migrationData.vaultFrom], 36 | ...migrationData, 37 | }; 38 | 39 | return triCryptoMigrationData; 40 | }, 41 | ); 42 | -------------------------------------------------------------------------------- /app/drizzle/store/index.js: -------------------------------------------------------------------------------- 1 | import Drizzle from './Drizzle.js'; 2 | import { generateStore } from './generateStore'; 3 | import { generateContractsInitialState } from './contractStateUtils'; 4 | 5 | // Events 6 | import * as EventActions from './contracts/constants'; 7 | 8 | // Reducers 9 | import blocksReducer from './blocks/blocksReducer'; 10 | import contractsReducer from './contracts/contractsReducer'; 11 | import drizzleStatusReducer from './drizzleStatus/drizzleStatusReducer'; 12 | import contractsSubscriptionsReducer from './contracts/contractsSubscriptionsReducer'; 13 | import transactionsReducer from './transactions/transactionsReducer'; 14 | import transactionStackReducer from './transactions/transactionStackReducer'; 15 | import web3Reducer from './web3/web3Reducer'; 16 | 17 | // Sagas 18 | import blocksSaga from './blocks/blocksSaga'; 19 | import contractsSaga from './contracts/contractsSaga'; 20 | import drizzleStatusSaga from './drizzleStatus/drizzleStatusSaga'; 21 | 22 | const drizzleReducers = { 23 | contracts: contractsReducer, 24 | subscriptions: contractsSubscriptionsReducer, 25 | currentBlock: blocksReducer, 26 | drizzleStatus: drizzleStatusReducer, 27 | transactions: transactionsReducer, 28 | transactionStack: transactionStackReducer, 29 | web3: web3Reducer, 30 | }; 31 | 32 | const drizzleSagas = [blocksSaga, contractsSaga, drizzleStatusSaga]; 33 | 34 | export { 35 | Drizzle, 36 | generateContractsInitialState, 37 | generateStore, 38 | drizzleReducers, 39 | drizzleSagas, 40 | EventActions, 41 | }; 42 | -------------------------------------------------------------------------------- /app/drizzle/store/drizzle-middleware.js: -------------------------------------------------------------------------------- 1 | export const drizzleMiddleware = (drizzleInstance) => (store) => (next) => ( 2 | action, 3 | ) => { 4 | const { type } = action; 5 | 6 | if (type === 'DRIZZLE_INITIALIZING') { 7 | drizzleInstance = action.drizzle; 8 | } 9 | 10 | // console.log('action', action.type); 11 | if ( 12 | type === 'ACCOUNTS_FETCHED' && 13 | drizzleInstance && 14 | drizzleInstance.contractList.length 15 | ) { 16 | const newAccount = action.accounts[0]; 17 | const oldAccount = drizzleInstance.contractList[0].options.from; 18 | 19 | // Update `from` fields with newAccount 20 | if (oldAccount !== newAccount) { 21 | drizzleInstance.contractList.forEach((contract) => { 22 | contract.options.from = newAccount; 23 | }); 24 | } 25 | } 26 | 27 | if (type === 'ADD_CONTRACT' && drizzleInstance) { 28 | try { 29 | const { contractConfig, events } = action; 30 | drizzleInstance.addContract(contractConfig, events); 31 | } catch (error) { 32 | console.error('Attempt to add a duplicate contract.\n', error); 33 | 34 | // Notify user via 35 | const notificationAction = { 36 | type: 'ERROR_ADD_CONTRACT', 37 | error, 38 | attemptedAction: action, 39 | }; 40 | store.dispatch(notificationAction); 41 | 42 | // Don't propogate current action 43 | return; 44 | } 45 | } 46 | return next(action); 47 | }; 48 | 49 | const initializedMiddleware = drizzleMiddleware(undefined); 50 | export default initializedMiddleware; 51 | -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | yearn.finance 21 | 25 | 26 | 27 | 28 | 29 | 34 | 35 | 36 |
37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/middleware/websocket/connection.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-depth */ 2 | import { 3 | websocketMessageReceived, 4 | websocketConnected, 5 | } from 'middleware/websocket/actions'; 6 | 7 | const connectionUrl = 'wss://stream.firehose.finance'; 8 | 9 | class websocketConnection { 10 | constructor(dispatch) { 11 | this.dispatch = dispatch; 12 | this.connection = new WebSocket(connectionUrl); 13 | this.connection.onopen = this.onOpen; 14 | this.connection.onclose = this.onClose; 15 | this.connection.onerror = this.onError; 16 | this.connection.onmessage = this.onMessage; 17 | this.ready = false; 18 | } 19 | 20 | onError = (err) => { 21 | console.log('Websocket error', err); 22 | }; 23 | 24 | onClose = (evt) => { 25 | console.log('Websocket closed', evt); 26 | }; 27 | 28 | onOpen = () => { 29 | this.dispatch(websocketConnected()); 30 | // this.connection.send( 31 | // JSON.stringify({ 32 | // action: 'subscribe', 33 | // topic: 'protocol', 34 | // subject: 'yearn', 35 | // }), 36 | // ); 37 | this.send = this.connection.send; 38 | // this.connection.send('ping'); 39 | setInterval(() => { 40 | // this.connection.send('ping'); 41 | }, 30000); 42 | }; 43 | 44 | onMessage = (message) => { 45 | const { data } = message; 46 | const parsedData = JSON.parse(data); 47 | this.dispatch(websocketMessageReceived(parsedData)); 48 | }; 49 | 50 | close = () => { 51 | this.connection.close(); 52 | }; 53 | } 54 | 55 | export default websocketConnection; 56 | -------------------------------------------------------------------------------- /app/components/AddVault/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import styled, { css } from 'styled-components'; 3 | import { useDispatch } from 'react-redux'; 4 | import Input from 'components/Input'; 5 | import { addWatchedContracts } from 'containers/DrizzleProvider/actions'; 6 | 7 | const Wrapper = styled.form` 8 | margin: 0 auto; 9 | height: 36px; 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | margin-top: 50px; 14 | opacity: ${(props) => (props.devVaults ? 1 : 0)}; 15 | transition: opacity 100ms ease-in, margin-top 100ms ease-out; 16 | margin-top: 10px; 17 | ${(props) => 18 | props.devVaults && 19 | css` 20 | margin-top: 30px; 21 | color: black; 22 | transition: opacity 100ms ease-in, margin-top 100ms ease-out; 23 | `} 24 | `; 25 | 26 | export default function AddContract(props) { 27 | const { devVaults } = props; 28 | const [addresses, setAddresses] = useState(''); 29 | const dispatch = useDispatch(); 30 | const addContract = (evt) => { 31 | evt.preventDefault(); 32 | dispatch(addWatchedContracts(addresses)); 33 | setAddresses(''); 34 | }; 35 | 36 | return ( 37 | 38 | setAddresses(evt.target.value)} 46 | /> 47 | 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /app/i18n.js: -------------------------------------------------------------------------------- 1 | /** 2 | * i18n.js 3 | * 4 | * This will setup the i18n language files and locale data for your app. 5 | * 6 | * IMPORTANT: This file is used by the internal build 7 | * script `extract-intl`, and must use CommonJS module syntax 8 | * You CANNOT use import/export in this file. 9 | */ 10 | const addLocaleData = require('react-intl').addLocaleData; //eslint-disable-line 11 | const enLocaleData = require('react-intl/locale-data/en'); 12 | 13 | const enTranslationMessages = require('./translations/en.json'); 14 | 15 | addLocaleData(enLocaleData); 16 | 17 | const DEFAULT_LOCALE = 'en'; 18 | 19 | // prettier-ignore 20 | const appLocales = [ 21 | 'en', 22 | ]; 23 | 24 | const formatTranslationMessages = (locale, messages) => { 25 | const defaultFormattedMessages = 26 | locale !== DEFAULT_LOCALE 27 | ? formatTranslationMessages(DEFAULT_LOCALE, enTranslationMessages) 28 | : {}; 29 | const flattenFormattedMessages = (formattedMessages, key) => { 30 | const formattedMessage = 31 | !messages[key] && locale !== DEFAULT_LOCALE 32 | ? defaultFormattedMessages[key] 33 | : messages[key]; 34 | return Object.assign(formattedMessages, { [key]: formattedMessage }); 35 | }; 36 | return Object.keys(messages).reduce(flattenFormattedMessages, {}); 37 | }; 38 | 39 | const translationMessages = { 40 | en: formatTranslationMessages('en', enTranslationMessages), 41 | }; 42 | 43 | exports.appLocales = appLocales; 44 | exports.formatTranslationMessages = formatTranslationMessages; 45 | exports.translationMessages = translationMessages; 46 | exports.DEFAULT_LOCALE = DEFAULT_LOCALE; 47 | -------------------------------------------------------------------------------- /app/images/github-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/containers/Main/index.js: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import { Switch, Route, Redirect } from 'react-router-dom'; 3 | import { compose } from 'redux'; 4 | import Vaults from 'containers/Vaults/Loadable'; 5 | import Header from 'components/Header'; 6 | import LiteHeader from 'components/LiteHeader'; 7 | import { Splash } from 'containers/Splash'; 8 | import Cover from 'containers/Cover/Loadable'; 9 | import Cream from 'containers/Cream/Loadable'; 10 | import LiteVaults from 'containers/LiteVaults/Loadable'; 11 | import LiteCover from 'containers/LiteCover/Loadable'; 12 | import Box from 'components/Box'; 13 | 14 | let header; 15 | 16 | const liteMode = JSON.parse(localStorage.getItem('liteMode')); 17 | let content; 18 | if (!liteMode) { 19 | header =
; 20 | content = ( 21 | 22 | 23 | 24 | 25 | 26 | 27 | ); 28 | } else { 29 | header = ; 30 | content = ( 31 | 32 | } /> 33 | 34 | 35 | 36 | 37 | ); 38 | } 39 | 40 | function Main() { 41 | return ( 42 | 43 | {header} 44 | {content} 45 | 46 | ); 47 | } 48 | 49 | export default compose(memo)(Main); 50 | -------------------------------------------------------------------------------- /internals/webpack/webpack.dev.babel.js: -------------------------------------------------------------------------------- 1 | /** 2 | * DEVELOPMENT WEBPACK CONFIGURATION 3 | */ 4 | 5 | const path = require('path'); 6 | const webpack = require('webpack'); 7 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 8 | const CircularDependencyPlugin = require('circular-dependency-plugin'); 9 | 10 | module.exports = require('./webpack.base.babel')({ 11 | mode: 'development', 12 | 13 | // Add hot reloading in development 14 | entry: [ 15 | require.resolve('react-app-polyfill/ie11'), 16 | 'webpack-hot-middleware/client?reload=true', 17 | path.join(process.cwd(), 'app/app.js'), // Start with js/app.js 18 | ], 19 | 20 | // Don't use hashes in dev mode for better performance 21 | output: { 22 | filename: '[name].js', 23 | chunkFilename: '[name].chunk.js', 24 | }, 25 | 26 | optimization: { 27 | splitChunks: { 28 | chunks: 'all', 29 | }, 30 | }, 31 | 32 | // Add development plugins 33 | plugins: [ 34 | new webpack.HotModuleReplacementPlugin(), // Tell webpack we want hot reloading 35 | new HtmlWebpackPlugin({ 36 | inject: true, // Inject all files that are generated by webpack, e.g. bundle.js 37 | template: 'app/index.html', 38 | }), 39 | new CircularDependencyPlugin({ 40 | exclude: /a\.js|node_modules/, // exclude node_modules 41 | failOnError: false, // show a warning when there is a circular dependency 42 | }), 43 | ], 44 | 45 | // Emit a source map for easier debugging 46 | // See https://webpack.js.org/configuration/devtool/#devtool 47 | devtool: 'eval-source-map', 48 | 49 | performance: { 50 | hints: false, 51 | }, 52 | }); 53 | -------------------------------------------------------------------------------- /app/containers/LanguageProvider/tests/index.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-testing-library'; 3 | import { FormattedMessage, defineMessages } from 'react-intl'; 4 | import { Provider } from 'react-redux'; 5 | import { browserHistory } from 'react-router-dom'; 6 | 7 | import ConnectedLanguageProvider, { LanguageProvider } from '../index'; 8 | import configureStore from '../../../configureStore'; 9 | 10 | import { translationMessages } from '../../../i18n'; 11 | 12 | const messages = defineMessages({ 13 | someMessage: { 14 | id: 'some.id', 15 | defaultMessage: 'This is some default message', 16 | en: 'This is some en message', 17 | }, 18 | }); 19 | 20 | describe('', () => { 21 | it('should render its children', () => { 22 | const children =

Test

; 23 | const { container } = render( 24 | 25 | {children} 26 | , 27 | ); 28 | expect(container.firstChild).not.toBeNull(); 29 | }); 30 | }); 31 | 32 | describe('', () => { 33 | let store; 34 | 35 | beforeAll(() => { 36 | store = configureStore({}, browserHistory); 37 | }); 38 | 39 | it('should render the default language messages', () => { 40 | const { queryByText } = render( 41 | 42 | 43 | 44 | 45 | , 46 | ); 47 | expect(queryByText(messages.someMessage.defaultMessage)).not.toBeNull(); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /app/components/Icon/index.js: -------------------------------------------------------------------------------- 1 | /* eslint jsx-a11y/click-events-have-key-events: 0 */ 2 | /* eslint jsx-a11y/no-noninteractive-element-interactions: 0 */ 3 | 4 | /** 5 | * TODO: Refactor 6 | */ 7 | 8 | import React from 'react'; 9 | import Copy from './copy.svg'; 10 | import Clock from './clock.svg'; 11 | import ChevronLeft from './chevronLeft.svg'; 12 | import ChevronRight from './chevronRight.svg'; 13 | import Stats from './stats.svg'; 14 | import ExternalLink from './externalLink.svg'; 15 | import ExternalLinkBlack from './externalLinkBlack.svg'; 16 | import ArrowDown from './arrowDown.svg'; 17 | import ArrowDownAlt from './arrowDownAlt.svg'; 18 | import ArrowUpAlt from './arrowUpAlt.svg'; 19 | import Info from './info.svg'; 20 | import Close from './close.svg'; 21 | import ArrowRight from './arrowRight.svg'; 22 | import BluePill from './bluePill.svg'; 23 | import Shine from './shine.svg'; 24 | 25 | export default function Icon(props) { 26 | const { onClick, disabled, className, type } = props; 27 | const icons = { 28 | copy: Copy, 29 | clock: Clock, 30 | stats: Stats, 31 | chevronLeft: ChevronLeft, 32 | chevronRight: ChevronRight, 33 | externalLink: ExternalLink, 34 | externalLinkBlack: ExternalLinkBlack, 35 | info: Info, 36 | arrowDownAlt: ArrowDownAlt, 37 | arrowUpAlt: ArrowUpAlt, 38 | arrowDown: ArrowDown, 39 | arrowRight: ArrowRight, 40 | close: Close, 41 | bluePill: BluePill, 42 | shine: Shine, 43 | }; 44 | const src = icons[type]; 45 | 46 | return ( 47 | 54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /app/images/lazy-ape-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/containers/Cream/actions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Placeholders for API data loading 3 | */ 4 | // import { CREAM_DATA_LOADED } from './constants'; 5 | // export function creamDataLoaded(payload) { 6 | // return { 7 | // type: CREAM_DATA_LOADED, 8 | // payload, 9 | // }; 10 | // } 11 | 12 | import { 13 | CREAM_ENTER_MARKETS, 14 | INITIALIZE_CREAM, 15 | CREAM_SUPPLY, 16 | CREAM_BORROW, 17 | CREAM_REPAY, 18 | CREAM_WITHDRAW, 19 | CREAM_APPROVE, 20 | } from './constants'; 21 | 22 | export function initializeCream() { 23 | return { 24 | type: INITIALIZE_CREAM, 25 | }; 26 | } 27 | 28 | export function creamEnterMarkets({ 29 | creamCTokenAddress, 30 | creamComptrollerContract, 31 | }) { 32 | return { 33 | type: CREAM_ENTER_MARKETS, 34 | creamCTokenAddress, 35 | creamComptrollerContract, 36 | }; 37 | } 38 | 39 | export function creamSupply({ crTokenContract, amount }) { 40 | return { 41 | type: CREAM_SUPPLY, 42 | crTokenContract, 43 | amount, 44 | }; 45 | } 46 | 47 | export function creamBorrow({ crTokenContract, amount }) { 48 | return { 49 | type: CREAM_BORROW, 50 | crTokenContract, 51 | amount, 52 | }; 53 | } 54 | 55 | export function creamRepay({ crTokenContract, amount }) { 56 | return { 57 | type: CREAM_REPAY, 58 | crTokenContract, 59 | amount, 60 | }; 61 | } 62 | 63 | export function creamWithdraw({ crTokenContract, amount }) { 64 | return { 65 | type: CREAM_WITHDRAW, 66 | crTokenContract, 67 | amount, 68 | }; 69 | } 70 | 71 | export function creamApprove({ tokenContract, creamCTokenAddress }) { 72 | return { 73 | type: CREAM_APPROVE, 74 | tokenContract, 75 | creamCTokenAddress, 76 | }; 77 | } 78 | -------------------------------------------------------------------------------- /internals/generators/component/test.js.hbs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Tests for {{ properCase name }} 4 | * 5 | * @see https://github.com/react-boilerplate/react-boilerplate/tree/master/docs/testing 6 | * 7 | */ 8 | 9 | import React from 'react'; 10 | import { render } from 'react-testing-library'; 11 | {{#if wantMessages}} 12 | import { IntlProvider } from 'react-intl'; 13 | {{/if}} 14 | // import 'jest-dom/extend-expect'; // add some helpful assertions 15 | 16 | import {{ properCase name }} from '../index'; 17 | {{#if wantMessages}} 18 | import { DEFAULT_LOCALE } from '../../../i18n'; 19 | {{/if}} 20 | 21 | describe('<{{ properCase name }} />', () => { 22 | it('Expect to not log errors in console', () => { 23 | const spy = jest.spyOn(global.console, 'error'); 24 | {{#if wantMessages}} 25 | render( 26 | 27 | <{{ properCase name }} /> 28 | , 29 | ); 30 | {{else}} 31 | render(<{{ properCase name }} />); 32 | {{/if}} 33 | expect(spy).not.toHaveBeenCalled(); 34 | }); 35 | 36 | it('Expect to have additional unit tests specified', () => { 37 | expect(true).toEqual(false); 38 | }); 39 | 40 | /** 41 | * Unskip this test to use it 42 | * 43 | * @see {@link https://jestjs.io/docs/en/api#testskipname-fn} 44 | */ 45 | it.skip('Should render and match the snapshot', () => { 46 | {{#if wantMessages}} 47 | const { 48 | container: { firstChild }, 49 | } = render( 50 | 51 | <{{ properCase name }} /> 52 | , 53 | ); 54 | {{else}} 55 | const { 56 | container: { firstChild }, 57 | } = render(<{{ properCase name }} />); 58 | {{/if}} 59 | expect(firstChild).toMatchSnapshot(); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /app/components/Icon/bluePill.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/containers/PasswordProtector/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import md5 from 'md5'; 4 | 5 | const PasswordWall = styled.div` 6 | background-image: url(https://i.giphy.com/media/1APhDggUPlkRdK5w1n/giphy.webp); 7 | background-size: cover; 8 | position: absolute; 9 | top: 0; 10 | bottom: 0; 11 | left: 0; 12 | right: 0; 13 | opacity: 0.8; 14 | background-position-x: center; 15 | background-position-y: center; 16 | `; 17 | 18 | const BlackBackground = styled.div` 19 | background-color: #000; 20 | position: absolute; 21 | top: 0; 22 | bottom: 0; 23 | left: 0; 24 | right: 0; 25 | `; 26 | 27 | const EnterPasswordText = styled.div` 28 | font-size: 60px; 29 | position: absolute; 30 | color: #fff; 31 | top: 0; 32 | bottom: 0; 33 | left: 0; 34 | right: 0; 35 | display: flex; 36 | display: none; 37 | justify-content: center; 38 | align-items: center; 39 | filter: drop-shadow(6px 5px 4px #44d); 40 | `; 41 | 42 | const passwordValid = (password) => { 43 | const requiredHash = '4490cbe5c8c0e9cbba2f6dcd0e557560'; 44 | const passwordHash = md5(md5(md5(md5(password)))); 45 | 46 | const valid = passwordHash === requiredHash; 47 | return valid; 48 | }; 49 | 50 | const PasswordProtector = (props) => { 51 | const { children } = props; 52 | const password = localStorage.getItem('password') || ''; 53 | const authenticated = passwordValid(password); 54 | let content; 55 | if (authenticated) { 56 | content = children; 57 | } else { 58 | console.log('wrong password ser'); 59 | content = ( 60 | 61 | 62 | Enter password 63 | 64 | ); 65 | } 66 | 67 | return
{content}
; 68 | }; 69 | 70 | export default PasswordProtector; 71 | -------------------------------------------------------------------------------- /internals/generators/container/test.js.hbs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Tests for {{ properCase name }} 4 | * 5 | * @see https://github.com/react-boilerplate/react-boilerplate/tree/master/docs/testing 6 | * 7 | */ 8 | 9 | import React from 'react'; 10 | import { render } from 'react-testing-library'; 11 | {{#if wantMessages}} 12 | import { IntlProvider } from 'react-intl'; 13 | {{/if}} 14 | // import 'jest-dom/extend-expect'; // add some helpful assertions 15 | 16 | import { {{ properCase name }} } from '../index'; 17 | {{#if wantMessages}} 18 | import { DEFAULT_LOCALE } from '../../../i18n'; 19 | {{/if}} 20 | 21 | describe('<{{ properCase name }} />', () => { 22 | it('Expect to not log errors in console', () => { 23 | const spy = jest.spyOn(global.console, 'error'); 24 | const dispatch = jest.fn(); 25 | {{#if wantMessages}} 26 | render( 27 | 28 | <{{ properCase name }} dispatch={dispatch} /> 29 | , 30 | ); 31 | {{else}} 32 | render(<{{ properCase name }} dispatch={dispatch} />); 33 | {{/if}} 34 | expect(spy).not.toHaveBeenCalled(); 35 | }); 36 | 37 | it('Expect to have additional unit tests specified', () => { 38 | expect(true).toEqual(false); 39 | }); 40 | 41 | /** 42 | * Unskip this test to use it 43 | * 44 | * @see {@link https://jestjs.io/docs/en/api#testskipname-fn} 45 | */ 46 | it.skip('Should render and match the snapshot', () => { 47 | {{#if wantMessages}} 48 | const { 49 | container: { firstChild }, 50 | } = render( 51 | 52 | <{{ properCase name }} /> 53 | , 54 | ); 55 | {{else}} 56 | const { 57 | container: { firstChild }, 58 | } = render(<{{ properCase name }} />); 59 | {{/if}} 60 | expect(firstChild).toMatchSnapshot(); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /app/components/ConnectButton/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import { 4 | useWallet, 5 | useSelectWallet, 6 | useAccount, 7 | } from 'containers/ConnectionProvider/hooks'; 8 | import Text from 'components/Text'; 9 | import ConnectedAccount from './ConnectedAccount'; 10 | 11 | const StyledButton = styled.button` 12 | cursor: pointer; 13 | border: 1px solid 14 | ${(props) => (props.inverted ? props.theme.primary : '#fff')}; 15 | border-radius: 33px; 16 | display: block; 17 | width: ${(props) => (props.inverted ? '100%' : null)}; 18 | :focus { 19 | outline: 0; 20 | } 21 | `; 22 | 23 | const StyledText = styled(Text)` 24 | color: ${(props) => (props.inverted ? props.theme.primary : '#fff')}; 25 | `; 26 | 27 | export default function ConnectButton(props) { 28 | const { className, inverted } = props; 29 | const wallet = useWallet(); 30 | const account = useAccount(); 31 | const selectWallet = useSelectWallet(); 32 | let content; 33 | 34 | if (wallet.provider && account) { 35 | content = ( 36 | 42 | ); 43 | } else { 44 | content = ( 45 | 51 | 59 | Connect Wallet 60 | 61 | {/* */} 62 | 63 | ); 64 | } 65 | 66 | return {content}; 67 | } 68 | --------------------------------------------------------------------------------