├── .nvmrc ├── src ├── react-app-env.d.ts ├── modules │ ├── smart-yield │ │ ├── views │ │ │ ├── markets-view │ │ │ │ └── s.module.scss │ │ │ ├── stats-view │ │ │ │ ├── market │ │ │ │ │ └── s.module.scss │ │ │ │ ├── apy │ │ │ │ │ └── s.module.scss │ │ │ │ ├── liquidity │ │ │ │ │ └── s.module.scss │ │ │ │ └── s.module.scss │ │ │ ├── withdraw-view │ │ │ │ ├── initiate-withdraw │ │ │ │ │ └── s.module.scss │ │ │ │ └── index.tsx │ │ │ ├── portfolio-view │ │ │ │ ├── overview │ │ │ │ │ └── s.module.scss │ │ │ │ ├── senior │ │ │ │ │ ├── past-positions-list │ │ │ │ │ │ └── s.module.scss │ │ │ │ │ └── s.module.scss │ │ │ │ └── junior │ │ │ │ │ └── s.module.scss │ │ │ ├── pool-view │ │ │ │ ├── transactions │ │ │ │ │ └── s.module.scss │ │ │ │ ├── stake │ │ │ │ │ └── s.module.scss │ │ │ │ ├── statistics │ │ │ │ │ └── s.module.scss │ │ │ │ └── s.module.scss │ │ │ ├── deposit-view │ │ │ │ ├── s.module.scss │ │ │ │ ├── select-tranche │ │ │ │ │ └── s.module.scss │ │ │ │ └── index.tsx │ │ │ └── pools-view │ │ │ │ └── pool-card │ │ │ │ └── s.module.scss │ │ ├── components │ │ │ ├── tx-confirm-modal │ │ │ │ └── s.module.scss │ │ │ └── transaction-summary │ │ │ │ └── s.module.scss │ │ ├── contracts │ │ │ ├── syProviderContract.tsx │ │ │ └── syJuniorBondContract.ts │ │ └── providers │ │ │ └── markets.tsx │ ├── smart-exposure │ │ └── views │ │ │ ├── tranche-view │ │ │ ├── details │ │ │ │ └── s.module.scss │ │ │ └── s.module.scss │ │ │ └── pool-actions-view │ │ │ └── index.tsx │ ├── yield-farming │ │ ├── components │ │ │ ├── pool-stake │ │ │ │ └── s.module.scss │ │ │ ├── pool-unstake │ │ │ │ └── s.module.scss │ │ │ ├── pool-card │ │ │ │ └── s.module.scss │ │ │ ├── pool-rewards │ │ │ │ └── s.module.scss │ │ │ ├── pool-statistics │ │ │ │ └── s.module.scss │ │ │ ├── pool-header │ │ │ │ └── s.module.scss │ │ │ ├── pool-chart │ │ │ │ └── s.module.scss │ │ │ └── pool-stats │ │ │ │ └── s.module.scss │ │ ├── views │ │ │ ├── pools-view │ │ │ │ └── s.module.scss │ │ │ └── pool-view │ │ │ │ └── s.module.scss │ │ └── index.tsx │ ├── smart-alpha │ │ ├── views │ │ │ ├── deposit-view │ │ │ │ └── s.module.scss │ │ │ ├── kpi-options │ │ │ │ ├── s.module.scss │ │ │ │ ├── kpi-option-card │ │ │ │ │ └── s.module.scss │ │ │ │ └── index.tsx │ │ │ ├── kpi-option │ │ │ │ ├── transactions │ │ │ │ │ └── s.module.scss │ │ │ │ ├── stake │ │ │ │ │ └── s.module.scss │ │ │ │ ├── statistics │ │ │ │ │ └── s.module.scss │ │ │ │ └── s.module.scss │ │ │ ├── pool-view │ │ │ │ └── queue-state │ │ │ │ │ └── s.module.scss │ │ │ ├── launch │ │ │ │ └── s.module.scss │ │ │ ├── portfolio-view │ │ │ │ ├── s.module.scss │ │ │ │ └── statistics.tsx │ │ │ ├── simulate-epoch │ │ │ │ └── s.module.scss │ │ │ └── pools-view │ │ │ │ └── s.module.scss │ │ ├── contracts │ │ │ ├── chainlinkOracleContract.ts │ │ │ ├── seniorRateModelContract.ts │ │ │ └── accountingModelContract.ts │ │ ├── components │ │ │ └── trade │ │ │ │ └── s.module.scss │ │ └── utils.ts │ ├── faucets │ │ ├── views │ │ │ └── faucet-list-view │ │ │ │ └── s.module.scss │ │ └── index.tsx │ └── governance │ │ ├── views │ │ ├── proposal-detail-view │ │ │ ├── components │ │ │ │ ├── queue-for-execution-modal │ │ │ │ │ └── s.module.scss │ │ │ │ ├── proposal-vote-modal │ │ │ │ │ └── s.module.scss │ │ │ │ ├── abrogation-vote-modal │ │ │ │ │ └── s.module.scss │ │ │ │ ├── abrogation-proposal-modal │ │ │ │ │ └── s.module.scss │ │ │ │ ├── proposal-voters-modal │ │ │ │ │ └── s.module.scss │ │ │ │ ├── abrogation-voters-modal │ │ │ │ │ └── s.module.scss │ │ │ │ ├── proposal-approval-card │ │ │ │ │ └── index.tsx │ │ │ │ ├── abrogation-approval-card │ │ │ │ │ └── index.tsx │ │ │ │ ├── proposal-quorum-card │ │ │ │ │ └── index.tsx │ │ │ │ └── abrogation-details-card │ │ │ │ │ └── index.tsx │ │ │ └── s.module.scss │ │ ├── overview-view │ │ │ ├── components │ │ │ │ └── voting-stat-list │ │ │ │ │ └── s.module.scss │ │ │ └── index.tsx │ │ ├── proposal-create-view │ │ │ └── s.module.scss │ │ ├── proposals-view │ │ │ ├── s.module.scss │ │ │ └── components │ │ │ │ └── proposal-status-tag │ │ │ │ └── index.tsx │ │ └── treasury-view │ │ │ ├── treasury-fees │ │ │ └── index.tsx │ │ │ └── index.tsx │ │ └── components │ │ ├── proposal-action-card │ │ └── s.module.scss │ │ ├── voting-header │ │ └── s.module.scss │ │ ├── create-proposal-action-modal │ │ └── s.module.scss │ │ ├── voting-detailed-modal │ │ └── s.module.scss │ │ └── delete-proposal-action-modal │ │ └── index.tsx ├── components │ ├── custom │ │ ├── form │ │ │ └── s.module.scss │ │ ├── token-input │ │ │ ├── s.module.scss │ │ │ └── index.tsx │ │ ├── identicon │ │ │ ├── s.module.scss │ │ │ └── index.tsx │ │ ├── gas-fee-list │ │ │ └── s.module.scss │ │ ├── icons-set │ │ │ ├── s.module.scss │ │ │ └── index.tsx │ │ ├── token-amount │ │ │ └── s.module.scss │ │ ├── transaction-details │ │ │ └── s.module.scss │ │ ├── transaction-summary │ │ │ ├── s.module.scss │ │ │ └── index.tsx │ │ ├── status-dot │ │ │ ├── index.tsx │ │ │ └── s.module.scss │ │ ├── tooltip │ │ │ └── index.tsx │ │ ├── expandable-card │ │ │ ├── s.module.scss │ │ │ └── index.tsx │ │ ├── spinner │ │ │ ├── s.module.scss │ │ │ └── index.tsx │ │ ├── status-tag │ │ │ ├── s.module.scss │ │ │ └── index.tsx │ │ ├── icon │ │ │ └── s.module.scss │ │ ├── table │ │ │ └── s.module.scss │ │ ├── outside-click │ │ │ └── index.tsx │ │ ├── notification │ │ │ ├── icon.tsx │ │ │ └── s.module.scss │ │ ├── slider │ │ │ └── index.tsx │ │ ├── pagination │ │ │ └── s.module.scss │ │ ├── badge │ │ │ ├── index.tsx │ │ │ └── s.module.scss │ │ ├── label │ │ │ ├── index.tsx │ │ │ └── s.module.scss │ │ ├── dropdown │ │ │ └── s.module.scss │ │ ├── icon-notification │ │ │ └── index.tsx │ │ ├── grid │ │ │ └── s.module.scss │ │ ├── error-boundary │ │ │ └── index.tsx │ │ └── typography │ │ │ └── s.module.scss │ ├── token-icon │ │ └── s.module.scss │ ├── antd │ │ ├── spin │ │ │ ├── s.module.scss │ │ │ └── index.tsx │ │ ├── skeleton │ │ │ ├── s.module.scss │ │ │ └── index.tsx │ │ ├── divider │ │ │ ├── s.module.scss │ │ │ └── index.tsx │ │ ├── textarea │ │ │ ├── s.module.scss │ │ │ └── index.tsx │ │ ├── tooltip │ │ │ ├── s.module.scss │ │ │ └── index.tsx │ │ ├── slider │ │ │ ├── index.tsx │ │ │ └── s.module.scss │ │ ├── input │ │ │ └── index.tsx │ │ ├── popover │ │ │ ├── s.module.scss │ │ │ └── index.tsx │ │ ├── datepicker │ │ │ └── index.tsx │ │ ├── radio-button │ │ │ ├── index.tsx │ │ │ └── s.module.scss │ │ ├── alert │ │ │ ├── index.tsx │ │ │ └── s.module.scss │ │ ├── form │ │ │ └── s.module.scss │ │ ├── table │ │ │ └── index.tsx │ │ ├── yes-no-selector │ │ │ ├── s.module.scss │ │ │ └── index.tsx │ │ ├── popover-menu │ │ │ ├── s.module.scss │ │ │ └── index.tsx │ │ ├── tabs │ │ │ ├── index.tsx │ │ │ └── s.module.scss │ │ ├── modal │ │ │ └── s.module.scss │ │ ├── button │ │ │ └── index.tsx │ │ ├── progress │ │ │ ├── index.tsx │ │ │ └── s.module.scss │ │ └── field-label │ │ │ └── index.tsx │ ├── types.tx.ts │ ├── warning │ │ ├── s.module.scss │ │ └── index.tsx │ ├── portfolio-balance │ │ └── s.module.scss │ ├── radio-card │ │ ├── index.tsx │ │ └── s.module.scss │ ├── alert │ │ ├── s.module.scss │ │ └── index.tsx │ ├── checkbox │ │ ├── s.module.scss │ │ └── index.tsx │ ├── icon │ │ └── s.module.scss │ ├── input │ │ ├── s.module.scss │ │ └── index.tsx │ ├── modal │ │ ├── s.module.scss │ │ └── index.tsx │ └── providers │ │ └── configProvider.tsx ├── resources │ ├── img │ │ └── rocket.png │ └── fonts │ │ ├── Montserrat-Bold.ttf │ │ ├── Montserrat-Regular.ttf │ │ └── Montserrat-SemiBold.ttf ├── web3 │ ├── components │ │ ├── tx-confirm-modal │ │ │ └── s.module.scss │ │ └── user-rejected-modal │ │ │ └── index.tsx │ └── types.ts ├── styles │ ├── antd.less │ ├── reset.scss │ ├── rechart.scss │ ├── flex.scss │ └── colors.scss ├── layout │ ├── s.module.scss │ └── components │ │ └── layout-footer │ │ └── s.module.scss ├── wallets │ ├── components │ │ ├── notifications │ │ │ ├── s.module.scss │ │ │ └── index.tsx │ │ ├── install-metamask-modal │ │ │ └── index.tsx │ │ ├── unsupported-chain-modal │ │ │ └── index.tsx │ │ └── ledger-deriviation-path-modal │ │ │ └── index.tsx │ ├── types.ts │ └── connectors │ │ ├── portis │ │ └── index.ts │ │ ├── wallet-connect │ │ └── index.ts │ │ ├── trezor │ │ └── index.ts │ │ ├── coinbase │ │ └── index.ts │ │ ├── ledger │ │ └── index.ts │ │ └── gnosis-safe │ │ └── index.ts ├── utils │ ├── context.ts │ ├── bignumber.ts │ └── chart.ts ├── hooks │ ├── useIsUnmount.ts │ ├── useReload.ts │ ├── useRouteQuery.ts │ └── useMergeState.ts ├── models │ └── managedEntity.ts ├── checkFlexGap.tsx ├── stories │ ├── TokenIconPair.stories.tsx │ ├── assets │ │ ├── direction.svg │ │ ├── flow.svg │ │ ├── code-brackets.svg │ │ ├── comments.svg │ │ └── repo.svg │ └── TokenIcon.stories.tsx └── global.d.ts ├── public ├── robots.txt ├── favicon.ico ├── favicon-16x16.png ├── favicon-32x32.png ├── mstile-150x150.png ├── apple-touch-icon.png ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── browserconfig.xml ├── logo.svg ├── manifest.json ├── index.html └── safari-pinned-tab.svg ├── .env.sample ├── .editorconfig ├── .stylelintrc ├── .dockerignore ├── .storybook ├── main.js └── preview.js ├── Dockerfile ├── scripts └── build-non-split.js ├── .gitignore ├── .docker └── nginx.conf ├── tsconfig.json └── README.md /.nvmrc: -------------------------------------------------------------------------------- 1 | 12 2 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BarnBridge/barnbridge-frontend/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/modules/smart-yield/views/markets-view/s.module.scss: -------------------------------------------------------------------------------- 1 | .tableCardHeader { 2 | padding: 12px; 3 | } 4 | -------------------------------------------------------------------------------- /src/modules/smart-yield/views/stats-view/market/s.module.scss: -------------------------------------------------------------------------------- 1 | .header.header { 2 | padding: 0 24px; 3 | } 4 | -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BarnBridge/barnbridge-frontend/HEAD/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BarnBridge/barnbridge-frontend/HEAD/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BarnBridge/barnbridge-frontend/HEAD/public/mstile-150x150.png -------------------------------------------------------------------------------- /src/components/custom/form/s.module.scss: -------------------------------------------------------------------------------- 1 | .form { 2 | &.disabled { 3 | pointer-events: none; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/modules/smart-exposure/views/tranche-view/details/s.module.scss: -------------------------------------------------------------------------------- 1 | .header.header { 2 | padding: 0 24px; 3 | } 4 | -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BarnBridge/barnbridge-frontend/HEAD/public/apple-touch-icon.png -------------------------------------------------------------------------------- /src/modules/smart-yield/views/stats-view/apy/s.module.scss: -------------------------------------------------------------------------------- 1 | .header.header { 2 | padding: 16px 16px 16px 24px; 3 | } 4 | -------------------------------------------------------------------------------- /src/resources/img/rocket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BarnBridge/barnbridge-frontend/HEAD/src/resources/img/rocket.png -------------------------------------------------------------------------------- /src/modules/smart-yield/views/stats-view/liquidity/s.module.scss: -------------------------------------------------------------------------------- 1 | .header.header { 2 | padding: 16px 16px 16px 24px; 3 | } 4 | -------------------------------------------------------------------------------- /public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BarnBridge/barnbridge-frontend/HEAD/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BarnBridge/barnbridge-frontend/HEAD/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /src/components/token-icon/s.module.scss: -------------------------------------------------------------------------------- 1 | .tokenIcon { 2 | flex-shrink: 0; 3 | } 4 | 5 | .tokenIconPair { 6 | flex-shrink: 0; 7 | } 8 | -------------------------------------------------------------------------------- /src/components/custom/token-input/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | :global(.ant-input-group-addon) { 3 | width: 72px; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/components/custom/identicon/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | border: 1px solid var(--theme-border-color); 3 | border-radius: 4px; 4 | } 5 | -------------------------------------------------------------------------------- /src/resources/fonts/Montserrat-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BarnBridge/barnbridge-frontend/HEAD/src/resources/fonts/Montserrat-Bold.ttf -------------------------------------------------------------------------------- /src/resources/fonts/Montserrat-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BarnBridge/barnbridge-frontend/HEAD/src/resources/fonts/Montserrat-Regular.ttf -------------------------------------------------------------------------------- /src/resources/fonts/Montserrat-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BarnBridge/barnbridge-frontend/HEAD/src/resources/fonts/Montserrat-SemiBold.ttf -------------------------------------------------------------------------------- /src/modules/yield-farming/components/pool-stake/s.module.scss: -------------------------------------------------------------------------------- 1 | .stakeBlock { 2 | background: var(--theme-body-color); 3 | border-radius: 4px; 4 | } 5 | -------------------------------------------------------------------------------- /src/modules/yield-farming/components/pool-unstake/s.module.scss: -------------------------------------------------------------------------------- 1 | .stakeBlock { 2 | background: var(--theme-body-color); 3 | border-radius: 4px; 4 | } 5 | -------------------------------------------------------------------------------- /src/web3/components/tx-confirm-modal/s.module.scss: -------------------------------------------------------------------------------- 1 | .divider:global(.ant-divider) { 2 | width: calc(100% + 48px); 3 | margin: 24px 0 24px -24px; 4 | } 5 | -------------------------------------------------------------------------------- /src/modules/smart-yield/components/tx-confirm-modal/s.module.scss: -------------------------------------------------------------------------------- 1 | .divider:global(.ant-divider) { 2 | width: calc(100% + 48px); 3 | margin: 24px 0 24px -24px; 4 | } 5 | -------------------------------------------------------------------------------- /src/modules/smart-alpha/views/deposit-view/s.module.scss: -------------------------------------------------------------------------------- 1 | .depositBalance { 2 | background: var(--theme-body-color); 3 | border-radius: 4px; 4 | padding: 24px; 5 | } 6 | -------------------------------------------------------------------------------- /.env.sample: -------------------------------------------------------------------------------- 1 | # Server Settings 2 | PORT = 3000 3 | HTTPS = true 4 | GENERATE_SOURCEMAP = false 5 | SKIP_PREFLIGHT_CHECK = true 6 | 7 | # WebApp Settings 8 | REACT_APP_ENV = 'development' 9 | -------------------------------------------------------------------------------- /src/modules/smart-yield/views/withdraw-view/initiate-withdraw/s.module.scss: -------------------------------------------------------------------------------- 1 | .alertLink { 2 | font-weight: bold; 3 | text-decoration: underline; 4 | color: var(--theme-yellow-color); 5 | } 6 | -------------------------------------------------------------------------------- /src/components/antd/spin/s.module.scss: -------------------------------------------------------------------------------- 1 | button { 2 | .spin { 3 | margin-right: 8px; 4 | } 5 | } 6 | 7 | button:global(.button-primary) { 8 | .spin { 9 | color: #fff; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/modules/smart-alpha/views/kpi-options/s.module.scss: -------------------------------------------------------------------------------- 1 | .cards { 2 | display: grid; 3 | grid-template-columns: repeat(auto-fit, minmax(min-content, 392px)); 4 | grid-auto-rows: auto; 5 | grid-gap: 32px; 6 | } 7 | -------------------------------------------------------------------------------- /src/modules/yield-farming/components/pool-card/s.module.scss: -------------------------------------------------------------------------------- 1 | .box { 2 | padding: 16px; 3 | background: rgba(113, 121, 128, 0.04); 4 | border: 1px solid rgba(113, 121, 128, 0.16); 5 | border-radius: 4px; 6 | } 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | max_line_length = 120 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "stylelint-config-sass-guidelines" 4 | ], 5 | "rules": { 6 | "selector-class-pattern": null, 7 | "declaration-property-value-disallowed-list": null 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/modules/smart-yield/components/transaction-summary/s.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | background-color: var(--theme-card-color); 3 | border: 1px solid rgba(var(--theme-secondary-color-rgb), 0.16); 4 | border-radius: 4px; 5 | } 6 | -------------------------------------------------------------------------------- /src/styles/antd.less: -------------------------------------------------------------------------------- 1 | @import 'node_modules/antd/lib/style/themes/default.less'; 2 | @import 'node_modules/antd/dist/antd.less'; 3 | 4 | @primary-color: #ff4339; 5 | @border-radius-base: 4px; 6 | @modal-mask-bg: fade(#13202b, 80%); 7 | -------------------------------------------------------------------------------- /src/components/antd/skeleton/s.module.scss: -------------------------------------------------------------------------------- 1 | .skeleton:global(.ant-skeleton) { 2 | :global(.ant-skeleton-title) { 3 | margin: 0; 4 | } 5 | 6 | :global(.ant-skeleton-paragraph):empty { 7 | display: none; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/components/antd/divider/s.module.scss: -------------------------------------------------------------------------------- 1 | .divider:global(.ant-divider) { 2 | margin: 0; 3 | border-color: var(--theme-border-color); 4 | 5 | &:global(.ant-divider-vertical) { 6 | top: 0; 7 | height: 100%; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/modules/faucets/views/faucet-list-view/s.module.scss: -------------------------------------------------------------------------------- 1 | .alert { 2 | max-width: 640px; 3 | width: 100%; 4 | margin: 40px auto 0; 5 | } 6 | 7 | .faucets { 8 | max-width: 640px; 9 | width: 100%; 10 | margin: 16px auto 0; 11 | } 12 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | 3 | Dockerfile 4 | .dockerignore 5 | 6 | .git 7 | .gitignore 8 | 9 | # .gitignore stuff 10 | 11 | npm-debug.log 12 | node_modules 13 | dev/node_modules 14 | dist 15 | coverage 16 | .nyc_output 17 | .idea 18 | .env 19 | -------------------------------------------------------------------------------- /src/components/custom/gas-fee-list/s.module.scss: -------------------------------------------------------------------------------- 1 | .list { 2 | display: grid; 3 | gap: 16px; 4 | grid-template-columns: minmax(166px, 1fr) minmax(166px, 1fr); 5 | 6 | @media (max-width: 768px) { 7 | gap: 8px; 8 | grid-template-columns: 1fr; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/layout/s.module.scss: -------------------------------------------------------------------------------- 1 | .layout { 2 | display: flex; 3 | min-height: 100vh; 4 | background: var(--theme-body-color); 5 | 6 | @media (max-width: 768px) { 7 | display: block; 8 | } 9 | } 10 | 11 | .main { 12 | grid-area: content; 13 | flex-grow: 1; 14 | } 15 | -------------------------------------------------------------------------------- /src/modules/governance/views/proposal-detail-view/components/queue-for-execution-modal/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | :global(.ant-modal-body) { 3 | padding: 0; 4 | } 5 | 6 | .row { 7 | padding: 32px; 8 | } 9 | 10 | .actionBtn { 11 | width: 100%; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "stories": [ 3 | "../src/**/*.stories.mdx", 4 | "../src/**/*.stories.@(js|jsx|ts|tsx)" 5 | ], 6 | "addons": [ 7 | "@storybook/addon-links", 8 | "@storybook/addon-essentials", 9 | "@storybook/preset-create-react-app" 10 | ] 11 | } -------------------------------------------------------------------------------- /src/modules/smart-yield/views/stats-view/s.module.scss: -------------------------------------------------------------------------------- 1 | .apyMarketRow { 2 | display: grid; 3 | align-items: stretch; 4 | grid-template-columns: minmax(280px, 1fr) minmax(280px, 482px); 5 | gap: 32px; 6 | 7 | @media (max-width: 768px) { 8 | grid-template-columns: 1fr; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #ff4339 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/modules/smart-exposure/views/tranche-view/s.module.scss: -------------------------------------------------------------------------------- 1 | .trendDetailsRow { 2 | display: grid; 3 | align-items: stretch; 4 | grid-template-columns: minmax(280px, 1fr) minmax(280px, 482px); 5 | gap: 32px; 6 | 7 | @media (max-width: 768px) { 8 | grid-template-columns: 1fr; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/modules/smart-yield/views/portfolio-view/overview/s.module.scss: -------------------------------------------------------------------------------- 1 | .portfolioContainer { 2 | column-gap: 32px; 3 | display: grid; 4 | grid-template-columns: 40% 1fr; 5 | margin-bottom: 32px; 6 | 7 | @media (max-width: 768px) { 8 | gap: 8px; 9 | grid-template-columns: 1fr; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.storybook/preview.js: -------------------------------------------------------------------------------- 1 | import '!style-loader!css-loader!sass-loader!../src/styles/index.scss'; 2 | 3 | export const parameters = { 4 | actions: { argTypesRegex: "^on[A-Z].*" }, 5 | controls: { 6 | matchers: { 7 | color: /(background|color)$/i, 8 | date: /Date$/, 9 | }, 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /src/components/custom/icons-set/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | display: flex; 3 | flex-direction: row; 4 | align-items: center; 5 | justify-content: center; 6 | } 7 | 8 | .component svg { 9 | width: 40px; 10 | height: 40px; 11 | } 12 | 13 | .component svg:not(:first-child) { 14 | margin-left: -20px; 15 | } 16 | -------------------------------------------------------------------------------- /src/modules/yield-farming/components/pool-rewards/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | background: var(--theme-card-color); 3 | border-bottom: 1px solid var(--theme-border-color); 4 | padding: 24px var(--horizontal-padding); 5 | } 6 | 7 | .list { 8 | display: flex; 9 | flex-wrap: wrap; 10 | gap: 24px 48px; 11 | } 12 | -------------------------------------------------------------------------------- /src/modules/yield-farming/components/pool-statistics/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | max-width: 384px; 3 | width: 100%; 4 | 5 | @media (max-width: 768px) { 6 | max-width: none; 7 | } 8 | } 9 | 10 | .claimBlock { 11 | padding: 16px 20px; 12 | background: var(--theme-body-color); 13 | border-radius: 4px; 14 | } 15 | -------------------------------------------------------------------------------- /src/wallets/components/notifications/s.module.scss: -------------------------------------------------------------------------------- 1 | .list { 2 | padding: 0; 3 | list-style-type: none; 4 | overflow-y: auto; 5 | margin: 0; 6 | 7 | li { 8 | border-bottom: 1px solid rgba(var(--theme-secondary-color-rgb), 0.08); 9 | 10 | &:last-of-type { 11 | border-bottom: 0; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/web3/types.ts: -------------------------------------------------------------------------------- 1 | export type Web3EventType> = { 2 | event: string; 3 | id: string; 4 | address: string; 5 | blockHash: string; 6 | blockNumber: number; 7 | raw: { 8 | data: string; 9 | }; 10 | returnValues: T; 11 | signature: string; 12 | transactionHash: string; 13 | }; 14 | -------------------------------------------------------------------------------- /src/modules/governance/views/overview-view/components/voting-stat-list/s.module.scss: -------------------------------------------------------------------------------- 1 | .cards { 2 | display: grid; 3 | gap: 32px; 4 | grid-template-columns: repeat(auto-fit, minmax(392px, 1fr)); 5 | 6 | @media (max-width: 768px) { 7 | gap: 8px; 8 | grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/modules/governance/views/proposal-detail-view/components/proposal-vote-modal/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | :global(.ant-modal-body) { 3 | padding: 0; 4 | } 5 | 6 | .row { 7 | padding: 32px; 8 | } 9 | 10 | .changeGroup { 11 | width: 100%; 12 | } 13 | 14 | .actionBtn { 15 | width: 100%; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/modules/governance/views/proposal-detail-view/components/abrogation-vote-modal/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | :global(.ant-modal-body) { 3 | padding: 0; 4 | } 5 | 6 | .row { 7 | padding: 32px; 8 | } 9 | 10 | .changeGroup { 11 | width: 100%; 12 | } 13 | 14 | .actionBtn { 15 | width: 100%; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/modules/yield-farming/views/pools-view/s.module.scss: -------------------------------------------------------------------------------- 1 | .poolCards { 2 | display: grid; 3 | gap: 32px; 4 | grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); 5 | margin-bottom: 32px; 6 | 7 | @media (max-width: 768px) { 8 | gap: 8px; 9 | grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/context.ts: -------------------------------------------------------------------------------- 1 | export function InvariantContext(contextName: string): T { 2 | return new Proxy( 3 | {}, 4 | { 5 | get: function (target, name: string) { 6 | // throw new Error(`${contextName}.${name} is not implemented yet.`); /// TODO 7 | return undefined; 8 | }, 9 | }, 10 | ) as T; 11 | } 12 | -------------------------------------------------------------------------------- /src/modules/smart-yield/views/pool-view/transactions/s.module.scss: -------------------------------------------------------------------------------- 1 | .tabs { 2 | :global(.ant-tabs-nav) { 3 | padding: 0 24px !important; 4 | border-top: 0; 5 | border-left: 0; 6 | border-right: 0; 7 | background-color: transparent; 8 | } 9 | 10 | :global(.ant-tabs-extra-content) { 11 | padding: 12px 0; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/modules/smart-alpha/views/kpi-option/transactions/s.module.scss: -------------------------------------------------------------------------------- 1 | .tabs { 2 | :global(.ant-tabs-nav) { 3 | padding: 0 24px !important; 4 | border-top: 0; 5 | border-left: 0; 6 | border-right: 0; 7 | background-color: transparent; 8 | } 9 | 10 | :global(.ant-tabs-extra-content) { 11 | padding: 12px 0; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/components/custom/token-amount/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | :global(.ant-input-group) { 3 | height: 64px; 4 | 5 | :global(.ant-input) { 6 | height: 64px; 7 | } 8 | 9 | :global(.ant-input-group-addon) { 10 | &:first-of-type { 11 | width: 64px; 12 | padding: 0 20px; 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/components/custom/transaction-details/s.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | background-color: rgba(var(--theme-secondary-color-rgb), 0.04); 3 | border: 1px solid rgba(var(--theme-secondary-color-rgb), 0.16); 4 | border-radius: 4px; 5 | } 6 | 7 | .deadlineInput { 8 | :global(.ant-input) { 9 | width: 75px; 10 | text-align: right; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/hooks/useIsUnmount.ts: -------------------------------------------------------------------------------- 1 | import { MutableRefObject, useEffect, useRef } from 'react'; 2 | 3 | export function useIsUnmount(): MutableRefObject { 4 | const isUnmountRef = useRef(false); 5 | 6 | useEffect(() => { 7 | return () => { 8 | isUnmountRef.current = true; 9 | }; 10 | }, []); 11 | 12 | return isUnmountRef; 13 | } 14 | -------------------------------------------------------------------------------- /src/components/types.tx.ts: -------------------------------------------------------------------------------- 1 | import { CSSProperties, FC, ReactNode } from 'react'; 2 | 3 | export type SCP

= P & { 4 | className?: string; 5 | style?: CSSProperties; 6 | }; 7 | 8 | export type CP

= P & { 9 | children?: ReactNode; 10 | className?: string; 11 | style?: CSSProperties; 12 | }; 13 | 14 | export type FCx

= FC>; 15 | -------------------------------------------------------------------------------- /src/modules/governance/views/proposal-create-view/s.module.scss: -------------------------------------------------------------------------------- 1 | .addActionBtn { 2 | width: 100%; 3 | flex-direction: row-reverse; 4 | } 5 | 6 | .cardsContainer { 7 | display: grid; 8 | gap: 24px; 9 | grid-template-columns: 1fr 1fr; 10 | align-items: start; 11 | 12 | @media (max-width: 768px) { 13 | grid-template-columns: 1fr; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12.18 AS build 2 | 3 | WORKDIR /build 4 | 5 | COPY package.json package-lock.json ./ 6 | RUN npm ci 7 | 8 | COPY . . 9 | 10 | RUN npm run build --verbose 11 | 12 | FROM nginx:stable-alpine 13 | 14 | COPY --from=build /build/build /usr/share/nginx/html 15 | COPY .docker/nginx.conf /etc/nginx/conf.d/default.conf 16 | 17 | CMD ["nginx", "-g", "daemon off;"] 18 | -------------------------------------------------------------------------------- /src/modules/faucets/index.tsx: -------------------------------------------------------------------------------- 1 | import { FauceteerProvider } from 'modules/faucets/providers/fauceteerProvider'; 2 | import FaucetListView from 'modules/faucets/views/faucet-list-view'; 3 | 4 | const FaucetsView: React.FC = () => { 5 | return ( 6 | 7 | 8 | 9 | ); 10 | }; 11 | 12 | export default FaucetsView; 13 | -------------------------------------------------------------------------------- /src/modules/governance/components/proposal-action-card/s.module.scss: -------------------------------------------------------------------------------- 1 | .content { 2 | min-height: 24px; 3 | } 4 | 5 | .address { 6 | float: left; 7 | max-width: 200px; 8 | } 9 | 10 | div.paragraph:global(.ant-typography) { 11 | font: var(--font-p1); 12 | margin: 0; 13 | color: var(--theme-primary-color); 14 | 15 | &.expanded { 16 | white-space: pre-wrap; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/modules/governance/components/voting-header/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | background: var(--theme-card-color); 3 | border-bottom: 1px solid var(--theme-border-color); 4 | } 5 | 6 | .items { 7 | display: flex; 8 | flex-wrap: wrap; 9 | gap: 40px; 10 | } 11 | 12 | .ratio { 13 | background: rgba(255, 67, 57, 0.08); 14 | border-radius: 18px; 15 | padding: 0 12px; 16 | } 17 | -------------------------------------------------------------------------------- /src/modules/smart-yield/views/portfolio-view/senior/past-positions-list/s.module.scss: -------------------------------------------------------------------------------- 1 | .cards { 2 | display: grid; 3 | gap: 32px; 4 | grid-template-columns: repeat(auto-fit, minmax(350px, 33.3333%)); 5 | margin-top: 32px; 6 | margin-bottom: 32px; 7 | 8 | @media (max-width: 768px) { 9 | gap: 8px; 10 | grid-template-columns: repeat(auto-fit, minmax(288px, 1fr)); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/modules/governance/views/proposal-detail-view/components/abrogation-proposal-modal/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | top: 0; 3 | bottom: 0; 4 | width: 100vw !important; 5 | height: 100vh; 6 | margin: 0 auto; 7 | padding: 24px; 8 | 9 | :global(.ant-modal-content) { 10 | height: 100%; 11 | } 12 | 13 | :global(.ant-modal-body) { 14 | padding: 106px 32px 32px; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/modules/governance/views/proposals-view/s.module.scss: -------------------------------------------------------------------------------- 1 | .tabs { 2 | :global(.ant-tabs-nav) { 3 | padding: 12px 24px 0 !important; 4 | } 5 | 6 | :global(.ant-tabs-tab) { 7 | padding-bottom: 24px; 8 | } 9 | } 10 | 11 | .search:global(.ant-input-affix-wrapper) { 12 | width: 300px; 13 | height: 48px; 14 | margin: 12px 24px; 15 | 16 | input { 17 | height: 100%; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/modules/smart-yield/views/portfolio-view/junior/s.module.scss: -------------------------------------------------------------------------------- 1 | .portfolioContainer { 2 | column-gap: 32px; 3 | display: grid; 4 | grid-template-columns: 40% 1fr; 5 | margin-bottom: 32px; 6 | 7 | @media (max-width: 768px) { 8 | gap: 8px; 9 | grid-template-columns: 1fr; 10 | } 11 | } 12 | 13 | .tabs { 14 | :global(.ant-tabs-nav) { 15 | padding: 12px 24px 0 !important; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/components/warning/s.module.scss: -------------------------------------------------------------------------------- 1 | .warnings { 2 | &:empty { 3 | display: none; 4 | } 5 | } 6 | 7 | .warning { 8 | background: var(--theme-yellow-color); // clr-? 9 | padding: 12px var(--horizontal-padding); 10 | 11 | button { 12 | color: #665426; // clr-yellow700 13 | 14 | &:hover { 15 | opacity: 0.9; 16 | } 17 | } 18 | } 19 | 20 | .text { 21 | color: #665426; // clr-yellow800 22 | } 23 | -------------------------------------------------------------------------------- /src/components/antd/textarea/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | font: var(--font-p1); 3 | font-weight: 600; 4 | width: 100%; 5 | min-height: 64px; 6 | padding: 20px 24px; 7 | background: var(--theme-input-background); 8 | border: 1px solid var(--theme-border-color); 9 | border-radius: 4px; 10 | color: var(--theme-primary-color); 11 | 12 | &::placeholder { 13 | color: var(--theme-input-placeholder); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/components/custom/icons-set/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cn from 'classnames'; 3 | 4 | import s from './s.module.scss'; 5 | 6 | export type IconsSetProps = { 7 | icons: React.ReactNode[]; 8 | className?: string; 9 | }; 10 | 11 | const IconsSet: React.FC = props => { 12 | return

{props.icons}
; 13 | }; 14 | 15 | export default IconsSet; 16 | -------------------------------------------------------------------------------- /scripts/build-non-split.js: -------------------------------------------------------------------------------- 1 | const rewire = require('rewire') 2 | const defaults = rewire('react-scripts/scripts/build.js') // If you ejected, use this instead: const defaults = rewire('./build.js') 3 | let config = defaults.__get__('config') 4 | 5 | // config.optimization.splitChunks = { 6 | // cacheGroups: { 7 | // default: false 8 | // } 9 | // } 10 | // config.optimization.runtimeChunk = false 11 | 12 | config.optimization.minimize = false -------------------------------------------------------------------------------- /src/modules/smart-yield/views/deposit-view/s.module.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | margin: 0 auto 64px; 3 | min-width: 640px; 4 | 5 | @media (max-width: 768px) { 6 | min-width: initial; 7 | margin: 0 0 24px; 8 | } 9 | } 10 | 11 | .content { 12 | flex-grow: 1; 13 | max-width: 640px; 14 | width: 100%; 15 | margin-left: auto; 16 | margin-right: auto; 17 | 18 | @media (max-width: 1024px) { 19 | max-width: none; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/modules/yield-farming/components/pool-header/s.module.scss: -------------------------------------------------------------------------------- 1 | .stakedBar { 2 | display: flex; 3 | width: 100%; 4 | height: 16px; 5 | background-color: var(--theme-border-color); 6 | border-radius: 8px; 7 | 8 | > :first-child { 9 | border-top-left-radius: 8px; 10 | border-bottom-left-radius: 8px; 11 | } 12 | 13 | > :last-child { 14 | border-top-right-radius: 8px; 15 | border-bottom-right-radius: 8px; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/modules/yield-farming/views/pool-view/s.module.scss: -------------------------------------------------------------------------------- 1 | .stakeColumns { 2 | display: grid; 3 | grid-template-columns: auto auto; 4 | gap: 32px 16px; 5 | margin-bottom: 32px; 6 | 7 | @media (max-width: 768px) { 8 | grid-template-columns: 1fr; 9 | } 10 | } 11 | 12 | .stakeCard { 13 | display: flex; 14 | flex-direction: column; 15 | flex-grow: 1; 16 | width: 100%; 17 | } 18 | 19 | .stakeCardHeader { 20 | height: 73px; 21 | } 22 | -------------------------------------------------------------------------------- /src/components/antd/divider/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AntdDivider, { DividerProps as AntdDividerProps } from 'antd/lib/divider'; 3 | import cn from 'classnames'; 4 | 5 | import s from './s.module.scss'; 6 | 7 | const Divider: React.FC = props => { 8 | const { className, ...dividerProps } = props; 9 | 10 | return ; 11 | }; 12 | 13 | export default Divider; 14 | -------------------------------------------------------------------------------- /src/components/antd/tooltip/s.module.scss: -------------------------------------------------------------------------------- 1 | .overlay:global(.ant-tooltip) { 2 | :global(.ant-tooltip-inner) { 3 | font: var(--font-p1); 4 | max-width: 360px; 5 | padding: 16px; 6 | background: var(--theme-card-color); 7 | box-shadow: var(--theme-overlay-shadow); 8 | border-radius: 4px; 9 | color: var(--theme-primary-color); 10 | } 11 | 12 | :global(.ant-tooltip-arrow-content) { 13 | background: var(--theme-card-color); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/components/custom/transaction-summary/s.module.scss: -------------------------------------------------------------------------------- 1 | .section { 2 | background-color: var(--theme-card-color); 3 | border: 1px solid var(--theme-border-color); 4 | border-radius: 4px; 5 | } 6 | 7 | .header { 8 | display: flex; 9 | padding: 16px 24px; 10 | border-bottom: 1px solid var(--theme-border-color); 11 | } 12 | 13 | .item { 14 | display: flex; 15 | align-items: center; 16 | 17 | &:not(:last-of-type) { 18 | margin-bottom: 16px; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/modules/yield-farming/components/pool-chart/s.module.scss: -------------------------------------------------------------------------------- 1 | .chartTitleContainer { 2 | @media (max-width: 480px) { 3 | display: block; 4 | } 5 | } 6 | 7 | .chartTitleFilters { 8 | @media (max-width: 480px) { 9 | margin-top: 8px; 10 | } 11 | } 12 | 13 | .chartTooltip { 14 | padding: 24px !important; 15 | background: var(--theme-card-color) !important; 16 | border: 1px solid var(--theme-border-color) !important; 17 | border-radius: 4px !important; 18 | } 19 | -------------------------------------------------------------------------------- /src/modules/smart-yield/views/deposit-view/select-tranche/s.module.scss: -------------------------------------------------------------------------------- 1 | .cards { 2 | display: grid; 3 | gap: 32px; 4 | grid-auto-flow: column; 5 | grid-template-columns: 1fr 1fr; 6 | margin-bottom: 32px; 7 | 8 | @media (max-width: 768px) { 9 | gap: 8px; 10 | grid-auto-flow: row; 11 | grid-template-columns: 1fr; 12 | } 13 | } 14 | 15 | .alertLink { 16 | font-weight: bold; 17 | text-decoration: underline; 18 | color: var(--theme-yellow-color); 19 | } 20 | -------------------------------------------------------------------------------- /src/components/custom/status-dot/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cn from 'classnames'; 3 | 4 | import s from './s.module.scss'; 5 | 6 | export type StatusTagProps = { 7 | color: 'red' | 'green' | 'blue' | 'purple'; 8 | className?: string; 9 | }; 10 | 11 | const StatusDot: React.FC = props => { 12 | const { color, className } = props; 13 | 14 | return
; 15 | }; 16 | 17 | export default StatusDot; 18 | -------------------------------------------------------------------------------- /src/components/portfolio-balance/s.module.scss: -------------------------------------------------------------------------------- 1 | .progress { 2 | display: flex; 3 | 4 | :global(.ant-progress-outer) { 5 | display: flex; 6 | } 7 | } 8 | 9 | .dataColumn { 10 | position: relative; 11 | padding-left: 16px; 12 | width: 50%; 13 | 14 | &::before { 15 | content: ''; 16 | position: absolute; 17 | top: 4px; 18 | left: 0; 19 | width: 8px; 20 | height: 8px; 21 | border-radius: 50%; 22 | background-color: var(--color); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | 17 | npm-debug.log* 18 | yarn-debug.log* 19 | yarn-error.log* 20 | 21 | # env 22 | .env 23 | .env.local 24 | .env.*.local 25 | 26 | # IDEs 27 | .idea 28 | .vscode 29 | 30 | # cache 31 | .eslintcache 32 | 33 | # husky 34 | .husky/ 35 | -------------------------------------------------------------------------------- /src/modules/governance/views/proposal-detail-view/s.module.scss: -------------------------------------------------------------------------------- 1 | .cardsAndSidebarContainer { 2 | display: flex; 3 | gap: 32px; 4 | 5 | @media (max-width: 1024px) { 6 | flex-direction: column; 7 | } 8 | } 9 | 10 | .cardsContainer { 11 | flex-grow: 1; 12 | } 13 | 14 | .sidebarContainer { 15 | max-width: 428px; 16 | width: 100%; 17 | 18 | @media (max-width: 1200px) { 19 | max-width: 300px; 20 | } 21 | 22 | @media (max-width: 1024px) { 23 | max-width: none; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/components/antd/slider/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AntdSlider, { SliderSingleProps } from 'antd/lib/slider'; 3 | import cn from 'classnames'; 4 | 5 | import s from './s.module.scss'; 6 | 7 | export type SliderProps = SliderSingleProps; 8 | 9 | const Slider: React.FC = props => { 10 | const { className, ...rest } = props; 11 | 12 | return ; 13 | }; 14 | 15 | export default Slider; 16 | -------------------------------------------------------------------------------- /src/hooks/useReload.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import useDebounce from '@rooks/use-debounce'; 3 | 4 | type ReloadFn = () => void; 5 | 6 | export type ReloadHook = [ReloadFn, number]; 7 | 8 | export function useReload(): ReloadHook { 9 | const [version, setVersion] = React.useState(0); 10 | const reloadRef = React.useRef( 11 | useDebounce(() => { 12 | setVersion(prevState => prevState + 1); 13 | }, 400), 14 | ); 15 | 16 | return [reloadRef.current as ReloadFn, version]; 17 | } 18 | -------------------------------------------------------------------------------- /src/modules/yield-farming/components/pool-stats/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | display: grid; 3 | gap: 32px; 4 | grid-template-columns: repeat(auto-fit, minmax(175px, 1fr)); 5 | 6 | @media (max-width: 768px) { 7 | gap: 8px; 8 | } 9 | 10 | .epochProgress { 11 | position: absolute; 12 | bottom: 0; 13 | left: 0; 14 | right: 0; 15 | width: 0; 16 | height: 4px; 17 | background: linear-gradient(90deg, #4f6ae6 0%, #00d395 100%); 18 | border-radius: 0 4px 4px; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.docker/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | # Define the server name, IP address, and/or port of the server 3 | listen 80; 4 | 5 | # Define the specified charset to the “Content-Type” response header field 6 | charset utf-8; 7 | 8 | root /usr/share/nginx/html; 9 | index index.html index.html; 10 | 11 | # static content in production 12 | location / { 13 | try_files $uri $uri/ /index.html; 14 | 15 | # maximum file size on file uploads 16 | client_max_body_size 100K; 17 | } 18 | } -------------------------------------------------------------------------------- /src/components/antd/textarea/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AntdInput, { TextAreaProps as AntdTextAreaProps } from 'antd/lib/input'; 3 | import cn from 'classnames'; 4 | 5 | import s from './s.module.scss'; 6 | 7 | export type TextareaProps = AntdTextAreaProps; 8 | 9 | const Textarea: React.FC = props => { 10 | const { className, ...inputProps } = props; 11 | 12 | return ; 13 | }; 14 | 15 | export default Textarea; 16 | -------------------------------------------------------------------------------- /src/components/custom/status-dot/s.module.scss: -------------------------------------------------------------------------------- 1 | .statusDot { 2 | display: inline-block; 3 | width: 8px; 4 | height: 8px; 5 | border-radius: 100%; 6 | margin-right: 8px; 7 | 8 | background: var(--bg-color); 9 | 10 | &.red { 11 | --bg-color: var(--theme-red-color); 12 | } 13 | 14 | &.green { 15 | --bg-color: var(--theme-green-color); 16 | } 17 | 18 | &.blue { 19 | --bg-color: var(--theme-blue-color); 20 | } 21 | 22 | &.purple { 23 | --bg-color: var(--theme-purple-color); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/components/custom/tooltip/index.tsx: -------------------------------------------------------------------------------- 1 | import Tooltip from 'components/antd/tooltip'; 2 | import { Icon } from 'components/icon'; 3 | 4 | type PropsType = { 5 | className?: string; 6 | style?: React.CSSProperties; 7 | }; 8 | 9 | export const InfoTooltip: React.FC = ({ children, className, style }) => ( 10 | 11 | 20 | 21 | ); 22 | -------------------------------------------------------------------------------- /src/components/custom/expandable-card/s.module.scss: -------------------------------------------------------------------------------- 1 | .component:global(.ant-card) { 2 | background: rgba(113, 121, 128, 0.04); 3 | border: 1px solid rgba(113, 121, 128, 0.16); 4 | border-radius: 4px; 5 | 6 | > :global(.ant-card-head .ant-card-head-title) { 7 | padding: 16px 0; 8 | } 9 | 10 | > :global(.ant-card-body) { 11 | padding: 0; 12 | } 13 | 14 | .content { 15 | padding: 16px 24px; 16 | } 17 | 18 | .footer { 19 | height: 44px; 20 | padding: 12px 24px; 21 | border-top: 1px solid rgba(113, 121, 128, 0.16); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/modules/smart-yield/views/pool-view/stake/s.module.scss: -------------------------------------------------------------------------------- 1 | .tabs { 2 | flex-shrink: 0; 3 | 4 | > :global(.ant-tabs-nav) { 5 | border-top: 0; 6 | border-left: 0; 7 | border-right: 0; 8 | height: 74px; 9 | background-color: transparent; 10 | 11 | @media (max-width: 480px) { 12 | padding-left: 8px; 13 | padding-right: 8px; 14 | } 15 | } 16 | } 17 | 18 | .form { 19 | display: flex; 20 | flex-direction: column; 21 | height: 100%; 22 | } 23 | 24 | .stakeBlock { 25 | background: var(--theme-body-color); 26 | border-radius: 4px; 27 | } 28 | -------------------------------------------------------------------------------- /src/modules/smart-alpha/views/kpi-option/stake/s.module.scss: -------------------------------------------------------------------------------- 1 | .tabs { 2 | flex-shrink: 0; 3 | 4 | > :global(.ant-tabs-nav) { 5 | border-top: 0; 6 | border-left: 0; 7 | border-right: 0; 8 | height: 74px; 9 | background-color: transparent; 10 | 11 | @media (max-width: 480px) { 12 | padding-left: 8px; 13 | padding-right: 8px; 14 | } 15 | } 16 | } 17 | 18 | .form { 19 | display: flex; 20 | flex-direction: column; 21 | height: 100%; 22 | } 23 | 24 | .stakeBlock { 25 | background: var(--theme-body-color); 26 | border-radius: 4px; 27 | } 28 | -------------------------------------------------------------------------------- /src/modules/smart-alpha/views/pool-view/queue-state/s.module.scss: -------------------------------------------------------------------------------- 1 | .section { 2 | border: 1px solid var(--theme-border-color); 3 | background-color: var(--theme-card-color); 4 | border-radius: 4px; 5 | overflow: hidden; 6 | 7 | &:not(:last-of-type) { 8 | margin-bottom: 16px; 9 | } 10 | } 11 | 12 | .sectionHeader { 13 | padding: 12px 16px; 14 | background-color: var(--theme-body-color); 15 | border-bottom: 1px solid var(--theme-border-color); 16 | } 17 | 18 | .sectionBody { 19 | padding: 12px 16px; 20 | display: grid; 21 | grid-template-columns: 1fr 1fr; 22 | } 23 | -------------------------------------------------------------------------------- /src/components/antd/input/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AntdInput, { InputProps as AntdInputProps } from 'antd/lib/input'; 3 | import cn from 'classnames'; 4 | 5 | import s from './s.module.scss'; 6 | 7 | export type InputProps = AntdInputProps; 8 | 9 | const Input: React.FC = props => { 10 | const { className, disabled, ...inputProps } = props; 11 | 12 | return ( 13 | 19 | ); 20 | }; 21 | 22 | export default Input; 23 | -------------------------------------------------------------------------------- /src/models/managedEntity.ts: -------------------------------------------------------------------------------- 1 | import EventEmitter from 'wolfy87-eventemitter'; 2 | 3 | export default class ManagedEntity { 4 | private readonly _event: EventEmitter; 5 | 6 | constructor() { 7 | this._event = new EventEmitter(); 8 | } 9 | 10 | get event(): EventEmitter { 11 | return this._event; 12 | } 13 | 14 | onDataUpdate = (listener: Function): Function => { 15 | this._event.on('data:update', listener); 16 | 17 | return () => { 18 | return this._event.off('data:update', listener); 19 | }; 20 | }; 21 | 22 | emitDataUpdate = () => { 23 | this._event?.emit('data:update'); 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /src/styles/reset.scss: -------------------------------------------------------------------------------- 1 | * { 2 | margin: unset; 3 | padding: unset; 4 | } 5 | 6 | *, 7 | *::before, 8 | *::after { 9 | box-sizing: border-box; 10 | } 11 | 12 | svg, 13 | svg * { 14 | transition: none !important; 15 | } 16 | 17 | h1, 18 | h2, 19 | h3 { 20 | margin-bottom: 0; 21 | } 22 | 23 | p { 24 | margin-bottom: 0; 25 | } 26 | 27 | ol, 28 | ul, 29 | dl { 30 | margin: unset; 31 | padding: unset; 32 | } 33 | 34 | ol, 35 | ul { 36 | list-style-type: none; 37 | } 38 | 39 | dd { 40 | margin-bottom: unset; 41 | margin-left: unset; 42 | } 43 | 44 | hr { 45 | border: 0; 46 | border-top: 1px solid var(--theme-border-color); 47 | } 48 | -------------------------------------------------------------------------------- /src/components/custom/spinner/s.module.scss: -------------------------------------------------------------------------------- 1 | .spinner { 2 | animation: rotate 1s linear infinite; 3 | } 4 | 5 | @keyframes rotate { 6 | 100% { 7 | transform: rotate(360deg); 8 | } 9 | } 10 | 11 | .container { 12 | position: relative; 13 | 14 | .spinner { 15 | position: absolute; 16 | top: 50%; 17 | left: 50%; 18 | margin: -12px; 19 | } 20 | } 21 | 22 | .children { 23 | opacity: 0.5; 24 | user-select: none; 25 | pointer-events: none; 26 | } 27 | 28 | .pageSpinnerContainer { 29 | padding: 24px; 30 | display: flex; 31 | align-items: center; 32 | justify-content: center; 33 | color: var(--theme-red-color); 34 | } 35 | -------------------------------------------------------------------------------- /src/wallets/types.ts: -------------------------------------------------------------------------------- 1 | import { AbstractConnector } from '@web3-react/abstract-connector'; 2 | 3 | import { Web3Network } from 'networks/types'; 4 | 5 | export type BaseWalletConfig = { 6 | id: string; 7 | /** @description use string value for all theme image, or tuple [light, dark] to display different theme images */ 8 | logo: string | [string, string]; 9 | name: string; 10 | factory(network: Web3Network, args?: Record): AbstractConnector; 11 | onConnect?(connector: AbstractConnector, args?: Record): void; 12 | onDisconnect?(connector?: AbstractConnector): void; 13 | onError?(error: Error): Error | undefined; 14 | }; 15 | -------------------------------------------------------------------------------- /src/hooks/useRouteQuery.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useMemo } from 'react'; 2 | import { useLocation } from 'react-router-dom'; 3 | 4 | function useRouteQuery() { 5 | const location = useLocation(); 6 | const urlQuery = useMemo(() => new URLSearchParams(location.search), [location.search]); 7 | 8 | const get = useCallback( 9 | (paramName: string): string | undefined => { 10 | const value = urlQuery.get(paramName) ?? undefined; 11 | 12 | return value ? decodeURIComponent(value) : undefined; 13 | }, 14 | [location.search], 15 | ); 16 | 17 | return { 18 | urlQuery, 19 | get, 20 | }; 21 | } 22 | 23 | export default useRouteQuery; 24 | -------------------------------------------------------------------------------- /src/components/antd/tooltip/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AntdTooltip, { TooltipPropsWithTitle as AntdTooltipPropsWithTitle } from 'antd/lib/tooltip'; 3 | import cn from 'classnames'; 4 | 5 | import s from './s.module.scss'; 6 | 7 | export type TooltipProps = Partial; 8 | 9 | const Tooltip: React.FC = props => { 10 | const { overlayClassName, children, ...tooltipProps } = props; 11 | 12 | return ( 13 | 14 | {children} 15 | 16 | ); 17 | }; 18 | 19 | export default Tooltip; 20 | -------------------------------------------------------------------------------- /src/modules/governance/views/overview-view/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { useDAO } from '../../components/dao-provider'; 4 | import ActivationThreshold from './components/activation-threshold'; 5 | import VotersTable from './components/voters-table'; 6 | import VotingStatList from './components/voting-stat-list'; 7 | 8 | const OverviewView: React.FC = () => { 9 | const daoCtx = useDAO(); 10 | 11 | return ( 12 | <> 13 | {daoCtx.isActive === false && } 14 | 15 | 16 | 17 | ); 18 | }; 19 | 20 | export default OverviewView; 21 | -------------------------------------------------------------------------------- /src/components/antd/popover/s.module.scss: -------------------------------------------------------------------------------- 1 | .overlay:global(.ant-popover) { 2 | :global(.ant-popover-inner) { 3 | background: var(--theme-overlay-color); 4 | } 5 | 6 | :global(.ant-popover-arrow) { 7 | border-color: var(--theme-overlay-color); 8 | } 9 | 10 | :global(.ant-popover-title) { 11 | font: var(--font-p1); 12 | font-weight: 600; 13 | padding: 24px; 14 | border-bottom: 1px solid var(--theme-border-color); 15 | color: var(--theme-primary-color); 16 | } 17 | 18 | :global(.ant-popover-inner-content) { 19 | padding: 24px; 20 | } 21 | 22 | &.noPadding { 23 | :global(.ant-popover-inner-content) { 24 | padding: 0; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/components/custom/status-tag/s.module.scss: -------------------------------------------------------------------------------- 1 | .statusTag { 2 | min-width: 80px; 3 | height: 32px; 4 | padding: 8px 24px; 5 | border-radius: 32px; 6 | text-align: center; 7 | 8 | background: var(--bg-color); 9 | 10 | &.red { 11 | --bg-color: rgba(var(--theme-red-color-rgb), 0.08); 12 | --color: var(--theme-red-color); 13 | } 14 | 15 | &.green { 16 | --bg-color: rgba(var(--theme-green-color-rgb), 0.08); 17 | --color: var(--theme-green-color); 18 | } 19 | 20 | &.blue { 21 | --bg-color: rgba(var(--theme-blue-color-rgb), 0.08); 22 | --color: var(--theme-blue-color); 23 | } 24 | 25 | label { 26 | color: var(--color); 27 | display: block; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/components/radio-card/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cn from 'classnames'; 3 | 4 | import s from './s.module.scss'; 5 | 6 | type Props = { 7 | children?: any; 8 | selected?: boolean; 9 | onClick: (e: any) => void; 10 | }; 11 | 12 | export const RadioCard: React.FC = props => { 13 | const { children, selected = false, ...rest } = props; 14 | 15 | return ( 16 | 19 | ); 20 | }; 21 | 22 | export const RadioCards: React.FC<{ className?: string }> = ({ className, ...rest }) => ( 23 |
24 | ); 25 | -------------------------------------------------------------------------------- /src/components/antd/slider/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | :global(.ant-slider-rail), 3 | &:global(.ant-slider:hover .ant-slider-rail) { 4 | height: 8px; 5 | background: var(--theme-border-color); 6 | } 7 | 8 | :global(.ant-slider-track), 9 | &:global(.ant-slider:hover .ant-slider-track) { 10 | height: 8px; 11 | background: var(--theme-red-color); 12 | } 13 | 14 | :global(.ant-slider-step) { 15 | height: 8px; 16 | } 17 | 18 | :global(.ant-slider-handle) { 19 | width: 24px; 20 | height: 24px; 21 | margin-top: -8px; 22 | border: 6px solid var(--theme-red-color); 23 | background: var(--theme-card-color); 24 | box-shadow: 0 0 0 5px var(--theme-card-color); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/checkFlexGap.tsx: -------------------------------------------------------------------------------- 1 | function checkFlexGapSupport() { 2 | // create flex container with row-gap set 3 | const flex = document.createElement('div'); 4 | flex.style.display = 'flex'; 5 | flex.style.flexDirection = 'column'; 6 | flex.style.rowGap = '1px'; 7 | 8 | // create two, elements inside it 9 | flex.appendChild(document.createElement('div')); 10 | flex.appendChild(document.createElement('div')); 11 | 12 | // append to the DOM (needed to obtain scrollHeight) 13 | document.body.appendChild(flex); 14 | const isSupported = flex.scrollHeight === 1; // flex container should be 1px high from the row-gap 15 | document.body.removeChild(flex); 16 | 17 | return isSupported; 18 | } 19 | 20 | export { checkFlexGapSupport }; 21 | -------------------------------------------------------------------------------- /src/components/antd/datepicker/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import generatePicker, { PickerProps as AntdPickerProps } from 'antd/lib/date-picker/generatePicker'; 3 | import cn from 'classnames'; 4 | import dateFnsGenerateConfig from 'rc-picker/lib/generate/dateFns'; 5 | 6 | import s from './s.module.scss'; 7 | 8 | export type DatePickerProps = AntdPickerProps; 9 | 10 | const DateFNSPicker = generatePicker(dateFnsGenerateConfig); 11 | 12 | const DatePicker: React.FC = props => { 13 | const { className, ...datePickerProps } = props; 14 | 15 | return ; 16 | }; 17 | 18 | export default DatePicker; 19 | -------------------------------------------------------------------------------- /src/components/custom/expandable-card/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AntdCard, { CardProps as AntdCardProps } from 'antd/lib/card'; 3 | import cn from 'classnames'; 4 | 5 | import s from './s.module.scss'; 6 | 7 | export type ExpandableCardProps = AntdCardProps & { 8 | footer?: React.ReactNode; 9 | }; 10 | 11 | const ExpandableCard: React.FC = props => { 12 | const { className, children, footer, ...cardProps } = props; 13 | 14 | return ( 15 | 16 |
{children}
17 | {footer &&
{footer}
} 18 |
19 | ); 20 | }; 21 | export default ExpandableCard; 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "esnext", 4 | "moduleResolution": "node", 5 | "removeComments": true, 6 | "allowSyntheticDefaultImports": true, 7 | "esModuleInterop": true, 8 | "resolveJsonModule": true, 9 | "isolatedModules": true, 10 | "target": "es5", 11 | "baseUrl": "src/", 12 | "jsx": "react-jsx", 13 | "allowJs": true, 14 | "skipLibCheck": true, 15 | "lib": [ 16 | "dom", 17 | "dom.iterable", 18 | "esnext" 19 | ], 20 | "strict": true, 21 | "forceConsistentCasingInFileNames": true, 22 | "noEmit": true, 23 | "noFallthroughCasesInSwitch": true, 24 | "noImplicitAny": false 25 | }, 26 | "include": [ 27 | "src" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /src/components/antd/popover/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AntdPopover, { PopoverProps as AntdPopoverProps } from 'antd/lib/popover'; 3 | import cn from 'classnames'; 4 | 5 | import s from './s.module.scss'; 6 | 7 | export type PopoverProps = { 8 | noPadding?: boolean; 9 | }; 10 | 11 | const Popover: React.FC = props => { 12 | const { noPadding, children, className, ...popoverProps } = props; 13 | 14 | return ( 15 | 20 | {children} 21 | 22 | ); 23 | }; 24 | 25 | export default Popover; 26 | -------------------------------------------------------------------------------- /src/modules/smart-yield/views/portfolio-view/senior/s.module.scss: -------------------------------------------------------------------------------- 1 | .portfolioContainer { 2 | column-gap: 32px; 3 | display: grid; 4 | grid-template-columns: 40% 1fr; 5 | margin-bottom: 32px; 6 | 7 | @media (max-width: 768px) { 8 | gap: 8px; 9 | grid-template-columns: 1fr; 10 | } 11 | } 12 | 13 | .cards { 14 | display: grid; 15 | gap: 32px; 16 | grid-template-columns: 1fr 1fr 1fr; 17 | margin-bottom: 32px; 18 | margin-top: 32px; 19 | 20 | @media (max-width: 1200px) { 21 | grid-template-columns: 1fr 1fr; 22 | } 23 | // @media (max-width: 768px) { 24 | // gap: 8px; 25 | // grid-template-columns: 1fr 1fr; 26 | // } 27 | 28 | @media (max-width: 480px) { 29 | grid-template-columns: 1fr; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/components/custom/status-tag/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cn from 'classnames'; 3 | 4 | import { Text } from 'components/custom/typography'; 5 | 6 | import s from './s.module.scss'; 7 | 8 | export type StatusTagProps = { 9 | text: React.ReactNode; 10 | color: 'red' | 'green' | 'blue'; 11 | className?: string; 12 | style?: React.CSSProperties; 13 | }; 14 | 15 | const StatusTag: React.FC = props => { 16 | const { text, color, className, style } = props; 17 | 18 | return ( 19 |
20 | 21 | {text} 22 | 23 |
24 | ); 25 | }; 26 | 27 | export default StatusTag; 28 | -------------------------------------------------------------------------------- /public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/modules/smart-alpha/contracts/chainlinkOracleContract.ts: -------------------------------------------------------------------------------- 1 | import BigNumber from 'bignumber.js'; 2 | import { AbiItem } from 'web3-utils'; 3 | import Web3Contract, { createAbiItem } from 'web3/web3Contract'; 4 | 5 | const ABI: AbiItem[] = [ 6 | // call 7 | createAbiItem('getPrice', [], ['uint256']), 8 | ]; 9 | 10 | class ChainlinkOracleContract extends Web3Contract { 11 | price: BigNumber | undefined; 12 | 13 | constructor(address: string) { 14 | super(ABI, address, 'SA Chainlink Oracle'); 15 | } 16 | 17 | async loadCommon() { 18 | const [price] = await this.batch([{ method: 'getPrice' }]); 19 | 20 | this.price = BigNumber.from(price); 21 | this.emit(Web3Contract.UPDATE_DATA); 22 | } 23 | } 24 | 25 | export default ChainlinkOracleContract; 26 | -------------------------------------------------------------------------------- /src/stories/TokenIconPair.stories.tsx: -------------------------------------------------------------------------------- 1 | import { ComponentMeta, ComponentStory } from '@storybook/react'; 2 | 3 | import { TokenIconPair as TokenIconPairComponent } from 'components/token-icon'; 4 | import { ReactComponent as TokenIconsStaticSprite } from 'resources/svg/token-icons-sprite-static.svg'; 5 | 6 | export default { 7 | title: 'Components/TokenIconPair', 8 | component: TokenIconPairComponent, 9 | } as ComponentMeta; 10 | 11 | const Template: ComponentStory = args => ( 12 | <> 13 | 14 | 15 | ); 16 | export const TokenPair = Template.bind({}); 17 | TokenPair.args = { 18 | name1: 'usdc', 19 | name2: 'bond', 20 | size: 40, 21 | }; 22 | -------------------------------------------------------------------------------- /src/components/alert/s.module.scss: -------------------------------------------------------------------------------- 1 | .alert { 2 | font: var(--font-p2); 3 | font-weight: 600; 4 | padding: 16px 24px; 5 | display: flex; 6 | border-radius: 4px; 7 | 8 | a { 9 | color: inherit; 10 | text-decoration: underline; 11 | } 12 | } 13 | 14 | .info { 15 | background: rgba(79, 106, 229, 0.04); 16 | border: 1px solid rgba(79, 106, 229, 0.32); 17 | color: var(--theme-blue-color); 18 | } 19 | 20 | .warning { 21 | background: rgba(var(--theme-yellow-color-rgb), 0.04); 22 | border: 1px solid rgba(var(--theme-yellow-color-rgb), 0.32); 23 | color: var(--theme-yellow-color); 24 | } 25 | 26 | .error { 27 | background: rgba(var(--theme-red-color-rgb), 0.04); 28 | border: 1px solid rgba(204, 167, 77, 0.32); 29 | color: var(--theme-red-color); 30 | } 31 | -------------------------------------------------------------------------------- /src/components/antd/radio-button/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AntdRadio, { RadioProps as AntdRadioProps } from 'antd/lib/radio'; 3 | import cn from 'classnames'; 4 | 5 | import Grid from 'components/custom/grid'; 6 | 7 | import s from './s.module.scss'; 8 | 9 | export type RadioButtonProps = { 10 | label: React.ReactNode; 11 | hint?: React.ReactNode; 12 | }; 13 | 14 | const RadioButton: React.FC = props => { 15 | const { label, hint, className, ...radioProps } = props; 16 | 17 | return ( 18 | 19 | 20 | {label} 21 | {hint} 22 | 23 | 24 | ); 25 | }; 26 | 27 | export default RadioButton; 28 | -------------------------------------------------------------------------------- /src/components/custom/icon/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | color: var(--theme-icon-color); 3 | flex-shrink: 0; 4 | 5 | &:global(.rotate-90) { 6 | transform: rotate(90deg); 7 | } 8 | 9 | &:global(.rotate-180) { 10 | transform: rotate(180deg); 11 | } 12 | 13 | &:global(.rotate-270) { 14 | transform: rotate(270deg); 15 | } 16 | 17 | &.primary-color { 18 | color: var(--theme-primary-color); 19 | } 20 | 21 | &.secondary-color { 22 | color: var(--theme-secondary-color); 23 | } 24 | 25 | &.red-color { 26 | color: var(--theme-red-color); 27 | } 28 | 29 | &.green-color { 30 | color: var(--theme-green-color); 31 | } 32 | 33 | &.blue-color { 34 | color: var(--theme-blue-color); 35 | } 36 | 37 | &.inherit-color { 38 | color: inherit; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BarnBridge", 3 | "short_name": "BarnBridge", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "theme_color": "#ffffff", 7 | "background_color": "#ffffff", 8 | "iconPath": "logo.svg", 9 | "description": "Risk Tokenizing Protocol", 10 | "providedBy": { 11 | "name": "BarnBridge", 12 | "url": "https://branbridge.com" 13 | }, 14 | "icons": [ 15 | { 16 | "src": "favicon.ico", 17 | "sizes": "64x64 32x32 24x24 16x16", 18 | "type": "image/x-icon" 19 | }, 20 | { 21 | "src": "/android-chrome-192x192.png", 22 | "sizes": "192x192", 23 | "type": "image/png" 24 | }, 25 | { 26 | "src": "/android-chrome-512x512.png", 27 | "sizes": "512x512", 28 | "type": "image/png" 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /src/components/antd/alert/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AntdAlert, { AlertProps as AntdAlertProps } from 'antd/lib/alert'; 3 | import cn from 'classnames'; 4 | 5 | import { Icon } from 'components/icon'; 6 | 7 | import s from './s.module.scss'; 8 | 9 | export type AlertProps = AntdAlertProps; 10 | 11 | const Alert: React.FC = props => { 12 | const { className, type = 'info', ...alertProps } = props; 13 | 14 | const icon = React.useMemo(() => { 15 | switch (type) { 16 | case 'info': 17 | return ; 18 | default: 19 | return undefined; 20 | } 21 | }, [type]); 22 | 23 | return ; 24 | }; 25 | 26 | export default Alert; 27 | -------------------------------------------------------------------------------- /src/components/checkbox/s.module.scss: -------------------------------------------------------------------------------- 1 | .checkbox { 2 | display: flex; 3 | align-items: center; 4 | 5 | input { 6 | display: none; 7 | 8 | &:checked ~ svg { 9 | background-color: var(--theme-red-color); 10 | border-color: var(--theme-red-color); 11 | } 12 | } 13 | 14 | &:hover { 15 | svg { 16 | border-color: var(--theme-red-color); 17 | } 18 | } 19 | 20 | &.hasChildren { 21 | svg { 22 | margin-right: 12px; 23 | } 24 | } 25 | 26 | &.size-small { 27 | svg { 28 | width: 16px; 29 | height: 16px; 30 | } 31 | } 32 | 33 | &.size-normal { 34 | svg { 35 | width: 24px; 36 | height: 24px; 37 | } 38 | } 39 | 40 | svg { 41 | border: 2px solid var(--theme-border-color); 42 | border-radius: 8px; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/components/antd/form/s.module.scss: -------------------------------------------------------------------------------- 1 | .form { 2 | opacity: 1; 3 | } 4 | 5 | .item { 6 | margin-bottom: 0; 7 | 8 | :global(.ant-form-item-label) label { 9 | font: var(--font-sm); 10 | font-weight: 600; 11 | display: inline-grid; 12 | grid-template-columns: repeat(2, max-content); 13 | column-gap: 8px; 14 | align-items: center; 15 | justify-content: space-between; 16 | width: 100%; 17 | color: #717980; 18 | 19 | :global(.ant-form-item-tooltip) { 20 | width: 16px; 21 | height: 16px; 22 | color: var(--theme-icon-color); 23 | } 24 | } 25 | 26 | &:global(.ant-form-item-has-error) { 27 | :global(.ant-input), 28 | :global(.ant-input-group-addon) { 29 | background: inherit; 30 | border-color: var(--theme-red-color); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/components/custom/table/s.module.scss: -------------------------------------------------------------------------------- 1 | .tableContainer { 2 | position: relative; 3 | 4 | &.loading { 5 | tbody { 6 | tr { 7 | filter: blur(1px) grayscale(1); 8 | pointer-events: none; 9 | } 10 | } 11 | } 12 | } 13 | 14 | .table { 15 | &.clickable { 16 | tr { 17 | cursor: pointer; 18 | } 19 | } 20 | 21 | tr { 22 | &:hover { 23 | .arrowIcon { 24 | visibility: visible; 25 | } 26 | } 27 | } 28 | } 29 | 30 | .arrowIcon { 31 | visibility: hidden; 32 | } 33 | 34 | .tableFooter { 35 | display: grid; 36 | grid-template-columns: 1fr max-content; 37 | gap: 16px; 38 | 39 | @media (max-width: 768px) { 40 | grid-template-columns: 1fr; 41 | } 42 | } 43 | 44 | .spinner { 45 | position: absolute; 46 | top: 50%; 47 | left: 50%; 48 | } 49 | -------------------------------------------------------------------------------- /src/components/antd/skeleton/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AntdSkeleton, { SkeletonProps as AntdSkeletonProps } from 'antd/lib/skeleton'; 3 | import cn from 'classnames'; 4 | 5 | import s from './s.module.scss'; 6 | 7 | export type SkeletonProps = AntdSkeletonProps & { 8 | width?: number; 9 | height?: number; 10 | }; 11 | 12 | const Skeleton: React.FC = props => { 13 | const { className, width, height, loading, children, ...skeletonProps } = props; 14 | 15 | return ( 16 | 23 | {children} 24 | 25 | ); 26 | }; 27 | 28 | export default Skeleton; 29 | -------------------------------------------------------------------------------- /src/components/custom/outside-click/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | type Props = { 4 | handler: Function; 5 | nodes: (HTMLElement | null)[]; 6 | }; 7 | 8 | export const OutsideClick: React.FC = ({ children, nodes, handler }) => { 9 | React.useEffect(() => { 10 | const listener = (e: Event) => { 11 | for (var i = nodes.length; i--; ) { 12 | if (nodes[i]?.contains(e.target as HTMLElement)) return; 13 | } 14 | handler(e); 15 | }; 16 | 17 | document.addEventListener('mousedown', listener); 18 | document.addEventListener('touchstart', listener); 19 | 20 | return () => { 21 | document.removeEventListener('mousedown', listener); 22 | document.removeEventListener('touchstart', listener); 23 | }; 24 | }, [nodes, handler]); 25 | 26 | return <>{children}; 27 | }; 28 | -------------------------------------------------------------------------------- /src/components/antd/spin/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { LoadingOutlined } from '@ant-design/icons'; 3 | import AntdSpin, { SpinProps as AntdSpinProps } from 'antd/lib/spin'; 4 | import cn from 'classnames'; 5 | 6 | import s from './s.module.scss'; 7 | 8 | type Props = AntdSpinProps & { 9 | type?: 'default' | 'circle'; 10 | }; 11 | 12 | const Spin: React.FC = props => { 13 | const { type = 'default', className, ...spinProps } = props; 14 | 15 | const indicator = React.useMemo(() => { 16 | switch (type) { 17 | case 'circle': 18 | return ; 19 | default: 20 | break; 21 | } 22 | 23 | return undefined; 24 | }, [type]); 25 | 26 | return ; 27 | }; 28 | 29 | export default Spin; 30 | -------------------------------------------------------------------------------- /src/components/antd/table/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AntdTable, { TableProps as AntdTableProps } from 'antd/lib/table'; 3 | import cn from 'classnames'; 4 | 5 | import s from './s.module.scss'; 6 | 7 | const Table = >( 8 | props: React.PropsWithChildren>, 9 | ): React.ReactElement => { 10 | const { className, pagination, ...tableProps } = props; 11 | 12 | return ( 13 | 14 | className={cn(s.component, className)} 15 | bordered={false} 16 | showSorterTooltip={false} 17 | pagination={ 18 | pagination 19 | ? { 20 | showSizeChanger: false, 21 | ...pagination, 22 | } 23 | : false 24 | } 25 | {...tableProps} 26 | /> 27 | ); 28 | }; 29 | 30 | export default Table; 31 | -------------------------------------------------------------------------------- /src/components/antd/yes-no-selector/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | display: grid; 3 | grid-auto-flow: column; 4 | grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); 5 | grid-column-gap: 32px; 6 | width: 100%; 7 | height: 64px; 8 | 9 | label:global(.ant-radio-button-wrapper) { 10 | font: var(--font-p1); 11 | font-weight: 600; 12 | display: flex; 13 | align-items: center; 14 | justify-content: center; 15 | height: auto; 16 | background: var(--theme-card-color); 17 | border: 1px solid var(--theme-border-color); 18 | border-radius: 4px; 19 | color: var(--theme-primary-color); 20 | 21 | &::before { 22 | display: none; 23 | } 24 | 25 | &:global(.ant-radio-button-wrapper-checked) { 26 | border: 1px solid #ff4339; 27 | box-shadow: 0 16px 32px rgba(255, 67, 57, 0.04); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/components/custom/notification/icon.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | type NotificationIconType = { 4 | rgbVarName: string; 5 | children: React.ReactNode; 6 | }; 7 | 8 | const NotificationIcon: React.FC = ({ rgbVarName, children }) => { 9 | const width = 40; 10 | const height = 40; 11 | const childWidth = 24; 12 | const childHeight = 24; 13 | 14 | return ( 15 | 16 | 17 | {React.isValidElement(children) && 18 | React.cloneElement(children, { 19 | width: childWidth, 20 | height: childHeight, 21 | x: (width - childWidth) / 2, 22 | y: (height - childHeight) / 2, 23 | })} 24 | 25 | ); 26 | }; 27 | 28 | export default NotificationIcon; 29 | -------------------------------------------------------------------------------- /src/components/antd/radio-button/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | display: flex; 3 | flex-direction: row-reverse; 4 | align-items: center; 5 | justify-content: space-between; 6 | min-height: 72px; 7 | margin: 0; 8 | padding: 24px; 9 | background: var(--theme-card-color); 10 | box-shadow: 0 16px 32px rgba(255, 67, 57, 0.04); 11 | border: 1px solid var(--theme-border-color); 12 | border-radius: 4px; 13 | 14 | :global(.ant-radio-inner) { 15 | width: 24px; 16 | height: 24px; 17 | background: var(--theme-body-color); 18 | border: 2px solid var(--theme-icon-color); 19 | 20 | &::after { 21 | display: none; 22 | } 23 | } 24 | 25 | &:global(.ant-radio-wrapper-checked) { 26 | border: 1px solid var(--theme-red-color); 27 | 28 | :global(.ant-radio-inner) { 29 | border: 8px solid var(--theme-red-color); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/modules/governance/components/create-proposal-action-modal/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | top: 0; 3 | bottom: 0; 4 | width: 100vw !important; 5 | height: 100vh; 6 | margin: 0 auto; 7 | padding: 24px; 8 | 9 | :global(.ant-modal-content) { 10 | height: 100%; 11 | } 12 | 13 | :global(.ant-modal-body) { 14 | padding: 106px 32px 32px; 15 | height: 100%; 16 | background: var(--theme-body-color); 17 | } 18 | 19 | .wrap { 20 | width: 460px; 21 | margin: 0 auto; 22 | } 23 | 24 | .form { 25 | display: grid; 26 | grid-auto-flow: row; 27 | grid-row-gap: 24px; 28 | margin-top: 64px; 29 | } 30 | 31 | .textarea { 32 | color: #060a0d; 33 | } 34 | 35 | .actions { 36 | display: grid; 37 | grid-auto-flow: column; 38 | justify-content: space-between; 39 | align-items: center; 40 | margin-top: 16px; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/modules/smart-alpha/components/trade/s.module.scss: -------------------------------------------------------------------------------- 1 | .list { 2 | display: grid; 3 | gap: 16px; 4 | } 5 | 6 | .link { 7 | display: grid; 8 | grid-template-columns: 1fr max-content 1fr; 9 | gap: 16px; 10 | align-items: center; 11 | border: 1px solid var(--theme-border-color); 12 | border-radius: 4px; 13 | padding: 16px; 14 | color: var(--theme-primary-color); 15 | 16 | &:hover { 17 | border-color: var(--theme-red400-color); 18 | } 19 | 20 | &:active { 21 | border-color: var(--theme-red600-color); 22 | } 23 | } 24 | 25 | .token { 26 | display: grid; 27 | grid-template-columns: max-content 1fr; 28 | gap: 12px; 29 | align-items: center; 30 | font: var(--font-p2-semibold); 31 | } 32 | 33 | .arrowIcon { 34 | width: 32px; 35 | height: 32px; 36 | border-radius: 50%; 37 | background-color: var(--theme-body-color); 38 | display: grid; 39 | place-content: center; 40 | } 41 | -------------------------------------------------------------------------------- /src/modules/smart-alpha/views/launch/s.module.scss: -------------------------------------------------------------------------------- 1 | .slogan { 2 | display: grid; 3 | grid-template-columns: max-content 1fr; 4 | align-items: center; 5 | column-gap: 32px; 6 | row-gap: 32px; 7 | 8 | @media (max-width: 1200px) { 9 | grid-template-columns: 1fr; 10 | } 11 | } 12 | 13 | .countdown { 14 | margin-left: auto; 15 | 16 | @media (max-width: 1200px) { 17 | margin-left: unset; 18 | } 19 | } 20 | 21 | .timeCard { 22 | background-color: var(--theme-card-color); 23 | box-shadow: 0 32px 64px -16px rgba(0, 0, 0, 0.12); 24 | border-radius: 4px; 25 | width: 120px; 26 | height: 120px; 27 | display: grid; 28 | place-content: center; 29 | text-align: center; 30 | 31 | dt { 32 | font: var(--font-sm); 33 | font-weight: bold; 34 | } 35 | 36 | dd { 37 | font-weight: 800; 38 | font-size: 48px; 39 | line-height: 56px; 40 | color: var(--theme-red-color); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/modules/governance/components/voting-detailed-modal/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | :global(.ant-modal-body) { 3 | padding: 0; 4 | } 5 | 6 | .separator { 7 | width: 100%; 8 | height: 1px; 9 | border-top: 1px solid var(--theme-border-color); 10 | margin: 16px 0; 11 | } 12 | } 13 | 14 | .list { 15 | margin: 0; 16 | padding: 16px 0; 17 | } 18 | 19 | .row { 20 | font: var(--font-p1); 21 | font-weight: 600; 22 | padding: 16px 32px; 23 | display: flex; 24 | align-items: center; 25 | } 26 | 27 | .term { 28 | color: var(--theme-secondary-color); 29 | display: flex; 30 | align-items: center; 31 | font-weight: inherit; 32 | } 33 | 34 | .termIcon { 35 | margin-right: 16px; 36 | } 37 | 38 | .data { 39 | margin-left: auto; 40 | margin-bottom: 0; 41 | display: flex; 42 | align-items: center; 43 | color: var(--theme-primary-color); 44 | } 45 | 46 | .dataIcon { 47 | margin-left: 8px; 48 | } 49 | -------------------------------------------------------------------------------- /src/modules/smart-alpha/contracts/seniorRateModelContract.ts: -------------------------------------------------------------------------------- 1 | import BigNumber from 'bignumber.js'; 2 | import { AbiItem } from 'web3-utils'; 3 | import Web3Contract, { createAbiItem } from 'web3/web3Contract'; 4 | 5 | const ABI: AbiItem[] = [ 6 | // call 7 | createAbiItem('getRates', ['uint256', 'uint256'], ['uint256', 'uint256']), 8 | ]; 9 | 10 | class SeniorRateModelContract extends Web3Contract { 11 | price: BigNumber | undefined; 12 | 13 | constructor(address: string) { 14 | super(ABI, address, 'SA Chainlink Oracle'); 15 | } 16 | 17 | getRates( 18 | juniorLiquidity: BigNumber, 19 | seniorLiquidity: BigNumber, 20 | ): Promise<[BigNumber | undefined, BigNumber | undefined]> { 21 | return this.call('getRates', [juniorLiquidity, seniorLiquidity], {}).then(result => [ 22 | BigNumber.from(result[0]), 23 | BigNumber.from(result[1]), 24 | ]); 25 | } 26 | } 27 | 28 | export default SeniorRateModelContract; 29 | -------------------------------------------------------------------------------- /src/components/antd/yes-no-selector/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { CheckboxOptionType } from 'antd/lib/checkbox/Group'; 3 | import AntdRadio, { RadioGroupProps as AntdRadioGroupProps } from 'antd/lib/radio'; 4 | import cn from 'classnames'; 5 | 6 | import s from './s.module.scss'; 7 | 8 | export type YesNoSelectorProps = AntdRadioGroupProps; 9 | 10 | const YesNoOptions: CheckboxOptionType[] = [ 11 | { 12 | label: 'Yes', 13 | value: true, 14 | }, 15 | { 16 | label: 'No', 17 | value: false, 18 | }, 19 | ]; 20 | 21 | const YesNoSelector: React.FC = props => { 22 | const { className, ...groupProps } = props; 23 | 24 | return ( 25 | 32 | ); 33 | }; 34 | 35 | export default YesNoSelector; 36 | -------------------------------------------------------------------------------- /src/components/custom/slider/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cn from 'classnames'; 3 | 4 | import s from './s.module.scss'; 5 | 6 | type Props = React.DetailedHTMLProps, HTMLInputElement>; 7 | 8 | export const Slider: React.FC = ({ className, value, ...rest }) => { 9 | const max = Number(rest.max) || 0; 10 | const slicedMax = Math.floor(max * 1e6) / 1e6; 11 | const slicedValue = Math.floor(Number(value) * 1e6) / 1e6; 12 | const percent = (slicedValue / slicedMax) * 100 || 0; 13 | const disabled = slicedMax === 0; 14 | 15 | return ( 16 | 25 | ); 26 | }; 27 | -------------------------------------------------------------------------------- /src/components/custom/pagination/s.module.scss: -------------------------------------------------------------------------------- 1 | .pagination { 2 | display: flex; 3 | justify-content: center; 4 | gap: 8px; 5 | } 6 | 7 | .page, 8 | .separator { 9 | color: var(--theme-secondary-color); 10 | min-width: 24px; 11 | height: 24px; 12 | display: flex; 13 | align-items: center; 14 | justify-content: center; 15 | font-weight: 600; 16 | } 17 | 18 | .page { 19 | background-color: transparent; 20 | border: 0; 21 | padding: 0; 22 | 23 | &:hover { 24 | color: var(--theme-icon-hover-color); 25 | } 26 | 27 | &.prev, 28 | &.next { 29 | color: var(--theme-icon-color); 30 | 31 | &:disabled, 32 | &[disabled] { 33 | pointer-events: none; 34 | } 35 | 36 | &:hover { 37 | color: var(--theme-icon-hover-color); 38 | } 39 | } 40 | 41 | &.active { 42 | color: var(--theme-red-color); 43 | pointer-events: none; 44 | } 45 | } 46 | 47 | .separator { 48 | pointer-events: none; 49 | } 50 | -------------------------------------------------------------------------------- /src/components/antd/popover-menu/s.module.scss: -------------------------------------------------------------------------------- 1 | .component:global(.ant-popover) { 2 | pointer-events: all !important; 3 | 4 | :global(.ant-popover-inner-content) { 5 | padding: 0; 6 | 7 | :global(.ant-menu) { 8 | border-radius: 4px; 9 | } 10 | 11 | :global(.ant-menu-item) { 12 | font: var(--font-p1); 13 | font-weight: 600; 14 | display: grid; 15 | grid-auto-flow: column; 16 | grid-template-columns: repeat(auto-fit, minmax(0, max-content)); 17 | column-gap: 8px; 18 | align-items: center; 19 | justify-content: flex-start; 20 | margin: 4px; 21 | color: var(--theme-secondary-color); 22 | 23 | &:global(.ant-menu-item-active) { 24 | background: var(--theme-body-color); 25 | border-radius: 4px; 26 | color: var(--theme-primary-color); 27 | 28 | svg { 29 | color: var(--theme-secondary-color); 30 | } 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/wallets/connectors/portis/index.ts: -------------------------------------------------------------------------------- 1 | import { AbstractConnector } from '@web3-react/abstract-connector'; 2 | import { PortisConnector } from '@web3-react/portis-connector'; 3 | 4 | import PortisLogo from 'resources/svg/wallets/portis-logo.svg'; 5 | 6 | import { Web3Network } from 'networks/types'; 7 | import { BaseWalletConfig } from 'wallets/types'; 8 | 9 | const PortisWalletConfig: BaseWalletConfig = { 10 | id: 'portis', 11 | logo: PortisLogo, 12 | name: 'Portis', 13 | factory(network: Web3Network): AbstractConnector { 14 | return new PortisConnector({ 15 | dAppId: network.config.wallets.portisId, 16 | networks: [network.meta.chainId], 17 | }); 18 | }, 19 | onError(error: Error | string): Error | undefined { 20 | if (typeof error === 'string') { 21 | if (error === 'User denied login.') { 22 | return undefined; 23 | } 24 | } 25 | 26 | return error as Error; 27 | }, 28 | }; 29 | 30 | export default PortisWalletConfig; 31 | -------------------------------------------------------------------------------- /src/components/icon/s.module.scss: -------------------------------------------------------------------------------- 1 | .component.component { 2 | flex-shrink: 0; 3 | 4 | &:hover { 5 | --icon-display__light: none; 6 | --icon-display__dark: none; 7 | --icon-display__hover: block; 8 | } 9 | 10 | &.color-icon { 11 | color: var(--theme-icon-color); 12 | } 13 | 14 | &.color-primary { 15 | color: var(--theme-primary-color); 16 | } 17 | 18 | &.color-secondary { 19 | color: var(--theme-secondary-color); 20 | } 21 | 22 | &.color-red { 23 | color: var(--theme-red-color); 24 | } 25 | 26 | &.color-green { 27 | color: var(--theme-green-color); 28 | } 29 | 30 | &.color-blue { 31 | color: var(--theme-blue-color); 32 | } 33 | 34 | &.color-yellow { 35 | color: var(--theme-yellow-color); 36 | } 37 | } 38 | 39 | [data-theme='light'] { 40 | .component { 41 | --icon-display__light: block; 42 | } 43 | } 44 | 45 | [data-theme='dark'] { 46 | .component { 47 | --icon-display__dark: block; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/components/antd/tabs/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AntdTabs, { TabPaneProps as AntdTabPaneProps, TabsProps as AntdTabsProps } from 'antd/lib/tabs'; 3 | import cn from 'classnames'; 4 | 5 | import s from './s.module.scss'; 6 | 7 | export type TabProps = AntdTabPaneProps; 8 | 9 | const Tab: React.FC = props => { 10 | const { className, ...tabProps } = props; 11 | 12 | return ; 13 | }; 14 | 15 | export type TabsProps = AntdTabsProps & { 16 | simple?: boolean; 17 | }; 18 | 19 | export type StaticTabsProps = { 20 | Tab: React.FC; 21 | }; 22 | 23 | const Tabs: React.FC & StaticTabsProps = ((props: TabsProps) => { 24 | const { className, simple = false, ...tabsProps } = props; 25 | 26 | return ; 27 | }) as any; 28 | 29 | Tabs.Tab = Tab; 30 | 31 | export default Tabs; 32 | -------------------------------------------------------------------------------- /src/components/radio-card/s.module.scss: -------------------------------------------------------------------------------- 1 | .cards { 2 | display: grid; 3 | gap: 32px; 4 | grid-auto-flow: column; 5 | grid-template-columns: 1fr 1fr; 6 | 7 | @media (max-width: 768px) { 8 | gap: 8px; 9 | grid-auto-flow: row; 10 | grid-template-columns: 1fr; 11 | } 12 | } 13 | 14 | .card { 15 | background: var(--theme-card-color); 16 | border: 1px solid var(--theme-border-color); 17 | border-radius: 4px; 18 | box-sizing: border-box; 19 | display: flex; 20 | flex-direction: column; 21 | justify-content: flex-start; 22 | padding: 32px; 23 | position: relative; 24 | text-align: left; 25 | } 26 | 27 | .card::after { 28 | border: 2px solid var(--theme-border-color); 29 | border-radius: 50%; 30 | content: ''; 31 | height: 24px; 32 | position: absolute; 33 | right: 32px; 34 | top: 32px; 35 | width: 24px; 36 | } 37 | 38 | .card.selected { 39 | border-color: #ff4339; 40 | } 41 | 42 | .card.selected::after { 43 | border: 8px solid #ff4339; 44 | } 45 | -------------------------------------------------------------------------------- /src/components/custom/identicon/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cn from 'classnames'; 3 | import IdenticonJS from 'identicon.js'; 4 | 5 | import s from './s.module.scss'; 6 | 7 | export type IdenticonProps = { 8 | className?: string; 9 | address?: string; 10 | width?: number; 11 | height?: number; 12 | alt?: string; 13 | }; 14 | 15 | const EMPTY_ADDRESS = '000000000000000'; 16 | 17 | const Identicon: React.FC = props => { 18 | const { address = EMPTY_ADDRESS, className, width = 24, height = 24, alt } = props; 19 | 20 | const icon = React.useMemo(() => { 21 | return new IdenticonJS(address, { 22 | format: 'svg', 23 | }).toString(); 24 | }, [address]); 25 | 26 | return ( 27 | {alt 34 | ); 35 | }; 36 | 37 | export default Identicon; 38 | -------------------------------------------------------------------------------- /src/modules/smart-alpha/views/kpi-option/statistics/s.module.scss: -------------------------------------------------------------------------------- 1 | .statistics { 2 | display: flex; 3 | flex-direction: column; 4 | } 5 | 6 | .defs { 7 | margin: 0; 8 | padding: 24px; 9 | } 10 | 11 | .def { 12 | display: flex; 13 | align-items: center; 14 | margin-bottom: 24px; 15 | 16 | &:last-of-type { 17 | margin-bottom: 0; 18 | } 19 | 20 | dt { 21 | font: var(--font-sm); 22 | font-weight: 600; 23 | color: var(--theme-secondary-color); 24 | } 25 | 26 | dd { 27 | font: var(--font-p1); 28 | font-weight: 600; 29 | color: var(--theme-primary-color); 30 | margin: 0 0 0 auto; 31 | display: flex; 32 | align-items: center; 33 | } 34 | } 35 | 36 | .footer { 37 | display: flex; 38 | align-items: center; 39 | background-color: var(--theme-body-color); 40 | border-radius: 4px; 41 | margin: auto 4px 4px; 42 | padding: 16px; 43 | } 44 | 45 | .footerReward { 46 | display: flex; 47 | align-items: center; 48 | margin-bottom: 4px; 49 | } 50 | -------------------------------------------------------------------------------- /src/modules/smart-yield/views/pool-view/statistics/s.module.scss: -------------------------------------------------------------------------------- 1 | .statistics { 2 | display: flex; 3 | flex-direction: column; 4 | } 5 | 6 | .defs { 7 | margin: 0; 8 | padding: 24px; 9 | } 10 | 11 | .def { 12 | display: flex; 13 | align-items: center; 14 | margin-bottom: 24px; 15 | 16 | &:last-of-type { 17 | margin-bottom: 0; 18 | } 19 | 20 | dt { 21 | font: var(--font-sm); 22 | font-weight: 600; 23 | color: var(--theme-secondary-color); 24 | } 25 | 26 | dd { 27 | font: var(--font-p1); 28 | font-weight: 600; 29 | color: var(--theme-primary-color); 30 | margin: 0 0 0 auto; 31 | display: flex; 32 | align-items: center; 33 | } 34 | } 35 | 36 | .footer { 37 | display: flex; 38 | align-items: center; 39 | background-color: var(--theme-body-color); 40 | border-radius: 4px; 41 | margin: auto 4px 4px; 42 | padding: 16px; 43 | } 44 | 45 | .footerReward { 46 | display: flex; 47 | align-items: center; 48 | margin-bottom: 4px; 49 | } 50 | -------------------------------------------------------------------------------- /src/components/antd/modal/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | :global(.ant-modal-content) { 3 | box-shadow: 0 16px 32px rgba(19, 32, 43, 0.4), 0 0 0.5px rgba(6, 10, 13, 0.4); 4 | } 5 | 6 | :global(.ant-modal-close) { 7 | top: 32px; 8 | right: 32px; 9 | } 10 | 11 | :global(.ant-modal-close-x) { 12 | width: 24px; 13 | height: 24px; 14 | line-height: 24px; 15 | } 16 | 17 | :global(.ant-modal-close-x) svg { 18 | color: var(--theme-icon-color); 19 | } 20 | 21 | :global(.ant-modal-header) { 22 | padding: 32px; 23 | background: var(--theme-card-color); 24 | border-bottom: 1px solid var(--theme-border-color); 25 | 26 | :global(.ant-modal-title) { 27 | font: var(--font-p1); 28 | font-weight: 600; 29 | color: var(--theme-primary-color); 30 | } 31 | } 32 | 33 | :global(.ant-modal-body) { 34 | background: var(--theme-card-color); 35 | border-bottom-left-radius: 4px; 36 | border-bottom-right-radius: 4px; 37 | overflow-y: auto; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- 1 | import BN from 'bignumber.js/bignumber'; 2 | 3 | declare module 'bignumber.js' { 4 | export default class BigNumber extends BN { 5 | static ZERO: BigNumber; 6 | static MAX_UINT_256: BigNumber; 7 | 8 | static from(value?: BN.Value): BigNumber | undefined; 9 | 10 | static parse(value: BN.Value): BigNumber; 11 | 12 | static sumEach(items: T[], predicate: (item: T) => BigNumber | undefined): BigNumber | undefined; 13 | 14 | scaleBy(decimals?: number): BigNumber | undefined; 15 | 16 | unscaleBy(decimals?: number): BigNumber | undefined; 17 | 18 | round(): BigNumber; 19 | } 20 | } 21 | 22 | declare module 'valirator' { 23 | namespace Module { 24 | function validate(schema: any, values: any): any; 25 | } 26 | 27 | export = Module; 28 | } 29 | 30 | declare module 'outy' { 31 | export default function outy( 32 | nodes: HTMLElement[], 33 | types: (Event | MouseEvent | TouchEvent)[], 34 | eventHandler: Function, 35 | ): { remove: () => void }; 36 | } 37 | -------------------------------------------------------------------------------- /src/components/antd/button/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AntdButton, { ButtonProps as AntdButtonProps, ButtonType as AntdButtonType } from 'antd/lib/button'; 3 | import cn from 'classnames'; 4 | 5 | import s from './s.module.scss'; 6 | 7 | export type ButtonProps = Omit & { 8 | type: 'default' | 'primary' | 'ghost' | 'link' | 'light' | 'select'; 9 | }; 10 | 11 | const Button: React.FC = props => { 12 | const { children, className, type, ...btnProps } = props; 13 | 14 | let btnType: AntdButtonType; 15 | 16 | if (type === 'light') { 17 | btnType = 'link'; 18 | } else if (type === 'select') { 19 | btnType = 'ghost'; 20 | } else { 21 | btnType = type; 22 | } 23 | 24 | return ( 25 | 29 | {props.children} 30 | 31 | ); 32 | }; 33 | 34 | export default Button; 35 | -------------------------------------------------------------------------------- /src/components/custom/badge/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cn from 'classnames'; 3 | 4 | import s from './s.module.scss'; 5 | 6 | export type BadgeProps = { 7 | className?: string; 8 | color?: 'green' | 'blue' | 'red' | 'grey' | 'purple'; 9 | size?: 'small' | 'medium' | 'large'; 10 | }; 11 | 12 | export const Badge: React.FC = ({ color = 'grey', size = 'medium', children, className, ...rest }) => { 13 | if (!children) return null; 14 | 15 | return ( 16 |
17 | {children} 18 |
19 | ); 20 | }; 21 | 22 | type SquareBadgeProps = { 23 | className?: string; 24 | color?: 'green' | 'blue' | 'red' | 'grey'; 25 | }; 26 | 27 | export const SquareBadge: React.FC = ({ children, className, color = 'grey', ...rest }) => { 28 | if (!children) return null; 29 | 30 | return ( 31 |
32 | {children} 33 |
34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /src/components/custom/label/index.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | 3 | import IconsSet from 'components/custom/icons-set'; 4 | import { TokenIcon, TokenIconNames } from 'components/token-icon'; 5 | 6 | import s from './s.module.scss'; 7 | 8 | type AprLabelPropsType = { 9 | icons: TokenIconNames[]; 10 | size?: 'large'; 11 | }; 12 | 13 | export const AprLabel: React.FC = ({ icons, size, children }) => { 14 | const iconSize = size === 'large' ? 16 : 12; 15 | 16 | return ( 17 |
21 | ( 24 | 25 | ))} 26 | /> 27 |
1, 30 | })}> 31 | {children} 32 |
33 |
34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /src/components/custom/notification/s.module.scss: -------------------------------------------------------------------------------- 1 | .n { 2 | display: flex; 3 | align-items: flex-start; 4 | word-break: break-word; 5 | color: var(--theme-secondary-color); 6 | padding: 24px; 7 | // max-width: 400px; 8 | // width: 100%; 9 | 10 | svg { 11 | flex-shrink: 0; 12 | } 13 | 14 | &.toast { 15 | background-color: var(--theme-card-color); 16 | box-shadow: var(--theme-overlay-shadow); 17 | border-radius: 4px; 18 | 19 | & + & { 20 | margin-top: 16px; 21 | } 22 | } 23 | } 24 | 25 | .close { 26 | margin-left: 16px; 27 | padding: 8px; 28 | border: 0; 29 | background-color: transparent; 30 | display: flex; 31 | color: var(--theme-icon-color); 32 | 33 | &:hover { 34 | color: var(--theme-icon-hover-color); 35 | } 36 | } 37 | 38 | .time { 39 | font: var(--font-sm); 40 | font-weight: 600; 41 | color: var(--theme-icon-color); 42 | margin-left: auto; 43 | padding-left: 8px; 44 | flex-shrink: 0; 45 | white-space: pre; 46 | text-align: end; 47 | min-width: 6ch; 48 | } 49 | -------------------------------------------------------------------------------- /src/components/custom/token-input/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, forwardRef } from 'react'; 2 | import { InputProps as AntdInputProps } from 'antd/lib/input/Input'; 3 | import cn from 'classnames'; 4 | import { isAddress } from 'web3-utils'; 5 | 6 | import Input from 'components/antd/input'; 7 | import Identicon from 'components/custom/identicon'; 8 | 9 | import s from './s.module.scss'; 10 | 11 | export type TokenInputProps = AntdInputProps; 12 | 13 | export const TokenInput: FC = forwardRef(props => { 14 | const { className, value, ...inputProps } = props; 15 | 16 | const addonBefore = React.useMemo( 17 | () => (isAddress(String(value)) ? :
), 18 | [value], 19 | ); 20 | 21 | return ( 22 | 29 | ); 30 | }); 31 | 32 | export default TokenInput; 33 | -------------------------------------------------------------------------------- /src/components/input/s.module.scss: -------------------------------------------------------------------------------- 1 | .inputWrapper { 2 | border: 1px solid var(--theme-border-color); 3 | border-radius: 4px; 4 | display: flex; 5 | background-color: var(--theme-card-color); 6 | position: relative; 7 | } 8 | 9 | .input { 10 | font: var(--font-p1); 11 | font-weight: 600; 12 | border: 0; 13 | padding: 11px 12px; 14 | background-color: transparent; 15 | width: 100%; 16 | 17 | &.large { 18 | padding: 19px 20px; 19 | } 20 | 21 | &.inputSearch { 22 | padding-left: 44px; 23 | } 24 | 25 | &::placeholder { 26 | color: var(--theme-icon-color); 27 | } 28 | } 29 | 30 | .before { 31 | border-right: 1px solid var(--theme-border-color); 32 | padding-left: 11px; 33 | padding-right: 11px; 34 | display: flex; 35 | align-items: center; 36 | 37 | &:empty { 38 | display: none; 39 | } 40 | 41 | &.large { 42 | padding-left: 19px; 43 | padding-right: 19px; 44 | } 45 | } 46 | 47 | .searchIcon { 48 | position: absolute; 49 | top: 50%; 50 | left: 16px; 51 | transform: translateY(-50%); 52 | } 53 | -------------------------------------------------------------------------------- /src/modules/governance/views/proposals-view/components/proposal-status-tag/index.tsx: -------------------------------------------------------------------------------- 1 | import { Badge, BadgeProps } from 'components/custom/badge'; 2 | import { APIProposalState, APIProposalStateMap } from 'modules/governance/api'; 3 | 4 | export type ProposalStatusTagProps = { 5 | className?: string; 6 | state: APIProposalState; 7 | }; 8 | 9 | const ProposalStatusTag: React.FC = ({ state, className }) => { 10 | let color: BadgeProps['color'] = 'grey'; 11 | switch (state) { 12 | case 'ACCEPTED': 13 | case 'EXECUTED': 14 | color = 'green'; 15 | break; 16 | case 'WARMUP': 17 | case 'ACTIVE': 18 | case 'QUEUED': 19 | case 'GRACE': 20 | color = 'blue'; 21 | break; 22 | case 'EXPIRED': 23 | case 'FAILED': 24 | case 'CANCELED': 25 | case 'ABROGATED': 26 | color = 'red'; 27 | break; 28 | } 29 | 30 | return ( 31 | 32 | {APIProposalStateMap.get(state)} 33 | 34 | ); 35 | }; 36 | 37 | export default ProposalStatusTag; 38 | -------------------------------------------------------------------------------- /src/components/custom/transaction-summary/index.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | 3 | import { Text } from 'components/custom/typography'; 4 | 5 | import s from './s.module.scss'; 6 | 7 | type PropsType = { 8 | items: [React.ReactNode, React.ReactNode][]; 9 | heading?: string; 10 | className?: string; 11 | }; 12 | 13 | export const TransactionSummary: React.FC = ({ items, heading, className }) => { 14 | return ( 15 |
16 |
17 | {heading ? ( 18 | heading 19 | ) : ( 20 | 21 | Transaction summary 22 | 23 | )} 24 |
25 |
26 | {items.map(([left, right], idx) => ( 27 |
28 |
{left}
29 |
{right}
30 |
31 | ))} 32 |
33 |
34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /src/web3/components/user-rejected-modal/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Modal, { ModalProps } from 'components/antd/modal'; 4 | import Icon from 'components/custom/icon'; 5 | import { Text } from 'components/custom/typography'; 6 | 7 | const UserRejectedModal: React.FC = props => { 8 | const { ...modalProps } = props; 9 | 10 | return ( 11 | 12 |
13 |
14 | 15 | 16 | Error 17 | 18 | 19 | Transaction rejected 20 | 21 |
22 | 25 |
26 |
27 | ); 28 | }; 29 | 30 | export default UserRejectedModal; 31 | -------------------------------------------------------------------------------- /src/hooks/useMergeState.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export type MergeStateUpdate = Partial | ((prevState: S) => Partial); 4 | 5 | export function mergeState(state: Partial): (prevState: S) => S { 6 | return (prevState: S): S => { 7 | return { 8 | ...prevState, 9 | ...state, 10 | }; 11 | }; 12 | } 13 | 14 | function useMergeState( 15 | initialState: S | (() => S), 16 | callback?: (state: S) => void, 17 | ): [S, React.Dispatch>] { 18 | const [state, set] = React.useState(initialState); 19 | 20 | const setState = React.useCallback( 21 | (updater: MergeStateUpdate) => { 22 | set(prev => { 23 | const next = { 24 | ...prev, 25 | ...(typeof updater === 'function' ? (updater as (value: S) => S)(prev) : updater), 26 | }; 27 | 28 | if (typeof callback === 'function') { 29 | callback(next); 30 | } 31 | 32 | return next; 33 | }); 34 | }, 35 | [callback], 36 | ); 37 | 38 | return [state, setState]; 39 | } 40 | 41 | export default useMergeState; 42 | -------------------------------------------------------------------------------- /src/wallets/connectors/wallet-connect/index.ts: -------------------------------------------------------------------------------- 1 | import { AbstractConnector } from '@web3-react/abstract-connector'; 2 | import { WalletConnectConnector } from '@web3-react/walletconnect-connector'; 3 | 4 | import WalletConnectLogo from 'resources/svg/wallets/walletconnect-logo.svg'; 5 | 6 | import { Web3Network } from 'networks/types'; 7 | import { BaseWalletConfig } from 'wallets/types'; 8 | 9 | const WalletConnectConfig: BaseWalletConfig = { 10 | id: 'walletconnect', 11 | logo: WalletConnectLogo, 12 | name: 'WalletConnect', 13 | factory(network: Web3Network): AbstractConnector { 14 | return new WalletConnectConnector({ 15 | rpc: { 16 | [network.meta.chainId]: network.rpc.httpsUrl, 17 | }, 18 | pollingInterval: network.rpc.poolingInterval, 19 | bridge: network.config.wallets.walletConnectBridge, 20 | qrcode: true, 21 | }); 22 | }, 23 | onDisconnect(connector?: WalletConnectConnector): void { 24 | connector?.close(); 25 | }, 26 | onError(error: Error): Error | undefined { 27 | return error; 28 | }, 29 | }; 30 | 31 | export default WalletConnectConfig; 32 | -------------------------------------------------------------------------------- /src/modules/smart-alpha/views/portfolio-view/s.module.scss: -------------------------------------------------------------------------------- 1 | .portfolioContainer { 2 | column-gap: 32px; 3 | display: grid; 4 | grid-template-columns: 40% 1fr; 5 | margin-bottom: 32px; 6 | 7 | @media (max-width: 768px) { 8 | gap: 8px; 9 | grid-template-columns: 1fr; 10 | } 11 | } 12 | 13 | .positionsCards { 14 | display: grid; 15 | grid-template-columns: 1fr 1fr 1fr; 16 | column-gap: 32px; 17 | align-items: start; 18 | 19 | > * { 20 | min-height: 250px; 21 | } 22 | } 23 | 24 | .positionsWalletSecondaryValues { 25 | display: flex; 26 | align-items: center; 27 | background-color: var(--theme-body-color); 28 | padding: 8px 16px; 29 | border-radius: 55px; 30 | margin-left: auto; 31 | margin-right: auto; 32 | } 33 | 34 | .positionsWalletSecondaryValue { 35 | display: flex; 36 | align-items: center; 37 | 38 | &:not(:last-of-type) { 39 | border-right: 2px solid var(--theme-border-color); 40 | padding-right: 16px; 41 | margin-right: 16px; 42 | } 43 | } 44 | 45 | .spinner { 46 | position: absolute; 47 | top: calc(50% - 12px); 48 | left: calc(50% - 12px); 49 | } 50 | -------------------------------------------------------------------------------- /src/modules/smart-alpha/views/simulate-epoch/s.module.scss: -------------------------------------------------------------------------------- 1 | .cards { 2 | display: grid; 3 | grid-template-columns: 438px 1fr; 4 | column-gap: 32px; 5 | row-gap: 32px; 6 | align-items: flex-start; 7 | 8 | @media (max-width: 1200px) { 9 | grid-template-columns: 320px 1fr; 10 | } 11 | 12 | @media (max-width: 1024px) { 13 | grid-template-columns: 1fr; 14 | } 15 | } 16 | 17 | .legend { 18 | display: flex; 19 | align-items: flex-start; 20 | justify-content: center; 21 | gap: 16px 24px; 22 | margin-top: 32px; 23 | overflow-y: auto; 24 | 25 | @media (max-width: 768px) { 26 | justify-content: flex-start; 27 | } 28 | } 29 | 30 | .legendItem { 31 | background: var(--theme-body-color); 32 | border-radius: 4px; 33 | padding: 8px 12px; 34 | } 35 | 36 | .legendTitle { 37 | position: relative; 38 | display: flex; 39 | align-items: center; 40 | 41 | &::before { 42 | content: ''; 43 | width: 8px; 44 | height: 8px; 45 | background-color: var(--dot-color, var(--theme-primary-color)); 46 | border-radius: 50%; 47 | margin-right: 8px; 48 | flex-shrink: 0; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/components/antd/progress/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AntdProgress, { ProgressProps as AntdProgressProps } from 'antd/lib/progress'; 3 | import cn from 'classnames'; 4 | 5 | import s from './s.module.scss'; 6 | 7 | export type ProgressProps = AntdProgressProps & { 8 | className?: string; 9 | acceptance?: number; 10 | }; 11 | 12 | const Progress: React.FC = props => { 13 | const { className, acceptance, ...progressProps } = props; 14 | const acceptanceMode = acceptance !== undefined; 15 | const acceptanceFulfilled = Number(acceptance) <= Number(props.percent); 16 | 17 | return ( 18 | 31 | ); 32 | }; 33 | 34 | export default Progress; 35 | -------------------------------------------------------------------------------- /src/components/antd/field-label/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from 'react'; 2 | import classnames from 'classnames'; 3 | 4 | import { Hint, Text } from 'components/custom/typography'; 5 | import { FCx } from 'components/types.tx'; 6 | 7 | type FieldLabelProps = { 8 | label: ReactNode; 9 | hint?: ReactNode; 10 | extra?: ReactNode; 11 | }; 12 | 13 | const FieldLabel: FCx = props => { 14 | const { children, className, label, hint, extra } = props; 15 | 16 | return ( 17 |
18 |
19 |
20 | 21 | {typeof label === 'string' ? ( 22 | 23 | {label} 24 | 25 | ) : ( 26 | label 27 | )} 28 | 29 |
30 | {extra} 31 |
32 | {children} 33 |
34 | ); 35 | }; 36 | 37 | export default FieldLabel; 38 | -------------------------------------------------------------------------------- /src/styles/rechart.scss: -------------------------------------------------------------------------------- 1 | .recharts-legend-item-text { 2 | font-size: var(--font-size-p2); 3 | font-weight: 600; 4 | line-height: calc(var(--font-size-p2) + 10px); 5 | } 6 | 7 | .chart-label { 8 | font: var(--font-sm); 9 | font-weight: 600; 10 | background: var(--bg-color, var(--theme-body-color)); 11 | color: var(--theme-secondary-color); 12 | border-radius: 4px; 13 | padding: 8px 12px; 14 | position: relative; 15 | display: flex; 16 | align-items: center; 17 | 18 | &::before { 19 | content: ''; 20 | width: 8px; 21 | height: 8px; 22 | background-color: var(--dot-color, var(--theme-primary-color)); 23 | border-radius: 50%; 24 | margin-right: 8px; 25 | } 26 | } 27 | 28 | .recharts-text.recharts-cartesian-axis-tick-value { 29 | font-size: var(--font-size-sm); 30 | line-height: calc(var(--font-size-sm) + 4px); 31 | color: var(--theme-default-color); 32 | } 33 | 34 | .recharts-wrapper { 35 | .recharts-default-tooltip { 36 | background: var(--theme-card-color) !important; 37 | border: 1px solid var(--theme-border-color) !important; 38 | color: var(--theme-color-secondary) !important; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/components/antd/progress/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | line-height: 24px; 3 | 4 | :global(.ant-progress-outer) { 5 | position: relative; 6 | } 7 | 8 | :global(.ant-progress-inner) { 9 | position: initial; 10 | } 11 | 12 | &.acceptance { 13 | :global(.ant-progress-bg) { 14 | border-top-right-radius: 0; 15 | border-bottom-right-radius: 0; 16 | } 17 | 18 | :global(.ant-progress-success-bg) { 19 | top: 9px; 20 | height: 0 !important; 21 | background: transparent; 22 | 23 | &::before, 24 | &::after { 25 | content: ''; 26 | position: absolute; 27 | left: 100%; 28 | z-index: 1; 29 | width: 4px; 30 | height: 8px; 31 | background: inherit; 32 | } 33 | 34 | &::before { 35 | top: -8px; 36 | border-radius: 4px 4px 0 0; 37 | } 38 | 39 | &::after { 40 | top: 8px; 41 | border-radius: 0 0 4px 4px; 42 | } 43 | } 44 | 45 | &.fulfilled { 46 | :global(.ant-progress-success-bg) { 47 | &::before, 48 | &::after { 49 | left: calc(100% - 4px); 50 | } 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/modules/smart-alpha/views/kpi-option/s.module.scss: -------------------------------------------------------------------------------- 1 | .headerTerms { 2 | display: flex; 3 | flex-wrap: wrap; 4 | column-gap: 80px; 5 | row-gap: 32px; 6 | margin: 0; 7 | 8 | :global(.no-flexbox-gap) & { 9 | margin: -16px -40px; 10 | } 11 | } 12 | 13 | .headerTermRow { 14 | :global(.no-flexbox-gap) & { 15 | margin: 16px 40px; 16 | } 17 | 18 | dt { 19 | font: var(--font-sm); 20 | font-weight: 600; 21 | color: var(--theme-secondary-color); 22 | margin-bottom: 8px; 23 | } 24 | 25 | dd { 26 | font: var(--font-p1); 27 | font-weight: 600; 28 | margin: 0; 29 | color: var(--theme-primary-color); 30 | display: flex; 31 | align-items: center; 32 | } 33 | } 34 | 35 | .stakeStatisticsContainer { 36 | display: flex; 37 | align-items: stretch; 38 | margin-bottom: 32px; 39 | gap: 32px; 40 | 41 | @media (max-width: 768px) { 42 | flex-direction: column; 43 | } 44 | } 45 | 46 | .stake { 47 | display: flex; 48 | flex-direction: column; 49 | flex-grow: 1; 50 | width: 100%; 51 | } 52 | 53 | .statistics { 54 | max-width: 384px; 55 | width: 100%; 56 | 57 | @media (max-width: 768px) { 58 | max-width: none; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/modules/smart-yield/views/pool-view/s.module.scss: -------------------------------------------------------------------------------- 1 | .headerTerms { 2 | display: flex; 3 | flex-wrap: wrap; 4 | column-gap: 80px; 5 | row-gap: 32px; 6 | margin: 0; 7 | 8 | :global(.no-flexbox-gap) & { 9 | margin: -16px -40px; 10 | } 11 | } 12 | 13 | .headerTermRow { 14 | :global(.no-flexbox-gap) & { 15 | margin: 16px 40px; 16 | } 17 | 18 | dt { 19 | font: var(--font-sm); 20 | font-weight: 600; 21 | color: var(--theme-secondary-color); 22 | margin-bottom: 8px; 23 | } 24 | 25 | dd { 26 | font: var(--font-p1); 27 | font-weight: 600; 28 | margin: 0; 29 | color: var(--theme-primary-color); 30 | display: flex; 31 | align-items: center; 32 | } 33 | } 34 | 35 | .stakeStatisticsContainer { 36 | display: flex; 37 | align-items: stretch; 38 | margin-bottom: 32px; 39 | gap: 32px; 40 | 41 | @media (max-width: 768px) { 42 | flex-direction: column; 43 | } 44 | } 45 | 46 | .stake { 47 | display: flex; 48 | flex-direction: column; 49 | flex-grow: 1; 50 | width: 100%; 51 | } 52 | 53 | .statistics { 54 | max-width: 384px; 55 | width: 100%; 56 | 57 | @media (max-width: 768px) { 58 | max-width: none; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/components/custom/label/s.module.scss: -------------------------------------------------------------------------------- 1 | .aprLabel { 2 | font: var(--font-sm); 3 | font-weight: 600; 4 | display: inline-flex; 5 | align-items: center; 6 | white-space: nowrap; 7 | border: 1px solid var(--theme-border-color); 8 | border-radius: 38px; 9 | padding: 0 2px; 10 | background-color: var(--theme-body-color); 11 | 12 | &.aprLabelLarge { 13 | font: var(--font-p1); 14 | font-weight: 600; 15 | 16 | svg { 17 | width: 16px !important; 18 | height: 16px !important; 19 | } 20 | 21 | .aprLabelText { 22 | color: var(--theme-primary-color); 23 | } 24 | } 25 | 26 | svg { 27 | width: 12px !important; 28 | height: 12px !important; 29 | 30 | &:not(:first-child) { 31 | margin-left: -6px !important; 32 | } 33 | } 34 | } 35 | 36 | .aprLabelText { 37 | color: var(--theme-red-color); 38 | 39 | &.aprLabelTextGradient { 40 | background-color: var(--theme-blue-color); 41 | background-image: linear-gradient(45deg, var(--theme-blue-color), var(--theme-red-color)); 42 | background-size: 100%; 43 | background-clip: text; 44 | -webkit-text-fill-color: transparent; 45 | -moz-text-fill-color: transparent; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/components/custom/spinner/index.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | 3 | import { Icon } from 'components/icon'; 4 | 5 | import s from './s.module.scss'; 6 | 7 | type PropsType = { 8 | spinning?: boolean; 9 | className?: string; 10 | style?: React.CSSProperties; 11 | }; 12 | 13 | export const Spinner: React.FC = ({ className, children, spinning, ...restProps }) => { 14 | if (children) { 15 | if (spinning) { 16 | return ( 17 |
18 |
{children}
19 | 20 |
21 | ); 22 | } 23 | 24 | return <>{children}; 25 | } 26 | 27 | return ; 28 | }; 29 | 30 | type PageSpinnerProps = { 31 | className?: string; 32 | style?: React.CSSProperties; 33 | }; 34 | 35 | export const PageSpinner: React.FC = ({ className, ...restProps }) => ( 36 |
37 | 38 |
39 | ); 40 | -------------------------------------------------------------------------------- /src/components/input/index.tsx: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | import classNames from 'classnames'; 3 | import { nanoid } from 'nanoid'; 4 | 5 | import { Icon } from 'components/icon'; 6 | 7 | import s from './s.module.scss'; 8 | 9 | type InputPropsType = React.InputHTMLAttributes & { 10 | id?: string; 11 | /** "size" prop sounds better, but this word already reserved in input props */ 12 | dimension?: 'normal' | 'large'; 13 | before?: React.ReactNode; 14 | }; 15 | 16 | export const Input: React.FC = ({ id, before, className, dimension = 'normal', ...rest }) => { 17 | const inputId = useMemo(() => id ?? nanoid(), [id]); 18 | const isSearch = rest.type === 'search'; 19 | 20 | return ( 21 | 33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /src/modules/smart-alpha/views/pools-view/s.module.scss: -------------------------------------------------------------------------------- 1 | .cards { 2 | display: grid; 3 | grid-template-columns: repeat(auto-fit, minmax(min-content, 392px)); 4 | grid-auto-rows: auto; 5 | grid-gap: 32px; 6 | } 7 | 8 | .poolCard { 9 | display: flex; 10 | flex-direction: column; 11 | position: relative; 12 | 13 | &::before { 14 | content: ''; 15 | position: absolute; 16 | bottom: 0; 17 | left: 0; 18 | right: 0; 19 | width: 100%; 20 | height: 4px; 21 | background-color: var(--theme-border-color); 22 | border-radius: 0 4px 4px; 23 | } 24 | 25 | &::after { 26 | content: ''; 27 | position: absolute; 28 | bottom: 0; 29 | left: 0; 30 | right: 0; 31 | width: calc(var(--pool-card-progress, 0) * 1%); 32 | height: 4px; 33 | background-image: linear-gradient(90deg, #4f6ae6 0%, #00d395 100%); 34 | border-radius: 0 4px 4px; 35 | } 36 | } 37 | 38 | .poolCardDlRow { 39 | display: flex; 40 | align-items: center; 41 | padding-left: 24px; 42 | padding-right: 24px; 43 | 44 | dt { 45 | margin-right: 8px; 46 | } 47 | 48 | dd { 49 | margin-left: auto; 50 | text-align: right; 51 | } 52 | } 53 | 54 | .poolCardFooter { 55 | margin-top: auto; 56 | padding: 0 24px 24px; 57 | } 58 | -------------------------------------------------------------------------------- /src/stories/assets/direction.svg: -------------------------------------------------------------------------------- 1 | illustration/direction -------------------------------------------------------------------------------- /src/modules/smart-exposure/views/pool-actions-view/index.tsx: -------------------------------------------------------------------------------- 1 | import { lazy } from 'react'; 2 | import { Redirect, Route, Switch, useParams } from 'react-router-dom'; 3 | 4 | import { useWallet } from 'wallets/walletProvider'; 5 | 6 | // const DepositView = lazy(() => import('../deposit-view')); 7 | const WithdrawView = lazy(() => import('../withdraw-view')); 8 | const ChangeTrancheView = lazy(() => import('../change-tranche-view')); 9 | 10 | export const PoolActionsView: React.FC = () => { 11 | const { pool } = useParams<{ pool: string }>(); 12 | const wallet = useWallet(); 13 | 14 | if (!wallet.initialized) { 15 | return null; 16 | } 17 | 18 | if (!wallet.isActive) { 19 | return ; 20 | } 21 | 22 | return ( 23 | 24 | {/* 25 | 26 | */} 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | ); 36 | }; 37 | -------------------------------------------------------------------------------- /src/modules/smart-yield/contracts/syProviderContract.tsx: -------------------------------------------------------------------------------- 1 | import BigNumber from 'bignumber.js'; 2 | import Web3Contract from 'web3/web3Contract'; 3 | 4 | const ABI: any[] = [ 5 | { 6 | name: 'underlyingFees', 7 | type: 'function', 8 | inputs: [], 9 | outputs: [ 10 | { 11 | name: 'amount', 12 | type: 'uint256', 13 | }, 14 | ], 15 | }, 16 | { 17 | name: 'transferFees', 18 | type: 'function', 19 | inputs: [], 20 | outputs: [], 21 | }, 22 | ]; 23 | 24 | class SYProviderContract extends Web3Contract { 25 | constructor(address: string) { 26 | super(ABI, address, ''); 27 | } 28 | 29 | underlyingFees?: BigNumber; 30 | 31 | async loadUnderlyingFees(): Promise { 32 | return this.call('underlyingFees').then(value => { 33 | this.underlyingFees = BigNumber.from(value); 34 | this.emit(Web3Contract.UPDATE_DATA); 35 | }); 36 | } 37 | 38 | transferFeesSend(gasPrice?: number): Promise { 39 | if (!this.account) { 40 | return Promise.reject(); 41 | } 42 | 43 | return this.send( 44 | 'transferFees', 45 | [], 46 | { 47 | from: this.account, 48 | }, 49 | gasPrice, 50 | ); 51 | } 52 | } 53 | 54 | export default SYProviderContract; 55 | -------------------------------------------------------------------------------- /src/modules/smart-alpha/utils.ts: -------------------------------------------------------------------------------- 1 | import { getUnixTime } from 'date-fns'; 2 | 3 | import { TokenIconNames } from 'components/token-icon'; 4 | import { PoolApiType } from 'modules/smart-alpha/api'; 5 | 6 | function getCurrentEpoch(pool: PoolApiType) { 7 | const now = getUnixTime(Date.now()); 8 | 9 | if (now < pool.epoch1Start) { 10 | return 0; 11 | } 12 | 13 | return Math.floor((now - pool.epoch1Start) / pool.epochDuration + 1); 14 | } 15 | 16 | export function tillNextEpoch(pool: PoolApiType): number { 17 | const now = getUnixTime(Date.now()); 18 | const currentEpoch = getCurrentEpoch(pool); 19 | const nextEpochStart = pool.epoch1Start + currentEpoch * pool.epochDuration; 20 | 21 | return now < nextEpochStart ? nextEpochStart - now : 0; 22 | } 23 | 24 | export function getKpiOptionTokenIconNames(symbol: string): [TokenIconNames, TokenIconNames, TokenIconNames] { 25 | switch (symbol) { 26 | case 'WETH (USD) BPT': 27 | return ['balancer', 'usd', 'eth']; 28 | case 'WBTC (USD) BPT': 29 | return ['balancer', 'usd', 'wbtc']; 30 | case 'KPI_WETH-USD-1w': 31 | return ['uma', 'usd', 'weth']; 32 | case 'KPI_WBTC-USD-1w': 33 | return ['uma', 'usd', 'wbtc']; 34 | default: 35 | return ['unknown', 'unknown', 'unknown']; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/modules/smart-alpha/views/kpi-options/kpi-option-card/s.module.scss: -------------------------------------------------------------------------------- 1 | .card { 2 | background: var(--theme-card-color); 3 | box-shadow: var(--theme-card-shadow); 4 | border-radius: 4px; 5 | padding: 24px; 6 | max-width: 392px; 7 | flex-grow: 1; 8 | display: flex; 9 | flex-direction: column; 10 | } 11 | 12 | .header { 13 | display: flex; 14 | align-items: center; 15 | padding-bottom: 16px; 16 | } 17 | 18 | .endedLabel { 19 | font: var(--font-lb2); 20 | font-weight: bold; 21 | background: rgba(113, 121, 128, 0.08); 22 | border-radius: 32px; 23 | margin-left: auto; 24 | padding: 8px 24px; 25 | } 26 | 27 | .defRow { 28 | display: flex; 29 | 30 | &:not(:last-of-type) { 31 | margin-bottom: 32px; 32 | } 33 | } 34 | 35 | .defRow dt { 36 | font: var(--font-sm); 37 | font-weight: 600; 38 | color: var(--theme-secondary-color); 39 | display: flex; 40 | align-items: center; 41 | } 42 | 43 | .defRow dd { 44 | font: var(--font-p1); 45 | font-weight: 600; 46 | margin: 0 0 0 auto; 47 | color: var(--theme-primary-color); 48 | display: flex; 49 | align-items: center; 50 | } 51 | 52 | .footer { 53 | display: flex; 54 | gap: 24px; 55 | margin-top: auto; 56 | padding-top: 24px; 57 | } 58 | 59 | .footer button { 60 | flex-grow: 1; 61 | } 62 | -------------------------------------------------------------------------------- /src/modules/smart-yield/views/pools-view/pool-card/s.module.scss: -------------------------------------------------------------------------------- 1 | .card { 2 | background: var(--theme-card-color); 3 | box-shadow: var(--theme-card-shadow); 4 | border-radius: 4px; 5 | padding: 24px; 6 | max-width: 392px; 7 | flex-grow: 1; 8 | display: flex; 9 | flex-direction: column; 10 | } 11 | 12 | .header { 13 | display: flex; 14 | align-items: center; 15 | padding-bottom: 16px; 16 | } 17 | 18 | .endedLabel { 19 | font: var(--font-lb2); 20 | font-weight: bold; 21 | background: rgba(113, 121, 128, 0.08); 22 | border-radius: 32px; 23 | margin-left: auto; 24 | padding: 8px 24px; 25 | } 26 | 27 | .defRow { 28 | display: flex; 29 | 30 | &:not(:last-of-type) { 31 | margin-bottom: 32px; 32 | } 33 | } 34 | 35 | .defRow dt { 36 | font: var(--font-sm); 37 | font-weight: 600; 38 | color: var(--theme-secondary-color); 39 | display: flex; 40 | align-items: center; 41 | } 42 | 43 | .defRow dd { 44 | font: var(--font-p1); 45 | font-weight: 600; 46 | margin: 0 0 0 auto; 47 | color: var(--theme-primary-color); 48 | display: flex; 49 | align-items: center; 50 | } 51 | 52 | .footer { 53 | display: flex; 54 | flex-wrap: wrap; 55 | gap: 16px 24px; 56 | margin-top: auto; 57 | padding-top: 24px; 58 | 59 | button, 60 | a { 61 | flex-grow: 1; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/wallets/connectors/trezor/index.ts: -------------------------------------------------------------------------------- 1 | import { AbstractConnector } from '@web3-react/abstract-connector'; 2 | import { TrezorConnector } from '@web3-react/trezor-connector'; 3 | 4 | import TrezorLogoDark from 'resources/svg/wallets/trezor-logo-dark.svg'; 5 | import TrezorLogo from 'resources/svg/wallets/trezor-logo.svg'; 6 | 7 | import { Web3Network } from 'networks/types'; 8 | import { BaseWalletConfig } from 'wallets/types'; 9 | 10 | const TrezorWalletConfig: BaseWalletConfig = { 11 | id: 'trezor', 12 | logo: [TrezorLogo, TrezorLogoDark], 13 | name: 'Trezor', 14 | factory(network: Web3Network): AbstractConnector { 15 | return new TrezorConnector({ 16 | chainId: network.meta.chainId, 17 | url: network.rpc.httpsUrl, 18 | pollingInterval: network.rpc.poolingInterval, 19 | manifestEmail: network.config.wallets.trezorEmail, 20 | manifestAppUrl: network.config.wallets.trezorAppUrl, 21 | config: { 22 | networkId: network.meta.chainId, 23 | }, 24 | }); 25 | }, 26 | onError(error: Error): Error | undefined { 27 | if (error.message === 'Cancelled') { 28 | return undefined; 29 | } 30 | if (error.message === 'Popup closed') { 31 | return undefined; 32 | } 33 | 34 | return error; 35 | }, 36 | }; 37 | 38 | export default TrezorWalletConfig; 39 | -------------------------------------------------------------------------------- /src/modules/governance/components/delete-proposal-action-modal/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Button from 'components/antd/button'; 4 | import Modal, { ModalProps } from 'components/antd/modal'; 5 | import Icon from 'components/custom/icon'; 6 | import { Text } from 'components/custom/typography'; 7 | 8 | export type DeleteProposalActionModalProps = ModalProps; 9 | 10 | const DeleteProposalActionModal: React.FC = props => { 11 | const { ...modalProps } = props; 12 | 13 | return ( 14 | 15 |
16 |
17 | 18 | 19 | Are you sure you want to delete the action? 20 | 21 |
22 |
23 | 26 | 29 |
30 |
31 |
32 | ); 33 | }; 34 | 35 | export default DeleteProposalActionModal; 36 | -------------------------------------------------------------------------------- /src/wallets/connectors/coinbase/index.ts: -------------------------------------------------------------------------------- 1 | import { AbstractConnector } from '@web3-react/abstract-connector'; 2 | import { WalletLinkConnector } from '@web3-react/walletlink-connector'; 3 | 4 | import CoinbaseWalletLogo from 'resources/svg/wallets/coinbase-logo.svg'; 5 | 6 | import { Web3Network } from 'networks/types'; 7 | import { BaseWalletConfig } from 'wallets/types'; 8 | 9 | export type CoinbaseWalletArgs = { 10 | darkMode?: boolean; 11 | }; 12 | 13 | const CoinbaseWalletConfig: BaseWalletConfig = { 14 | id: 'coinbase', 15 | logo: CoinbaseWalletLogo, 16 | name: 'Coinbase Wallet', 17 | factory(network: Web3Network, args?: CoinbaseWalletArgs): AbstractConnector { 18 | const darkMode = args?.darkMode ?? false; 19 | 20 | return new WalletLinkConnector({ 21 | url: network.rpc.httpsUrl, 22 | appName: network.config.wallets.coinbaseAppName, 23 | appLogoUrl: '', 24 | darkMode, 25 | }); 26 | }, 27 | onDisconnect(connector?: WalletLinkConnector): void { 28 | connector?.close(); 29 | }, 30 | onError(error: Error): Error | undefined { 31 | const { code } = error as any as { code: number }; 32 | 33 | if (code === 4001) { 34 | // USER_DENIED_REQUEST_ACCOUNTS 35 | return undefined; 36 | } 37 | 38 | return error; 39 | }, 40 | }; 41 | 42 | export default CoinbaseWalletConfig; 43 | -------------------------------------------------------------------------------- /src/stories/assets/flow.svg: -------------------------------------------------------------------------------- 1 | illustration/flow -------------------------------------------------------------------------------- /src/components/antd/alert/s.module.scss: -------------------------------------------------------------------------------- 1 | .component:global(.ant-alert) { 2 | padding: 16px 24px; 3 | border-radius: 4px; 4 | 5 | background: rgba(var(--alert-bg-color), 0.04); 6 | border: 1px solid rgba(var(--alert-bg-color), 0.32); 7 | 8 | > :global(.ant-alert-icon) { 9 | margin-right: 12px; 10 | width: 24px; 11 | height: 24px; 12 | 13 | svg { 14 | width: 24px; 15 | height: 24px; 16 | } 17 | } 18 | 19 | > :global(.ant-alert-content) { 20 | font: var(--font-p2); 21 | font-weight: 600; 22 | } 23 | 24 | &:global(.ant-alert-info) { 25 | --alert-bg-color: var(--theme-blue-color-rgb); 26 | --alert-text-color: var(--theme-blue-color); 27 | } 28 | 29 | &:global(.ant-alert-success) { 30 | --alert-bg-color: var(--theme-green-color-rgb); 31 | --alert-text-color: var(--theme-green-color); 32 | } 33 | 34 | &:global(.ant-alert-warning) { 35 | --alert-bg-color: var(--theme-yellow-color-rgb); 36 | --alert-text-color: var(--theme-yellow-color); 37 | } 38 | 39 | &:global(.ant-alert-error) { 40 | --alert-bg-color: var(--theme-red-color-rgb); 41 | --alert-text-color: var(--theme-red-color); 42 | } 43 | 44 | > :global(.ant-alert-icon), 45 | > :global(.ant-alert-content .ant-alert-message), 46 | > :global(.ant-alert-content .ant-alert-description) { 47 | color: var(--alert-text-color); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/modules/smart-yield/providers/markets.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react'; 2 | 3 | import { ExternalLink } from 'components/button'; 4 | 5 | export type MarketMeta = { 6 | id: string; 7 | name: string; 8 | icon: { 9 | active: string; 10 | }; 11 | warning?: ReactElement; 12 | depositDisabled?: boolean; 13 | }; 14 | 15 | export const CompoundMarket: MarketMeta = { 16 | id: 'compound/v2', 17 | name: 'Compound', 18 | icon: { 19 | active: 'compound', 20 | }, 21 | }; 22 | 23 | export const AaveMarket: MarketMeta = { 24 | id: 'aave/v2', 25 | name: 'AAVE', 26 | icon: { 27 | active: `aave`, 28 | }, 29 | }; 30 | 31 | const CreamFinanceMarket: MarketMeta = { 32 | id: 'cream/v2', 33 | name: 'C.R.E.A.M Finance', 34 | icon: { 35 | active: `cream`, 36 | }, 37 | warning: ( 38 | 39 | C.R.E.A.M pool deposits have been disabled. 40 |
41 | 42 | Click here to learn more 43 | 44 |
45 | ), 46 | depositDisabled: true, 47 | }; 48 | 49 | export const KnownMarkets: MarketMeta[] = [CompoundMarket, AaveMarket, CreamFinanceMarket]; 50 | 51 | export function getKnownMarketById(marketId: string): MarketMeta | undefined { 52 | return KnownMarkets.find(km => km.id === marketId); 53 | } 54 | -------------------------------------------------------------------------------- /src/modules/smart-yield/views/withdraw-view/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { Suspense, lazy } from 'react'; 2 | import { Redirect, Route, Switch } from 'react-router-dom'; 3 | import AntdSpin from 'antd/lib/spin'; 4 | 5 | import { useSYPool } from 'modules/smart-yield/providers/pool-provider'; 6 | import WithdrawHeader from 'modules/smart-yield/views/withdraw-view/withdraw-header'; 7 | 8 | const InitiateWithdraw = lazy(() => import('./initiate-withdraw')); 9 | const InstantWithdraw = lazy(() => import('./instant-withdraw')); 10 | const TwoStepWithdraw = lazy(() => import('./two-step-withdraw')); 11 | 12 | const WithdrawView: React.FC = () => { 13 | const poolCtx = useSYPool(); 14 | 15 | if (poolCtx.loading) { 16 | return ; 17 | } 18 | 19 | if (poolCtx.pool === null) { 20 | return ; 21 | } 22 | 23 | return ( 24 |
25 | 26 | }> 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | ); 35 | }; 36 | 37 | export default WithdrawView; 38 | -------------------------------------------------------------------------------- /src/components/checkbox/index.tsx: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | import classNames from 'classnames'; 3 | import { nanoid } from 'nanoid'; 4 | 5 | import { Text } from 'components/custom/typography'; 6 | 7 | import s from './s.module.scss'; 8 | 9 | type PropsType = { 10 | checked: boolean; 11 | onChange: React.ChangeEventHandler; 12 | indeterminate?: boolean; 13 | size?: 'normal' | 'small'; 14 | }; 15 | 16 | export const Checkbox: React.FC = ({ checked, onChange, indeterminate, size = 'normal', children }) => { 17 | const id = useMemo(() => nanoid(), []); 18 | return ( 19 | 41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /src/components/custom/dropdown/s.module.scss: -------------------------------------------------------------------------------- 1 | .tokenSelectList { 2 | list-style-type: none; 3 | padding: 0; 4 | margin: 0; 5 | background-color: var(--theme-card-color); 6 | box-shadow: var(--theme-overlay-shadow); 7 | padding: 4px; 8 | border-radius: 4px; 9 | max-width: 90vw; 10 | 11 | li:not(:last-of-type) { 12 | margin-bottom: 4px; 13 | } 14 | } 15 | 16 | .tokenSelectListButton { 17 | font: var(--font-p2); 18 | font-weight: 600; 19 | background-color: transparent; 20 | border: 0; 21 | padding: 12px; 22 | display: flex; 23 | width: 100%; 24 | color: var(--theme-secondary-color); 25 | border-radius: 4px; 26 | text-align: left; 27 | 28 | &.active, 29 | &[aria-selected='true'] { 30 | color: var(--theme-primary-color); 31 | } 32 | 33 | &:hover { 34 | color: var(--theme-primary-color); 35 | background: var(--theme-body-color); 36 | } 37 | } 38 | 39 | .dropdown { 40 | border: 1px solid var(--theme-border-color); 41 | border-radius: 4px; 42 | display: flex; 43 | align-items: center; 44 | height: 48px; 45 | padding: 0 16px; 46 | width: 100%; 47 | background-color: var(--theme-card-color); 48 | 49 | &.large { 50 | height: 64px; 51 | padding: 0 24px; 52 | } 53 | } 54 | 55 | .dropdownChevron { 56 | color: var(--theme-icon-color); 57 | margin-left: auto; 58 | 59 | button:hover & { 60 | color: var(--theme-icon-hover-color); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | BarnBridge 19 | 20 | 21 | 22 |
23 |
24 |
25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/stories/assets/code-brackets.svg: -------------------------------------------------------------------------------- 1 | illustration/code-brackets -------------------------------------------------------------------------------- /src/modules/smart-yield/views/deposit-view/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { Suspense, lazy } from 'react'; 2 | import { Redirect, Route, Switch } from 'react-router-dom'; 3 | import AntdSpin from 'antd/lib/spin'; 4 | import cn from 'classnames'; 5 | 6 | import { useSYPool } from '../../providers/pool-provider'; 7 | import DepositHeader from './deposit-header'; 8 | 9 | import s from './s.module.scss'; 10 | 11 | const SelectTranche = lazy(() => import('./select-tranche')); 12 | const SeniorTranche = lazy(() => import('./senior-tranche')); 13 | const JuniorTranche = lazy(() => import('./junior-tranche')); 14 | 15 | const DepositView: React.FC = () => { 16 | const poolCtx = useSYPool(); 17 | 18 | if (poolCtx.loading) { 19 | return ; 20 | } 21 | 22 | if (poolCtx.pool === null) { 23 | return ; 24 | } 25 | 26 | return ( 27 | <> 28 | 29 |
30 | }> 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 | 39 | ); 40 | }; 41 | 42 | export default DepositView; 43 | -------------------------------------------------------------------------------- /src/modules/yield-farming/index.tsx: -------------------------------------------------------------------------------- 1 | import { FC, Suspense, lazy, useEffect } from 'react'; 2 | import { Redirect, Route, Switch } from 'react-router-dom'; 3 | import AntdSpin from 'antd/lib/spin'; 4 | 5 | import { useNotifications } from 'components/providers/notificationsProvider'; 6 | import YfAPIProvider from 'modules/yield-farming/api'; 7 | 8 | import YfPoolsProvider from './providers/pools-provider'; 9 | 10 | const PoolsView = lazy(() => import('./views/pools-view')); 11 | const PoolView = lazy(() => import('./views/pool-view')); 12 | 13 | const YieldFarmingView: FC = () => { 14 | const { addWarn } = useNotifications(); 15 | 16 | useEffect(() => { 17 | let warningDestructor; 18 | 19 | warningDestructor = addWarn({ 20 | text: 'Do not send funds directly to the contract!', 21 | closable: true, 22 | storageIdentity: 'bb_send_funds_warn', 23 | }); 24 | 25 | return () => { 26 | warningDestructor?.(); 27 | }; 28 | }, []); 29 | 30 | return ( 31 | 32 | 33 | }> 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | ); 43 | }; 44 | 45 | export default YieldFarmingView; 46 | -------------------------------------------------------------------------------- /src/modules/governance/views/proposal-detail-view/components/proposal-voters-modal/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | :global(.ant-modal-body) { 3 | padding: 0; 4 | } 5 | 6 | .tabs:global > { 7 | .ant-tabs-nav { 8 | height: 80px; 9 | margin-bottom: 0; 10 | padding: 0 24px; 11 | background: var(--theme-card-color); 12 | box-shadow: 0 0 0.5px rgba(6, 10, 13, 0.4), 0 8px 16px rgba(113, 121, 128, 0.08); 13 | border: 1px solid var(--theme-border-color); 14 | border-radius: 4px 4px 0 0; 15 | } 16 | } 17 | 18 | .table :global { 19 | .ant-table-thead .ant-table-cell { 20 | border-top-left-radius: 0; 21 | border-bottom-right-radius: 0; 22 | border-top: 0; 23 | } 24 | 25 | .ant-table-tbody:last-child .ant-table-row:last-child .ant-table-cell { 26 | border-bottom: 0; 27 | } 28 | } 29 | 30 | .powerCell { 31 | line-height: 32px !important; 32 | } 33 | 34 | .forTag { 35 | min-width: 80px; 36 | height: 32px; 37 | padding: 8px 24px; 38 | border-radius: 32px; 39 | text-align: center; 40 | 41 | background: rgba(var(--theme-green-color-rgb), 0.08); 42 | color: var(--theme-green-color); 43 | } 44 | 45 | .againstTag { 46 | min-width: 80px; 47 | height: 32px; 48 | padding: 8px 24px; 49 | border-radius: 32px; 50 | text-align: center; 51 | 52 | background: rgba(var(--theme-red-color-rgb), 0.08); 53 | color: var(--theme-red-color); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/modules/governance/views/proposal-detail-view/components/abrogation-voters-modal/s.module.scss: -------------------------------------------------------------------------------- 1 | .component { 2 | :global(.ant-modal-body) { 3 | padding: 0; 4 | } 5 | 6 | .tabs:global > { 7 | .ant-tabs-nav { 8 | height: 80px; 9 | margin-bottom: 0; 10 | padding: 0 24px; 11 | background: var(--theme-card-color); 12 | box-shadow: 0 0 0.5px rgba(6, 10, 13, 0.4), 0 8px 16px rgba(113, 121, 128, 0.08); 13 | border: 1px solid var(--theme-border-color); 14 | border-radius: 4px 4px 0 0; 15 | } 16 | } 17 | 18 | .table :global { 19 | .ant-table-thead .ant-table-cell { 20 | border-top-left-radius: 0; 21 | border-bottom-right-radius: 0; 22 | border-top: 0; 23 | } 24 | 25 | .ant-table-tbody:last-child .ant-table-row:last-child .ant-table-cell { 26 | border-bottom: 0; 27 | } 28 | } 29 | 30 | .powerCell { 31 | line-height: 32px !important; 32 | } 33 | 34 | .forTag { 35 | min-width: 80px; 36 | height: 32px; 37 | padding: 8px 24px; 38 | border-radius: 32px; 39 | text-align: center; 40 | 41 | background: rgba(var(--theme-green-color-rgb), 0.08); 42 | color: var(--theme-green-color); 43 | } 44 | 45 | .againstTag { 46 | min-width: 80px; 47 | height: 32px; 48 | padding: 8px 24px; 49 | border-radius: 32px; 50 | text-align: center; 51 | 52 | background: rgba(var(--theme-red-color-rgb), 0.08); 53 | color: var(--theme-red-color); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/components/antd/popover-menu/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AntdMenu from 'antd/lib/menu'; 3 | import AntdPopover, { PopoverProps as AntdPopoverProps } from 'antd/lib/popover'; 4 | import { MenuInfo } from 'rc-menu/lib/interface'; 5 | 6 | import s from './s.module.scss'; 7 | 8 | export type PopoverMenuItem = { 9 | key: string | number; 10 | icon?: React.ReactNode; 11 | title?: React.ReactNode; 12 | }; 13 | 14 | export type PopoverMenuProps = AntdPopoverProps & { 15 | items: PopoverMenuItem[]; 16 | onClick: (key: string | number) => void; 17 | }; 18 | 19 | const PopoverMenu: React.FC = props => { 20 | const { children, items, onClick, ...popoverProps } = props; 21 | 22 | const popoverRef = React.useRef(); 23 | 24 | function handleMenuClick(info: MenuInfo) { 25 | props.onClick?.(info.key); 26 | (popoverRef.current as any)?.close(); 27 | } 28 | 29 | return ( 30 | 37 | {items?.map(item => ( 38 | 39 | {item.icon} 40 | {item.title} 41 | 42 | ))} 43 | 44 | } 45 | {...popoverProps}> 46 | {children} 47 | 48 | ); 49 | }; 50 | 51 | export default PopoverMenu; 52 | -------------------------------------------------------------------------------- /src/stories/assets/comments.svg: -------------------------------------------------------------------------------- 1 | illustration/comments -------------------------------------------------------------------------------- /src/components/custom/icon-notification/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { nanoid } from 'nanoid'; 3 | 4 | export type IconBubbleProps = { 5 | bubble: boolean; 6 | className?: string; 7 | style?: Object; 8 | width: number; 9 | height: number; 10 | notificationSize?: number; 11 | notificationGap?: number; 12 | }; 13 | 14 | export const IconNotification: React.FunctionComponent = props => { 15 | const { 16 | bubble, 17 | style = {}, 18 | width = 24, 19 | height = 24, 20 | notificationSize = 10, 21 | notificationGap = 2, 22 | children, 23 | ...rest 24 | } = props; 25 | 26 | const id = React.useMemo(() => nanoid(), []); 27 | 28 | return ( 29 | 37 | 38 | 39 | 45 | 46 | {children} 47 | {bubble && ( 48 | 54 | )} 55 | 56 | ); 57 | }; 58 | -------------------------------------------------------------------------------- /src/utils/bignumber.ts: -------------------------------------------------------------------------------- 1 | import BigNumber from 'bignumber.js'; 2 | 3 | BigNumber.ZERO = new BigNumber(0); 4 | BigNumber.MAX_UINT_256 = new BigNumber(2).pow(256).minus(1); 5 | 6 | BigNumber.from = (value: BigNumber.Value | undefined): BigNumber | undefined => { 7 | if (value === undefined || value === null) { 8 | return undefined; 9 | } 10 | 11 | const bnValue = new BigNumber(value); 12 | 13 | if (bnValue.isNaN()) { 14 | return undefined; 15 | } 16 | 17 | return bnValue; 18 | }; 19 | 20 | BigNumber.parse = (value: BigNumber.Value) => { 21 | return new BigNumber(value); 22 | }; 23 | 24 | BigNumber.sumEach = (items: T[], predicate: (item: T) => BigNumber | undefined): BigNumber | undefined => { 25 | let sum = BigNumber.ZERO; 26 | 27 | for (let item of items) { 28 | const val = predicate?.(item); 29 | 30 | if (!val || val.isNaN()) { 31 | return undefined; 32 | } 33 | 34 | sum = sum.plus(val); 35 | } 36 | 37 | return sum; 38 | }; 39 | 40 | BigNumber.prototype.scaleBy = function (decimals?: number): BigNumber | undefined { 41 | if (decimals === undefined) { 42 | return undefined; 43 | } 44 | 45 | return this.multipliedBy(10 ** decimals); 46 | }; 47 | 48 | BigNumber.prototype.unscaleBy = function (decimals?: number): BigNumber | undefined { 49 | if (decimals === undefined) { 50 | return undefined; 51 | } 52 | 53 | return this.dividedBy(10 ** decimals); 54 | }; 55 | 56 | BigNumber.prototype.round = function (): BigNumber { 57 | return new BigNumber(this.toPrecision(1, 0)); 58 | }; 59 | -------------------------------------------------------------------------------- /src/modules/governance/views/treasury-view/treasury-fees/index.tsx: -------------------------------------------------------------------------------- 1 | import { FC, useMemo } from 'react'; 2 | import BigNumber from 'bignumber.js'; 3 | import { formatUSD } from 'web3/utils'; 4 | 5 | import { Text } from 'components/custom/typography'; 6 | 7 | import { SASection, useSAData } from './sa-section'; 8 | import { SESection, useSEData } from './se-section'; 9 | import { SYSection, useSYData } from './sy-section'; 10 | 11 | const TreasuryFees: FC = () => { 12 | const [syPools, syTotalFees, syLoading, syListeners] = useSYData(); 13 | const [sePools, seTotalFees, seLoading, seListeners] = useSEData(); 14 | const [saPools, saTotalFees, saLoading, saListeners] = useSAData(); 15 | 16 | const totalFeesUSD = useMemo(() => { 17 | return BigNumber.sum(syTotalFees, seTotalFees, saTotalFees); 18 | }, [syTotalFees, seTotalFees, saTotalFees]); 19 | 20 | return ( 21 | <> 22 | 23 | {formatUSD(totalFeesUSD) ?? '-'} 24 | 25 | 26 | Total fees accrued 27 | 28 | 29 | 30 | 31 | {syListeners} 32 | {seListeners} 33 | {saListeners} 34 | 35 | ); 36 | }; 37 | 38 | export default TreasuryFees; 39 | -------------------------------------------------------------------------------- /src/modules/smart-alpha/contracts/accountingModelContract.ts: -------------------------------------------------------------------------------- 1 | import BigNumber from 'bignumber.js'; 2 | import { AbiItem } from 'web3-utils'; 3 | import Web3Contract, { createAbiItem } from 'web3/web3Contract'; 4 | 5 | const ABI: AbiItem[] = [ 6 | // call 7 | createAbiItem('calcJuniorProfits', ['uint256', 'uint256', 'uint256', 'uint256', 'uint256'], ['uint256']), 8 | createAbiItem('calcSeniorProfits', ['uint256', 'uint256', 'uint256', 'uint256', 'uint256'], ['uint256']), 9 | ]; 10 | 11 | class AccountingModelContract extends Web3Contract { 12 | price: BigNumber | undefined; 13 | 14 | constructor(address: string) { 15 | super(ABI, address, 'SA Chainlink Oracle'); 16 | } 17 | 18 | calcJuniorProfits( 19 | entryPrice: BigNumber, 20 | currentPrice: BigNumber, 21 | upsideExposureRate: BigNumber, 22 | totalSeniors: BigNumber, 23 | ): Promise { 24 | return this.call( 25 | 'calcJuniorProfits', 26 | [entryPrice, currentPrice, upsideExposureRate, totalSeniors, BigNumber.ZERO], 27 | {}, 28 | ).then(BigNumber.from); 29 | } 30 | 31 | calcSeniorProfits( 32 | entryPrice: BigNumber, 33 | currentPrice: BigNumber, 34 | downsideProtectionRate: BigNumber, 35 | totalSeniors: BigNumber, 36 | ): Promise { 37 | return this.call( 38 | 'calcSeniorProfits', 39 | [entryPrice, currentPrice, downsideProtectionRate, totalSeniors, BigNumber.ZERO], 40 | {}, 41 | ).then(BigNumber.from); 42 | } 43 | } 44 | 45 | export default AccountingModelContract; 46 | -------------------------------------------------------------------------------- /src/wallets/components/notifications/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Grid from 'components/custom/grid'; 4 | import { Notification } from 'components/custom/notification'; 5 | import { Text } from 'components/custom/typography'; 6 | import { useGeneral } from 'components/providers/generalProvider'; 7 | import { useNotifications } from 'components/providers/notificationsProvider'; 8 | import { ReactComponent as ZeroNotificationsDarkSvg } from 'resources/svg/zero-notifications-dark.svg'; 9 | import { ReactComponent as ZeroNotificationsSvg } from 'resources/svg/zero-notifications.svg'; 10 | 11 | import s from './s.module.scss'; 12 | 13 | const Notifications: React.FC = () => { 14 | const { theme } = useGeneral(); 15 | const { notifications } = useNotifications(); 16 | 17 | if (!notifications.length) { 18 | return ( 19 | 20 | {theme === 'dark' ? ( 21 | 22 | ) : ( 23 | 24 | )} 25 | 26 | There are no notifications to show 27 | 28 | 29 | ); 30 | } 31 | 32 | return ( 33 |
    34 | {notifications 35 | .sort((a, b) => b.startsOn - a.startsOn) 36 | .map(n => ( 37 |
  • 38 | 39 |
  • 40 | ))} 41 |
42 | ); 43 | }; 44 | 45 | export default Notifications; 46 | -------------------------------------------------------------------------------- /src/components/custom/grid/s.module.scss: -------------------------------------------------------------------------------- 1 | $gaps: 4, 8, 12, 16, 24, 32, 48, 64; 2 | $align-items: start, center, end; 3 | $justify-contents: start, center, end, space-between; 4 | 5 | .grid { 6 | display: grid; 7 | 8 | &.row { 9 | grid-auto-flow: row; 10 | grid-template-rows: repeat(auto-fit, minmax(0, max-content)); 11 | 12 | @each $align in $align-items { 13 | &.align-#{$align} { 14 | justify-items: #{$align}; 15 | } 16 | 17 | &.align-self-#{$align} { 18 | justify-self: #{$align}; 19 | } 20 | } 21 | 22 | @each $justify in $justify-contents { 23 | &.justify-#{$justify} { 24 | align-content: #{$justify}; 25 | } 26 | 27 | &.justify-self-#{$justify} { 28 | align-self: #{$justify}; 29 | } 30 | } 31 | } 32 | 33 | &.col { 34 | grid-auto-flow: column; 35 | grid-template-columns: repeat(auto-fit, minmax(0, max-content)); 36 | 37 | @each $align in $align-items { 38 | &.align-#{$align} { 39 | align-items: #{$align}; 40 | } 41 | 42 | &.align-self-#{$align} { 43 | align-self: #{$align}; 44 | } 45 | } 46 | 47 | @each $justify in $justify-contents { 48 | &.justify-#{$justify} { 49 | justify-content: #{$justify}; 50 | } 51 | 52 | &.justify-self-#{$justify} { 53 | justify-self: #{$justify}; 54 | } 55 | } 56 | } 57 | 58 | @each $gap in $gaps { 59 | &.row-gap-#{$gap} { 60 | grid-row-gap: #{$gap}px; 61 | } 62 | 63 | &.col-gap-#{$gap} { 64 | grid-column-gap: #{$gap}px; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/stories/assets/repo.svg: -------------------------------------------------------------------------------- 1 | illustration/repo -------------------------------------------------------------------------------- /public/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 9 | 20 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/wallets/components/install-metamask-modal/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Button from 'components/antd/button'; 4 | import Modal, { ModalProps } from 'components/antd/modal'; 5 | import Grid from 'components/custom/grid'; 6 | import { Text } from 'components/custom/typography'; 7 | 8 | const METAMASK_CHROME_EXT_URL = 'https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn'; 9 | 10 | const InstallMetaMaskModal: React.FC = props => { 11 | const { ...modalProps } = props; 12 | 13 | return ( 14 | 15 | 16 | 17 | 18 | Install MetaMask 19 | 20 | 21 | You need to have{' '} 22 | 23 | MetaMask 24 | {' '} 25 | installed to continue. 26 |
27 | Once you have installed it, please refresh the page 28 |
29 |
30 | 31 | 34 | 35 | 38 | 39 |
40 |
41 | ); 42 | }; 43 | 44 | export default InstallMetaMaskModal; 45 | -------------------------------------------------------------------------------- /src/styles/flex.scss: -------------------------------------------------------------------------------- 1 | $gaps: 4, 8, 12, 16, 24, 32, 40, 44, 48, 64; 2 | 3 | .flex { 4 | display: flex; 5 | flex-wrap: nowrap; 6 | 7 | &.inline { 8 | display: inline-flex; 9 | } 10 | 11 | &.flow-row { 12 | flex-direction: column; 13 | } 14 | 15 | &.flow-col { 16 | flex-direction: row; 17 | } 18 | 19 | &.wrap { 20 | flex-wrap: wrap; 21 | } 22 | 23 | &.flex-grow, 24 | > .flex-grow { 25 | flex-grow: 1; 26 | } 27 | 28 | > .flex-shrink { 29 | flex-shrink: 1; 30 | } 31 | 32 | &.align-start { 33 | align-items: flex-start; 34 | } 35 | 36 | &.align-center { 37 | align-items: center; 38 | } 39 | 40 | &.align-end { 41 | align-items: flex-end; 42 | } 43 | 44 | > .align-self-start { 45 | align-self: flex-start; 46 | } 47 | 48 | > .align-self-center { 49 | align-self: center; 50 | } 51 | 52 | > .align-self-end { 53 | align-self: flex-end; 54 | } 55 | 56 | &.justify-start { 57 | justify-content: flex-start; 58 | } 59 | 60 | &.justify-center { 61 | justify-content: center; 62 | } 63 | 64 | &.justify-space-between { 65 | justify-content: space-between; 66 | } 67 | 68 | &.justify-end { 69 | justify-content: flex-end; 70 | } 71 | 72 | > .justify-self-start { 73 | justify-self: flex-start; 74 | } 75 | 76 | > .justify-self-center { 77 | justify-self: center; 78 | } 79 | 80 | > .justify-self-end { 81 | justify-self: end; 82 | } 83 | 84 | @each $gap in $gaps { 85 | &.row-gap-#{$gap} { 86 | row-gap: #{$gap}px; 87 | } 88 | 89 | &.col-gap-#{$gap} { 90 | column-gap: #{$gap}px; 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/wallets/components/unsupported-chain-modal/index.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | 3 | import Button from 'components/antd/button'; 4 | import Modal, { ModalProps } from 'components/antd/modal'; 5 | import Grid from 'components/custom/grid'; 6 | import { Text } from 'components/custom/typography'; 7 | import { useNetwork } from 'components/providers/networkProvider'; 8 | import { useWallet } from 'wallets/walletProvider'; 9 | 10 | export type UnsupportedChainModalProps = ModalProps; 11 | 12 | const UnsupportedChainModal: FC = props => { 13 | const { ...modalProps } = props; 14 | 15 | const { activeNetwork } = useNetwork(); 16 | const wallet = useWallet(); 17 | 18 | return ( 19 | 20 | 21 | 22 | 23 | Wrong network 24 | 25 | 26 | Please switch your wallet network to {activeNetwork.meta.name} to use the app 27 | 28 | 29 | If you still encounter problems, you may want to switch to a different wallet 30 | 31 | 32 | 33 | 41 | 42 | 43 | ); 44 | }; 45 | 46 | export default UnsupportedChainModal; 47 | -------------------------------------------------------------------------------- /src/modules/smart-yield/contracts/syJuniorBondContract.ts: -------------------------------------------------------------------------------- 1 | import Web3Contract from 'web3/web3Contract'; 2 | 3 | const ABI: any[] = [ 4 | { 5 | name: 'balanceOf', 6 | type: 'function', 7 | inputs: [ 8 | { 9 | name: 'owner', 10 | type: 'address', 11 | }, 12 | ], 13 | outputs: [ 14 | { 15 | name: '', 16 | type: 'uint256', 17 | }, 18 | ], 19 | }, 20 | { 21 | name: 'tokenOfOwnerByIndex', 22 | type: 'function', 23 | inputs: [ 24 | { 25 | name: 'owner', 26 | type: 'address', 27 | }, 28 | { 29 | name: 'index', 30 | type: 'uint256', 31 | }, 32 | ], 33 | outputs: [ 34 | { 35 | name: '', 36 | type: 'uint256', 37 | }, 38 | ], 39 | }, 40 | ]; 41 | 42 | class SYJuniorBondContract extends Web3Contract { 43 | constructor(address: string) { 44 | super(ABI, address, ''); 45 | } 46 | 47 | async getJuniorBondIds(): Promise { 48 | if (!this.account) { 49 | return Promise.reject(); 50 | } 51 | 52 | return this.call('balanceOf', [this.account]) 53 | .then(value => Number(value)) 54 | .then(balance => { 55 | if (balance > 0) { 56 | const methods = Array.from(Array(balance)).map((_, index) => ({ 57 | method: 'tokenOfOwnerByIndex', 58 | methodArgs: [this.account, index], 59 | transform: (value: any) => Number(value), 60 | })); 61 | 62 | return this.batch(methods); 63 | } 64 | 65 | return []; 66 | }); 67 | } 68 | } 69 | 70 | export default SYJuniorBondContract; 71 | -------------------------------------------------------------------------------- /src/modules/smart-alpha/views/kpi-options/index.tsx: -------------------------------------------------------------------------------- 1 | import AntdSpin from 'antd/lib/spin'; 2 | 3 | import { Alert } from 'components/alert'; 4 | import { useFetchKpiOptions } from 'modules/smart-alpha/api'; 5 | import { KpiOptionCard } from 'modules/smart-alpha/views/kpi-options/kpi-option-card'; 6 | 7 | import s from './s.module.scss'; 8 | 9 | const KpiOptionsView: React.FC = () => { 10 | const { loading, data: kpiOptions } = useFetchKpiOptions(); 11 | 12 | return ( 13 | 14 |
15 | 16 | Check out our ongoing Key Performance Indicator option program by depositing into either the WETH (USD) or 17 | WBTC (USD) Balancer pools linked below: 18 | 32 | 33 |
34 | {kpiOptions?.map(kpiOption => ( 35 | 36 | ))} 37 |
38 |
39 |
40 | ); 41 | }; 42 | 43 | export default KpiOptionsView; 44 | -------------------------------------------------------------------------------- /src/layout/components/layout-footer/s.module.scss: -------------------------------------------------------------------------------- 1 | .footer { 2 | background: var(--theme-body-color); 3 | grid-area: footer; 4 | padding: 16px var(--horizontal-padding) 0; 5 | margin: auto 0 0; 6 | } 7 | 8 | .footerTop { 9 | border-top: 1px solid var(--theme-border-color); 10 | padding: 64px 0; 11 | display: grid; 12 | grid-template-columns: max-content 1fr; 13 | gap: 40px 140px; 14 | 15 | @media (max-width: 768px) { 16 | grid-template-columns: 1fr; 17 | } 18 | } 19 | 20 | .footerTopLeft { 21 | max-width: 200px; 22 | } 23 | 24 | .logo { 25 | display: flex; 26 | align-items: center; 27 | margin-bottom: 24px; 28 | } 29 | 30 | .socialLinks { 31 | display: flex; 32 | gap: 24px; 33 | } 34 | 35 | .footerTopRight { 36 | display: flex; 37 | flex-wrap: wrap; 38 | gap: 40px 80px; 39 | } 40 | 41 | .navSection { 42 | ul { 43 | list-style-type: none; 44 | display: grid; 45 | gap: 16px; 46 | } 47 | 48 | a { 49 | font: var(--font-p2-semibold) !important; 50 | } 51 | } 52 | 53 | .footerBottom { 54 | border-top: 1px solid var(--theme-border-color); 55 | padding: 24px 0; 56 | display: grid; 57 | gap: 40px 16px; 58 | grid-template-columns: max-content 1fr; 59 | 60 | @media (max-width: 768px) { 61 | grid-template-columns: 1fr; 62 | 63 | .copyright { 64 | order: 2; 65 | } 66 | 67 | .footerBottomLinks { 68 | order: 1; 69 | } 70 | } 71 | } 72 | 73 | .copyright { 74 | color: var(--theme-icon-color); 75 | } 76 | 77 | .footerBottomLinks { 78 | margin-left: auto; 79 | display: flex; 80 | flex-wrap: wrap; 81 | gap: 16px 24px; 82 | 83 | a { 84 | font: var(--font-sm-semibold) !important; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/components/custom/error-boundary/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AntdNotification from 'antd/lib/notification'; 3 | import AntdResult from 'antd/lib/result'; 4 | 5 | import { Text } from 'components/custom/typography'; 6 | 7 | type State = { 8 | error?: Error; 9 | }; 10 | 11 | export default class ErrorBoundary extends React.Component { 12 | constructor(props: any) { 13 | super(props); 14 | 15 | this.state = { 16 | error: undefined, 17 | }; 18 | } 19 | 20 | componentDidCatch(error: Error) { 21 | this.setState({ 22 | error, 23 | }); 24 | 25 | AntdNotification.error({ 26 | message: error.message, 27 | }); 28 | } 29 | 30 | handleRefresh = () => { 31 | window.location.href = `${window.location.href}`; 32 | }; 33 | 34 | render() { 35 | if (this.state.error) { 36 | return ( 37 | 42 | 500 43 | 44 | } 45 | subTitle={ 46 | 47 | Sorry, something went wrong. 48 | 49 | } 50 | extra={ 51 | 58 | } 59 | /> 60 | ); 61 | } 62 | 63 | return this.props.children; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/stories/TokenIcon.stories.tsx: -------------------------------------------------------------------------------- 1 | import { ComponentMeta, ComponentStory } from '@storybook/react'; 2 | 3 | import { TokenIcon as TokenIconComponent, TokenIconNames } from 'components/token-icon'; 4 | import { ReactComponent as TokenIconsStaticSprite } from 'resources/svg/token-icons-sprite-static.svg'; 5 | 6 | export default { 7 | title: 'Components/TokenIcons', 8 | component: TokenIconComponent, 9 | } as ComponentMeta; 10 | 11 | const TokenIconTemplate: ComponentStory = args => ( 12 | <> 13 | 14 | 15 | ); 16 | export const TokenIcon = TokenIconTemplate.bind({}); 17 | TokenIcon.args = { 18 | name: 'usdc', 19 | size: 40, 20 | }; 21 | 22 | const tokenNames: TokenIconNames[] = [ 23 | 'aave', 24 | 'stkaave', 25 | 'compound', 26 | 'cream', 27 | 'yearn', 28 | 'polygon', 29 | 'bond', 30 | 'usdc', 31 | 'dai', 32 | 'susd', 33 | 'eth', 34 | 'wbtc', 35 | 'gusd', 36 | 'usdt', 37 | 'sy-pools', 38 | 'unknown', 39 | 'weth', 40 | 'fiat', 41 | 'usd', 42 | ]; 43 | 44 | export const TokenIcons = () => ( 45 | <> 46 | 47 |
52 | {tokenNames.map((name, idx) => ( 53 |
60 |
{name}
61 |
62 | 63 |
64 |
65 | ))} 66 |
67 | 68 | ); 69 | -------------------------------------------------------------------------------- /src/utils/chart.ts: -------------------------------------------------------------------------------- 1 | import { PeriodTabsKey } from 'components/custom/tabs'; 2 | 3 | import { formatDate, formatTime } from './date'; 4 | 5 | export function formatTick(value: string, filter: string) { 6 | if (typeof value !== 'string') { 7 | return ''; 8 | } 9 | 10 | const date = new Date(value); 11 | 12 | if (date.toString() === 'Invalid Date') { 13 | return ''; 14 | } 15 | 16 | switch (filter) { 17 | case PeriodTabsKey.day: 18 | return formatTime(date); 19 | case PeriodTabsKey.week: 20 | return formatDate(date, { weekday: 'short' }); 21 | case PeriodTabsKey.month: 22 | return formatDate(date, { month: '2-digit', day: '2-digit' }); 23 | default: 24 | return ''; 25 | } 26 | } 27 | 28 | export function generateTicks(items: { point: string }[], filter: string): number[] { 29 | const dates = items.map(d => new Date(d.point).getTime()); 30 | const minDate = Math.min(...dates); 31 | const maxDate = Math.max(...dates); 32 | 33 | if (!Number.isFinite(minDate) || !Number.isFinite(maxDate)) { 34 | return []; 35 | } 36 | 37 | let count = 0; 38 | let range = 0; 39 | 40 | switch (filter) { 41 | case '24h': 42 | count = 3; 43 | range = 8 * 60 * 60 * 1_000; // 8 hours 44 | break; 45 | case '1w': 46 | count = 7; 47 | range = 24 * 60 * 60 * 1_000; // 24 hours 48 | break; 49 | case '30d': 50 | count = 4; 51 | range = 7 * 24 * 60 * 60 * 1_000; // 7 days 52 | break; 53 | default: 54 | return []; 55 | } 56 | 57 | const minDt = maxDate - count * range; 58 | 59 | return Array.from({ length: count + 1 }).map((_, index) => minDt + range * index); 60 | // .map(tick => new Date(tick).toJSON()); 61 | } 62 | -------------------------------------------------------------------------------- /src/components/modal/s.module.scss: -------------------------------------------------------------------------------- 1 | .dialog { 2 | position: fixed; 3 | top: 0; 4 | right: 0; 5 | bottom: 0; 6 | left: 0; 7 | background: rgba(0, 0, 0, 0.6); 8 | z-index: 10; 9 | } 10 | 11 | .inner { 12 | position: absolute; 13 | top: 50%; 14 | left: 50%; 15 | transform: translate(-50%, -50%); 16 | width: 100%; 17 | max-width: 578px; 18 | background-color: var(--theme-card-color); 19 | border-radius: 4px; 20 | padding: 32px; 21 | overflow-y: auto; 22 | max-height: 100vh; 23 | 24 | &.fullscreen { 25 | max-width: none; 26 | height: 100%; 27 | border-radius: 0; 28 | padding: 64px; 29 | } 30 | 31 | @media (max-width: 768px) { 32 | top: auto; 33 | bottom: 16px; 34 | left: 16px; 35 | right: 16px; 36 | transform: none; 37 | max-width: initial; 38 | width: auto; 39 | } 40 | } 41 | 42 | .closeButton { 43 | background-color: transparent; 44 | border: 0; 45 | padding: 0; 46 | display: flex; 47 | flex-shrink: 0; 48 | color: var(--theme-icon-color); 49 | 50 | &:hover { 51 | color: var(--theme-icon-hover-color); 52 | } 53 | 54 | &.fullscreen { 55 | background-color: rgba(var(--theme-border-color-rgb), 0.4); 56 | padding: 8px; 57 | border-radius: 50%; 58 | } 59 | } 60 | 61 | .header { 62 | // padding: 32px; 63 | // border-bottom: 1px solid #ccc; 64 | max-width: 1240px; 65 | margin-left: auto; 66 | margin-right: auto; 67 | margin-bottom: 32px; 68 | display: flex; 69 | align-items: center; 70 | } 71 | 72 | .heading { 73 | flex-grow: 1; 74 | display: flex; 75 | align-items: center; 76 | } 77 | 78 | .content { 79 | &.fullscreen { 80 | max-width: 1240px; 81 | margin-left: auto; 82 | margin-right: auto; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/components/antd/tabs/s.module.scss: -------------------------------------------------------------------------------- 1 | .tabs { 2 | > :global(.ant-tabs-nav) { 3 | margin: 0; 4 | padding: 0 24px; 5 | background: var(--theme-card-color); 6 | border: 1px solid var(--theme-border-color); 7 | 8 | &::before { 9 | border: 0; 10 | } 11 | 12 | > :global(.ant-tabs-nav-wrap) { 13 | > :global(.ant-tabs-nav-list) { 14 | > :global(.ant-tabs-tab) { 15 | > :global(.ant-tabs-tab-btn) { 16 | font: var(--font-p1); 17 | font-weight: 600; 18 | display: flex; 19 | flex-direction: row; 20 | column-gap: 8px; 21 | align-items: center; 22 | color: var(--theme-secondary-color); 23 | 24 | &:hover { 25 | color: var(--theme-primary-color); 26 | 27 | svg { 28 | color: var(--theme-red-color); 29 | } 30 | } 31 | 32 | svg { 33 | :global(.no-flexbox-gap) & { 34 | margin-right: 8px; 35 | } 36 | } 37 | } 38 | 39 | &:global(.ant-tabs-tab-active) { 40 | > :global(.ant-tabs-tab-btn) { 41 | color: var(--theme-primary-color); 42 | 43 | > svg { 44 | color: var(--theme-red-color); 45 | } 46 | } 47 | } 48 | } 49 | 50 | > :global(.ant-tabs-ink-bar) { 51 | height: 2px; 52 | background: var(--theme-red-color); 53 | border-radius: 4px 4px 0 0; 54 | } 55 | } 56 | } 57 | } 58 | 59 | &.simple { 60 | > :global(.ant-tabs-nav) { 61 | padding: 0; 62 | background: transparent; 63 | border: 0; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/components/warning/index.tsx: -------------------------------------------------------------------------------- 1 | import { useLocalStorage } from 'react-use-storage'; 2 | import classNames from 'classnames'; 3 | 4 | import { Button } from 'components/button'; 5 | import Grid from 'components/custom/grid'; 6 | import { Text } from 'components/custom/typography'; 7 | import { Icon } from 'components/icon'; 8 | import { WarnType, useNotifications } from 'components/providers/notificationsProvider'; 9 | 10 | import s from './s.module.scss'; 11 | 12 | type WarnProps = WarnType & { 13 | onClose?: () => void; 14 | }; 15 | 16 | const Warn: React.FC = props => { 17 | const { storageIdentity, text, closable, onClose } = props; 18 | 19 | const [storageState, setStorageState] = useLocalStorage(storageIdentity ?? ''); 20 | 21 | function handleClose() { 22 | onClose?.(); 23 | 24 | if (storageIdentity) { 25 | setStorageState(false); 26 | } 27 | } 28 | 29 | if (storageIdentity && storageState === false) { 30 | return null; 31 | } 32 | 33 | return ( 34 |
35 | 36 | 37 | 38 | {text} 39 | 40 | 41 | {closable &&
43 | ); 44 | }; 45 | 46 | export const Warnings = () => { 47 | const { warns, removeWarn } = useNotifications(); 48 | return ( 49 |
50 | {warns.map((warn, idx) => ( 51 | removeWarn(warn)} /> 52 | ))} 53 |
54 | ); 55 | }; 56 | -------------------------------------------------------------------------------- /src/wallets/connectors/ledger/index.ts: -------------------------------------------------------------------------------- 1 | import { AbstractConnector } from '@web3-react/abstract-connector'; 2 | import { LedgerConnector } from '@web3-react/ledger-connector'; 3 | 4 | import LedgerLogoDark from 'resources/svg/wallets/ledger-logo-dark.svg'; 5 | import LedgerLogo from 'resources/svg/wallets/ledger-logo.svg'; 6 | 7 | import { Web3Network } from 'networks/types'; 8 | import { BaseWalletConfig } from 'wallets/types'; 9 | 10 | const LEDGER_BASE_DERIVATION_PATH = 'base_derivation_path'; 11 | 12 | export type LedgerWalletArgs = { 13 | baseDerivationPath?: string; 14 | }; 15 | 16 | const LedgerWalletConfig: BaseWalletConfig = { 17 | id: 'ledger', 18 | logo: [LedgerLogo, LedgerLogoDark], 19 | name: 'Ledger', 20 | factory(network: Web3Network, args?: LedgerWalletArgs): AbstractConnector { 21 | let baseDerivationPath: string | undefined = args?.baseDerivationPath; 22 | 23 | if (!baseDerivationPath) { 24 | baseDerivationPath = sessionStorage.getItem(LEDGER_BASE_DERIVATION_PATH) ?? undefined; 25 | } 26 | 27 | return new LedgerConnector({ 28 | chainId: network.meta.chainId, 29 | url: network.rpc.httpsUrl, 30 | pollingInterval: network.rpc.poolingInterval, 31 | baseDerivationPath, 32 | }); 33 | }, 34 | onConnect(connector: AbstractConnector, args?: LedgerWalletArgs): void { 35 | const { sessionStorage } = window; 36 | 37 | if (args?.baseDerivationPath) { 38 | sessionStorage.setItem(LEDGER_BASE_DERIVATION_PATH, args?.baseDerivationPath ?? ''); 39 | } 40 | }, 41 | onDisconnect(): void { 42 | const { sessionStorage } = window; 43 | sessionStorage.removeItem(LEDGER_BASE_DERIVATION_PATH); 44 | }, 45 | onError(error: Error): Error | undefined { 46 | return error; 47 | }, 48 | }; 49 | 50 | export default LedgerWalletConfig; 51 | -------------------------------------------------------------------------------- /src/components/custom/typography/s.module.scss: -------------------------------------------------------------------------------- 1 | .text { 2 | &.weight-semibold.weight-semibold { 3 | font-weight: 600; 4 | } 5 | 6 | &.weight-bold.weight-bold { 7 | font-weight: bold; 8 | } 9 | 10 | &.h1 { 11 | font: var(--font-h1); 12 | } 13 | 14 | &.h2 { 15 | font: var(--font-h2); 16 | } 17 | 18 | &.h3 { 19 | font: var(--font-h3); 20 | } 21 | 22 | &.p1 { 23 | font: var(--font-p1); 24 | } 25 | 26 | &.p2 { 27 | font: var(--font-p2); 28 | } 29 | 30 | &.lb1 { 31 | font: var(--font-lb1); 32 | } 33 | 34 | &.lb2 { 35 | font: var(--font-lb2); 36 | } 37 | 38 | &.small { 39 | font: var(--font-sm); 40 | } 41 | 42 | &.primary-color { 43 | color: var(--theme-primary-color); 44 | } 45 | 46 | &.secondary-color { 47 | color: var(--theme-secondary-color); 48 | } 49 | 50 | &.red-color { 51 | color: var(--theme-red-color); 52 | } 53 | 54 | &.green-color { 55 | color: var(--theme-green-color); 56 | } 57 | 58 | &.blue-color { 59 | color: var(--theme-blue-color); 60 | } 61 | 62 | &.purple-color { 63 | color: var(--theme-purple-color); 64 | } 65 | 66 | &.yellow-color { 67 | color: var(--theme-yellow-color); 68 | } 69 | 70 | &.hasTooltip { 71 | text-decoration: underline dotted var(--theme-border-color); 72 | text-underline-offset: 4px; 73 | } 74 | } 75 | 76 | .hint.hint { 77 | display: flex; 78 | flex-direction: row; 79 | align-items: center; 80 | column-gap: 8px; 81 | 82 | .tooltip { 83 | align-self: start; 84 | line-height: initial; 85 | cursor: pointer; 86 | } 87 | 88 | .icon { 89 | &:hover { 90 | color: var(--theme-grey900-color); 91 | 92 | [data-theme='dark'] & { 93 | color: var(--theme-white-color); 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/styles/colors.scss: -------------------------------------------------------------------------------- 1 | $color-red900: #4d1411; 2 | $color-red800: #661b17; 3 | $color-red700: #992822; 4 | $color-red600: #cc362e; // 5 | $color-red500: #ff4339; // 6 | $color-red400: #ff6961; // 7 | $color-red300: #ff8e88; // 8 | $color-red200: #ffb4b0; 9 | $color-red100: #ffd9d7; 10 | $color-red50: #fff6f5; 11 | 12 | $color-grey900: #060a0d; // 13 | $color-grey800: #2d3033; 14 | $color-grey700: #44494d; 15 | $color-grey600: #5a6166; 16 | $color-grey500: #717980; // 17 | $color-grey400: #8d9499; 18 | $color-grey300: #aaafb3; // 19 | $color-grey200: #c6c9cc; // 20 | $color-grey100: #e3e4e6; // 21 | $color-grey50: #f8f8f9; // 22 | 23 | $color-yellow900: #4d3f1d; 24 | $color-yellow800: #665426; // 25 | $color-yellow700: #997d3a; // 26 | $color-yellow600: #cca74d; 27 | $color-yellow500: #ffd160; // 28 | $color-yellow400: #ffda80; 29 | $color-yellow300: #ffe3a0; 30 | $color-yellow200: #ffedbf; 31 | $color-yellow100: #fff6df; 32 | $color-yellow50: #fffdf7; 33 | 34 | $color-purple900: #312144; 35 | $color-purple800: #412c5b; 36 | $color-purple700: #614288; 37 | $color-purple600: #8258b6; 38 | $color-purple500: #a26ee3; 39 | $color-purple400: #b58be9; 40 | $color-purple300: #c7a8ee; 41 | $color-purple200: #dac5f4; 42 | $color-purple100: #ece2f9; 43 | $color-purple50: #faf8fe; 44 | 45 | $color-blue900: #182045; 46 | $color-blue800: #202a5c; 47 | $color-blue700: #2f4089; 48 | $color-blue600: #3f55b7; // 49 | $color-blue500: #4f6ae5; // 50 | $color-blue400: #7288ea; // 51 | $color-blue300: #95a6ef; // 52 | $color-blue200: #b9c3f5; 53 | $color-blue100: #dce1fa; 54 | $color-blue50: #f6f8fe; 55 | 56 | $color-green900: #003f2d; 57 | $color-green800: #00543c; 58 | $color-green700: #007f59; 59 | $color-green600: #00a977; 60 | $color-green500: #00d395; // 61 | $color-green400: #33dcaa; 62 | $color-green300: #66e5bf; 63 | $color-green200: #99edd5; 64 | $color-green100: #ccf6ea; 65 | $color-green50: #f2fdfa; 66 | -------------------------------------------------------------------------------- /src/wallets/components/ledger-deriviation-path-modal/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Button from 'components/antd/button'; 4 | import Modal, { ModalProps } from 'components/antd/modal'; 5 | import Select, { SelectOption } from 'components/antd/select'; 6 | import Grid from 'components/custom/grid'; 7 | import LedgerWalletConfig from 'wallets/connectors/ledger'; 8 | import { useWallet } from 'wallets/walletProvider'; 9 | 10 | const WEB3_LEDGER_DERIVATION_PATHS: SelectOption[] = [ 11 | { 12 | value: `m/44'/60'/0'`, 13 | label: `Ethereum - m/44'/60'/0'`, 14 | }, 15 | { 16 | value: `m/44'/60'/0'/0`, 17 | label: `Ethereum - Ledger Live - m/44'/60'/0'/0`, 18 | }, 19 | ]; 20 | 21 | const LedgerDerivationPathModal: React.FC = props => { 22 | const { ...modalProps } = props; 23 | 24 | const wallet = useWallet(); 25 | 26 | const [derivationPath, setDerivationPath] = React.useState(String(WEB3_LEDGER_DERIVATION_PATHS[0].value)); 27 | 28 | function handleSelect(value: any) { 29 | setDerivationPath(value as string); 30 | } 31 | 32 | function handleConnect() { 33 | modalProps.onCancel?.(); 34 | 35 | setTimeout(() => { 36 | wallet 37 | .connect(LedgerWalletConfig, { 38 | baseDerivationPath: derivationPath, 39 | }) 40 | .catch(Error); 41 | }); 42 | } 43 | 44 | return ( 45 | 46 | 47 |