├── .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 |
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 |
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 |
4 |
--------------------------------------------------------------------------------
/app/images/Splash/square.svg:
--------------------------------------------------------------------------------
1 |
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 |
4 |
--------------------------------------------------------------------------------
/app/images/Splash/yellow-square.svg:
--------------------------------------------------------------------------------
1 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
6 |
--------------------------------------------------------------------------------
/app/images/Splash/b.svg:
--------------------------------------------------------------------------------
1 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------