├── src ├── react-app-env.d.ts ├── assets │ ├── images │ │ ├── torus.png │ │ ├── metamask.png │ │ ├── portisIcon.png │ │ ├── trustWallet.png │ │ ├── fortmaticIcon.png │ │ ├── arrow-right-white.png │ │ ├── dropdown.svg │ │ ├── dropup-blue.svg │ │ ├── dropdown-blue.svg │ │ ├── plus-blue.svg │ │ ├── plus-grey.svg │ │ ├── arrow-right.svg │ │ ├── x.svg │ │ ├── circle.svg │ │ ├── circle-grey.svg │ │ ├── arrow-down-blue.svg │ │ ├── arrow-down-grey.svg │ │ ├── spinner.svg │ │ ├── question.svg │ │ ├── question-mark.svg │ │ └── ethereum-logo.svg │ └── svg │ │ └── SVGArrowDown.js ├── pages │ ├── Swap │ │ └── index.js │ ├── NFT │ │ └── index.js │ ├── Send │ │ └── index.js │ ├── Bridge │ │ ├── web3.js │ │ └── index.js │ ├── Pool │ │ ├── index.js │ │ ├── ModeSelector.js │ │ └── CreateExchange.js │ └── App.js ├── connectors │ ├── Network.js │ ├── Fortmatic.js │ └── index.js ├── i18n.js ├── components │ ├── WalletModal │ │ ├── WalletConnectData.js │ │ ├── Option.js │ │ └── PendingView.js │ ├── Identicon │ │ └── index.js │ ├── OversizedPanel │ │ └── index.js │ ├── AccountDetails │ │ ├── Copy.js │ │ └── Transaction.js │ ├── Header │ │ └── index.js │ ├── TokenLogo │ │ └── index.js │ ├── Footer │ │ └── index.js │ ├── Web3ReactManager │ │ └── index.js │ ├── ContextualInfo │ │ └── index.js │ ├── ContextualInfoNew │ │ └── index.js │ ├── WarningCard │ │ └── index.js │ ├── AddressInputPanel │ │ └── index.js │ └── Modal │ │ └── index.js ├── utils │ ├── signer.js │ ├── price.js │ └── math.js ├── constants │ ├── abis │ │ ├── factory.json │ │ ├── erc20.json │ │ └── erc20_bytes32.json │ └── index.js ├── theme │ ├── components.js │ └── index.js ├── index.js └── contexts │ ├── Allowances.js │ ├── Application.js │ ├── LocalStorage.js │ ├── Tokens.js │ └── Transactions.js ├── .prettierrc ├── public ├── favicon.ico ├── 451.html ├── manifest.json ├── index.html └── locales │ ├── zh-CN.json │ ├── zh-TW.json │ ├── ru.json │ ├── es-AR.json │ ├── es-US.json │ ├── ro.json │ ├── vi.json │ ├── it-IT.json │ ├── de.json │ └── en.json ├── Compound Contracts ├── development │ ├── Compound │ │ ├── FakeComptroller.sol │ │ ├── FakeCDaiInterestRateModel.sol │ │ ├── FakeCDai.sol │ │ └── src │ │ │ ├── PriceOracle.sol │ │ │ ├── ReentrancyGuard.sol │ │ │ ├── ComptrollerStorage.sol │ │ │ ├── InterestRateModel.sol │ │ │ ├── CarefulMath.sol │ │ │ ├── EIP20Interface.sol │ │ │ ├── ComptrollerInterface.sol │ │ │ └── EIP20NonStandardInterface.sol │ └── FakeDai.sol └── Migrations.sol ├── NFT-Exchange ├── migrations │ └── 1_initial_migration.js ├── contracts │ └── NFTMarketplace.sol └── truffle-config.js ├── .travis.yml ├── .env.local.example ├── Uniswap Contracts ├── Migrations.sol └── uniswap_factory.vy ├── .gitignore ├── netlify.toml ├── tsconfig.json ├── depositerc20.js ├── package.json └── Compound Contract Addresses.txt /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "printWidth": 120 5 | } 6 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viraj124/Smart-Swap-Protocol/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/images/torus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viraj124/Smart-Swap-Protocol/HEAD/src/assets/images/torus.png -------------------------------------------------------------------------------- /src/assets/images/metamask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viraj124/Smart-Swap-Protocol/HEAD/src/assets/images/metamask.png -------------------------------------------------------------------------------- /src/assets/images/portisIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viraj124/Smart-Swap-Protocol/HEAD/src/assets/images/portisIcon.png -------------------------------------------------------------------------------- /src/assets/images/trustWallet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viraj124/Smart-Swap-Protocol/HEAD/src/assets/images/trustWallet.png -------------------------------------------------------------------------------- /src/assets/images/fortmaticIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viraj124/Smart-Swap-Protocol/HEAD/src/assets/images/fortmaticIcon.png -------------------------------------------------------------------------------- /src/assets/images/arrow-right-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viraj124/Smart-Swap-Protocol/HEAD/src/assets/images/arrow-right-white.png -------------------------------------------------------------------------------- /Compound Contracts/development/Compound/FakeComptroller.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.6.5; 2 | 3 | import "./src/Comptroller.sol"; 4 | 5 | contract FakeComptroller is Comptroller {} 6 | -------------------------------------------------------------------------------- /NFT-Exchange/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require("Migrations"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /src/assets/images/dropdown.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/images/dropup-blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/images/dropdown-blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/images/plus-blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/images/plus-grey.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | branches: 2 | except: 3 | - master 4 | language: node_js 5 | node_js: 6 | - '10' 7 | cache: 8 | directories: 9 | - node_modules 10 | install: yarn 11 | script: 12 | - yarn check:all 13 | - yarn build 14 | -------------------------------------------------------------------------------- /public/451.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Unavailable For Legal Reasons 6 | 7 | 8 |

Unavailable For Legal Reasons

9 | 10 | 11 | -------------------------------------------------------------------------------- /src/pages/Swap/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ExchangePage from '../../components/ExchangePage' 3 | 4 | export default function Swap({ initialCurrency, params }) { 5 | return 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/NFT/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import NFTExchangePage from '../../components/NFTExchangePage' 3 | 4 | export default function NFT({ initialCurrency, params }) { 5 | return 6 | } -------------------------------------------------------------------------------- /src/pages/Send/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ExchangePage from '../../components/ExchangePage' 3 | 4 | export default function Send({ initialCurrency, params }) { 5 | return 6 | } 7 | -------------------------------------------------------------------------------- /src/assets/images/arrow-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/images/x.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/images/circle-grey.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/images/arrow-down-blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/images/arrow-down-grey.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "SmartSwap", 3 | "name": "SmartSwap", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /Compound Contracts/development/Compound/FakeCDaiInterestRateModel.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.6.5; 2 | 3 | import "./src/WhitePaperInterestRateModel.sol"; 4 | 5 | contract FakeCDaiInterestRateModel is WhitePaperInterestRateModel { 6 | constructor() 7 | WhitePaperInterestRateModel( 8 | 0.05 * (10 ** 18), 9 | 0.12 * (10 ** 18) 10 | ) 11 | public 12 | {} 13 | } 14 | -------------------------------------------------------------------------------- /src/assets/svg/SVGArrowDown.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const SVGArrowDown = props => ( 4 | 5 | 9 | 10 | ) 11 | 12 | export default SVGArrowDown 13 | -------------------------------------------------------------------------------- /src/connectors/Network.js: -------------------------------------------------------------------------------- 1 | import { NetworkConnector as NetworkConnectorCore } from '@web3-react/network-connector' 2 | 3 | export class NetworkConnector extends NetworkConnectorCore { 4 | pause() { 5 | if (this.active) { 6 | this.providers[this.currentChainId].stop() 7 | } 8 | } 9 | 10 | resume() { 11 | if (this.active) { 12 | this.providers[this.currentChainId].start() 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.env.local.example: -------------------------------------------------------------------------------- 1 | { 2 | "network":"testnet", 3 | "version": "v2", 4 | 5 | "privateKey": "5D54A08F0B78E9190B17D4191A00B825E81C644F9C7B71A8F9B693BECEBD0C85", 6 | "from": "0x8727aff41b503b4c01d757391d3b3a9d38b67dcd", 7 | 8 | "Ropsten_Erc20Address": "0xcb79C63a66A7122D9EDc6C6f6a65d4eb0F01A141", 9 | "Matic_Erc20Address": "0xdb7D0379AB79E7C20c7286e749bD719d197A710f", 10 | 11 | "value": "1000" 12 | } 13 | 14 | -------------------------------------------------------------------------------- /Uniswap Contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.6.0; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | constructor() public { 8 | owner = msg.sender; 9 | } 10 | 11 | modifier restricted() { 12 | if (msg.sender == owner) _; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Compound Contracts/development/FakeDai.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.6.5; 2 | 3 | import "../../node_modules/openzeppelin-solidity/contracts/token/ERC20/ERC20.sol"; 4 | 5 | contract FakeDai is ERC20 { 6 | string public name = "Fake DAI"; 7 | string public symbol = "DAI"; 8 | uint public decimals = 18; 9 | uint public INITIAL_SUPPLY = 10000 * (10 ** decimals); 10 | 11 | constructor() public { 12 | _mint(msg.sender, INITIAL_SUPPLY); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | /.netlify 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | notes.txt 27 | .idea/ 28 | 29 | .vscode/ 30 | 31 | package-lock.json -------------------------------------------------------------------------------- /Compound Contracts/development/Compound/FakeCDai.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.6.5; 2 | 3 | import "./src/CErc20.sol"; 4 | 5 | contract FakeCDai is CErc20 { 6 | constructor( 7 | address _token, 8 | ComptrollerInterface _comptroller, 9 | InterestRateModel _interestRateModel 10 | ) 11 | CErc20( 12 | _token, 13 | _comptroller, 14 | _interestRateModel, 15 | 200000000 * (10 ** 18), 16 | "Fake Compound Dai", 17 | "cDAI", 18 | 8 19 | ) 20 | public 21 | {} 22 | } 23 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | # block some countries 2 | [[redirects]] 3 | from = "/*" 4 | to = "/451.html" 5 | status = 451 6 | force = true 7 | conditions = {Country=["BY","CU","IR","IQ","CI","LR","KP","SD","SY","ZW"]} 8 | headers = {Link=""} 9 | 10 | # support SPA setup 11 | [[redirects]] 12 | from = "/*" 13 | to = "/index.html" 14 | status = 200 15 | 16 | [build.environment] 17 | REACT_APP_IS_PRODUCTION_DEPLOY = "false" 18 | 19 | [context.production.environment] 20 | REACT_APP_IS_PRODUCTION_DEPLOY = "true" 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "noEmit": true, 20 | "jsx": "react" 21 | }, 22 | "include": [ 23 | "src" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /src/i18n.js: -------------------------------------------------------------------------------- 1 | import i18next from 'i18next' 2 | import { initReactI18next } from 'react-i18next' 3 | import XHR from 'i18next-xhr-backend' 4 | import LanguageDetector from 'i18next-browser-languagedetector' 5 | 6 | i18next 7 | .use(XHR) 8 | .use(LanguageDetector) 9 | .use(initReactI18next) 10 | .init({ 11 | backend: { 12 | loadPath: '/locales/{{lng}}.json' 13 | }, 14 | react: { 15 | useSuspense: true 16 | }, 17 | fallbackLng: 'en', 18 | preload: ['en'], 19 | keySeparator: false, 20 | interpolation: { escapeValue: false } 21 | }) 22 | 23 | export default i18next 24 | -------------------------------------------------------------------------------- /Compound Contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.6.5; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | constructor() public { 8 | owner = msg.sender; 9 | } 10 | 11 | modifier restricted() { 12 | if (msg.sender == owner) _; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Compound Contracts/development/Compound/src/PriceOracle.sol: -------------------------------------------------------------------------------- 1 | // File: contracts/PriceOracle.sol 2 | 3 | pragma solidity >=0.4.21 <0.6.5; 4 | 5 | import "./CToken.sol"; 6 | 7 | interface PriceOracle { 8 | /** 9 | * @notice Indicator that this is a PriceOracle contract (for inspection) 10 | */ 11 | function isPriceOracle() external pure returns (bool); 12 | 13 | /** 14 | * @notice Get the underlying price of a cToken asset 15 | * @param cToken The cToken to get the underlying price of 16 | * @return The underlying asset price mantissa (scaled by 1e18). 17 | * Zero means the price is unavailable. 18 | */ 19 | function getUnderlyingPrice(CToken cToken) external view returns (uint); 20 | } 21 | -------------------------------------------------------------------------------- /src/assets/images/spinner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/components/WalletModal/WalletConnectData.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import QRCode from 'qrcode.react' 4 | import { useDarkModeManager } from '../../contexts/LocalStorage' 5 | 6 | const QRCodeWrapper = styled.div` 7 | ${({ theme }) => theme.flexColumnNoWrap}; 8 | align-items: center; 9 | justify-content: center; 10 | border-radius: 12px; 11 | margin-bottom: 20px; 12 | ` 13 | 14 | export default function WalletConnectData({ uri = '', size }) { 15 | const [isDark] = useDarkModeManager() 16 | return ( 17 | 18 | {uri && ( 19 | 20 | )} 21 | 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /src/assets/images/question.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/components/Identicon/index.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react' 2 | 3 | import styled from 'styled-components' 4 | 5 | import { useWeb3React } from '../../hooks' 6 | import Jazzicon from 'jazzicon' 7 | 8 | const StyledIdenticon = styled.div` 9 | height: 1rem; 10 | width: 1rem; 11 | border-radius: 1.125rem; 12 | background-color: ${({ theme }) => theme.silverGray}; 13 | ` 14 | 15 | export default function Identicon() { 16 | const ref = useRef() 17 | 18 | const { account } = useWeb3React() 19 | 20 | useEffect(() => { 21 | if (account && ref.current) { 22 | ref.current.innerHTML = '' 23 | ref.current.appendChild(Jazzicon(16, parseInt(account.slice(2, 10), 16))) 24 | } 25 | }, [account]) 26 | 27 | return 28 | } 29 | -------------------------------------------------------------------------------- /src/assets/images/question-mark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/images/ethereum-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/pages/Bridge/web3.js: -------------------------------------------------------------------------------- 1 | const Web3 = require('web3'); 2 | 3 | const getWeb3 = () => new Promise((resolve) => { 4 | window.addEventListener('load', () => { 5 | let currentWeb3; 6 | 7 | if (window.ethereum) { 8 | currentWeb3 = new Web3(window.ethereum); 9 | try { 10 | // Request account access if needed 11 | window.ethereum.enable(); 12 | // Acccounts now exposed 13 | resolve(currentWeb3); 14 | } catch (error) { 15 | // User denied account access... 16 | alert('Please allow access for the app to work'); 17 | } 18 | } else if (window.web3) { 19 | window.web3 = new Web3(web3.currentProvider); 20 | // Acccounts always exposed 21 | resolve(currentWeb3); 22 | } else { 23 | console.log('Non-Ethereum browser detected. You should consider trying MetaMask!'); 24 | } 25 | }); 26 | }); 27 | 28 | module.exports = getWeb3; -------------------------------------------------------------------------------- /src/components/OversizedPanel/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | 4 | const Panel = styled.div` 5 | position: relative; 6 | background-color: ${({ theme }) => theme.concreteGray}; 7 | width: calc(100% - 1rem); 8 | margin: 0 auto; 9 | border-radius: 0.625rem; 10 | ` 11 | 12 | const PanelTop = styled.div` 13 | content: ''; 14 | position: absolute; 15 | top: -0.5rem; 16 | left: 0; 17 | height: 1rem; 18 | width: 100%; 19 | background-color: ${({ theme }) => theme.concreteGray}; 20 | ` 21 | 22 | const PanelBottom = styled.div` 23 | position: absolute; 24 | top: 80%; 25 | left: 0; 26 | height: 1rem; 27 | width: 100%; 28 | background-color: ${({ theme }) => theme.concreteGray}; 29 | ` 30 | 31 | export default function OversizedPanel({ hideTop, hideBottom, children }) { 32 | return ( 33 | 34 | {hideTop || } 35 | {children} 36 | {hideBottom || } 37 | 38 | ) 39 | } 40 | -------------------------------------------------------------------------------- /src/utils/signer.js: -------------------------------------------------------------------------------- 1 | import * as ethers from 'ethers' 2 | 3 | export default class UncheckedJsonRpcSigner extends ethers.Signer { 4 | constructor(signer) { 5 | super() 6 | ethers.utils.defineReadOnly(this, 'signer', signer) 7 | ethers.utils.defineReadOnly(this, 'provider', signer.provider) 8 | } 9 | 10 | getAddress() { 11 | return this.signer.getAddress() 12 | } 13 | 14 | sendTransaction(transaction) { 15 | return this.signer.sendUncheckedTransaction(transaction).then(hash => { 16 | return { 17 | hash: hash, 18 | nonce: null, 19 | gasLimit: null, 20 | gasPrice: null, 21 | data: null, 22 | value: null, 23 | chainId: null, 24 | confirmations: 0, 25 | from: null, 26 | wait: confirmations => { 27 | return this.signer.provider.waitForTransaction(hash, confirmations) 28 | } 29 | } 30 | }) 31 | } 32 | 33 | signMessage(message) { 34 | return this.signer.signMessage(message) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/connectors/Fortmatic.js: -------------------------------------------------------------------------------- 1 | import { FortmaticConnector as FortmaticConnectorCore } from '@web3-react/fortmatic-connector' 2 | 3 | export const OVERLAY_READY = 'OVERLAY_READY' 4 | 5 | const chainIdToNetwork = { 6 | 1: 'mainnet', 7 | 3: 'ropsten', 8 | 4: 'rinkeby', 9 | 42: 'kovan' 10 | } 11 | 12 | export class FortmaticConnector extends FortmaticConnectorCore { 13 | async activate() { 14 | if (!this.fortmatic) { 15 | const { default: Fortmatic } = await import('fortmatic') 16 | this.fortmatic = new Fortmatic( 17 | this.apiKey, 18 | this.chainId === 1 || this.chainId === 4 ? undefined : chainIdToNetwork[this.chainId] 19 | ) 20 | } 21 | 22 | const provider = this.fortmatic.getProvider() 23 | 24 | const pollForOverlayReady = new Promise(resolve => { 25 | const interval = setInterval(() => { 26 | if (provider.overlayReady) { 27 | clearInterval(interval) 28 | this.emit(OVERLAY_READY) 29 | resolve() 30 | } 31 | }, 200) 32 | }) 33 | 34 | const [account] = await Promise.all([provider.enable().then(accounts => accounts[0]), pollForOverlayReady]) 35 | 36 | return { provider: this.fortmatic.getProvider(), chainId: this.chainId, account } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/components/AccountDetails/Copy.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import { useCopyClipboard } from '../../hooks' 4 | 5 | import { Link } from '../../theme' 6 | import { CheckCircle, Copy } from 'react-feather' 7 | 8 | const CopyIcon = styled(Link)` 9 | color: ${({ theme }) => theme.silverGray}; 10 | flex-shrink: 0; 11 | margin-right: 1rem; 12 | margin-left: 0.5rem; 13 | text-decoration: none; 14 | :hover, 15 | :active, 16 | :focus { 17 | text-decoration: none; 18 | color: ${({ theme }) => theme.doveGray}; 19 | } 20 | ` 21 | const TransactionStatusText = styled.span` 22 | margin-left: 0.25rem; 23 | ${({ theme }) => theme.flexRowNoWrap}; 24 | align-items: center; 25 | ` 26 | 27 | export default function CopyHelper({ toCopy }) { 28 | const [isCopied, setCopied] = useCopyClipboard() 29 | 30 | return ( 31 | setCopied(toCopy)}> 32 | {isCopied ? ( 33 | 34 | 35 | Copied 36 | 37 | ) : ( 38 | 39 | 40 | 41 | )} 42 | 43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /Compound Contracts/development/Compound/src/ReentrancyGuard.sol: -------------------------------------------------------------------------------- 1 | // File: contracts/ReentrancyGuard.sol 2 | 3 | pragma solidity >=0.4.21 <0.6.5; 4 | 5 | /** 6 | * @title Helps contracts guard against reentrancy attacks. 7 | * @author Remco Bloemen , Eenae 8 | * @dev If you mark a function `nonReentrant`, you should also 9 | * mark it `external`. 10 | */ 11 | contract ReentrancyGuard { 12 | /// @dev counter to allow mutex lock with only one SSTORE operation 13 | uint256 private _guardCounter; 14 | 15 | constructor () internal { 16 | // The counter starts at one to prevent changing it from zero to a non-zero 17 | // value, which is a more expensive operation. 18 | _guardCounter = 1; 19 | } 20 | 21 | /** 22 | * @dev Prevents a contract from calling itself, directly or indirectly. 23 | * Calling a `nonReentrant` function from another `nonReentrant` 24 | * function is not supported. It is possible to prevent this from happening 25 | * by making the `nonReentrant` function external, and make it call a 26 | * `private` function that does the actual work. 27 | */ 28 | modifier nonReentrant() { 29 | _guardCounter += 1; 30 | uint256 localCounter = _guardCounter; 31 | _; 32 | require(localCounter == _guardCounter, "re-entered"); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/utils/price.js: -------------------------------------------------------------------------------- 1 | import { getMarketDetails } from '@uniswap/sdk' 2 | import { getMedian, getMean } from './math' 3 | 4 | const DAI = 'DAI' 5 | const USDC = 'USDC' 6 | const TUSD = 'TUSD' 7 | 8 | const USD_STABLECOINS = [DAI, USDC, TUSD] 9 | 10 | function forEachStablecoin(runner) { 11 | return USD_STABLECOINS.map((stablecoin, index) => runner(index, stablecoin)) 12 | } 13 | 14 | export function getUSDPrice(reserves) { 15 | const marketDetails = forEachStablecoin(i => getMarketDetails(reserves[i], undefined)) 16 | const ethPrices = forEachStablecoin(i => marketDetails[i].marketRate.rateInverted) 17 | 18 | const [median] = getMedian(ethPrices) 19 | const [mean] = getMean(ethPrices) 20 | const [weightedMean] = getMean( 21 | ethPrices, 22 | forEachStablecoin(i => reserves[i].ethReserve.amount) 23 | ) 24 | 25 | // const _stablecoinWeights = [ 26 | // getMean([medianWeights[0], meanWeights[0], weightedMeanWeights[0]])[0], 27 | // getMean([medianWeights[1], meanWeights[1], weightedMeanWeights[1]])[0], 28 | // getMean([medianWeights[2], meanWeights[2], weightedMeanWeights[2]])[0] 29 | // ] 30 | // const stablecoinWeights = forEachStablecoin((i, stablecoin) => ({ 31 | // [stablecoin]: _stablecoinWeights[i] 32 | // })).reduce((accumulator, currentValue) => ({ ...accumulator, ...currentValue }), {}) 33 | 34 | return getMean([median, mean, weightedMean])[0] 35 | } 36 | -------------------------------------------------------------------------------- /src/components/Header/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | 4 | import { Link } from '../../theme' 5 | import Web3Status from '../Web3Status' 6 | import { darken } from 'polished' 7 | 8 | const HeaderFrame = styled.div` 9 | display: flex; 10 | align-items: center; 11 | justify-content: space-between; 12 | width: 100%; 13 | ` 14 | 15 | const HeaderElement = styled.div` 16 | margin: 1.25rem; 17 | display: flex; 18 | min-width: 0; 19 | display: flex; 20 | align-items: center; 21 | ` 22 | 23 | const Nod = styled.span` 24 | transform: rotate(0deg); 25 | transition: transform 150ms ease-out; 26 | 27 | :hover { 28 | transform: rotate(-10deg); 29 | } 30 | ` 31 | 32 | const Title = styled.div` 33 | display: flex; 34 | align-items: center; 35 | 36 | :hover { 37 | cursor: pointer; 38 | } 39 | 40 | #link { 41 | text-decoration-color: ${({ theme }) => theme.UniswapPink}; 42 | } 43 | 44 | #title { 45 | display: inline; 46 | font-size: 1rem; 47 | font-weight: 500; 48 | color: ${({ theme }) => theme.wisteriaPurple}; 49 | :hover { 50 | color: ${({ theme }) => darken(0.1, theme.wisteriaPurple)}; 51 | } 52 | } 53 | ` 54 | 55 | export default function Header() { 56 | return ( 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | ) 67 | } 68 | -------------------------------------------------------------------------------- /Uniswap Contracts/uniswap_factory.vy: -------------------------------------------------------------------------------- 1 | contract Exchange(): 2 | def setup(token_addr: address): modifying 3 | 4 | NewExchange: event({token: indexed(address), exchange: indexed(address)}) 5 | 6 | exchangeTemplate: public(address) 7 | tokenCount: public(uint256) 8 | token_to_exchange: map(address, address) 9 | exchange_to_token: map(address, address) 10 | id_to_token: map(uint256, address) 11 | 12 | @public 13 | def initializeFactory(template: address): 14 | assert self.exchangeTemplate == ZERO_ADDRESS 15 | assert template != ZERO_ADDRESS 16 | self.exchangeTemplate = template 17 | 18 | @public 19 | def createExchange(token: address) -> address: 20 | assert token != ZERO_ADDRESS 21 | assert self.exchangeTemplate != ZERO_ADDRESS 22 | assert self.token_to_exchange[token] == ZERO_ADDRESS 23 | exchange: address = create_forwarder_to(self.exchangeTemplate) 24 | Exchange(exchange).setup(token) 25 | self.token_to_exchange[token] = exchange 26 | self.exchange_to_token[exchange] = token 27 | token_id: uint256 = self.tokenCount + 1 28 | self.tokenCount = token_id 29 | self.id_to_token[token_id] = token 30 | log.NewExchange(token, exchange) 31 | return exchange 32 | 33 | @public 34 | @constant 35 | def getExchange(token: address) -> address: 36 | return self.token_to_exchange[token] 37 | 38 | @public 39 | @constant 40 | def getToken(exchange: address) -> address: 41 | return self.exchange_to_token[exchange] 42 | 43 | @public 44 | @constant 45 | def getTokenWithId(token_id: uint256) -> address: 46 | return self.id_to_token[token_id] -------------------------------------------------------------------------------- /Compound Contracts/development/Compound/src/ComptrollerStorage.sol: -------------------------------------------------------------------------------- 1 | // File: contracts/ComptrollerStorage.sol 2 | 3 | pragma solidity >=0.4.21 <0.6.5; 4 | 5 | import "./CToken.sol"; 6 | import "./PriceOracle.sol"; 7 | 8 | contract UnitrollerAdminStorage { 9 | /** 10 | * @notice Administrator for this contract 11 | */ 12 | address public admin; 13 | 14 | /** 15 | * @notice Pending administrator for this contract 16 | */ 17 | address public pendingAdmin; 18 | 19 | /** 20 | * @notice Active brains of Unitroller 21 | */ 22 | address public comptrollerImplementation; 23 | 24 | /** 25 | * @notice Pending brains of Unitroller 26 | */ 27 | address public pendingComptrollerImplementation; 28 | } 29 | 30 | contract ComptrollerV1Storage is UnitrollerAdminStorage { 31 | 32 | /** 33 | * @notice Oracle which gives the price of any given asset 34 | */ 35 | PriceOracle public oracle; 36 | 37 | /** 38 | * @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow 39 | */ 40 | uint public closeFactorMantissa; 41 | 42 | /** 43 | * @notice Multiplier representing the discount on collateral that a liquidator receives 44 | */ 45 | uint public liquidationIncentiveMantissa; 46 | 47 | /** 48 | * @notice Max number of assets a single account can participate in (borrow or use as collateral) 49 | */ 50 | uint public maxAssets; 51 | 52 | /** 53 | * @notice Per-account mapping of "assets you are in", capped by maxAssets 54 | */ 55 | mapping(address => CToken[]) public accountAssets; 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/pages/Bridge/index.js: -------------------------------------------------------------------------------- 1 | const getWeb3 = require("web3"); 2 | const web3 = require('web3'); 3 | 4 | const from = web3.currentProvider.selectedAddress; 5 | 6 | const Network = require("@maticnetwork/meta/network"); 7 | const Matic = require("@maticnetwork/maticjs").default; 8 | // const config = require("../../../"); 9 | 10 | const network = new Network("testnet", "v2"); 11 | const MaticNetwork = network.Matic; 12 | const MainNetwork = network.Main; 13 | 14 | const Ropsten_Erc20Address = '0xcb79C63a66A7122D9EDc6C6f6a65d4eb0F01A141'; 15 | // const Matic_Erc20Address = config.Matic_Erc20Address; 16 | 17 | const from = '0x2a5c6E0Eb76915466C0CE771DCFb6f258a572336'; // from address 18 | 19 | const matic = new Matic({ 20 | maticProvider: this.web3, 21 | parentProvider: this.web3, 22 | rootChainAddress: config.ROOTCHAIN_ADDRESS, 23 | syncerUrl: config.SYNCER_URL, 24 | watcherUrl: config.WATCHER_URL, 25 | }); 26 | 27 | async function init() { 28 | await matic.initialize(); 29 | await matic.setWallet("5D54A08F0B78E9190B17D4191A00B825E81C644F9C7B71A8F9B693BECEBD0C85"); 30 | } 31 | 32 | getWeb3().then((result) => { 33 | this.web3 = result; 34 | }); 35 | 36 | async function demoErc20() { 37 | await init(); 38 | const amount = "1000000"; // amount in wei 39 | console.log("*****Deposit ERC20*****"); 40 | 41 | let token = Ropsten_Erc20Address; 42 | 43 | matic 44 | .approveERC20TokensForDeposit(token, amount, { 45 | from 46 | }) 47 | .then(logs => console.log(logs.transactionHash)) 48 | .then(() => { 49 | matic.depositERC20ForUser(token, from, amount, { 50 | from 51 | }) 52 | .then(logs => console.log(logs.transactionHash)); 53 | }) 54 | } 55 | 56 | demoErc20(); 57 | -------------------------------------------------------------------------------- /Compound Contracts/development/Compound/src/InterestRateModel.sol: -------------------------------------------------------------------------------- 1 | // File: contracts/InterestRateModel.sol 2 | 3 | pragma solidity >=0.4.21 <0.6.5; 4 | 5 | /** 6 | * @title The Compound InterestRateModel Interface 7 | * @author Compound 8 | * @notice Any interest rate model should derive from this contract. 9 | * @dev These functions are specifically not marked `pure` as implementations of this 10 | * contract may read from storage variables. 11 | */ 12 | interface InterestRateModel { 13 | /** 14 | * @notice Gets the current borrow interest rate based on the given asset, total cash, total borrows 15 | * and total reserves. 16 | * @dev The return value should be scaled by 1e18, thus a return value of 17 | * `(true, 1000000000000)` implies an interest rate of 0.000001 or 0.0001% *per block*. 18 | * @param cash The total cash of the underlying asset in the CToken 19 | * @param borrows The total borrows of the underlying asset in the CToken 20 | * @param reserves The total reserves of the underlying asset in the CToken 21 | * @return Success or failure and the borrow interest rate per block scaled by 10e18 22 | */ 23 | function getBorrowRate(uint cash, uint borrows, uint reserves) external view returns (uint, uint); 24 | 25 | /** 26 | * @notice Marker function used for light validation when updating the interest rate model of a market 27 | * @dev Marker function used for light validation when updating the interest rate model of a market. Implementations should simply return true. 28 | * @return Success or failure 29 | */ 30 | function isInterestRateModel() external view returns (bool); 31 | } 32 | -------------------------------------------------------------------------------- /src/pages/Pool/index.js: -------------------------------------------------------------------------------- 1 | import React, { Suspense, lazy, useEffect } from 'react' 2 | import ReactGA from 'react-ga' 3 | import { Switch, Route, Redirect } from 'react-router-dom' 4 | import ModeSelector from './ModeSelector' 5 | 6 | const AddLiquidity = lazy(() => import('./AddLiquidity')) 7 | const RemoveLiquidity = lazy(() => import('./RemoveLiquidity')) 8 | const CreateExchange = lazy(() => import('./CreateExchange')) 9 | 10 | export default function Pool({ params }) { 11 | useEffect(() => { 12 | ReactGA.pageview(window.location.pathname + window.location.search) 13 | }, []) 14 | 15 | const AddLiquidityParams = () => 16 | 17 | const RemoveLiquidityParams = () => 18 | 19 | const CreateExchangeParams = () => 20 | 21 | return ( 22 | <> 23 | 24 | {/* this Suspense is for route code-splitting */} 25 | 26 | 27 | 28 | 29 | 30 | { 33 | return ( 34 | 35 | ) 36 | }} 37 | /> 38 | 39 | 40 | 41 | 42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /src/components/TokenLogo/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import styled from 'styled-components' 3 | import { isAddress } from '../../utils' 4 | 5 | import { ReactComponent as EthereumLogo } from '../../assets/images/ethereum-logo.svg' 6 | 7 | const TOKEN_ICON_API = address => 8 | `https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/${isAddress( 9 | address 10 | )}/logo.png` 11 | const BAD_IMAGES = {} 12 | 13 | const Image = styled.img` 14 | width: ${({ size }) => size}; 15 | height: ${({ size }) => size}; 16 | background-color: white; 17 | border-radius: 1rem; 18 | ` 19 | 20 | const Emoji = styled.span` 21 | display: flex; 22 | align-items: center; 23 | justify-content: center; 24 | width: ${({ size }) => size}; 25 | height: ${({ size }) => size}; 26 | ` 27 | 28 | const StyledEthereumLogo = styled(EthereumLogo)` 29 | width: ${({ size }) => size}; 30 | height: ${({ size }) => size}; 31 | ` 32 | 33 | export default function TokenLogo({ address, size = '1rem', ...rest }) { 34 | const [error, setError] = useState(false) 35 | 36 | let path = '' 37 | if (address === 'ETH') { 38 | return 39 | } else if (!error && !BAD_IMAGES[address]) { 40 | path = TOKEN_ICON_API(address.toLowerCase()) 41 | } else { 42 | return ( 43 | 44 | 45 | 🤔 46 | 47 | 48 | ) 49 | } 50 | 51 | return ( 52 | {address} { 58 | BAD_IMAGES[address] = true 59 | setError(true) 60 | }} 61 | /> 62 | ) 63 | } 64 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | 14 | 23 | SmartSwap 24 | 25 | 26 | 27 |
28 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/utils/math.js: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@uniswap/sdk' 2 | 3 | // returns a deep copied + sorted list of values, as well as a sortmap 4 | export function sortBigNumbers(values) { 5 | const valueMap = values.map((value, i) => ({ value, i })) 6 | 7 | valueMap.sort((a, b) => { 8 | if (a.value.isGreaterThan(b.value)) { 9 | return 1 10 | } else if (a.value.isLessThan(b.value)) { 11 | return -1 12 | } else { 13 | return 0 14 | } 15 | }) 16 | 17 | return [ 18 | valueMap.map(element => values[element.i]), 19 | values.map((_, i) => valueMap.findIndex(element => element.i === i)) 20 | ] 21 | } 22 | 23 | export function getMedian(values) { 24 | const [sortedValues, sortMap] = sortBigNumbers(values) 25 | if (values.length % 2 === 0) { 26 | const middle = values.length / 2 27 | const indices = [middle - 1, middle] 28 | return [ 29 | sortedValues[middle - 1].plus(sortedValues[middle]).dividedBy(2), 30 | sortMap.map(element => (indices.includes(element) ? new BigNumber(0.5) : new BigNumber(0))) 31 | ] 32 | } else { 33 | const middle = Math.floor(values.length / 2) 34 | return [sortedValues[middle], sortMap.map(element => (element === middle ? new BigNumber(1) : new BigNumber(0)))] 35 | } 36 | } 37 | 38 | export function getMean(values, _weights) { 39 | const weights = _weights ? _weights : values.map(() => new BigNumber(1)) 40 | 41 | const weightedValues = values.map((value, i) => value.multipliedBy(weights[i])) 42 | const numerator = weightedValues.reduce( 43 | (accumulator, currentValue) => accumulator.plus(currentValue), 44 | new BigNumber(0) 45 | ) 46 | const denominator = weights.reduce((accumulator, currentValue) => accumulator.plus(currentValue), new BigNumber(0)) 47 | 48 | return [numerator.dividedBy(denominator), weights.map(weight => weight.dividedBy(denominator))] 49 | } 50 | -------------------------------------------------------------------------------- /src/connectors/index.js: -------------------------------------------------------------------------------- 1 | import { InjectedConnector } from '@web3-react/injected-connector' 2 | import { WalletConnectConnector } from '@web3-react/walletconnect-connector' 3 | import { WalletLinkConnector } from '@web3-react/walletlink-connector' 4 | import { PortisConnector } from '@web3-react/portis-connector' 5 | import { TorusConnector } from '@web3-react/torus-connector' 6 | 7 | import { NetworkConnector } from './Network' 8 | import { FortmaticConnector } from './Fortmatic' 9 | 10 | const POLLING_INTERVAL = 10000 11 | const NETWORK_URL = "https://testnet2.matic.network" 12 | // process.env.REACT_APP_IS_PRODUCTION_DEPLOY === 'true' 13 | // ? process.env.REACT_APP_NETWORK_URL_PROD 14 | // : process.env.REACT_APP_NETWORK_URL 15 | 16 | export const network = new NetworkConnector({ 17 | urls: { [Number("8995")]: NETWORK_URL }, 18 | pollingInterval: POLLING_INTERVAL * 3 19 | }) 20 | 21 | export const injected = new InjectedConnector({ 22 | supportedChainIds: [Number("8995")] 23 | }) 24 | 25 | // matic testnet 26 | export const walletconnect = new WalletConnectConnector({ 27 | rpc: { [Number("8995")]: NETWORK_URL }, 28 | bridge: 'https://walletconnect.matic.network', 29 | qrcode: false, 30 | pollingInterval: POLLING_INTERVAL * 3 31 | }) 32 | 33 | // matic testnet 34 | export const fortmatic = new FortmaticConnector({ 35 | apiKey: 'pk_live_36DA4D2F0A4F9F2C', 36 | chainId: 1 37 | }) 38 | 39 | // matic testnet 40 | export const portis = new PortisConnector({ 41 | dAppId: 'ad1d9de5-18e7-48ab-bcbf-a9929dc6d9cb', 42 | networks: [15001] 43 | }) 44 | 45 | export const torus = new TorusConnector({ 46 | chainId: 8995, 47 | initOptions: { 48 | host: "https://testnet2.matic.network", 49 | showTorusButton: false 50 | } 51 | }) 52 | // matic testnet 53 | export const walletlink = new WalletLinkConnector({ 54 | url: NETWORK_URL, 55 | appName: 'Uniswap', 56 | appLogoUrl: 57 | 'https://mpng.pngfly.com/20181202/bex/kisspng-emoji-domain-unicorn-pin-badges-sticker-unicorn-tumblr-emoji-unicorn-iphoneemoji-5c046729264a77.5671679315437924251569.jpg' 58 | }) 59 | -------------------------------------------------------------------------------- /src/constants/abis/factory.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "NewExchange", 4 | "inputs": [ 5 | { "type": "address", "name": "token", "indexed": true }, 6 | { "type": "address", "name": "exchange", "indexed": true } 7 | ], 8 | "anonymous": false, 9 | "type": "event" 10 | }, 11 | { 12 | "name": "initializeFactory", 13 | "outputs": [], 14 | "inputs": [{ "type": "address", "name": "template" }], 15 | "constant": false, 16 | "payable": false, 17 | "type": "function", 18 | "gas": 35725 19 | }, 20 | { 21 | "name": "createExchange", 22 | "outputs": [{ "type": "address", "name": "out" }], 23 | "inputs": [{ "type": "address", "name": "token" }], 24 | "constant": false, 25 | "payable": false, 26 | "type": "function", 27 | "gas": 187911 28 | }, 29 | { 30 | "name": "getExchange", 31 | "outputs": [{ "type": "address", "name": "out" }], 32 | "inputs": [{ "type": "address", "name": "token" }], 33 | "constant": true, 34 | "payable": false, 35 | "type": "function", 36 | "gas": 715 37 | }, 38 | { 39 | "name": "getToken", 40 | "outputs": [{ "type": "address", "name": "out" }], 41 | "inputs": [{ "type": "address", "name": "exchange" }], 42 | "constant": true, 43 | "payable": false, 44 | "type": "function", 45 | "gas": 745 46 | }, 47 | { 48 | "name": "getTokenWithId", 49 | "outputs": [{ "type": "address", "name": "out" }], 50 | "inputs": [{ "type": "uint256", "name": "token_id" }], 51 | "constant": true, 52 | "payable": false, 53 | "type": "function", 54 | "gas": 736 55 | }, 56 | { 57 | "name": "exchangeTemplate", 58 | "outputs": [{ "type": "address", "name": "out" }], 59 | "inputs": [], 60 | "constant": true, 61 | "payable": false, 62 | "type": "function", 63 | "gas": 633 64 | }, 65 | { 66 | "name": "tokenCount", 67 | "outputs": [{ "type": "uint256", "name": "out" }], 68 | "inputs": [], 69 | "constant": true, 70 | "payable": false, 71 | "type": "function", 72 | "gas": 663 73 | } 74 | ] 75 | -------------------------------------------------------------------------------- /src/theme/components.js: -------------------------------------------------------------------------------- 1 | import styled, { keyframes } from 'styled-components' 2 | import { darken } from 'polished' 3 | 4 | export const Button = styled.button.attrs(({ warning, theme }) => ({ 5 | backgroundColor: warning ? theme.salmonRed : theme.royalBlue 6 | }))` 7 | padding: 1rem 2rem 1rem 2rem; 8 | border-radius: 3rem; 9 | cursor: pointer; 10 | user-select: none; 11 | font-size: 1rem; 12 | border: none; 13 | outline: none; 14 | background-color: ${({ backgroundColor }) => backgroundColor}; 15 | color: ${({ theme }) => theme.white}; 16 | width: 100%; 17 | 18 | :hover, 19 | :focus { 20 | background-color: ${({ backgroundColor }) => darken(0.05, backgroundColor)}; 21 | } 22 | 23 | :active { 24 | background-color: ${({ backgroundColor }) => darken(0.1, backgroundColor)}; 25 | } 26 | 27 | :disabled { 28 | background-color: ${({ theme }) => theme.concreteGray}; 29 | color: ${({ theme }) => theme.silverGray}; 30 | cursor: auto; 31 | } 32 | ` 33 | 34 | export const Link = styled.a.attrs({ 35 | target: '_blank', 36 | rel: 'noopener noreferrer' 37 | })` 38 | text-decoration: none; 39 | cursor: pointer; 40 | color: ${({ theme }) => theme.royalBlue}; 41 | 42 | :focus { 43 | outline: none; 44 | text-decoration: underline; 45 | } 46 | 47 | :active { 48 | text-decoration: none; 49 | } 50 | ` 51 | 52 | export const BorderlessInput = styled.input` 53 | color: ${({ theme }) => theme.textColor}; 54 | font-size: 1rem; 55 | outline: none; 56 | border: none; 57 | flex: 1 1 auto; 58 | width: 0; 59 | background-color: ${({ theme }) => theme.inputBackground}; 60 | 61 | [type='number'] { 62 | -moz-appearance: textfield; 63 | } 64 | 65 | ::-webkit-outer-spin-button, 66 | ::-webkit-inner-spin-button { 67 | -webkit-appearance: none; 68 | } 69 | 70 | ::placeholder { 71 | color: ${({ theme }) => theme.chaliceGray}; 72 | } 73 | ` 74 | 75 | const rotate = keyframes` 76 | from { 77 | transform: rotate(0deg); 78 | } 79 | to { 80 | transform: rotate(360deg); 81 | } 82 | ` 83 | 84 | export const Spinner = styled.img` 85 | animation: 2s ${rotate} linear infinite; 86 | width: 16px; 87 | height: 16px; 88 | ` 89 | -------------------------------------------------------------------------------- /depositerc20.js: -------------------------------------------------------------------------------- 1 | const Network = require("@maticnetwork/meta/network"); 2 | const Matic = require("@maticnetwork/maticjs").default; 3 | 4 | 5 | const network = new Network("testnet","v3"); 6 | const MaticNetwork = network.Matic; 7 | const MainNetwork = network.Main; 8 | 9 | const Ropsten_Erc20Address = '0xec5c207897c4378658f52bccce0ea648d1f17d65'; 10 | 11 | 12 | const from = '0x8727aff41b503b4c01d757391d3b3a9d38b67dcd'; // from address 13 | 14 | const matic = new Matic({ 15 | maticProvider: MaticNetwork.RPC, 16 | parentProvider: MainNetwork.RPC, 17 | rootChain: MainNetwork.Contracts.RootChain, 18 | withdrawManager: MainNetwork.Contracts.WithdrawManagerProxy, 19 | depositManager: MainNetwork.Contracts.DepositManagerProxy, 20 | registry: MainNetwork.Contracts.Registry 21 | }); 22 | 23 | async function init() { 24 | await matic.initialize(); 25 | await matic.setWallet('0xE86F1698548CFC955C3B3556DFB2C8F35FEB6EA5B35AB90608CBC6B04B9F700F'); 26 | } 27 | 28 | async function PromiseTimeout(delayms) { 29 | return new Promise(function(resolve, reject) { 30 | setTimeout(resolve, delayms); 31 | }); 32 | } 33 | 34 | async function demoErc20() { 35 | await init(); 36 | const amount = "100000000000000000"; // amount in wei 37 | console.log("*****Deposit ERC20*****"); 38 | 39 | let token = Ropsten_Erc20Address; 40 | 41 | await matic 42 | .approveERC20TokensForDeposit(token, amount, { 43 | from 44 | }) 45 | .then(async logs => { 46 | console.log("Approve on Ropsten:" + logs.transactionHash); 47 | await PromiseTimeout(10000); 48 | await matic 49 | .depositERC20ForUser(token, from, amount, { 50 | from 51 | }) 52 | .then(async logs => { 53 | console.log("Deposit on Ropsten:" + logs.transactionHash); 54 | await PromiseTimeout(10000); 55 | 56 | 57 | }); 58 | }); 59 | } 60 | 61 | demoErc20(); 62 | -------------------------------------------------------------------------------- /public/locales/zh-CN.json: -------------------------------------------------------------------------------- 1 | { 2 | "noWallet": "未发现以太钱包", 3 | "wrongNetwork": "网络错误", 4 | "switchNetwork": "请切换到 {{ correctNetwork }}", 5 | "installWeb3MobileBrowser": "请从支持web3的移动端浏览器,如 Trust Wallet 或 Coinbase Wallet 访问。", 6 | "installMetamask": "请从安装了 Metamask 插件的 Chrome 或 Brave 访问。", 7 | "disconnected": "未连接", 8 | "swap": "兑换", 9 | "send": "发送", 10 | "pool": "资金池", 11 | "betaWarning": "项目尚处于beta阶段。使用需自行承担风险。", 12 | "input": "输入", 13 | "output": "输出", 14 | "estimated": "估计", 15 | "balance": "余额: {{ balanceInput }}", 16 | "unlock": "解锁", 17 | "pending": "处理中", 18 | "selectToken": "选择通证", 19 | "searchOrPaste": "搜索通证或粘贴地址", 20 | "noExchange": "未找到交易所", 21 | "exchangeRate": "兑换率", 22 | "enterValueCont": "输入{{ missingCurrencyValue }}值并继续。", 23 | "selectTokenCont": "选取通证继续。", 24 | "noLiquidity": "没有流动金。", 25 | "unlockTokenCont": "请解锁通证并继续。", 26 | "transactionDetails": "交易明细", 27 | "hideDetails": "隐藏明细", 28 | "youAreSelling": "你正在出售", 29 | "orTransFail": "或交易失败。", 30 | "youWillReceive": "你将至少收到", 31 | "youAreBuying": "你正在购买", 32 | "itWillCost": "它将至少花费", 33 | "insufficientBalance": "余额不足", 34 | "inputNotValid": "无效的输入值", 35 | "differentToken": "必须是不同的通证。", 36 | "noRecipient": "输入接收钱包地址。", 37 | "invalidRecipient": "请输入有效的收钱地址。", 38 | "recipientAddress": "接收地址", 39 | "youAreSending": "你正在发送", 40 | "willReceive": "将至少收到", 41 | "to": "至", 42 | "addLiquidity": "添加流动金", 43 | "deposit": "存入", 44 | "currentPoolSize": "当前资金池大小", 45 | "yourPoolShare": "你的资金池份额", 46 | "noZero": "金额不能为零。", 47 | "mustBeETH": "输入中必须有一个是 ETH。", 48 | "enterCurrencyOrLabelCont": "输入 {{ inputCurrency }} 或 {{ label }} 值并继续。", 49 | "youAreAdding": "你将添加", 50 | "and": "和", 51 | "intoPool": "入流动资金池。", 52 | "outPool": "出流动资金池。", 53 | "youWillMint": "你将铸造", 54 | "liquidityTokens": "流动通证。", 55 | "totalSupplyIs": "当前流动通证的总量是", 56 | "youAreSettingExRate": "你将初始兑换率设置为", 57 | "totalSupplyIs0": "当前流动通证的总量是0。", 58 | "tokenWorth": "当前兑换率下,每个资金池通证价值", 59 | "firstLiquidity": "你是第一个添加流动金的人!", 60 | "initialExchangeRate": "初始兑换率将由你的存入情况决定。请确保你存入的 ETH 和 {{ label }} 具有相同的总市值。", 61 | "removeLiquidity": "删除流动金", 62 | "poolTokens": "资金池通证", 63 | "enterLabelCont": "输入 {{ label }} 值并继续。", 64 | "youAreRemoving": "你正在移除", 65 | "youWillRemove": "你将移除", 66 | "createExchange": "创建交易所", 67 | "invalidTokenAddress": "通证地址无效", 68 | "exchangeExists": "{{ label }} 交易所已存在!", 69 | "invalidSymbol": "通证符号无效", 70 | "invalidDecimals": "小数位数无效", 71 | "tokenAddress": "通证地址", 72 | "label": "通证符号", 73 | "decimals": "小数位数", 74 | "enterTokenCont": "输入通证地址并继续" 75 | } 76 | -------------------------------------------------------------------------------- /public/locales/zh-TW.json: -------------------------------------------------------------------------------- 1 | { 2 | "noWallet": "未偵測到以太坊錢包", 3 | "wrongNetwork": "你位在錯誤的網路", 4 | "switchNetwork": "請切換到 {{ correctNetwork }}", 5 | "installWeb3MobileBrowser": "請安裝含有 web3 瀏覽器的手機錢包,如 Trust Wallet 或 Coinbase Wallet。", 6 | "installMetamask": "請使用 Chrome 或 Brave 瀏覽器安裝 Metamask。", 7 | "disconnected": "未連接", 8 | "swap": "兌換", 9 | "send": "發送", 10 | "pool": "資金池", 11 | "betaWarning": "本產品仍在測試階段。使用者需自負風險。", 12 | "input": "輸入", 13 | "output": "輸出", 14 | "estimated": "估計", 15 | "balance": "餘額: {{ balanceInput }}", 16 | "unlock": "解鎖", 17 | "pending": "處理中", 18 | "selectToken": "選擇代幣", 19 | "searchOrPaste": "選擇代幣或輸入地址", 20 | "noExchange": "找不到交易所", 21 | "exchangeRate": "匯率", 22 | "enterValueCont": "輸入 {{ missingCurrencyValue }} 以繼續。", 23 | "selectTokenCont": "選擇代幣以繼續。", 24 | "noLiquidity": "沒有流動性資金。", 25 | "unlockTokenCont": "解鎖代幣以繼續。", 26 | "transactionDetails": "交易明細", 27 | "hideDetails": "隱藏明細", 28 | "youAreSelling": "你正在出售", 29 | "orTransFail": "或交易失敗。", 30 | "youWillReceive": "你將至少收到", 31 | "youAreBuying": "你正在購買", 32 | "itWillCost": "這將花費至多", 33 | "insufficientBalance": "餘額不足", 34 | "inputNotValid": "無效的輸入值", 35 | "differentToken": "必須是不同的代幣。", 36 | "noRecipient": "請輸入收款人錢包地址。", 37 | "invalidRecipient": "請輸入有效的錢包地址。", 38 | "recipientAddress": "收款人錢包地址", 39 | "youAreSending": "你正在發送", 40 | "willReceive": "將至少收到", 41 | "to": "至", 42 | "addLiquidity": "增加流動性資金", 43 | "deposit": "存入", 44 | "currentPoolSize": "目前的資金池總量", 45 | "yourPoolShare": "你在資金池中的佔比", 46 | "noZero": "金額不能為零。", 47 | "mustBeETH": "輸入中必須包含 ETH。", 48 | "enterCurrencyOrLabelCont": "輸入 {{ inputCurrency }} 或 {{ label }} 以繼續。", 49 | "youAreAdding": "你將把", 50 | "and": "和", 51 | "intoPool": "加入資金池。", 52 | "outPool": "領出資金池。", 53 | "youWillMint": "你將產生", 54 | "liquidityTokens": "流動性代幣。", 55 | "totalSupplyIs": "目前流動性代幣供給總量為", 56 | "youAreSettingExRate": "初始的匯率將被設定為", 57 | "totalSupplyIs0": "目前流動性代幣供給為零。", 58 | "tokenWorth": "依據目前的匯率,每個流動性代幣價值", 59 | "firstLiquidity": "您是第一個提供流動性資金的人!", 60 | "initialExchangeRate": "初始的匯率將取決於你存入的資金。請確保存入的 ETH 和 {{ label }} 的價值相等。", 61 | "removeLiquidity": "領出流動性資金", 62 | "poolTokens": "資金池代幣", 63 | "enterLabelCont": "輸入 {{ label }} 以繼續。", 64 | "youAreRemoving": "您正在移除", 65 | "youWillRemove": "您即將移除", 66 | "createExchange": "創建交易所", 67 | "invalidTokenAddress": "無效的代幣地址", 68 | "exchangeExists": "{{ label }} 的交易所已經存在!", 69 | "invalidSymbol": "代幣符號錯誤", 70 | "invalidDecimals": "小數位數錯誤", 71 | "tokenAddress": "代幣地址", 72 | "label": "代幣符號", 73 | "decimals": "小數位數", 74 | "enterTokenCont": "輸入代幣地址" 75 | } 76 | -------------------------------------------------------------------------------- /Compound Contracts/development/Compound/src/CarefulMath.sol: -------------------------------------------------------------------------------- 1 | // File: contracts/CarefulMath.sol 2 | 3 | pragma solidity >=0.4.21 <0.6.5; 4 | 5 | /** 6 | * @title Careful Math 7 | * @author Compound 8 | * @notice Derived from OpenZeppelin's SafeMath library 9 | * https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol 10 | */ 11 | contract CarefulMath { 12 | 13 | /** 14 | * @dev Possible error codes that we can return 15 | */ 16 | enum MathError { 17 | NO_ERROR, 18 | DIVISION_BY_ZERO, 19 | INTEGER_OVERFLOW, 20 | INTEGER_UNDERFLOW 21 | } 22 | 23 | /** 24 | * @dev Multiplies two numbers, returns an error on overflow. 25 | */ 26 | function mulUInt(uint a, uint b) internal pure returns (MathError, uint) { 27 | if (a == 0) { 28 | return (MathError.NO_ERROR, 0); 29 | } 30 | 31 | uint c = a * b; 32 | 33 | if (c / a != b) { 34 | return (MathError.INTEGER_OVERFLOW, 0); 35 | } else { 36 | return (MathError.NO_ERROR, c); 37 | } 38 | } 39 | 40 | /** 41 | * @dev Integer division of two numbers, truncating the quotient. 42 | */ 43 | function divUInt(uint a, uint b) internal pure returns (MathError, uint) { 44 | if (b == 0) { 45 | return (MathError.DIVISION_BY_ZERO, 0); 46 | } 47 | 48 | return (MathError.NO_ERROR, a / b); 49 | } 50 | 51 | /** 52 | * @dev Subtracts two numbers, returns an error on overflow (i.e. if subtrahend is greater than minuend). 53 | */ 54 | function subUInt(uint a, uint b) internal pure returns (MathError, uint) { 55 | if (b <= a) { 56 | return (MathError.NO_ERROR, a - b); 57 | } else { 58 | return (MathError.INTEGER_UNDERFLOW, 0); 59 | } 60 | } 61 | 62 | /** 63 | * @dev Adds two numbers, returns an error on overflow. 64 | */ 65 | function addUInt(uint a, uint b) internal pure returns (MathError, uint) { 66 | uint c = a + b; 67 | 68 | if (c >= a) { 69 | return (MathError.NO_ERROR, c); 70 | } else { 71 | return (MathError.INTEGER_OVERFLOW, 0); 72 | } 73 | } 74 | 75 | /** 76 | * @dev add a and b and then subtract c 77 | */ 78 | function addThenSubUInt(uint a, uint b, uint c) internal pure returns (MathError, uint) { 79 | (MathError err0, uint sum) = addUInt(a, b); 80 | 81 | if (err0 != MathError.NO_ERROR) { 82 | return (err0, 0); 83 | } 84 | 85 | return subUInt(sum, c); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Compound Contracts/development/Compound/src/EIP20Interface.sol: -------------------------------------------------------------------------------- 1 | // File: contracts/EIP20Interface.sol 2 | 3 | pragma solidity >=0.4.21 <0.6.5; 4 | 5 | /** 6 | * @title ERC 20 Token Standard Interface 7 | * https://eips.ethereum.org/EIPS/eip-20 8 | */ 9 | interface EIP20Interface { 10 | 11 | /** 12 | * @notice Get the total number of tokens in circulation 13 | * @return The supply of tokens 14 | */ 15 | function totalSupply() external view returns (uint256); 16 | 17 | /** 18 | * @notice Gets the balance of the specified address 19 | * @param owner The address from which the balance will be retrieved 20 | * @return The balance 21 | */ 22 | function balanceOf(address owner) external view returns (uint256 balance); 23 | 24 | /** 25 | * @notice Transfer `amount` tokens from `msg.sender` to `dst` 26 | * @param dst The address of the destination account 27 | * @param amount The number of tokens to transfer 28 | * @return Whether or not the transfer succeeded 29 | */ 30 | function transfer(address dst, uint256 amount) external returns (bool success); 31 | 32 | /** 33 | * @notice Transfer `amount` tokens from `src` to `dst` 34 | * @param src The address of the source account 35 | * @param dst The address of the destination account 36 | * @param amount The number of tokens to transfer 37 | * @return Whether or not the transfer succeeded 38 | */ 39 | function transferFrom(address src, address dst, uint256 amount) external returns (bool success); 40 | 41 | /** 42 | * @notice Approve `spender` to transfer up to `amount` from `src` 43 | * @dev This will overwrite the approval amount for `spender` 44 | * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) 45 | * @param spender The address of the account which may transfer tokens 46 | * @param amount The number of tokens that are approved (-1 means infinite) 47 | * @return Whether or not the approval succeeded 48 | */ 49 | function approve(address spender, uint256 amount) external returns (bool success); 50 | 51 | /** 52 | * @notice Get the current allowance from `owner` for `spender` 53 | * @param owner The address of the account which owns the tokens to be spent 54 | * @param spender The address of the account which may transfer tokens 55 | * @return The number of tokens allowed to be spent (-1 means infinite) 56 | */ 57 | function allowance(address owner, address spender) external view returns (uint256 remaining); 58 | 59 | event Transfer(address indexed from, address indexed to, uint256 amount); 60 | event Approval(address indexed owner, address indexed spender, uint256 amount); 61 | } 62 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import ReactGA from 'react-ga' 4 | import { Web3ReactProvider, createWeb3ReactRoot } from '@web3-react/core' 5 | import { ethers } from 'ethers' 6 | 7 | import { NetworkContextName } from './constants' 8 | import { isMobile } from 'react-device-detect' 9 | import LocalStorageContextProvider, { Updater as LocalStorageContextUpdater } from './contexts/LocalStorage' 10 | import ApplicationContextProvider, { Updater as ApplicationContextUpdater } from './contexts/Application' 11 | import TransactionContextProvider, { Updater as TransactionContextUpdater } from './contexts/Transactions' 12 | import BalancesContextProvider, { Updater as BalancesContextUpdater } from './contexts/Balances' 13 | import TokensContextProvider from './contexts/Tokens' 14 | import AllowancesContextProvider from './contexts/Allowances' 15 | import App from './pages/App' 16 | import ThemeProvider, { GlobalStyle } from './theme' 17 | import './i18n' 18 | 19 | const Web3ProviderNetwork = createWeb3ReactRoot(NetworkContextName) 20 | 21 | function getLibrary(provider) { 22 | const library = new ethers.providers.Web3Provider(provider) 23 | library.pollingInterval = 10000 24 | return library 25 | } 26 | 27 | if (process.env.NODE_ENV === 'production') { 28 | ReactGA.initialize('UA-128182339-1') 29 | ReactGA.set({ 30 | customBrowserType: !isMobile ? 'desktop' : window.web3 || window.ethereum ? 'mobileWeb3' : 'mobileRegular' 31 | }) 32 | } else { 33 | ReactGA.initialize('test', { testMode: true }) 34 | } 35 | 36 | ReactGA.pageview(window.location.pathname + window.location.search) 37 | 38 | function ContextProviders({ children }) { 39 | return ( 40 | 41 | 42 | 43 | 44 | 45 | {children} 46 | 47 | 48 | 49 | 50 | 51 | ) 52 | } 53 | 54 | function Updaters() { 55 | return ( 56 | <> 57 | 58 | 59 | 60 | 61 | 62 | ) 63 | } 64 | 65 | ReactDOM.render( 66 | 67 | 68 | 69 | 70 | 71 | <> 72 | 73 | 74 | 75 | 76 | 77 | 78 | , 79 | document.getElementById('root') 80 | ) 81 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "smartswap", 3 | "description": "smartswap Exchange Protocol", 4 | "version": "0.1.0", 5 | "homepage": "https://viraj124.github.io/Smart-Swap", 6 | "private": true, 7 | "dependencies": { 8 | "@maticnetwork/maticjs": "^2.0.0-beta.9", 9 | "@maticnetwork/meta": "^1.0.3", 10 | "@reach/dialog": "^0.2.8", 11 | "@reach/tooltip": "^0.2.0", 12 | "@types/jest": "^25.1.3", 13 | "@types/node": "^13.7.4", 14 | "@types/react": "^16.9.22", 15 | "@types/react-dom": "^16.9.5", 16 | "@uniswap/sdk": "^1.0.0-beta.4", 17 | "@web3-react/core": "^6.0.2", 18 | "@web3-react/fortmatic-connector": "^6.0.2", 19 | "@web3-react/injected-connector": "^6.0.3", 20 | "@web3-react/network-connector": "^6.0.4", 21 | "@web3-react/portis-connector": "^6.0.2", 22 | "@web3-react/torus-connector": "^6.0.8", 23 | "@web3-react/walletconnect-connector": "^6.0.2", 24 | "@web3-react/walletlink-connector": "^6.0.2", 25 | "copy-to-clipboard": "^3.2.0", 26 | "escape-string-regexp": "^2.0.0", 27 | "ethers": "^4.0.44", 28 | "history": "^4.9.0", 29 | "i18next": "^15.0.9", 30 | "i18next-browser-languagedetector": "^3.0.1", 31 | "i18next-xhr-backend": "^2.0.1", 32 | "jazzicon": "^1.5.0", 33 | "polished": "^3.3.2", 34 | "qrcode.react": "^0.9.3", 35 | "react": "^16.8.6", 36 | "react-device-detect": "^1.6.2", 37 | "react-dom": "^16.8.6", 38 | "react-feather": "^1.1.6", 39 | "react-ga": "^2.5.7", 40 | "react-i18next": "^10.7.0", 41 | "react-router-dom": "^5.0.0", 42 | "react-scripts": "^3.0.1", 43 | "react-spring": "^8.0.27", 44 | "react-switch": "^5.0.1", 45 | "react-use-gesture": "^6.0.14", 46 | "styled-components": "^4.2.0", 47 | "typescript": "^3.8.2", 48 | "maticjs": "^1.0.1", 49 | "web3": "^1.2.6" 50 | }, 51 | "scripts": { 52 | "start": "react-scripts start", 53 | "build": "react-scripts build", 54 | "predeploy": "npm run build", 55 | "deploy": "gh-pages -d build", 56 | "test": "react-scripts test --env=jsdom", 57 | "eject": "react-scripts eject", 58 | "lint:base": "yarn eslint './src/**/*.{js,jsx}'", 59 | "format:base": "yarn prettier './src/**/*.{js,jsx,scss}'", 60 | "fix:lint": "yarn lint:base --fix", 61 | "fix:format": "yarn format:base --write", 62 | "fix:all": "yarn fix:lint && yarn fix:format", 63 | "check:lint": "yarn lint:base", 64 | "check:format": "yarn format:base --check", 65 | "check:all": "yarn check:lint && yarn check:format" 66 | }, 67 | "eslintConfig": { 68 | "extends": "react-app" 69 | }, 70 | "browserslist": { 71 | "production": [ 72 | ">0.2%", 73 | "not dead", 74 | "not op_mini all" 75 | ], 76 | "development": [ 77 | "last 1 chrome version", 78 | "last 1 firefox version", 79 | "last 1 safari version" 80 | ] 81 | }, 82 | "license": "GPL-3.0-or-later", 83 | "devDependencies": { 84 | "gh-pages": "^2.2.0", 85 | "prettier": "^1.17.0" 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Compound Contracts/development/Compound/src/ComptrollerInterface.sol: -------------------------------------------------------------------------------- 1 | // File: contracts/ComptrollerInterface.sol 2 | 3 | pragma solidity >=0.4.21 <0.6.5; 4 | 5 | interface ComptrollerInterface { 6 | /** 7 | * @notice Marker function used for light validation when updating the comptroller of a market 8 | * @dev Implementations should simply return true. 9 | * @return true 10 | */ 11 | function isComptroller() external view returns (bool); 12 | 13 | /*** Assets You Are In ***/ 14 | 15 | function enterMarkets(address[] calldata cTokens) external returns (uint[] memory); 16 | function exitMarket(address cToken) external returns (uint); 17 | 18 | /*** Policy Hooks ***/ 19 | 20 | function mintAllowed(address cToken, address minter, uint mintAmount) external returns (uint); 21 | function mintVerify(address cToken, address minter, uint mintAmount, uint mintTokens) external; 22 | 23 | function redeemAllowed(address cToken, address redeemer, uint redeemTokens) external returns (uint); 24 | function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) external; 25 | 26 | function borrowAllowed(address cToken, address borrower, uint borrowAmount) external returns (uint); 27 | function borrowVerify(address cToken, address borrower, uint borrowAmount) external; 28 | 29 | function repayBorrowAllowed( 30 | address cToken, 31 | address payer, 32 | address borrower, 33 | uint repayAmount) external returns (uint); 34 | function repayBorrowVerify( 35 | address cToken, 36 | address payer, 37 | address borrower, 38 | uint repayAmount, 39 | uint borrowerIndex) external; 40 | 41 | function liquidateBorrowAllowed( 42 | address cTokenBorrowed, 43 | address cTokenCollateral, 44 | address liquidator, 45 | address borrower, 46 | uint repayAmount) external returns (uint); 47 | function liquidateBorrowVerify( 48 | address cTokenBorrowed, 49 | address cTokenCollateral, 50 | address liquidator, 51 | address borrower, 52 | uint repayAmount, 53 | uint seizeTokens) external; 54 | 55 | function seizeAllowed( 56 | address cTokenCollateral, 57 | address cTokenBorrowed, 58 | address liquidator, 59 | address borrower, 60 | uint seizeTokens) external returns (uint); 61 | function seizeVerify( 62 | address cTokenCollateral, 63 | address cTokenBorrowed, 64 | address liquidator, 65 | address borrower, 66 | uint seizeTokens) external; 67 | 68 | function transferAllowed(address cToken, address src, address dst, uint transferTokens) external returns (uint); 69 | function transferVerify(address cToken, address src, address dst, uint transferTokens) external; 70 | 71 | /*** Liquidity/Liquidation Calculations ***/ 72 | 73 | function liquidateCalculateSeizeTokens( 74 | address cTokenBorrowed, 75 | address cTokenCollateral, 76 | uint repayAmount) external view returns (uint, uint); 77 | } 78 | -------------------------------------------------------------------------------- /Compound Contracts/development/Compound/src/EIP20NonStandardInterface.sol: -------------------------------------------------------------------------------- 1 | // File: contracts/EIP20NonStandardInterface.sol 2 | 3 | pragma solidity >=0.4.21 <0.6.5; 4 | 5 | /** 6 | * @title EIP20NonStandardInterface 7 | * @dev Version of ERC20 with no return values for `transfer` and `transferFrom` 8 | * See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca 9 | */ 10 | interface EIP20NonStandardInterface { 11 | 12 | /** 13 | * @notice Get the total number of tokens in circulation 14 | * @return The supply of tokens 15 | */ 16 | function totalSupply() external view returns (uint256); 17 | 18 | /** 19 | * @notice Gets the balance of the specified address 20 | * @param owner The address from which the balance will be retrieved 21 | * @return The balance 22 | */ 23 | function balanceOf(address owner) external view returns (uint256 balance); 24 | 25 | /// 26 | /// !!!!!!!!!!!!!! 27 | /// !!! NOTICE !!! `transfer` does not return a value, in violation of the ERC-20 specification 28 | /// !!!!!!!!!!!!!! 29 | /// 30 | 31 | /** 32 | * @notice Transfer `amount` tokens from `msg.sender` to `dst` 33 | * @param dst The address of the destination account 34 | * @param amount The number of tokens to transfer 35 | */ 36 | function transfer(address dst, uint256 amount) external; 37 | 38 | /// 39 | /// !!!!!!!!!!!!!! 40 | /// !!! NOTICE !!! `transferFrom` does not return a value, in violation of the ERC-20 specification 41 | /// !!!!!!!!!!!!!! 42 | /// 43 | 44 | /** 45 | * @notice Transfer `amount` tokens from `src` to `dst` 46 | * @param src The address of the source account 47 | * @param dst The address of the destination account 48 | * @param amount The number of tokens to transfer 49 | */ 50 | function transferFrom(address src, address dst, uint256 amount) external; 51 | 52 | /** 53 | * @notice Approve `spender` to transfer up to `amount` from `src` 54 | * @dev This will overwrite the approval amount for `spender` 55 | * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) 56 | * @param spender The address of the account which may transfer tokens 57 | * @param amount The number of tokens that are approved 58 | * @return Whether or not the approval succeeded 59 | */ 60 | function approve(address spender, uint256 amount) external returns (bool success); 61 | 62 | /** 63 | * @notice Get the current allowance from `owner` for `spender` 64 | * @param owner The address of the account which owns the tokens to be spent 65 | * @param spender The address of the account which may transfer tokens 66 | * @return The number of tokens allowed to be spent 67 | */ 68 | function allowance(address owner, address spender) external view returns (uint256 remaining); 69 | 70 | event Transfer(address indexed from, address indexed to, uint256 amount); 71 | event Approval(address indexed owner, address indexed spender, uint256 amount); 72 | } 73 | -------------------------------------------------------------------------------- /src/components/Footer/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactGA from 'react-ga' 3 | import styled from 'styled-components' 4 | import { darken, transparentize } from 'polished' 5 | import Toggle from 'react-switch' 6 | 7 | import { Link } from '../../theme' 8 | import { useDarkModeManager } from '../../contexts/LocalStorage' 9 | 10 | const FooterFrame = styled.div` 11 | display: flex; 12 | align-items: center; 13 | justify-content: space-between; 14 | width: 100%; 15 | ` 16 | 17 | const FooterElement = styled.div` 18 | margin: 1.25rem; 19 | display: flex; 20 | min-width: 0; 21 | display: flex; 22 | align-items: center; 23 | ` 24 | 25 | const Title = styled.div` 26 | display: flex; 27 | align-items: center; 28 | color: ${({ theme }) => theme.uniswapPink}; 29 | 30 | :hover { 31 | cursor: pointer; 32 | } 33 | #link { 34 | text-decoration-color: ${({ theme }) => theme.uniswapPink}; 35 | } 36 | 37 | #title { 38 | display: inline; 39 | font-size: 0.825rem; 40 | margin-right: 12px; 41 | font-weight: 400; 42 | color: ${({ theme }) => theme.uniswapPink}; 43 | :hover { 44 | color: ${({ theme }) => darken(0.2, theme.uniswapPink)}; 45 | } 46 | } 47 | ` 48 | 49 | const StyledToggle = styled(Toggle)` 50 | margin-right: 24px; 51 | 52 | .react-switch-bg[style] { 53 | background-color: ${({ theme }) => darken(0.05, theme.inputBackground)} !important; 54 | border: 1px solid ${({ theme }) => theme.concreteGray} !important; 55 | } 56 | 57 | .react-switch-handle[style] { 58 | background-color: ${({ theme }) => theme.inputBackground}; 59 | box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.93, theme.shadowColor)}; 60 | border: 1px solid ${({ theme }) => theme.mercuryGray}; 61 | border-color: ${({ theme }) => theme.mercuryGray} !important; 62 | top: 2px !important; 63 | } 64 | ` 65 | 66 | const EmojiToggle = styled.span` 67 | display: flex; 68 | justify-content: center; 69 | align-items: center; 70 | height: 100%; 71 | font-family: Arial sans-serif; 72 | ` 73 | 74 | export default function Footer() { 75 | const [isDark, toggleDarkMode] = useDarkModeManager() 76 | 77 | return ( 78 | 79 | 80 | 81 | 82 | 83 | 84 | 88 | {/* eslint-disable-line jsx-a11y/accessible-emoji */} 89 | 🌙️ 90 | 91 | } 92 | checkedIcon={ 93 | 94 | {/* eslint-disable-line jsx-a11y/accessible-emoji */} 95 | {'☀️'} 96 | 97 | } 98 | onChange={() => { 99 | ReactGA.event({ 100 | category: 'Advanced Interaction', 101 | action: 'Toggle Theme', 102 | label: isDark ? 'Light' : 'Dark' 103 | }) 104 | toggleDarkMode() 105 | }} 106 | /> 107 | 108 | ) 109 | } 110 | -------------------------------------------------------------------------------- /src/contexts/Allowances.js: -------------------------------------------------------------------------------- 1 | import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react' 2 | import {} from '@web3-react/core' 3 | 4 | import { useWeb3React } from '../hooks' 5 | import { safeAccess, isAddress, getTokenAllowance } from '../utils' 6 | import { useBlockNumber } from './Application' 7 | 8 | const UPDATE = 'UPDATE' 9 | 10 | const AllowancesContext = createContext() 11 | 12 | function useAllowancesContext() { 13 | return useContext(AllowancesContext) 14 | } 15 | 16 | function reducer(state, { type, payload }) { 17 | switch (type) { 18 | case UPDATE: { 19 | const { networkId, address, tokenAddress, spenderAddress, value, blockNumber } = payload 20 | return { 21 | ...state, 22 | [networkId]: { 23 | ...(safeAccess(state, [networkId]) || {}), 24 | [address]: { 25 | ...(safeAccess(state, [networkId, address]) || {}), 26 | [tokenAddress]: { 27 | ...(safeAccess(state, [networkId, address, tokenAddress]) || {}), 28 | [spenderAddress]: { 29 | value, 30 | blockNumber 31 | } 32 | } 33 | } 34 | } 35 | } 36 | } 37 | default: { 38 | throw Error(`Unexpected action type in AllowancesContext reducer: '${type}'.`) 39 | } 40 | } 41 | } 42 | 43 | export default function Provider({ children }) { 44 | const [state, dispatch] = useReducer(reducer, {}) 45 | 46 | const update = useCallback((networkId, address, tokenAddress, spenderAddress, value, blockNumber) => { 47 | dispatch({ type: UPDATE, payload: { networkId, address, tokenAddress, spenderAddress, value, blockNumber } }) 48 | }, []) 49 | 50 | return ( 51 | [state, { update }], [state, update])}> 52 | {children} 53 | 54 | ) 55 | } 56 | 57 | export function useAddressAllowance(address, tokenAddress, spenderAddress) { 58 | const { library, chainId } = useWeb3React() 59 | 60 | const globalBlockNumber = useBlockNumber() 61 | 62 | const [state, { update }] = useAllowancesContext() 63 | const { value, blockNumber } = safeAccess(state, [chainId, address, tokenAddress, spenderAddress]) || {} 64 | 65 | useEffect(() => { 66 | if ( 67 | isAddress(address) && 68 | isAddress(tokenAddress) && 69 | isAddress(spenderAddress) && 70 | (value === undefined || blockNumber !== globalBlockNumber) && 71 | (chainId || chainId === 0) && 72 | library 73 | ) { 74 | let stale = false 75 | 76 | getTokenAllowance(address, tokenAddress, spenderAddress, library) 77 | .then(value => { 78 | if (!stale) { 79 | update(chainId, address, tokenAddress, spenderAddress, value, globalBlockNumber) 80 | } 81 | }) 82 | .catch(() => { 83 | if (!stale) { 84 | update(chainId, address, tokenAddress, spenderAddress, null, globalBlockNumber) 85 | } 86 | }) 87 | 88 | return () => { 89 | stale = true 90 | } 91 | } 92 | }, [address, tokenAddress, spenderAddress, value, blockNumber, globalBlockNumber, chainId, library, update]) 93 | 94 | return value 95 | } 96 | -------------------------------------------------------------------------------- /src/constants/abis/erc20.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [], 5 | "name": "name", 6 | "outputs": [{ "name": "", "type": "string" }], 7 | "payable": false, 8 | "stateMutability": "view", 9 | "type": "function" 10 | }, 11 | { 12 | "constant": false, 13 | "inputs": [{ "name": "_spender", "type": "address" }, { "name": "_value", "type": "uint256" }], 14 | "name": "approve", 15 | "outputs": [{ "name": "", "type": "bool" }], 16 | "payable": false, 17 | "stateMutability": "nonpayable", 18 | "type": "function" 19 | }, 20 | { 21 | "constant": true, 22 | "inputs": [], 23 | "name": "totalSupply", 24 | "outputs": [{ "name": "", "type": "uint256" }], 25 | "payable": false, 26 | "stateMutability": "view", 27 | "type": "function" 28 | }, 29 | { 30 | "constant": false, 31 | "inputs": [ 32 | { "name": "_from", "type": "address" }, 33 | { "name": "_to", "type": "address" }, 34 | { "name": "_value", "type": "uint256" } 35 | ], 36 | "name": "transferFrom", 37 | "outputs": [{ "name": "", "type": "bool" }], 38 | "payable": false, 39 | "stateMutability": "nonpayable", 40 | "type": "function" 41 | }, 42 | { 43 | "constant": true, 44 | "inputs": [], 45 | "name": "decimals", 46 | "outputs": [{ "name": "", "type": "uint8" }], 47 | "payable": false, 48 | "stateMutability": "view", 49 | "type": "function" 50 | }, 51 | { 52 | "constant": true, 53 | "inputs": [{ "name": "_owner", "type": "address" }], 54 | "name": "balanceOf", 55 | "outputs": [{ "name": "balance", "type": "uint256" }], 56 | "payable": false, 57 | "stateMutability": "view", 58 | "type": "function" 59 | }, 60 | { 61 | "constant": true, 62 | "inputs": [], 63 | "name": "symbol", 64 | "outputs": [{ "name": "", "type": "string" }], 65 | "payable": false, 66 | "stateMutability": "view", 67 | "type": "function" 68 | }, 69 | { 70 | "constant": false, 71 | "inputs": [{ "name": "_to", "type": "address" }, { "name": "_value", "type": "uint256" }], 72 | "name": "transfer", 73 | "outputs": [{ "name": "", "type": "bool" }], 74 | "payable": false, 75 | "stateMutability": "nonpayable", 76 | "type": "function" 77 | }, 78 | { 79 | "constant": true, 80 | "inputs": [{ "name": "_owner", "type": "address" }, { "name": "_spender", "type": "address" }], 81 | "name": "allowance", 82 | "outputs": [{ "name": "", "type": "uint256" }], 83 | "payable": false, 84 | "stateMutability": "view", 85 | "type": "function" 86 | }, 87 | { "payable": true, "stateMutability": "payable", "type": "fallback" }, 88 | { 89 | "anonymous": false, 90 | "inputs": [ 91 | { "indexed": true, "name": "owner", "type": "address" }, 92 | { "indexed": true, "name": "spender", "type": "address" }, 93 | { "indexed": false, "name": "value", "type": "uint256" } 94 | ], 95 | "name": "Approval", 96 | "type": "event" 97 | }, 98 | { 99 | "anonymous": false, 100 | "inputs": [ 101 | { "indexed": true, "name": "from", "type": "address" }, 102 | { "indexed": true, "name": "to", "type": "address" }, 103 | { "indexed": false, "name": "value", "type": "uint256" } 104 | ], 105 | "name": "Transfer", 106 | "type": "event" 107 | } 108 | ] 109 | -------------------------------------------------------------------------------- /src/constants/abis/erc20_bytes32.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [], 5 | "name": "name", 6 | "outputs": [{ "name": "", "type": "bytes32" }], 7 | "payable": false, 8 | "stateMutability": "view", 9 | "type": "function" 10 | }, 11 | { 12 | "constant": false, 13 | "inputs": [{ "name": "_spender", "type": "address" }, { "name": "_value", "type": "uint256" }], 14 | "name": "approve", 15 | "outputs": [{ "name": "", "type": "bool" }], 16 | "payable": false, 17 | "stateMutability": "nonpayable", 18 | "type": "function" 19 | }, 20 | { 21 | "constant": true, 22 | "inputs": [], 23 | "name": "totalSupply", 24 | "outputs": [{ "name": "", "type": "uint256" }], 25 | "payable": false, 26 | "stateMutability": "view", 27 | "type": "function" 28 | }, 29 | { 30 | "constant": false, 31 | "inputs": [ 32 | { "name": "_from", "type": "address" }, 33 | { "name": "_to", "type": "address" }, 34 | { "name": "_value", "type": "uint256" } 35 | ], 36 | "name": "transferFrom", 37 | "outputs": [{ "name": "", "type": "bool" }], 38 | "payable": false, 39 | "stateMutability": "nonpayable", 40 | "type": "function" 41 | }, 42 | { 43 | "constant": true, 44 | "inputs": [], 45 | "name": "decimals", 46 | "outputs": [{ "name": "", "type": "uint8" }], 47 | "payable": false, 48 | "stateMutability": "view", 49 | "type": "function" 50 | }, 51 | { 52 | "constant": true, 53 | "inputs": [{ "name": "_owner", "type": "address" }], 54 | "name": "balanceOf", 55 | "outputs": [{ "name": "balance", "type": "uint256" }], 56 | "payable": false, 57 | "stateMutability": "view", 58 | "type": "function" 59 | }, 60 | { 61 | "constant": true, 62 | "inputs": [], 63 | "name": "symbol", 64 | "outputs": [{ "name": "", "type": "bytes32" }], 65 | "payable": false, 66 | "stateMutability": "view", 67 | "type": "function" 68 | }, 69 | { 70 | "constant": false, 71 | "inputs": [{ "name": "_to", "type": "address" }, { "name": "_value", "type": "uint256" }], 72 | "name": "transfer", 73 | "outputs": [{ "name": "", "type": "bool" }], 74 | "payable": false, 75 | "stateMutability": "nonpayable", 76 | "type": "function" 77 | }, 78 | { 79 | "constant": true, 80 | "inputs": [{ "name": "_owner", "type": "address" }, { "name": "_spender", "type": "address" }], 81 | "name": "allowance", 82 | "outputs": [{ "name": "", "type": "uint256" }], 83 | "payable": false, 84 | "stateMutability": "view", 85 | "type": "function" 86 | }, 87 | { "payable": true, "stateMutability": "payable", "type": "fallback" }, 88 | { 89 | "anonymous": false, 90 | "inputs": [ 91 | { "indexed": true, "name": "owner", "type": "address" }, 92 | { "indexed": true, "name": "spender", "type": "address" }, 93 | { "indexed": false, "name": "value", "type": "uint256" } 94 | ], 95 | "name": "Approval", 96 | "type": "event" 97 | }, 98 | { 99 | "anonymous": false, 100 | "inputs": [ 101 | { "indexed": true, "name": "from", "type": "address" }, 102 | { "indexed": true, "name": "to", "type": "address" }, 103 | { "indexed": false, "name": "value", "type": "uint256" } 104 | ], 105 | "name": "Transfer", 106 | "type": "event" 107 | } 108 | ] 109 | -------------------------------------------------------------------------------- /src/components/Web3ReactManager/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import { useWeb3React } from '@web3-react/core' 3 | import styled from 'styled-components' 4 | import { useTranslation } from 'react-i18next' 5 | 6 | import { network } from '../../connectors' 7 | import { useEagerConnect, useInactiveListener } from '../../hooks' 8 | import { Spinner } from '../../theme' 9 | import Circle from '../../assets/images/circle.svg' 10 | import { NetworkContextName } from '../../constants' 11 | 12 | const MessageWrapper = styled.div` 13 | display: flex; 14 | align-items: center; 15 | justify-content: center; 16 | height: 20rem; 17 | ` 18 | 19 | const Message = styled.h2` 20 | color: ${({ theme }) => theme.uniswapPink}; 21 | ` 22 | 23 | const SpinnerWrapper = styled(Spinner)` 24 | font-size: 4rem; 25 | 26 | svg { 27 | path { 28 | color: ${({ theme }) => theme.uniswapPink}; 29 | } 30 | } 31 | ` 32 | 33 | export default function Web3ReactManager({ children }) { 34 | const { t } = useTranslation() 35 | const { active } = useWeb3React() 36 | const { active: networkActive, error: networkError, activate: activateNetwork } = useWeb3React(NetworkContextName) 37 | 38 | // try to eagerly connect to an injected provider, if it exists and has granted access already 39 | const triedEager = useEagerConnect() 40 | 41 | // after eagerly trying injected, if the network connect ever isn't active or in an error state, activate itd 42 | // TODO think about not doing this at all 43 | useEffect(() => { 44 | if (triedEager && !networkActive && !networkError && !active) { 45 | activateNetwork(network) 46 | } 47 | }, [triedEager, networkActive, networkError, activateNetwork, active]) 48 | 49 | // 'pause' the network connector if we're ever connected to an account and it's active 50 | useEffect(() => { 51 | if (active && networkActive) { 52 | network.pause() 53 | } 54 | }, [active, networkActive]) 55 | 56 | // 'resume' the network connector if we're ever not connected to an account and it's active 57 | useEffect(() => { 58 | if (!active && networkActive) { 59 | network.resume() 60 | } 61 | }, [active, networkActive]) 62 | 63 | // when there's no account connected, react to logins (broadly speaking) on the injected provider, if it exists 64 | useInactiveListener(!triedEager) 65 | 66 | // handle delayed loader state 67 | const [showLoader, setShowLoader] = useState(false) 68 | useEffect(() => { 69 | const timeout = setTimeout(() => { 70 | setShowLoader(true) 71 | }, 600) 72 | 73 | return () => { 74 | clearTimeout(timeout) 75 | } 76 | }, []) 77 | 78 | // on page load, do nothing until we've tried to connect to the injected connector 79 | if (!triedEager) { 80 | return null 81 | } 82 | 83 | // if the account context isn't active, and there's an error on the network context, it's an irrecoverable error 84 | if (!active && networkError) { 85 | return ( 86 | 87 | {t('unknownError')} 88 | 89 | ) 90 | } 91 | 92 | // if neither context is active, spin 93 | if (!active && !networkActive) { 94 | return showLoader ? ( 95 | 96 | 97 | 98 | ) : null 99 | } 100 | 101 | return children 102 | } 103 | -------------------------------------------------------------------------------- /src/components/AccountDetails/Transaction.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled, { keyframes } from 'styled-components' 3 | import { Check } from 'react-feather' 4 | 5 | import { useWeb3React } from '../../hooks' 6 | import { getEtherscanLink } from '../../utils' 7 | import { Link, Spinner } from '../../theme' 8 | import Copy from './Copy' 9 | import Circle from '../../assets/images/circle.svg' 10 | 11 | import { transparentize } from 'polished' 12 | 13 | const TransactionStatusWrapper = styled.div` 14 | display: flex; 15 | align-items: center; 16 | min-width: 12px; 17 | overflow: hidden; 18 | text-overflow: ellipsis; 19 | white-space: nowrap; 20 | ` 21 | 22 | const TransactionWrapper = styled.div` 23 | ${({ theme }) => theme.flexRowNoWrap} 24 | justify-content: space-between; 25 | width: 100%; 26 | margin-top: 0.75rem; 27 | a { 28 | /* flex: 1 1 auto; */ 29 | overflow: hidden; 30 | text-overflow: ellipsis; 31 | white-space: nowrap; 32 | min-width: 0; 33 | max-width: 250px; 34 | } 35 | ` 36 | 37 | const TransactionStatusText = styled.span` 38 | margin-left: 0.5rem; 39 | ` 40 | 41 | const rotate = keyframes` 42 | from { 43 | transform: rotate(0deg); 44 | } 45 | to { 46 | transform: rotate(360deg); 47 | } 48 | ` 49 | 50 | const TransactionState = styled.div` 51 | display: flex; 52 | background-color: ${({ pending, theme }) => 53 | pending ? transparentize(0.95, theme.royalBlue) : transparentize(0.95, theme.connectedGreen)}; 54 | border-radius: 1.5rem; 55 | padding: 0.5rem 0.75rem; 56 | font-weight: 500; 57 | font-size: 0.75rem; 58 | border: 1px solid; 59 | border-color: ${({ pending, theme }) => 60 | pending ? transparentize(0.75, theme.royalBlue) : transparentize(0.75, theme.connectedGreen)}; 61 | 62 | #pending { 63 | animation: 2s ${rotate} linear infinite; 64 | } 65 | 66 | :hover { 67 | border-color: ${({ pending, theme }) => 68 | pending ? transparentize(0, theme.royalBlue) : transparentize(0, theme.connectedGreen)}; 69 | } 70 | ` 71 | const ButtonWrapper = styled.div` 72 | a { 73 | color: ${({ pending, theme }) => (pending ? theme.royalBlue : theme.connectedGreen)}; 74 | } 75 | ` 76 | 77 | export default function Transaction({ hash, pending }) { 78 | const { chainId } = useWeb3React() 79 | 80 | return ( 81 | 82 | 83 | {hash} ↗ 84 | 85 | 86 | {pending ? ( 87 | 88 | 89 | 90 | 91 | Pending 92 | 93 | 94 | 95 | ) : ( 96 | 97 | 98 | 99 | 100 | Confirmed 101 | 102 | 103 | 104 | )} 105 | 106 | ) 107 | } 108 | -------------------------------------------------------------------------------- /src/contexts/Application.js: -------------------------------------------------------------------------------- 1 | import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react' 2 | 3 | import { useWeb3React } from '../hooks' 4 | import { safeAccess } from '../utils' 5 | 6 | const BLOCK_NUMBER = 'BLOCK_NUMBER' 7 | const USD_PRICE = 'USD_PRICE' 8 | const WALLET_MODAL_OPEN = 'WALLET_MODAL_OPEN' 9 | 10 | const UPDATE_BLOCK_NUMBER = 'UPDATE_BLOCK_NUMBER' 11 | const TOGGLE_WALLET_MODAL = 'TOGGLE_WALLET_MODAL' 12 | 13 | const ApplicationContext = createContext() 14 | 15 | function useApplicationContext() { 16 | return useContext(ApplicationContext) 17 | } 18 | 19 | function reducer(state, { type, payload }) { 20 | switch (type) { 21 | case UPDATE_BLOCK_NUMBER: { 22 | const { networkId, blockNumber } = payload 23 | return { 24 | ...state, 25 | [BLOCK_NUMBER]: { 26 | ...(safeAccess(state, [BLOCK_NUMBER]) || {}), 27 | [networkId]: blockNumber 28 | } 29 | } 30 | } 31 | 32 | case TOGGLE_WALLET_MODAL: { 33 | return { ...state, [WALLET_MODAL_OPEN]: !state[WALLET_MODAL_OPEN] } 34 | } 35 | default: { 36 | throw Error(`Unexpected action type in ApplicationContext reducer: '${type}'.`) 37 | } 38 | } 39 | } 40 | 41 | export default function Provider({ children }) { 42 | const [state, dispatch] = useReducer(reducer, { 43 | [BLOCK_NUMBER]: {}, 44 | [USD_PRICE]: {}, 45 | [WALLET_MODAL_OPEN]: true 46 | }) 47 | 48 | const updateBlockNumber = useCallback((networkId, blockNumber) => { 49 | dispatch({ type: UPDATE_BLOCK_NUMBER, payload: { networkId, blockNumber } }) 50 | }, []) 51 | 52 | const toggleWalletModal = useCallback(() => { 53 | dispatch({ type: TOGGLE_WALLET_MODAL }) 54 | }, []) 55 | 56 | return ( 57 | [state, { updateBlockNumber, toggleWalletModal }], [ 59 | state, 60 | updateBlockNumber, 61 | toggleWalletModal 62 | ])} 63 | > 64 | {children} 65 | 66 | ) 67 | } 68 | 69 | export function Updater() { 70 | const { library, chainId } = useWeb3React() 71 | 72 | const [, { updateBlockNumber }] = useApplicationContext() 73 | 74 | // update block number 75 | useEffect(() => { 76 | if (library) { 77 | let stale = false 78 | 79 | function update() { 80 | library 81 | .getBlockNumber() 82 | .then(blockNumber => { 83 | if (!stale) { 84 | updateBlockNumber(chainId, blockNumber) 85 | } 86 | }) 87 | .catch(() => { 88 | if (!stale) { 89 | updateBlockNumber(chainId, null) 90 | } 91 | }) 92 | } 93 | 94 | update() 95 | library.on('block', update) 96 | 97 | return () => { 98 | stale = true 99 | library.removeListener('block', update) 100 | } 101 | } 102 | }, [chainId, library, updateBlockNumber]) 103 | 104 | return null 105 | } 106 | 107 | export function useBlockNumber() { 108 | const { chainId } = useWeb3React() 109 | 110 | const [state] = useApplicationContext() 111 | 112 | return safeAccess(state, [BLOCK_NUMBER, chainId]) 113 | } 114 | 115 | export function useWalletModalOpen() { 116 | const [state] = useApplicationContext() 117 | 118 | return state[WALLET_MODAL_OPEN] 119 | } 120 | 121 | export function useWalletModalToggle() { 122 | const [, { toggleWalletModal }] = useApplicationContext() 123 | 124 | return toggleWalletModal 125 | } 126 | -------------------------------------------------------------------------------- /src/components/ContextualInfo/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | import styled from 'styled-components' 4 | 5 | import ReactGA from 'react-ga' 6 | import { ReactComponent as Dropup } from '../../assets/images/dropup-blue.svg' 7 | import { ReactComponent as Dropdown } from '../../assets/images/dropdown-blue.svg' 8 | 9 | const SummaryWrapper = styled.div` 10 | color: ${({ error, theme }) => (error ? theme.salmonRed : theme.doveGray)}; 11 | font-size: 0.75rem; 12 | text-align: center; 13 | margin-top: 1rem; 14 | padding-top: 1rem; 15 | ` 16 | 17 | const Details = styled.div` 18 | background-color: ${({ theme }) => theme.concreteGray}; 19 | padding: 1.5rem; 20 | border-radius: 1rem; 21 | font-size: 0.75rem; 22 | margin-top: 1rem; 23 | ` 24 | 25 | const SummaryWrapperContainer = styled.div` 26 | ${({ theme }) => theme.flexRowNoWrap}; 27 | color: ${({ theme }) => theme.royalBlue}; 28 | text-align: center; 29 | margin-top: 1rem; 30 | padding-top: 1rem; 31 | cursor: pointer; 32 | align-items: center; 33 | justify-content: center; 34 | font-size: 0.75rem; 35 | 36 | span { 37 | margin-right: 12px; 38 | } 39 | 40 | img { 41 | height: 0.75rem; 42 | width: 0.75rem; 43 | } 44 | ` 45 | 46 | const WrappedDropup = ({ isError, highSlippageWarning, ...rest }) => 47 | const ColoredDropup = styled(WrappedDropup)` 48 | path { 49 | stroke: ${({ theme }) => theme.royalBlue}; 50 | } 51 | ` 52 | 53 | const WrappedDropdown = ({ isError, highSlippageWarning, ...rest }) => 54 | const ColoredDropdown = styled(WrappedDropdown)` 55 | path { 56 | stroke: ${({ theme }) => theme.royalBlue}; 57 | } 58 | ` 59 | 60 | class ContextualInfo extends Component { 61 | static propTypes = { 62 | openDetailsText: PropTypes.string, 63 | renderTransactionDetails: PropTypes.func, 64 | contextualInfo: PropTypes.string, 65 | isError: PropTypes.bool 66 | } 67 | 68 | static defaultProps = { 69 | openDetailsText: 'Advanced Details', 70 | closeDetailsText: 'Hide Advanced', 71 | renderTransactionDetails() {}, 72 | contextualInfo: '', 73 | isError: false 74 | } 75 | 76 | state = { 77 | showDetails: false 78 | } 79 | 80 | renderDetails() { 81 | if (!this.state.showDetails) { 82 | return null 83 | } 84 | 85 | return
{this.props.renderTransactionDetails()}
86 | } 87 | 88 | render() { 89 | const { openDetailsText, closeDetailsText, contextualInfo, isError } = this.props 90 | 91 | if (contextualInfo) { 92 | return {contextualInfo} 93 | } 94 | 95 | return ( 96 | <> 97 | { 99 | !this.state.showDetails && 100 | ReactGA.event({ 101 | category: 'Advanced Interaction', 102 | action: 'Open Advanced Details', 103 | label: 'Pool Page Details' 104 | }) 105 | this.setState(prevState => { 106 | return { showDetails: !prevState.showDetails } 107 | }) 108 | }} 109 | > 110 | {!this.state.showDetails ? ( 111 | <> 112 | {openDetailsText} 113 | 114 | 115 | ) : ( 116 | <> 117 | {closeDetailsText} 118 | 119 | 120 | )} 121 | 122 | {this.renderDetails()} 123 | 124 | ) 125 | } 126 | } 127 | 128 | export default ContextualInfo 129 | -------------------------------------------------------------------------------- /public/locales/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "noWallet": "Кошелек эфира не найден", 3 | "wrongNetwork": "Некорректная сеть", 4 | "switchNetwork": "Пожалуйста, перейдите в {{ correctNetwork }}", 5 | "installWeb3MobileBrowser": "Пожалуйста, откройте в браузере с поддержкой web3, таком, как Trust Wallet или Coinbase Wallet.", 6 | "installMetamask": "Пожалуйста, откройте в Chrome или Brave с установленным расширением Metamask.", 7 | "disconnected": "Нет подключения", 8 | "swap": "Обменять", 9 | "send": "Отправить", 10 | "pool": "Вложить", 11 | "betaWarning": "Проект находится на стадии бета тестирования.", 12 | "input": "Ввести", 13 | "output": "Вывести", 14 | "estimated": "по оценке", 15 | "balance": "Баланс: {{ balanceInput }}", 16 | "unlock": "Разблокировать", 17 | "pending": "Ожидание", 18 | "selectToken": "Выберите токен", 19 | "searchOrPaste": "Поиск токена или вставить адрес токена", 20 | "noExchange": "Обмен не найден", 21 | "exchangeRate": "Курс обмена", 22 | "enterValueCont": "Введите {{ missingCurrencyValue }}, чтобы продолжить.", 23 | "selectTokenCont": "Введите токен, чтобы продолжить.", 24 | "noLiquidity": "Нет ликвидности.", 25 | "unlockTokenCont": "Пожалуйста, разблокируйте токен, чтобы продолжить.", 26 | "transactionDetails": "Детали транзакции", 27 | "hideDetails": "Скрыть подробности", 28 | "youAreSelling": "Вы продаете", 29 | "orTransFail": "или транзакция будет отклонена.", 30 | "youWillReceive": "Вы получите как минимум", 31 | "youAreBuying": "Вы покупаете", 32 | "itWillCost": "В крайнем случае это будет стоить", 33 | "insufficientBalance": "Недостаточно средств", 34 | "inputNotValid": "Некорректное значение", 35 | "differentToken": "Должны быть разные токены.", 36 | "noRecipient": "Введите адрес кошелька эфира, куда перечислить.", 37 | "invalidRecipient": "Пожалуйста, введите корректный адрес кошелька получателя.", 38 | "recipientAddress": "Адрес получателя", 39 | "youAreSending": "Вы отправляете", 40 | "willReceive": "получит как минимум", 41 | "to": "", 42 | "addLiquidity": "Добавить ликвидность", 43 | "deposit": "Вложить", 44 | "currentPoolSize": "Текущий размер пула", 45 | "yourPoolShare": "Ваша доля в пуле", 46 | "noZero": "Значение не может быть нулевым.", 47 | "mustBeETH": "Одно из значений должно быть ETH.", 48 | "enterCurrencyOrLabelCont": "Введите {{ inputCurrency }} или {{ label }}, чтобы продолжить.", 49 | "youAreAdding": "Вы добавляете от", 50 | "and": "и", 51 | "intoPool": "в пул ликвидности.", 52 | "outPool": "из пула ликвидности.", 53 | "youWillMint": "Вы произведёте", 54 | "liquidityTokens": "токенов ликвидности.", 55 | "totalSupplyIs": "Ваш объем токенов ликвидности", 56 | "youAreSettingExRate": "Вы устанавливаете начальный курс обмена", 57 | "totalSupplyIs0": "Ваш объем токенов ликвидности 0.", 58 | "tokenWorth": "При текущем курсе, каждый токен пула оценивается в", 59 | "firstLiquidity": "Вы первый, кто создаст ликвидность!", 60 | "initialExchangeRate": "Начальный курс обмена будет установлен согласно вашим депозитам. Убедитесь, что ваши депозиты ETH и {{ label }} имеют одинаковое значение в валюте.", 61 | "removeLiquidity": "Убрать ликвидность", 62 | "poolTokens": "Токены пула", 63 | "enterLabelCont": "Введите {{ label }}, чтобы продолжить.", 64 | "youAreRemoving": "Вы убираете в от", 65 | "youWillRemove": "Вы уберёте", 66 | "createExchange": "Создать обмен", 67 | "invalidTokenAddress": "Некорректный адрес токена", 68 | "exchangeExists": "{{ label }} Обмен уже существует!", 69 | "invalidSymbol": "Некорректный символ", 70 | "invalidDecimals": "Некорректное десятичное значение", 71 | "tokenAddress": "Адрес токена", 72 | "label": "Название", 73 | "decimals": "Десятичное значение", 74 | "enterTokenCont": "Чтобы продолжить, введите адрес токена", 75 | "connectToWallet": "Подключиться к кошельку" 76 | } 77 | -------------------------------------------------------------------------------- /src/components/WalletModal/Option.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import { Link } from '../../theme' 4 | 5 | const InfoCard = styled.button` 6 | background-color: ${({ theme, active }) => (active ? theme.activeGray : theme.backgroundColor)}; 7 | padding: 1rem; 8 | outline: none; 9 | border: 1px solid; 10 | border-radius: 12px; 11 | width: 100% !important; 12 | &:focus { 13 | box-shadow: 0 0 0 1px ${({ theme }) => theme.royalBlue}; 14 | } 15 | border-color: ${({ theme, active }) => (active ? 'transparent' : theme.placeholderGray)}; 16 | ` 17 | 18 | const OptionCard = styled(InfoCard)` 19 | display: flex; 20 | flex-direction: row; 21 | align-items: center; 22 | justify-content: space-between; 23 | margin-top: 2rem; 24 | padding: 1rem; 25 | ` 26 | 27 | const OptionCardLeft = styled.div` 28 | ${({ theme }) => theme.flexColumnNoWrap}; 29 | justify-content: center; 30 | height: 100%; 31 | ` 32 | 33 | const OptionCardClickable = styled(OptionCard)` 34 | margin-top: 0; 35 | &:hover { 36 | cursor: ${({ clickable }) => (clickable ? 'pointer' : '')}; 37 | border: ${({ clickable, theme }) => (clickable ? `1px solid ${theme.malibuBlue}` : ``)}; 38 | } 39 | opacity: ${({ disabled }) => (disabled ? '0.5' : '1')}; 40 | ` 41 | 42 | const GreenCircle = styled.div` 43 | ${({ theme }) => theme.flexRowNoWrap} 44 | justify-content: center; 45 | align-items: center; 46 | 47 | &:first-child { 48 | height: 8px; 49 | width: 8px; 50 | margin-right: 8px; 51 | background-color: ${({ theme }) => theme.connectedGreen}; 52 | border-radius: 50%; 53 | } 54 | ` 55 | 56 | const CircleWrapper = styled.div` 57 | color: ${({ theme }) => theme.connectedGreen}; 58 | display: flex; 59 | justify-content: center; 60 | align-items: center; 61 | ` 62 | 63 | const HeaderText = styled.div` 64 | ${({ theme }) => theme.flexRowNoWrap}; 65 | color: ${props => (props.color === 'blue' ? ({ theme }) => theme.royalBlue : ({ theme }) => theme.textColor)}; 66 | font-size: 1rem; 67 | font-weight: 500; 68 | ` 69 | 70 | const SubHeader = styled.div` 71 | color: ${({ theme }) => theme.textColor}; 72 | margin-top: 10px; 73 | font-size: 12px; 74 | ` 75 | 76 | const IconWrapper = styled.div` 77 | ${({ theme }) => theme.flexColumnNoWrap}; 78 | align-items: center; 79 | justify-content: center; 80 | & > img, 81 | span { 82 | height: ${({ size }) => (size ? size + 'px' : '24px')}; 83 | width: ${({ size }) => (size ? size + 'px' : '24px')}; 84 | } 85 | ${({ theme }) => theme.mediaWidth.upToMedium` 86 | align-items: flex-end; 87 | `}; 88 | ` 89 | 90 | export default function Option({ 91 | link = null, 92 | clickable = true, 93 | size = null, 94 | onClick = null, 95 | color, 96 | header, 97 | subheader = null, 98 | icon, 99 | active = false 100 | }) { 101 | const content = ( 102 | 103 | 104 | 105 | {' '} 106 | {active ? ( 107 | 108 | 109 |
110 | 111 | 112 | ) : ( 113 | '' 114 | )} 115 | {header} 116 | 117 | {subheader && {subheader}} 118 | 119 | 120 | {'Icon'} 121 | 122 | 123 | ) 124 | if (link) { 125 | return {content} 126 | } 127 | 128 | return content 129 | } 130 | -------------------------------------------------------------------------------- /public/locales/es-AR.json: -------------------------------------------------------------------------------- 1 | { 2 | "noWallet": "No se encontró billetera de Ethereum", 3 | "wrongNetwork": "Te encontrás en la red equivocada", 4 | "switchNetwork": "Por favor cambia a {{ correctNetwork }}", 5 | "installWeb3MobileBrowser": "Por favor ingresá desde un navegador móvil con web3 habilitado como Trust Wallet o Coinbase Wallet.", 6 | "installMetamask": "Por favor visítanos nuevamente luego de instalar Metamask en Chrome o Brave.", 7 | "disconnected": "Desconectado", 8 | "swap": "Intercambiar", 9 | "send": "Enviar", 10 | "pool": "Pool", 11 | "betaWarning": "Este proyecto se encuentra en beta. Usalo bajo tu propio riesgo.", 12 | "input": "Entrada", 13 | "output": "Salida", 14 | "estimated": "estimado", 15 | "balance": "Saldo: {{ balanceInput }}", 16 | "unlock": "Desbloquear", 17 | "pending": "Pendiente", 18 | "selectToken": "Seleccioná un token", 19 | "searchOrPaste": "Buscar Token o Pegar Dirección", 20 | "noExchange": "No se encontró la divisa", 21 | "exchangeRate": "Tasa de Cambio", 22 | "enterValueCont": "Ingresá un valor en {{ missingCurrencyValue }} para continuar.", 23 | "selectTokenCont": "Seleccioná un token para continuar.", 24 | "noLiquidity": "Sin liquidez.", 25 | "unlockTokenCont": "Por favor desbloqueá un token para continuar.", 26 | "transactionDetails": "Detalles de la transacción", 27 | "hideDetails": "Ocultar detalles", 28 | "youAreSelling": "Estás vendiendo", 29 | "orTransFail": "o la transacción fallará.", 30 | "youWillReceive": "Vas a recibir al menos", 31 | "youAreBuying": "Estás comprando", 32 | "itWillCost": "Costará a lo sumo", 33 | "insufficientBalance": "Saldo insuficiente", 34 | "inputNotValid": "No es un valor de entrada válido", 35 | "differentToken": "Debe ser un token distinto.", 36 | "noRecipient": "Ingresá una dirección de billetera para enviar.", 37 | "invalidRecipient": "Por favor ingrese una billetera de destino válida.", 38 | "recipientAddress": "Dirección del recipiente", 39 | "youAreSending": "Estás enviando", 40 | "willReceive": "recibirá al menos", 41 | "to": "a", 42 | "addLiquidity": "Agregar liquidez", 43 | "deposit": "Depositar", 44 | "currentPoolSize": "Tamaño del Pool Actual", 45 | "yourPoolShare": "Tu parte del Pool", 46 | "noZero": "El monto no puede ser cero.", 47 | "mustBeETH": "Una de las entradas debe ser ETH.", 48 | "enterCurrencyOrLabelCont": "Ingresá un valor de {{ inputCurrency }} o de {{ label }} para continuar.", 49 | "youAreAdding": "Estás agregando entre", 50 | "and": "y", 51 | "intoPool": "en el pool de liquidez.", 52 | "outPool": "en el pool de liquidez.", 53 | "youWillMint": "Vas a acuñar", 54 | "liquidityTokens": "tokens de liquidez.", 55 | "totalSupplyIs": "El actual suministro total de tokens de liquidez es", 56 | "youAreSettingExRate": "Está configurando el tipo de cambio inicial a", 57 | "totalSupplyIs0": "El actual suministro total de tokens de liquidez es 0.", 58 | "tokenWorth": "Al tipo de cambio actual, cada token del pool vale", 59 | "firstLiquidity": "Sos la primer persona en agregar liquidez!", 60 | "initialExchangeRate": "El tipo de cambio inicial se establecerá en función de tus depósitos. Por favor, asegúrate de que tus depósitos en ETH y {{ label }} tengan el mismo valor fíat.", 61 | "removeLiquidity": "Remover Liquidez", 62 | "poolTokens": "Pool de Tokens", 63 | "enterLabelCont": "Ingresá un valor de {{ label }} para continuar.", 64 | "youAreRemoving": "Estás quitando entre", 65 | "youWillRemove": "Vas a remover", 66 | "createExchange": "Crear divisa", 67 | "invalidTokenAddress": "No es una dirección de token válida", 68 | "exchangeExists": "La divisa {{ label }} ya existe!", 69 | "invalidSymbol": "Símbolo inválido", 70 | "invalidDecimals": "Decimales inválidos", 71 | "tokenAddress": "Dirección de Token", 72 | "label": "Etiqueta", 73 | "decimals": "Decimales", 74 | "enterTokenCont": "Ingresá una dirección de token para continuar" 75 | } 76 | -------------------------------------------------------------------------------- /public/locales/es-US.json: -------------------------------------------------------------------------------- 1 | { 2 | "noWallet": "No se ha encontrado billetera de Ethereum", 3 | "wrongNetwork": "Se encuentra en la red equivocada", 4 | "switchNetwork": "Por favor cambie a {{ correctNetwork }}", 5 | "installWeb3MobileBrowser": "Por favor ingrese desde un navegador móvil con web3 habilitado como Trust Wallet o Coinbase Wallet.", 6 | "installMetamask": "Por favor visítenos nuevamente luego de instalar Metamask en Chrome o Brave.", 7 | "disconnected": "Desconectado", 8 | "swap": "Intercambiar", 9 | "send": "Enviar", 10 | "pool": "Pool", 11 | "betaWarning": "Este proyecto se encuentra en beta. Úselo bajo tu propio riesgo.", 12 | "input": "Entrada", 13 | "output": "Salida", 14 | "estimated": "estimado", 15 | "balance": "Saldo: {{ balanceInput }}", 16 | "unlock": "Desbloquear", 17 | "pending": "Pendiente", 18 | "selectToken": "Seleccione un token", 19 | "searchOrPaste": "Buscar Token o Pegar Dirección", 20 | "noExchange": "No se ha encontrado la divisa", 21 | "exchangeRate": "Tasa de Cambio", 22 | "enterValueCont": "Ingrese un valor en {{ missingCurrencyValue }} para continuar.", 23 | "selectTokenCont": "Seleccione un token para continuar.", 24 | "noLiquidity": "Sin liquidez.", 25 | "unlockTokenCont": "Por favor desbloquea un token para continuar.", 26 | "transactionDetails": "Detalles de la transacción", 27 | "hideDetails": "Ocultar detalles", 28 | "youAreSelling": "Está vendiendo", 29 | "orTransFail": "o la transacción fallará.", 30 | "youWillReceive": "Va a recibir al menos", 31 | "youAreBuying": "Está comprando", 32 | "itWillCost": "Costará a lo sumo", 33 | "insufficientBalance": "Saldo insuficiente", 34 | "inputNotValid": "No es un valor de entrada válido", 35 | "differentToken": "Debe ser un token distinto.", 36 | "noRecipient": "Ingrese una dirección de billetera para enviar.", 37 | "invalidRecipient": "Por favor ingrese una billetera de destino válida.", 38 | "recipientAddress": "Dirección del recipiente", 39 | "youAreSending": "Está enviando", 40 | "willReceive": "recibirá al menos", 41 | "to": "a", 42 | "addLiquidity": "Agregar liquidez", 43 | "deposit": "Depositar", 44 | "currentPoolSize": "Tamaño del Pool Actual", 45 | "yourPoolShare": "Su parte del Pool", 46 | "noZero": "El monto no puede ser cero.", 47 | "mustBeETH": "Una de las entradas debe ser ETH.", 48 | "enterCurrencyOrLabelCont": "Ingrese un valor de {{ inputCurrency }} o de {{ label }} para continuar.", 49 | "youAreAdding": "Está agregando entre", 50 | "and": "y", 51 | "intoPool": "en el pool de liquidez.", 52 | "outPool": "en el pool de liquidez.", 53 | "youWillMint": "Va a acuñar", 54 | "liquidityTokens": "tokens de liquidez.", 55 | "totalSupplyIs": "El actual suministro total de tokens de liquidez es", 56 | "youAreSettingExRate": "Está configurando el tipo de cambio inicial a", 57 | "totalSupplyIs0": "El actual suministro total de tokens de liquidez es 0.", 58 | "tokenWorth": "Al tipo de cambio actual, cada token del pool vale", 59 | "firstLiquidity": "Es la primer persona en agregar liquidez!", 60 | "initialExchangeRate": "El tipo de cambio inicial se establecerá en función de sus depósitos. Por favor, asegúrese de que sus depósitos en ETH y {{ label }} tengan el mismo valor fíat.", 61 | "removeLiquidity": "Remover Liquidez", 62 | "poolTokens": "Pool de Tokens", 63 | "enterLabelCont": "Ingresa un valor de {{ label }} para continuar.", 64 | "youAreRemoving": "Está quitando entre", 65 | "youWillRemove": "Va a remover", 66 | "createExchange": "Crear tipo de cambio", 67 | "invalidTokenAddress": "No es una dirección de token válida", 68 | "exchangeExists": "El tipo de cambio {{ label }} ya existe!", 69 | "invalidSymbol": "Símbolo inválido", 70 | "invalidDecimals": "Decimales inválidos", 71 | "tokenAddress": "Dirección de Token", 72 | "label": "Etiqueta", 73 | "decimals": "Decimales", 74 | "enterTokenCont": "Ingrese una dirección de token para continuar" 75 | } 76 | -------------------------------------------------------------------------------- /public/locales/ro.json: -------------------------------------------------------------------------------- 1 | { 2 | "noWallet": "Niciun portofel Ethereum găsit", 3 | "wrongNetwork": "Nu ești conectat la rețeaua corectă", 4 | "switchNetwork": "Conectează-te te rog la {{ correctNetwork }}", 5 | "installWeb3MobileBrowser": "Incearcă să vizitezi această pagina folosind un browser precum Trust Wallet sau Coinbase Wallet.", 6 | "installMetamask": "Vizitează această pagină din nou după ce instalezi MetaMask în Chrome sau Brave", 7 | "disconnected": "Deconectat", 8 | "swap": "Schimba", 9 | "send": "Trimite", 10 | "pool": "Extrage", 11 | "betaWarning": "Proiectul este încă în versiunea beta. Folosește-l cu grijă, riscul este al tău.", 12 | "input": "Input", 13 | "output": "Output", 14 | "estimated": "estimat", 15 | "balance": "Balanță: {{ balanceInput }}", 16 | "unlock": "Deschide", 17 | "pending": "În Așteptare", 18 | "selectToken": "Selectează un token", 19 | "searchOrPaste": "Caută Token sau Setează Adresă", 20 | "noExchange": "Niciun Exchange Găsit", 21 | "exchangeRate": "Curs Valutar", 22 | "enterValueCont": "Setează o valoare pentru {{ missingCurrencyValue }} pentru a continua.", 23 | "selectTokenCont": "Selectează un token pentru a continua.", 24 | "noLiquidity": "Lichiditate Inexistentă.", 25 | "unlockTokenCont": "Te rog deblochează tokenul pentru a continua.", 26 | "transactionDetails": "Detalii Tranzacții", 27 | "hideDetails": "Ascunde Detaili", 28 | "youAreSelling": "Vinzi", 29 | "orTransFail": "sau tranzacția va eșua.", 30 | "youWillReceive": "Vei primi cel puțin", 31 | "youAreBuying": "Cumperi", 32 | "itWillCost": "Va costa cel mult", 33 | "insufficientBalance": "Balanță Insuficientă", 34 | "inputNotValid": "Valoarea setată nu este validă", 35 | "differentToken": "Trebuie să fie un token diferit.", 36 | "noRecipient": "Setează o adresă de portofel pentru destinatar.", 37 | "invalidRecipient": "Te rog setează o adresă de portofel validă pentru destinatar.", 38 | "recipientAddress": "Adresa Destinatarului", 39 | "youAreSending": "Trimiți", 40 | "willReceive": "vei primi cel puțin", 41 | "to": "spre", 42 | "addLiquidity": "Crește Lichiditatea", 43 | "deposit": "Depozitează", 44 | "currentPoolSize": "Mărimea Actualului Fond Comun", 45 | "yourPoolShare": "Partea Ta Din Fond Comun", 46 | "noZero": "Cantitatea nu poate fi zero.", 47 | "mustBeETH": "Una dintre valori trebuie să fie ETH.", 48 | "enterCurrencyOrLabelCont": "Setează o valoare pentru {{ inputCurrency }} sau {{ label }} pentru a continua. ", 49 | "youAreAdding": "Adaugi între", 50 | "and": "și", 51 | "intoPool": "în fondul comun de lichidatate.", 52 | "outPool": "din fondul comun de lichiditate.", 53 | "youWillMint": "Vei printa", 54 | "liquidityTokens": "tokene disponibile.", 55 | "totalSupplyIs": "Actuala ofertă totală de tokene disponibile este", 56 | "youAreSettingExRate": "Setezi cursul valutar inițial la", 57 | "totalSupplyIs0": "Actuala ofertă totală de tokene disponibile este 0.", 58 | "tokenWorth": "La acest schimb valutar, fiecare token disponibil este estimată la", 59 | "firstLiquidity": "Ești prima persoană care aduce lichiditate!", 60 | "initialExchangeRate": "Schimbul valutar inițial va fi setat în funcție de depozitul tău. Te rog asigură-te că depoziturile ETH și {{ label }} pe care le-ai făcut au aceeași valoare în monezi naționale.", 61 | "removeLiquidity": "Elimină Lichiditate", 62 | "poolTokens": "Extrage Tokene", 63 | "enterLabelCont": "Setează o valoare pentru {{ label }} pentru a continua.", 64 | "youAreRemoving": "Elimini între", 65 | "youWillRemove": "Vei elimina", 66 | "createExchange": "Creează Exchange", 67 | "invalidTokenAddress": "Adresă de token nu este validă", 68 | "exchangeExists": "{{ label }} Exchange există deja!", 69 | "invalidSymbol": "Simbol invalid!", 70 | "invalidDecimals": "Zecimale invalidate", 71 | "tokenAddress": "Adresă Token", 72 | "label": "Denumire", 73 | "decimals": "Zecimale", 74 | "enterTokenCont": "Setează o adresă de token pentru a continua" 75 | } 76 | -------------------------------------------------------------------------------- /src/components/WalletModal/PendingView.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import Option from './Option' 4 | import { SUPPORTED_WALLETS } from '../../constants' 5 | import WalletConnectData from './WalletConnectData' 6 | import { walletconnect, injected } from '../../connectors' 7 | import { Spinner } from '../../theme' 8 | import Circle from '../../assets/images/circle.svg' 9 | import { darken } from 'polished' 10 | 11 | const PendingSection = styled.div` 12 | ${({ theme }) => theme.flexColumnNoWrap}; 13 | align-items: center; 14 | justify-content: center; 15 | width: 100%; 16 | & > * { 17 | width: 100%; 18 | } 19 | ` 20 | 21 | const SpinnerWrapper = styled(Spinner)` 22 | font-size: 4rem; 23 | margin-right: 1rem; 24 | svg { 25 | path { 26 | color: ${({ theme }) => theme.placeholderGray}; 27 | } 28 | } 29 | ` 30 | 31 | const LoadingMessage = styled.div` 32 | ${({ theme }) => theme.flexRowNoWrap}; 33 | align-items: center; 34 | justify-content: flex-start; 35 | border-radius: 12px; 36 | margin-bottom: 20px; 37 | color: ${({ theme, error }) => (error ? theme.salmonRed : 'inherit')}; 38 | border: 1px solid ${({ theme, error }) => (error ? theme.salmonRed : theme.placeholderGray)}; 39 | 40 | & > * { 41 | padding: 1rem; 42 | } 43 | ` 44 | 45 | const ErrorGroup = styled.div` 46 | ${({ theme }) => theme.flexRowNoWrap}; 47 | align-items: center; 48 | justify-content: flex-start; 49 | ` 50 | 51 | const ErrorButton = styled.div` 52 | border-radius: 8px; 53 | font-size: 12px; 54 | color: ${({ theme }) => theme.textColor}; 55 | background-color: ${({ theme }) => theme.placeholderGray}; 56 | margin-left: 1rem; 57 | padding: 0.5rem; 58 | font-weight: 600; 59 | user-select: none; 60 | 61 | &:hover { 62 | cursor: pointer; 63 | background-color: ${({ theme }) => darken(0.1, theme.placeholderGray)}; 64 | } 65 | ` 66 | 67 | const LoadingWrapper = styled.div` 68 | ${({ theme }) => theme.flexRowNoWrap}; 69 | align-items: center; 70 | justify-content: center; 71 | ` 72 | 73 | export default function PendingView({ uri = '', size, connector, error = false, setPendingError, tryActivation }) { 74 | const isMetamask = window.ethereum && window.ethereum.isMetaMask 75 | 76 | return ( 77 | 78 | {!error && connector === walletconnect && } 79 | 80 | 81 | {!error && } 82 | {error ? ( 83 | 84 |
Error connecting.
85 | { 87 | setPendingError(false) 88 | tryActivation(connector) 89 | }} 90 | > 91 | Try Again 92 | 93 |
94 | ) : connector === walletconnect ? ( 95 | 'Scan QR code with a compatible wallet...' 96 | ) : ( 97 | 'Initializing...' 98 | )} 99 |
100 |
101 | {Object.keys(SUPPORTED_WALLETS).map(key => { 102 | const option = SUPPORTED_WALLETS[key] 103 | if (option.connector === connector) { 104 | if (option.connector === injected) { 105 | if (isMetamask && option.name !== 'MetaMask') { 106 | return null 107 | } 108 | if (!isMetamask && option.name === 'MetaMask') { 109 | return null 110 | } 111 | } 112 | return ( 113 |
126 | ) 127 | } 128 | -------------------------------------------------------------------------------- /src/contexts/LocalStorage.js: -------------------------------------------------------------------------------- 1 | import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react' 2 | 3 | const UNISWAP = 'UNISWAP' 4 | 5 | const VERSION = 'VERSION' 6 | const CURRENT_VERSION = 0 7 | const LAST_SAVED = 'LAST_SAVED' 8 | 9 | const BETA_MESSAGE_DISMISSED = 'BETA_MESSAGE_DISMISSED' 10 | const GENERAL_DAI__MESSAGE_DISMISSED = 'GENERAL_DAI__MESSAGE_DISMISSED' 11 | const SAI_HOLDER__MESSAGE_DISMISSED = 'SAI_HOLDER__MESSAGE_DISMISSED' 12 | const DARK_MODE = 'DARK_MODE' 13 | 14 | const UPDATABLE_KEYS = [ 15 | GENERAL_DAI__MESSAGE_DISMISSED, 16 | SAI_HOLDER__MESSAGE_DISMISSED, 17 | BETA_MESSAGE_DISMISSED, 18 | DARK_MODE 19 | ] 20 | 21 | const UPDATE_KEY = 'UPDATE_KEY' 22 | 23 | const LocalStorageContext = createContext() 24 | 25 | function useLocalStorageContext() { 26 | return useContext(LocalStorageContext) 27 | } 28 | 29 | function reducer(state, { type, payload }) { 30 | switch (type) { 31 | case UPDATE_KEY: { 32 | const { key, value } = payload 33 | if (!UPDATABLE_KEYS.some(k => k === key)) { 34 | throw Error(`Unexpected key in LocalStorageContext reducer: '${key}'.`) 35 | } else { 36 | return { 37 | ...state, 38 | [key]: value 39 | } 40 | } 41 | } 42 | default: { 43 | throw Error(`Unexpected action type in LocalStorageContext reducer: '${type}'.`) 44 | } 45 | } 46 | } 47 | 48 | function init() { 49 | const defaultLocalStorage = { 50 | [VERSION]: CURRENT_VERSION, 51 | [BETA_MESSAGE_DISMISSED]: false, 52 | [GENERAL_DAI__MESSAGE_DISMISSED]: false, 53 | [SAI_HOLDER__MESSAGE_DISMISSED]: false, 54 | [DARK_MODE]: false 55 | } 56 | 57 | try { 58 | const parsed = JSON.parse(window.localStorage.getItem(UNISWAP)) 59 | if (parsed[VERSION] !== CURRENT_VERSION) { 60 | // this is where we could run migration logic 61 | return defaultLocalStorage 62 | } else { 63 | return { ...defaultLocalStorage, ...parsed } 64 | } 65 | } catch { 66 | return defaultLocalStorage 67 | } 68 | } 69 | 70 | export default function Provider({ children }) { 71 | const [state, dispatch] = useReducer(reducer, undefined, init) 72 | 73 | const updateKey = useCallback((key, value) => { 74 | dispatch({ type: UPDATE_KEY, payload: { key, value } }) 75 | }, []) 76 | 77 | return ( 78 | [state, { updateKey }], [state, updateKey])}> 79 | {children} 80 | 81 | ) 82 | } 83 | 84 | export function Updater() { 85 | const [state] = useLocalStorageContext() 86 | 87 | useEffect(() => { 88 | window.localStorage.setItem(UNISWAP, JSON.stringify({ ...state, [LAST_SAVED]: Math.floor(Date.now() / 1000) })) 89 | }) 90 | 91 | return null 92 | } 93 | 94 | export function useSaiHolderMessageManager() { 95 | const [state, { updateKey }] = useLocalStorageContext() 96 | 97 | const dismissSaiHolderMessage = useCallback(() => { 98 | updateKey(SAI_HOLDER__MESSAGE_DISMISSED, true) 99 | }, [updateKey]) 100 | 101 | return [!state[SAI_HOLDER__MESSAGE_DISMISSED], dismissSaiHolderMessage] 102 | } 103 | 104 | export function useGeneralDaiMessageManager() { 105 | const [state, { updateKey }] = useLocalStorageContext() 106 | 107 | const dismissGeneralDaiMessage = useCallback(() => { 108 | updateKey(GENERAL_DAI__MESSAGE_DISMISSED, true) 109 | }, [updateKey]) 110 | 111 | return [!state[GENERAL_DAI__MESSAGE_DISMISSED], dismissGeneralDaiMessage] 112 | } 113 | 114 | export function useBetaMessageManager() { 115 | const [state, { updateKey }] = useLocalStorageContext() 116 | 117 | const dismissBetaMessage = useCallback(() => { 118 | updateKey(BETA_MESSAGE_DISMISSED, true) 119 | }, [updateKey]) 120 | 121 | return [!state[BETA_MESSAGE_DISMISSED], dismissBetaMessage] 122 | } 123 | 124 | export function useDarkModeManager() { 125 | const [state, { updateKey }] = useLocalStorageContext() 126 | 127 | let isDarkMode = state[DARK_MODE] 128 | 129 | const toggleDarkMode = useCallback( 130 | value => { 131 | updateKey(DARK_MODE, value === false || value === true ? value : !isDarkMode) 132 | }, 133 | [updateKey, isDarkMode] 134 | ) 135 | 136 | return [state[DARK_MODE], toggleDarkMode] 137 | } 138 | -------------------------------------------------------------------------------- /public/locales/vi.json: -------------------------------------------------------------------------------- 1 | { 2 | "noWallet": "Không tìm thấy ví tiền Ethereum", 3 | "wrongNetwork": "Kết nối mạng không đúng", 4 | "switchNetwork": "Vui lòng chuyển sang {{ correctNetwork }}", 5 | "installWeb3MobileBrowser": "Vui lòng truy cập từ trình duyệt di động hỗ trợ web3 như là Ví Trust hoặc Ví Coinbase", 6 | "installMetamask": "Vui lòng truy cập sau khi cài đặt Metamask trên Chrome hoặc Brave.", 7 | "disconnected": "Ngắt kết nối rồi", 8 | "swap": "Hoán đổi", 9 | "swapAnyway": "Tiếp tục hoán đổi?", 10 | "send": "Gửi", 11 | "sendAnyway": "Tiếp tục gửi?", 12 | "pool": "Chung vốn", 13 | "betaWarning": "Dự án này đang trong giai đoạn thử nghiệm. Sử dụng có rủi ro của riêng bạn", 14 | "input": "Đầu vào", 15 | "output": "Đầu ra", 16 | "estimated": "ước lượng", 17 | "balance": "Số dư: {{ balanceInput }}", 18 | "unlock": "Mở khóa", 19 | "pending": "Đang chờ xử lý", 20 | "selectToken": "Chọn một đồng tiền ảo", 21 | "searchOrPaste": "Tìm kiếm tên, biểu tượng, hoặc địa chỉ của đồng tiền ảo", 22 | "searchOrPasteMobile": "Tên, Biểu tương, hoặc Địa chỉ", 23 | "noExchange": "Không Tìm Thấy Giao Dịch", 24 | "exchangeRate": "Tỷ giá", 25 | "unknownError": "Rất tiếc! Xảy ra lỗi không xác định. Vui lòng làm mới trang, hoặc truy cập từ trình duyệt hay thiết bị khác.", 26 | "enterValueCont": "Nhập một giá trị {{ missingCurrencyValue }} để tiếp tục.", 27 | "selectTokenCont": "Chọn một đồng tiền ảo để tiếp tục.", 28 | "noLiquidity": "Không có tính thanh khoản.", 29 | "insufficientLiquidity": "Không đủ tính thanh khoản.", 30 | "unlockTokenCont": "Vui lòng mở khoá đồng tiền ảo để tiếp tục", 31 | "transactionDetails": "Chi tiết nâng cao", 32 | "hideDetails": "Che giấu chi tiết", 33 | "slippageWarning": "Cảnh báo trượt giá", 34 | "highSlippageWarning": "Cảnh báo trượt giá cao", 35 | "youAreSelling": "Bạn đang bán", 36 | "orTransFail": "hoặc giao dịch sẽ thất bại.", 37 | "youWillReceive": "Bạn sẽ nhận dược ít nhất là", 38 | "youAreBuying": "Bạn đang mua", 39 | "itWillCost": "Nó sẽ có giá cao nhất", 40 | "forAtMost": "nhiều nhất", 41 | "insufficientBalance": "Số dư không đủ", 42 | "inputNotValid": "Giá trị nhập vào không hợp lệ", 43 | "differentToken": "Đồng tiền ảo phải khác nhau.", 44 | "noRecipient": "Nhập địa chỉ ví để gửi đến.", 45 | "invalidRecipient": "Vui lòng nhập một người nhận địa chỉ ví hợp lệ.", 46 | "recipientAddress": "Địa chỉ người nhận", 47 | "youAreSending": "Bạn đang gửi", 48 | "willReceive": "sẽ nhận dược ít nhất là", 49 | "to": "đến", 50 | "addLiquidity": "Thêm tiền thanh khoản", 51 | "deposit": "Gửi tiền", 52 | "currentPoolSize": "Quy mô hiện tại của quỹ", 53 | "yourPoolShare": "Phần hùn của bạn trong quỹ", 54 | "noZero": "Số tiền không thể bằng không.", 55 | "mustBeETH": "Một trong những đầu vào phải là ETH.", 56 | "enterCurrencyOrLabelCont": "Nhập giá trị {{ inputCurrency }} hoặc {{ label }} để tiếp tục.", 57 | "youAreAdding": "Bạn đang thêm", 58 | "and": "và", 59 | "intoPool": "vào nhóm thanh khoản.", 60 | "outPool": "từ nhóm thanh khoản.", 61 | "youWillMint": "Bạn sẽ đúc tiền", 62 | "liquidityTokens": "dồng thanh khoản.", 63 | "totalSupplyIs": "Tổng cung hiện tại của đồng thanh khoản là", 64 | "youAreSettingExRate": "Bạn đang đặt tỷ giá hối đoái ban đầu thành", 65 | "totalSupplyIs0": "Tổng cung hiện tại của đồng thanh khoản là 0.", 66 | "tokenWorth": "Tại tỷ giá hối đoái hiện tại, giá trị đồng token của quỹ là", 67 | "firstLiquidity": "Bạn là người đầu tiên thêm thanh khoản!", 68 | "initialExchangeRate": "Tỷ giá hối đoái ban đầu sẽ được thiết lập dựa trên tiền gửi của bạn. Vui lòng đảm bảo rằng tiền gửi ETH và {{ label }} của bạn có cùng giá trị tiền định danh.", 69 | "removeLiquidity": "Loại bỏ thanh khoản", 70 | "poolTokens": "Đồng tiền ảo của quỹ", 71 | "enterLabelCont": "Nhập giá trị {{ label }} để tiếp tục", 72 | "youAreRemoving": "Bạn đang loại bỏ giữa", 73 | "youWillRemove": "Bạn sẽ loại bỏ", 74 | "createExchange": "Tạo giao dịch", 75 | "invalidTokenAddress": "Địa chỉ đồng tiền điện tử không hợp lệ", 76 | "exchangeExists": "{{ label }} Giao dịch đã tồn tại!", 77 | "invalidSymbol": "Biểu tượng không hợp lệ", 78 | "invalidDecimals": "Số thập phân không hợp lệ", 79 | "tokenAddress": "Địa chỉ đồng tiền điện tử", 80 | "label": "Nhãn", 81 | "name": "Tên", 82 | "symbol": "Biểu tượng", 83 | "decimals": "Số thập phân", 84 | "enterTokenCont": "Nhập địa chỉ đồng tiền ảo để tiếp tục", 85 | "priceChange": "Trượt giá dự kiến", 86 | "forAtLeast": "cho ít nhất " 87 | } 88 | -------------------------------------------------------------------------------- /public/locales/it-IT.json: -------------------------------------------------------------------------------- 1 | { 2 | "noWallet": "Wallet Ethereum non trovato", 3 | "wrongNetwork": "Sei connesso alla rete sbagliata", 4 | "switchNetwork": "Perfavore connettiti su {{ correctNetwork }}", 5 | "installWeb3MobileBrowser": "Perfavore visita il sito da un browser abilitato web3 o da un app mobile come Trust Wallet o Coinbase Wallet.", 6 | "installMetamask": "Perfavore ritorna dopo aver installato Metamask su Chrome o Brave.", 7 | "disconnected": "Disconnesso", 8 | "swap": "Scambia", 9 | "swapAnyway": "Scambia comunque", 10 | "send": "Invia", 11 | "sendAnyway": "Invia comunque", 12 | "pool": "Riserva", 13 | "betaWarning": "Questo progetto è in beta. Usalo a tuo rischio.", 14 | "input": "Input", 15 | "output": "Output", 16 | "estimated": "stimato", 17 | "balance": "Saldo: {{ balanceInput }}", 18 | "unlock": "Sblocca", 19 | "pending": "In attesa", 20 | "selectToken": "Seleziona un token", 21 | "searchOrPaste": "Cerca Nome, Simbolo o Indirizzo Token", 22 | "searchOrPasteMobile": "Nome, Simbolo, o Indirizzo", 23 | "noExchange": "Nessun Exchange Trovato", 24 | "exchangeRate": "Tasso di cambio", 25 | "unknownError": "Oops! Si è verificato un Errore imprevisto. Aggiorna la pagina o visita da un altro browser o dispositivo.", 26 | "enterValueCont": "Inserisci un valore {{ missingCurrencyValue }} per continuare.", 27 | "selectTokenCont": "Seleziona un token per continuare.", 28 | "noLiquidity": "Nessuna liquidità.", 29 | "insufficientLiquidity": "Liquidità insufficiente.", 30 | "unlockTokenCont": "Si prega di sbloccare il token per continuare.", 31 | "transactionDetails": "Dettagli avanzati", 32 | "hideDetails": "Nascondi dettagli", 33 | "slippageWarning": "Avviso di scostamento", 34 | "highSlippageWarning": "Avviso di elevato scostamento", 35 | "youAreSelling": "Stai vendendo", 36 | "orTransFail": "o la transazione fallità.", 37 | "youWillReceive": "Riceverai almeno", 38 | "youAreBuying": "Stai comprando", 39 | "itWillCost": "Costerà al massimo", 40 | "forAtMost": "per al massimo", 41 | "insufficientBalance": "Saldo insufficente", 42 | "inputNotValid": "Non è un valore di input valido", 43 | "differentToken": "Deve essere un token diverso.", 44 | "noRecipient": "Inserisci un indirizzo di wallet a cui inviare.", 45 | "invalidRecipient": "Inserisci un destinatario valido per l'indirizzo del wallet.", 46 | "recipientAddress": "Indirizzo del destinatario", 47 | "youAreSending": "Stai inviando", 48 | "willReceive": "riceverà almeno", 49 | "to": "a", 50 | "addLiquidity": "Aggiungi liquidità", 51 | "deposit": "Depositare", 52 | "currentPoolSize": "Dimensione attuale del pool", 53 | "yourPoolShare": "La tua parte di pool condivisa", 54 | "noZero": "L'importo non può essere zero.", 55 | "mustBeETH": "Uno degli input deve essere ETH.", 56 | "enterCurrencyOrLabelCont": "Inserisci un valore {{ inputCurrency }} o {{ label }} per continuare.", 57 | "youAreAdding": "Stai agginugendo", 58 | "and": "e", 59 | "intoPool": "nella riserva di liquidità.", 60 | "outPool": "dalla riserva di liquidità.", 61 | "youWillMint": "Tu conierai", 62 | "liquidityTokens": "token di liquidità.", 63 | "totalSupplyIs": "L'attuale disponibilità totale di token di liquidità è", 64 | "youAreSettingExRate": "Stai impostando il tasso di cambio iniziale su", 65 | "totalSupplyIs0": "L'attuale disponibilità totale di token di liquidità è 0.", 66 | "tokenWorth": "Al tasso di cambio corrente, ogni token del pool vale", 67 | "firstLiquidity": "Sei la prima persona ad aggiungere liquidità!", 68 | "initialExchangeRate": "Il tasso di cambio iniziale verrà impostato in base ai tuoi depositi. Assicurati che i tuoi depositi ETH e {{ label }} abbiano lo stesso valore fiat.", 69 | "removeLiquidity": "Rimuovi Liquidità", 70 | "poolTokens": "Token Pool", 71 | "enterLabelCont": "Inserisci un valore {{ label }} per continuare.", 72 | "youAreRemoving": "Stai rimuovendo tra", 73 | "youWillRemove": "Rimuoverai", 74 | "createExchange": "Crea scambio", 75 | "invalidTokenAddress": "Indirizzo token non valido", 76 | "exchangeExists": "{{ label }} Exchange già esistente!", 77 | "invalidSymbol": "Simbolo non valido", 78 | "invalidDecimals": "Decimali non validi", 79 | "tokenAddress": "Indirizzo Token", 80 | "label": "Etichetta", 81 | "name": "Nome", 82 | "symbol": "Simbolo", 83 | "decimals": "Decimali", 84 | "enterTokenCont": "Inserire un indirizzo token per continuare", 85 | "priceChange": "Scostamento del prezzo previsto", 86 | "forAtLeast": "per almeno " 87 | } 88 | -------------------------------------------------------------------------------- /src/theme/index.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import { ThemeProvider as StyledComponentsThemeProvider, createGlobalStyle, css } from 'styled-components' 3 | import { getQueryParam, checkSupportedTheme } from '../utils' 4 | import { SUPPORTED_THEMES } from '../constants' 5 | import { useDarkModeManager } from '../contexts/LocalStorage' 6 | 7 | export * from './components' 8 | 9 | const MEDIA_WIDTHS = { 10 | upToSmall: 600, 11 | upToMedium: 960, 12 | upToLarge: 1280 13 | } 14 | 15 | const mediaWidthTemplates = Object.keys(MEDIA_WIDTHS).reduce((accumulator, size) => { 16 | accumulator[size] = (...args) => css` 17 | @media (max-width: ${MEDIA_WIDTHS[size]}px) { 18 | ${css(...args)} 19 | } 20 | ` 21 | return accumulator 22 | }, {}) 23 | 24 | const white = '#FFFFFF' 25 | const black = '#000000' 26 | 27 | export default function ThemeProvider({ children }) { 28 | const [darkMode, toggleDarkMode] = useDarkModeManager() 29 | const themeURL = checkSupportedTheme(getQueryParam(window.location, 'theme')) 30 | const themeToRender = themeURL 31 | ? themeURL.toUpperCase() === SUPPORTED_THEMES.DARK 32 | ? true 33 | : themeURL.toUpperCase() === SUPPORTED_THEMES.LIGHT 34 | ? false 35 | : darkMode 36 | : darkMode 37 | useEffect(() => { 38 | toggleDarkMode(themeToRender) 39 | }, [toggleDarkMode, themeToRender]) 40 | return {children} 41 | } 42 | 43 | const theme = darkMode => ({ 44 | white, 45 | black, 46 | textColor: darkMode ? white : '#010101', 47 | greyText: darkMode ? white : '#6C7284', 48 | 49 | // for setting css on 50 | backgroundColor: white, 51 | 52 | modalBackground: darkMode ? 'rgba(0,0,0,0.6)' : 'rgba(0,0,0,0.5)', 53 | inputBackground: darkMode ? '#202124' : white, 54 | placeholderGray: darkMode ? '#5F5F5F' : '#E1E1E1', 55 | shadowColor: darkMode ? '#000' : '#2F80ED', 56 | 57 | // grays 58 | concreteGray: darkMode ? '#292C2F' : '#FAFAFA', 59 | mercuryGray: darkMode ? '#333333' : '#E1E1E1', 60 | silverGray: darkMode ? '#737373' : '#C4C4C4', 61 | chaliceGray: darkMode ? '#7B7B7B' : '#AEAEAE', 62 | doveGray: darkMode ? '#C4C4C4' : '#737373', 63 | mineshaftGray: darkMode ? '#E1E1E1' : '#2B2B2B', 64 | activeGray: darkMode ? '#292C2F' : '#F7F8FA', 65 | buttonOutlineGrey: darkMode ? '#FAFAFA' : '#F2F2F2', 66 | tokenRowHover: darkMode ? '#404040' : '#F2F2F2', 67 | 68 | //blacks 69 | charcoalBlack: darkMode ? '#F2F2F2' : '#404040', 70 | // blues 71 | zumthorBlue: darkMode ? '#212529' : '#EBF4FF', 72 | malibuBlue: darkMode ? '#E67AEF' : '#5CA2FF', 73 | royalBlue: darkMode ? '#DC6BE5' : '#2F80ED', 74 | loadingBlue: darkMode ? '#e4f0ff' : '#e4f0ff', 75 | 76 | // purples 77 | wisteriaPurple: '#DC6BE5', 78 | // reds 79 | salmonRed: '#FF6871', 80 | // orange 81 | pizazzOrange: '#FF8F05', 82 | // yellows 83 | warningYellow: '#FFE270', 84 | // pink 85 | uniswapPink: '#DC6BE5', 86 | //green 87 | connectedGreen: '#27AE60', 88 | 89 | //branded 90 | metaMaskOrange: '#E8831D', 91 | 92 | //specific 93 | textHover: darkMode ? theme.uniswapPink : theme.doveGray, 94 | 95 | // connect button when loggedout 96 | buttonFaded: darkMode ? '#DC6BE5' : '#737373', 97 | 98 | // media queries 99 | mediaWidth: mediaWidthTemplates, 100 | // css snippets 101 | flexColumnNoWrap: css` 102 | display: flex; 103 | flex-flow: column nowrap; 104 | `, 105 | flexRowNoWrap: css` 106 | display: flex; 107 | flex-flow: row nowrap; 108 | ` 109 | }) 110 | 111 | export const GlobalStyle = createGlobalStyle` 112 | @import url('https://rsms.me/inter/inter.css'); 113 | html { font-family: 'Inter', sans-serif; } 114 | @supports (font-variation-settings: normal) { 115 | html { font-family: 'Inter var', sans-serif; } 116 | } 117 | 118 | html, 119 | body { 120 | margin: 0; 121 | padding: 0; 122 | width: 100%; 123 | height: 100%; 124 | overflow: hidden; 125 | } 126 | 127 | body > div { 128 | height: 100%; 129 | overflow: auto; 130 | -webkit-overflow-scrolling: touch; 131 | } 132 | 133 | html { 134 | font-size: 16px; 135 | font-variant: none; 136 | color: ${({ theme }) => theme.textColor}; 137 | background-color: ${({ theme }) => theme.backgroundColor}; 138 | -webkit-font-smoothing: antialiased; 139 | -moz-osx-font-smoothing: grayscale; 140 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 141 | } 142 | ` 143 | -------------------------------------------------------------------------------- /src/pages/App.js: -------------------------------------------------------------------------------- 1 | import React, { Suspense, lazy } from 'react' 2 | import styled from 'styled-components' 3 | import { BrowserRouter, Redirect, Route, Switch } from 'react-router-dom' 4 | 5 | import Web3ReactManager from '../components/Web3ReactManager' 6 | import Header from '../components/Header' 7 | import Footer from '../components/Footer' 8 | 9 | import NavigationTabs from '../components/NavigationTabs' 10 | import { isAddress, getAllQueryParams } from '../utils' 11 | 12 | const Swap = lazy(() => import('./Swap')) 13 | const Send = lazy(() => import('./Send')) 14 | const Pool = lazy(() => import('./Pool')) 15 | const NFT = lazy(() => import('./NFT')) 16 | 17 | 18 | const AppWrapper = styled.div` 19 | display: flex; 20 | flex-flow: column; 21 | align-items: flex-start; 22 | height: 100vh; 23 | ` 24 | 25 | const HeaderWrapper = styled.div` 26 | ${({ theme }) => theme.flexRowNoWrap} 27 | width: 100%; 28 | justify-content: space-between; 29 | ` 30 | const FooterWrapper = styled.div` 31 | width: 100%; 32 | min-height: 30px; 33 | align-self: flex-end; 34 | ` 35 | 36 | const BodyWrapper = styled.div` 37 | display: flex; 38 | flex-direction: column; 39 | width: 100%; 40 | justify-content: flex-start; 41 | align-items: center; 42 | flex: 1; 43 | overflow: auto; 44 | ` 45 | 46 | const Body = styled.div` 47 | max-width: 35rem; 48 | width: 90%; 49 | /* margin: 0 1.25rem 1.25rem 1.25rem; */ 50 | ` 51 | 52 | export default function App() { 53 | const params = getAllQueryParams() 54 | return ( 55 | <> 56 | 57 | 58 | 59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | {/* this Suspense is for route code-splitting */} 67 | 68 | 69 | } /> 70 | { 75 | if (isAddress(match.params.tokenAddress)) { 76 | return ( 77 | 82 | ) 83 | } else { 84 | return 85 | } 86 | }} 87 | /> 88 | } /> 89 | { 94 | if (isAddress(match.params.tokenAddress)) { 95 | return 96 | } else { 97 | return 98 | } 99 | }} 100 | /> 101 | } /> 102 | } 110 | /> 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | ) 122 | } 123 | -------------------------------------------------------------------------------- /src/components/ContextualInfoNew/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import styled, { css } from 'styled-components' 3 | import { transparentize } from 'polished' 4 | import ReactGA from 'react-ga' 5 | import { ReactComponent as Dropup } from '../../assets/images/dropup-blue.svg' 6 | import { ReactComponent as Dropdown } from '../../assets/images/dropdown-blue.svg' 7 | 8 | const SummaryWrapper = styled.div` 9 | color: ${({ error, brokenTokenWarning, theme }) => (error || brokenTokenWarning ? theme.salmonRed : theme.doveGray)}; 10 | font-size: 0.75rem; 11 | text-align: center; 12 | margin-top: 1rem; 13 | padding-top: 1rem; 14 | ` 15 | 16 | const SummaryWrapperContainer = styled.div` 17 | ${({ theme }) => theme.flexRowNoWrap}; 18 | color: ${({ theme }) => theme.royalBlue}; 19 | text-align: center; 20 | margin-top: 1rem; 21 | padding-top: 1rem; 22 | cursor: pointer; 23 | align-items: center; 24 | justify-content: center; 25 | font-size: 0.75rem; 26 | 27 | img { 28 | height: 0.75rem; 29 | width: 0.75rem; 30 | } 31 | ` 32 | 33 | const Details = styled.div` 34 | background-color: ${({ theme }) => theme.concreteGray}; 35 | /* padding: 1.25rem 1.25rem 1rem 1.25rem; */ 36 | border-radius: 1rem; 37 | font-size: 0.75rem; 38 | margin: 1rem 0.5rem 0 0.5rem; 39 | ` 40 | 41 | const ErrorSpan = styled.span` 42 | margin-right: 12px; 43 | font-size: 0.75rem; 44 | line-height: 0.75rem; 45 | 46 | color: ${({ isError, theme }) => isError && theme.salmonRed}; 47 | ${({ slippageWarning, highSlippageWarning, theme }) => 48 | highSlippageWarning 49 | ? css` 50 | color: ${theme.salmonRed}; 51 | font-weight: 600; 52 | ` 53 | : slippageWarning && 54 | css` 55 | background-color: ${transparentize(0.6, theme.warningYellow)}; 56 | font-weight: 600; 57 | padding: 0.25rem; 58 | `} 59 | ` 60 | 61 | const WrappedDropup = ({ isError, highSlippageWarning, ...rest }) => 62 | const ColoredDropup = styled(WrappedDropup)` 63 | path { 64 | stroke: ${({ isError, theme }) => (isError ? theme.salmonRed : theme.royalBlue)}; 65 | 66 | ${({ highSlippageWarning, theme }) => 67 | highSlippageWarning && 68 | css` 69 | stroke: ${theme.salmonRed}; 70 | `} 71 | } 72 | ` 73 | 74 | const WrappedDropdown = ({ isError, highSlippageWarning, ...rest }) => 75 | const ColoredDropdown = styled(WrappedDropdown)` 76 | path { 77 | stroke: ${({ isError, theme }) => (isError ? theme.salmonRed : theme.royalBlue)}; 78 | 79 | ${({ highSlippageWarning, theme }) => 80 | highSlippageWarning && 81 | css` 82 | stroke: ${theme.salmonRed}; 83 | `} 84 | } 85 | ` 86 | 87 | export default function ContextualInfo({ 88 | openDetailsText = 'Advanced Details', 89 | closeDetailsText = 'Hide Advanced', 90 | contextualInfo = '', 91 | allowExpand = false, 92 | isError = false, 93 | slippageWarning, 94 | highSlippageWarning, 95 | brokenTokenWarning, 96 | dropDownContent 97 | }) { 98 | const [showDetails, setShowDetails] = useState(false) 99 | return !allowExpand ? ( 100 | {contextualInfo} 101 | ) : ( 102 | <> 103 | { 105 | !showDetails && 106 | ReactGA.event({ 107 | category: 'Advanced Interaction', 108 | action: 'Open Advanced Details', 109 | label: 'Swap/Send Page Details' 110 | }) 111 | setShowDetails(s => !s) 112 | }} 113 | > 114 | <> 115 | 116 | {(slippageWarning || highSlippageWarning) && ( 117 | 118 | ⚠️ 119 | 120 | )} 121 | {contextualInfo ? contextualInfo : showDetails ? closeDetailsText : openDetailsText} 122 | 123 | {showDetails ? ( 124 | 125 | ) : ( 126 | 127 | )} 128 | 129 | 130 | {showDetails &&
{dropDownContent()}
} 131 | 132 | ) 133 | } 134 | -------------------------------------------------------------------------------- /public/locales/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "noWallet": "Keine Ethereum-Wallet gefunden", 3 | "wrongNetwork": "Du bist auf dem falschen Netzwerk.", 4 | "switchNetwork": "Bitte wechsle zum {{ correctNetwork }}", 5 | "installWeb3MobileBrowser": "Bitte besuche uns mit einem web3-fähigen mobilen Browser wie z.B. Trust Wallet oder Coinbase Wallet.", 6 | "installMetamask": "Bitte besuch uns erneut, nachdem du Metamask oder Brave installiert hast.", 7 | "disconnected": "Nicht verbunden", 8 | "swap": "Tauschen", 9 | "swapAnyway": "Trotzdem tauschen", 10 | "send": "Senden", 11 | "sendAnyway": "Trotzdem senden", 12 | "pool": "Pool", 13 | "betaWarning": "Dieses Projekt ist in beta. Nutzung auf eigenes Risiko.", 14 | "input": "Input", 15 | "output": "Output", 16 | "estimated": "geschätzt", 17 | "balance": "Guthaben: {{ balanceInput }}", 18 | "unlock": "Freischalten", 19 | "pending": "hängige", 20 | "selectToken": "Token auswählen", 21 | "searchOrPaste": "Token Name, Symbol oder Adresse suchen", 22 | "searchOrPasteMobile": "Name, Symbol oder Adresse", 23 | "noExchange": "Exchange nicht gefunden", 24 | "exchangeRate": "Wechselkurs", 25 | "invertedRate": "Invertierter Wechselkurs", 26 | "unknownError": "Oops! Ein unbekannter Fehler ist aufgetreten. Bitte Seite neu laden oder uns von einem anderen Browser oder Gerät erneut besuchen.", 27 | "enterValueCont": "Wert {{ missingCurrencyValue }} eingeben um fortzufahren.", 28 | "selectTokenCont": "Token auswählen um fortzufahren.", 29 | "noLiquidity": "Keine Liquidität.", 30 | "insufficientLiquidity": "Liquidität ungenügend.", 31 | "unlockTokenCont": "Token freischalten um fortzufahren.", 32 | "transactionDetails": "Details der Transaktion", 33 | "hideDetails": "Details ausblenden", 34 | "slippageWarning": "Wechselkursrutsch", 35 | "highSlippageWarning": "Hoher Wechselkursrutsch", 36 | "youAreSelling": "Du verkaufst", 37 | "orTransFail": "oder die Transaktion wird fehlschlagen.", 38 | "youWillReceive": "Du erhältst mindestens", 39 | "youAreBuying": "Du kaufst", 40 | "itWillCost": "Es kostet höchstens", 41 | "forAtMost": "für maximal", 42 | "insufficientBalance": "Guthaben ungenügend", 43 | "inputNotValid": "Eingabewert ungültig", 44 | "differentToken": "Es müssen unterschiedliche Token sein.", 45 | "noRecipient": "Empfängeradresse angeben.", 46 | "invalidRecipient": "Bitte gib eine gültige Empfängeradresse an.", 47 | "recipientAddress": "Adresse des Empfängers", 48 | "youAreSending": "Du schickst", 49 | "willReceive": "erhält mindestens", 50 | "to": "zu", 51 | "addLiquidity": "Liquidität hinzufügen", 52 | "deposit": "Depot", 53 | "currentPoolSize": "Aktuelle Größe des Pools", 54 | "yourPoolShare": "Dein Anteil am Pool", 55 | "noZero": "Wert darf nicht Null sein.", 56 | "mustBeETH": "Einer der Inputs muß ETH sein.", 57 | "enterCurrencyOrLabelCont": "{{ inputCurrency }} oder {{ label }} Wert eingeben um fortzufahren.", 58 | "youAreAdding": "Du fügst zwischen", 59 | "and": "und", 60 | "intoPool": "in den Liquiditätspool.", 61 | "outPool": "vom Liquiditätspool.", 62 | "youWillMint": "Du prägst", 63 | "liquidityTokens": "Liquiditätstokens.", 64 | "totalSupplyIs": "Die gesamte Anzahl Liquiditätstokens ist aktuell", 65 | "youAreSettingExRate": "Du setzt den anfänglichen Wechselkurs auf", 66 | "totalSupplyIs0": "Die gesamte Anzahl Liquiditätstokens ist aktuell 0.", 67 | "tokenWorth": "Zum gegenwärtigen Wechselkurs ist jeder Pool Token so viel Wert", 68 | "firstLiquidity": "Du bist die erste Person die Liquidität bereitstellt!", 69 | "initialExchangeRate": "Der initiale Wechselkurs wird auf deiner Überweisung basieren. Stelle sicher, dass deine ETH und {{ label }} denselben Fiatwert haben.", 70 | "removeLiquidity": "Liquidität entfernen", 71 | "poolTokens": "Pool Tokens", 72 | "enterLabelCont": "{{ label }} Wert eingeben um fortzufahren.", 73 | "youAreRemoving": "Du entfernst zwischen", 74 | "youWillRemove": "Du entfernst", 75 | "createExchange": "Exchange erstellen", 76 | "invalidTokenAddress": "Ungültige Tokenadresse", 77 | "exchangeExists": "{{ label }} Exchange existiert bereits!", 78 | "invalidSymbol": "Symbol ungültig", 79 | "invalidDecimals": "Dezimalstellen ungültig", 80 | "tokenAddress": "Tokenadresse", 81 | "label": "Label", 82 | "name": "Name", 83 | "symbol": "Symbol", 84 | "decimals": "Dezimalstellen", 85 | "enterTokenCont": "Tokenadresse eingeben um fortzufahren", 86 | "priceChange": "Geschätzter Wechselkursrutsch", 87 | "forAtLeast": "für mindestens ", 88 | "brokenToken": "Der ausgewählte Token ist nicht kompatibel mit Uniswap V1. Liquidität hinzufügen wird zu nicht mehr zugänglichen Token führen!" 89 | } 90 | -------------------------------------------------------------------------------- /src/pages/Pool/ModeSelector.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useCallback } from 'react' 2 | import { withRouter, NavLink } from 'react-router-dom' 3 | import { useTranslation } from 'react-i18next' 4 | import styled from 'styled-components' 5 | 6 | import OversizedPanel from '../../components/OversizedPanel' 7 | import { ReactComponent as Dropdown } from '../../assets/images/dropdown-blue.svg' 8 | 9 | import Modal from '../../components/Modal' 10 | import { useBodyKeyDown } from '../../hooks' 11 | 12 | import { lighten } from 'polished' 13 | 14 | const poolTabOrder = [ 15 | { 16 | path: '/add-liquidity', 17 | textKey: 'addLiquidity', 18 | regex: /\/add-liquidity/ 19 | }, 20 | { 21 | path: '/remove-liquidity', 22 | textKey: 'removeLiquidity', 23 | regex: /\/remove-liquidity/ 24 | }, 25 | { 26 | path: '/create-exchange', 27 | textKey: 'createExchange', 28 | regex: /\/create-exchange.*/ 29 | } 30 | ] 31 | 32 | const LiquidityContainer = styled.div` 33 | ${({ theme }) => theme.flexRowNoWrap}; 34 | align-items: center; 35 | padding: 1rem 1rem; 36 | font-size: 1rem; 37 | color: ${({ theme }) => theme.royalBlue}; 38 | font-weight: 500; 39 | cursor: pointer; 40 | 41 | :hover { 42 | color: ${({ theme }) => lighten(0.1, theme.royalBlue)}; 43 | } 44 | 45 | img { 46 | height: 0.75rem; 47 | width: 0.75rem; 48 | } 49 | ` 50 | 51 | const LiquidityLabel = styled.span` 52 | flex: 1 0 auto; 53 | ` 54 | 55 | const activeClassName = 'MODE' 56 | 57 | const StyledNavLink = styled(NavLink).attrs({ 58 | activeClassName 59 | })` 60 | ${({ theme }) => theme.flexRowNoWrap} 61 | padding: 1rem; 62 | margin-left: 1rem; 63 | margin-right: 1rem; 64 | font-size: 1rem; 65 | cursor: pointer; 66 | text-decoration: none; 67 | color: ${({ theme }) => theme.doveGray}; 68 | font-size: 1rem; 69 | 70 | &.${activeClassName} { 71 | background-color: ${({ theme }) => theme.inputBackground}; 72 | border-radius: 3rem; 73 | border: 1px solid ${({ theme }) => theme.mercuryGray}; 74 | font-weight: 500; 75 | color: ${({ theme }) => theme.royalBlue}; 76 | } 77 | ` 78 | 79 | const PoolModal = styled.div` 80 | background-color: ${({ theme }) => theme.inputBackground}; 81 | width: 100%; 82 | height: 100%; 83 | padding: 2rem 0 2rem 0; 84 | ` 85 | 86 | const WrappedDropdown = ({ isError, highSlippageWarning, ...rest }) => 87 | const ColoredDropdown = styled(WrappedDropdown)` 88 | path { 89 | stroke: ${({ theme }) => theme.royalBlue}; 90 | } 91 | ` 92 | 93 | function ModeSelector({ location: { pathname }, history }) { 94 | const { t } = useTranslation() 95 | 96 | const [modalIsOpen, setModalIsOpen] = useState(false) 97 | 98 | const activeTabKey = poolTabOrder[poolTabOrder.findIndex(({ regex }) => pathname.match(regex))].textKey 99 | 100 | const navigate = useCallback( 101 | direction => { 102 | const tabIndex = poolTabOrder.findIndex(({ regex }) => pathname.match(regex)) 103 | history.push(poolTabOrder[(tabIndex + poolTabOrder.length + direction) % poolTabOrder.length].path) 104 | }, 105 | [pathname, history] 106 | ) 107 | const navigateRight = useCallback(() => { 108 | navigate(1) 109 | }, [navigate]) 110 | const navigateLeft = useCallback(() => { 111 | navigate(-1) 112 | }, [navigate]) 113 | 114 | useBodyKeyDown('ArrowDown', navigateRight, modalIsOpen) 115 | useBodyKeyDown('ArrowUp', navigateLeft, modalIsOpen) 116 | 117 | return ( 118 | 119 | { 121 | setModalIsOpen(true) 122 | }} 123 | > 124 | {t(activeTabKey)} 125 | 126 | 127 | { 131 | setModalIsOpen(false) 132 | }} 133 | > 134 | 135 | {poolTabOrder.map(({ path, textKey, regex }) => ( 136 | pathname.match(regex)} 140 | onClick={() => { 141 | setModalIsOpen(false) 142 | }} 143 | > 144 | {t(textKey)} 145 | 146 | ))} 147 | 148 | 149 | 150 | ) 151 | } 152 | 153 | export default withRouter(ModeSelector) 154 | -------------------------------------------------------------------------------- /NFT-Exchange/contracts/NFTMarketplace.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | import "https://github.com/snaketh4x0r/openzeppelin-contracts/blob/master/contracts/ownership/Ownable.sol"; 4 | import "https://github.com/snaketh4x0r/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721Full.sol"; 5 | import "https://github.com/snaketh4x0r/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol"; 6 | 7 | /** 8 | * flow 9 | * admin pass the address of nft token during deployment in Constructor 10 | * admin use setCurrentPrice setter function to set price 11 | * admin use sendTo to send funds in contract to desired address 12 | * 13 | * tokenSeller deposit token to sell by passing token id in depositToken function 14 | * 15 | * buyer call purchaseToken with token id with appropriate ether amount sent 16 | * 17 | * use depositToken function to sell NFt token by transfering it to contract 18 | * 19 | * */ 20 | 21 | contract nftMarketplace is Ownable { 22 | 23 | event Sent(address indexed payee, uint256 amount, uint256 balance); 24 | event Received(address indexed payer, uint256 tokenId, uint256 amount, uint256 balance); 25 | 26 | ERC721Full public nftAddress; 27 | ERC20 public manaAddress; 28 | uint256 public currentPrice; 29 | mapping(uint256 => address) public tokenSeller; 30 | 31 | /** 32 | * @dev Contract Constructor 33 | * @param _nftAddress address for non-fungible token contract 34 | * @param _currentPrice initial price 35 | */ 36 | constructor(address _nftAddress,address _manaAddress, uint256 _currentPrice) public { 37 | require(_nftAddress != address(0) && _nftAddress != address(this)); 38 | require(_currentPrice > 0); 39 | nftAddress = ERC721Full(_nftAddress); 40 | manaAddress = ERC20(_manaAddress); 41 | currentPrice = _currentPrice; 42 | } 43 | 44 | /** 45 | * @dev Deposit _tokenId 46 | * @param _tokenId uint256 token ID 47 | */ 48 | function depositToken(uint256 _tokenId) public { 49 | require(msg.sender != address(0) && msg.sender != address(this)); 50 | require(msg.sender == nftAddress.ownerOf(_tokenId),"You are Owner of NFT"); 51 | nftAddress.transferFrom(msg.sender, address(this), _tokenId); 52 | tokenSeller[_tokenId] = msg.sender; 53 | } 54 | 55 | /** 56 | * @dev Purchase _tokenId 57 | * @param _tokenId uint256 token ID 58 | */ 59 | function purchaseTokenETH(uint256 _tokenId) public payable { 60 | require(msg.sender != address(0) && msg.sender != address(this),"wrong addresses interaction"); 61 | require(msg.value >= currentPrice,"not enough ETH funds"); 62 | address temp = tokenSeller[_tokenId]; 63 | address payable Seller = address(uint160(temp)); 64 | Seller.transfer(msg.value); 65 | nftAddress.transferFrom(address(this), msg.sender, _tokenId); 66 | 67 | emit Received(msg.sender, _tokenId, msg.value, address(this).balance); 68 | } 69 | 70 | /** 71 | * @dev Purchase _tokenId 72 | * @param _tokenId uint256 token ID 73 | * @param _amount uint256 amount of ERC20 Mana 74 | */ 75 | function purchaseTokenMana(uint256 _tokenId,uint256 _amount) public returns (bool) { 76 | require(msg.sender != address(0) && msg.sender != address(this),"wrong addresses interaction"); 77 | require(_amount >= currentPrice,"not enough Mana funds"); 78 | nftAddress.approve(msg.sender,_tokenId); 79 | address temp = tokenSeller[_tokenId]; 80 | require(manaAddress.transferFrom(msg.sender, temp, _amount),"Not Enough tokens Transfered"); 81 | nftAddress.transferFrom(address(this), msg.sender, _tokenId); 82 | emit Received(msg.sender, _tokenId, _amount, address(this).balance); 83 | return true; 84 | } 85 | 86 | /** 87 | * @dev send / withdraw _amount to _payee 88 | */ 89 | function sendTo(address payable _payee, uint256 _amount) public onlyOwner { 90 | require(_payee != address(0) && _payee != address(this)); 91 | require(_amount > 0 && _amount <= address(this).balance); 92 | _payee.transfer(_amount); 93 | emit Sent(_payee, _amount, address(this).balance); 94 | } 95 | 96 | /** 97 | * @dev set _currentPrice 98 | */ 99 | function setCurrentPrice(uint256 _currentPrice) public onlyOwner { 100 | require(_currentPrice >= 0); 101 | currentPrice = _currentPrice; 102 | } 103 | 104 | function getCurrentRate() public view returns (uint256) { 105 | return currentPrice; 106 | } 107 | 108 | } -------------------------------------------------------------------------------- /NFT-Exchange/truffle-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Use this file to configure your truffle project. It's seeded with some 3 | * common settings for different networks and features like migrations, 4 | * compilation and testing. Uncomment the ones you need or modify 5 | * them to suit your project as necessary. 6 | * 7 | * More information about configuration can be found at: 8 | * 9 | * truffleframework.com/docs/advanced/configuration 10 | * 11 | * To deploy via Infura you'll need a wallet provider (like @truffle/hdwallet-provider) 12 | * to sign your transactions before they're sent to a remote public node. Infura accounts 13 | * are available for free at: infura.io/register. 14 | * 15 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate 16 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this 17 | * phrase from a file you've .gitignored so it doesn't accidentally become public. 18 | * 19 | */ 20 | 21 | // const HDWalletProvider = require('@truffle/hdwallet-provider'); 22 | // const infuraKey = "fj4jll3k....."; 23 | // 24 | // const fs = require('fs'); 25 | // const mnemonic = fs.readFileSync(".secret").toString().trim(); 26 | 27 | module.exports = { 28 | /** 29 | * Networks define how you connect to your ethereum client and let you set the 30 | * defaults web3 uses to send transactions. If you don't specify one truffle 31 | * will spin up a development blockchain for you on port 9545 when you 32 | * run `develop` or `test`. You can ask a truffle command to use a specific 33 | * network from the command line, e.g 34 | * 35 | * $ truffle test --network 36 | */ 37 | 38 | networks: { 39 | // Useful for testing. The `development` name is special - truffle uses it by default 40 | // if it's defined here and no other network is specified at the command line. 41 | // You should run a client (like ganache-cli, geth or parity) in a separate terminal 42 | // tab if you use this network and you must also set the `host`, `port` and `network_id` 43 | // options below to some value. 44 | // 45 | // development: { 46 | // host: "127.0.0.1", // Localhost (default: none) 47 | // port: 8545, // Standard Ethereum port (default: none) 48 | // network_id: "*", // Any network (default: none) 49 | // }, 50 | 51 | // Another network with more advanced options... 52 | // advanced: { 53 | // port: 8777, // Custom port 54 | // network_id: 1342, // Custom network 55 | // gas: 8500000, // Gas sent with each transaction (default: ~6700000) 56 | // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) 57 | // from:
, // Account to send txs from (default: accounts[0]) 58 | // websockets: true // Enable EventEmitter interface for web3 (default: false) 59 | // }, 60 | 61 | // Useful for deploying to a public network. 62 | // NB: It's important to wrap the provider as a function. 63 | // ropsten: { 64 | // provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`), 65 | // network_id: 3, // Ropsten's id 66 | // gas: 5500000, // Ropsten has a lower block limit than mainnet 67 | // confirmations: 2, // # of confs to wait between deployments. (default: 0) 68 | // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) 69 | // skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) 70 | // }, 71 | 72 | // Useful for private networks 73 | // private: { 74 | // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), 75 | // network_id: 2111, // This network is yours, in the cloud. 76 | // production: true // Treats this network as if it was a public net. (default: false) 77 | // } 78 | }, 79 | 80 | // Set default mocha options here, use special reporters etc. 81 | mocha: { 82 | // timeout: 100000 83 | }, 84 | 85 | // Configure your compilers 86 | compilers: { 87 | solc: { 88 | // version: "0.5.1", // Fetch exact version from solc-bin (default: truffle's version) 89 | // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) 90 | // settings: { // See the solidity docs for advice about optimization and evmVersion 91 | // optimizer: { 92 | // enabled: false, 93 | // runs: 200 94 | // }, 95 | // evmVersion: "byzantium" 96 | // } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /public/locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "noWallet": "No Ethereum wallet found", 3 | "wrongNetwork": "You are on the wrong network", 4 | "switchNetwork": "Please switch to {{ correctNetwork }}", 5 | "installWeb3MobileBrowser": "Please visit us from a web3-enabled mobile browser such as Trust Wallet or Coinbase Wallet.", 6 | "installMetamask": "Please visit us after installing Metamask on Chrome or Brave.", 7 | "disconnected": "Disconnected", 8 | "swap": "Swap", 9 | "swapAnyway": "Swap Anyway", 10 | "send": "Send", 11 | "connectToWallet": "Connect to a Wallet", 12 | "sendAnyway": "Send Anyway", 13 | "pool": "Pool", 14 | "nft": "NFT", 15 | "betaWarning": "This project is in beta. Use at your own risk.", 16 | "input": "Input", 17 | "output": "Output", 18 | "estimated": "estimated", 19 | "balance": "Balance: {{ balanceInput }}", 20 | "unlock": "Unlock", 21 | "pending": "Pending", 22 | "selectToken": "Select a token", 23 | "searchOrPaste": "Search Token Name, Symbol, or Address", 24 | "searchOrPasteMobile": "Name, Symbol, or Address", 25 | "noExchange": "No Exchange Found", 26 | "exchangeRate": "Exchange Rate", 27 | "unknownError": "Oops! An unknown error occurred. Please refresh the page, or visit from another browser or device.", 28 | "enterValueCont": "Enter a {{ missingCurrencyValue }} value to continue.", 29 | "selectTokenCont": "Select a token to continue.", 30 | "noLiquidity": "No liquidity.", 31 | "insufficientLiquidity": "Insufficient liquidity.", 32 | "unlockTokenCont": "Please unlock token to continue.", 33 | "transactionDetails": "Advanced Details", 34 | "hideDetails": "Hide Details", 35 | "slippageWarning": "Slippage Warning", 36 | "highSlippageWarning": "High Slippage Warning", 37 | "youAreSelling": "You are selling", 38 | "orTransFail": "or the transaction will fail.", 39 | "youWillReceive": "You will receive at least", 40 | "youAreBuying": "You are buying", 41 | "itWillCost": "It will cost at most", 42 | "forAtMost": "for at most", 43 | "insufficientBalance": "Insufficient Balance", 44 | "inputNotValid": "Not a valid input value", 45 | "differentToken": "Must be different token.", 46 | "noRecipient": "Enter a wallet address to send to.", 47 | "invalidRecipient": "Please enter a valid wallet address recipient.", 48 | "recipientAddress": "Recipient Address", 49 | "youAreSending": "You are sending", 50 | "willReceive": "will receive at least", 51 | "to": "to", 52 | "addLiquidity": "Add Liquidity", 53 | "deposit": "Deposit", 54 | "currentPoolSize": "Current Pool Size", 55 | "yourPoolShare": "Your Pool Share", 56 | "noZero": "Amount cannot be zero.", 57 | "mustBeETH": "One of the input must be ETH.", 58 | "enterCurrencyOrLabelCont": "Enter a {{ inputCurrency }} or {{ label }} value to continue.", 59 | "youAreAdding": "You are adding", 60 | "and": "and", 61 | "intoPool": "into the liquidity pool.", 62 | "outPool": "from the liquidity pool.", 63 | "youWillMint": "You will mint", 64 | "liquidityTokens": "liquidity tokens.", 65 | "totalSupplyIs": "Current total supply of liquidity tokens is", 66 | "youAreSettingExRate": "You are setting the initial exchange rate to", 67 | "totalSupplyIs0": "Current total supply of liquidity tokens is 0.", 68 | "tokenWorth": "At current exchange rate, each pool token is worth", 69 | "firstLiquidity": "You are the first person to add liquidity!", 70 | "initialExchangeRate": "The initial exchange rate will be set based on your deposits. Please make sure that your ETH and {{ label }} deposits have the same fiat value. ", 71 | "initialWarning": "A few improperly implemented ERC20 tokens have caused the remove liquidity function to break, permanently locking funds. As the first liquidity provider we recommend testing both add and remove with small amounts before adding a large amounts.", 72 | "removeLiquidity": "Remove Liquidity", 73 | "poolTokens": "Pool Tokens", 74 | "enterLabelCont": "Enter a {{ label }} value to continue.", 75 | "youAreRemoving": "You are removing between", 76 | "youWillRemove": "You will remove", 77 | "createExchange": "Create Exchange", 78 | "invalidTokenAddress": "Not a valid token address", 79 | "exchangeExists": "{{ label }} Exchange already exists!", 80 | "invalidSymbol": "Invalid symbol", 81 | "invalidDecimals": "Invalid decimals", 82 | "tokenAddress": "Token Address", 83 | "label": "Label", 84 | "name": "Name", 85 | "symbol": "Symbol", 86 | "decimals": "Decimals", 87 | "enterTokenCont": "Enter a token address to continue", 88 | "priceChange": "Expected price slippage", 89 | "forAtLeast": "for at least ", 90 | "brokenToken": "Error Occured - Wrong Set Of Inputs Please Contact Admin virajm72@gmail.com/aveeshshetty1@gmail.com/snaketh4xor@protonmail.com " 91 | } 92 | -------------------------------------------------------------------------------- /Compound Contract Addresses.txt: -------------------------------------------------------------------------------- 1 | Compiling .\contracts\development\Compound\src\CErc20.sol... 2 | Compiling .\contracts\development\Compound\src\CToken.sol... 3 | Compiling .\contracts\development\Compound\src\CarefulMath.sol... 4 | Compiling .\contracts\development\Compound\src\Comptroller.sol... 5 | Compiling .\contracts\development\Compound\src\ComptrollerInterface.sol... 6 | Compiling .\contracts\development\Compound\src\ComptrollerStorage.sol... 7 | Compiling .\contracts\development\Compound\src\EIP20Interface.sol... 8 | Compiling .\contracts\development\Compound\src\EIP20NonStandardInterface.sol... 9 | Compiling .\contracts\development\Compound\src\ErrorReporter.sol... 10 | Compiling .\contracts\development\Compound\src\Exponential.sol... 11 | Compiling .\contracts\development\Compound\src\InterestRateModel.sol... 12 | Compiling .\contracts\development\Compound\src\PriceOracle.sol... 13 | Compiling .\contracts\development\Compound\src\ReentrancyGuard.sol... 14 | Compiling .\contracts\development\Compound\src\Unitroller.sol... 15 | Compiling .\contracts\development\Compound\src\WhitePaperInterestRateModel.sol... 16 | Writing artifacts to .\build\contracts 17 | 18 | ⚠️ Important ⚠️ 19 | If you're using an HDWalletProvider, it must be Web3 1.0 enabled or your migration will hang. 20 | 21 | 22 | Starting migrations... 23 | ====================== 24 | > Network name: 'matic' 25 | > Network id: 8995 26 | > Block gas limit: 8000000 27 | 28 | 29 | 1_initial_migration.js 30 | ====================== 31 | 32 | Deploying 'Migrations' 33 | ---------------------- 34 | > transaction hash: 0x54eb5a535072bbfd668ef0d8ee4bab275a18ba8314e297ed002deb6ee54e9e0e 35 | > Blocks: 3 Seconds: 5 36 | > contract address: 0xa7B92D2E52589c7DAe67794e5163C2d4B3363041 37 | > account: 0x2a5c6E0Eb76915466C0CE771DCFb6f258a572336 38 | > balance: 32.197404867279979185 39 | > gas used: 221107 40 | > gas price: 10 gwei 41 | > value sent: 0 ETH 42 | > total cost: 0.00221107 ETH 43 | 44 | > Saving artifacts 45 | ------------------------------------- 46 | > Total cost: 0.00221107 ETH 47 | 48 | 49 | 2_deploy_compound_sandbox.js 50 | ============================ 51 | 52 | Deploying 'FakeDai' 53 | ------------------- 54 | > transaction hash: 0x4db5c778c75fff58a8c7eeb2a916b8f497d2bc2104e24947c5d3460c077c6827 55 | > Blocks: 3 Seconds: 5 56 | > contract address: 0x005FCCFADf26b9a9d93a48B9cFd7091750A5Ef01 57 | > account: 0x2a5c6E0Eb76915466C0CE771DCFb6f258a572336 58 | > balance: 32.189228667279979185 59 | > gas used: 817620 60 | > gas price: 10 gwei 61 | > value sent: 0 ETH 62 | > total cost: 0.0081762 ETH 63 | 64 | 65 | Deploying 'FakeCDaiInterestRateModel' 66 | ------------------------------------- 67 | > transaction hash: 0x99771fd8c49f72384508b7d5e82b4dde684b18490921908eabbd0f4543afaf2d 68 | > Blocks: 2 Seconds: 5 69 | > contract address: 0x77b5A39FDC7bff9b24cDfea24Da7377591489d3B 70 | > account: 0x2a5c6E0Eb76915466C0CE771DCFb6f258a572336 71 | > balance: 32.184070397279979185 72 | > gas used: 515827 73 | > gas price: 10 gwei 74 | > value sent: 0 ETH 75 | > total cost: 0.00515827 ETH 76 | 77 | 78 | Deploying 'FakeComptroller' 79 | --------------------------- 80 | > transaction hash: 0x45626cc753ec014f633f4e5ee70e71b63234ae7e528e9698e1356d3a5a052181 81 | > Blocks: 2 Seconds: 5 82 | > contract address: 0xeA83D43f6a43fB87777a9aa883104de91e7cd7c5 83 | > account: 0x2a5c6E0Eb76915466C0CE771DCFb6f258a572336 84 | > balance: 32.152230037279979185 85 | > gas used: 3184036 86 | > gas price: 10 gwei 87 | > value sent: 0 ETH 88 | > total cost: 0.03184036 ETH 89 | 90 | 91 | Deploying 'FakeCDai' 92 | -------------------- 93 | > transaction hash: 0x2214ff8670ef6b64db2ce09caecd21a21189ff2ef440f585a11e58066402a7eb 94 | > Blocks: 2 Seconds: 5 95 | > contract address: 0xf190929858f5f6dC55A03B40C25545fE9c79CF8c 96 | > account: 0x2a5c6E0Eb76915466C0CE771DCFb6f258a572336 97 | > balance: 32.096546927279979185 98 | > gas used: 5568311 99 | > gas price: 10 gwei 100 | > value sent: 0 ETH 101 | > total cost: 0.05568311 ETH 102 | 103 | > Saving artifacts 104 | ------------------------------------- 105 | > Total cost: 0.10085794 ETH 106 | 107 | 108 | Summary 109 | ======= 110 | > Total deployments: 5 111 | > Final cost: 0.10306901 ETH 112 | 113 | -------------------------------------------------------------------------------- /src/components/WarningCard/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import styled, { keyframes } from 'styled-components' 3 | 4 | import { useWeb3React } from '../../hooks' 5 | import { useTokenDetails } from '../../contexts/Tokens' 6 | import { getEtherscanLink } from '../../utils' 7 | 8 | import { Link } from '../../theme' 9 | import TokenLogo from '../TokenLogo' 10 | import { ReactComponent as Close } from '../../assets/images/x.svg' 11 | import question from '../../assets/images/question.svg' 12 | 13 | const Flex = styled.div` 14 | display: flex; 15 | justify-content: center; 16 | padding: 2rem; 17 | 18 | button { 19 | max-width: 20rem; 20 | } 21 | ` 22 | 23 | const Wrapper = styled.div` 24 | background: rgba(243, 190, 30, 0.1); 25 | position: relative; 26 | padding: 1rem; 27 | border: 0.5px solid #f3be1e; 28 | border-radius: 10px; 29 | margin-bottom: 20px; 30 | display: grid; 31 | grid-template-rows: 1fr 1fr 1fr; 32 | grid-row-gap: 10px; 33 | ` 34 | 35 | const Row = styled.div` 36 | display: flex; 37 | align-items: center; 38 | justify-items: flex-start; 39 | & > * { 40 | margin-right: 6px; 41 | } 42 | ` 43 | 44 | const CloseColor = styled(Close)` 45 | color: #aeaeae; 46 | ` 47 | 48 | const CloseIcon = styled.div` 49 | position: absolute; 50 | right: 1rem; 51 | top: 14px; 52 | &:hover { 53 | cursor: pointer; 54 | opacity: 0.6; 55 | } 56 | 57 | & > * { 58 | height: 14px; 59 | width: 14px; 60 | } 61 | ` 62 | 63 | const QuestionWrapper = styled.button` 64 | display: flex; 65 | align-items: center; 66 | justify-content: center; 67 | margin: 0; 68 | padding: 0; 69 | margin-left: 0.4rem; 70 | padding: 0.2rem; 71 | border: none; 72 | background: none; 73 | outline: none; 74 | cursor: default; 75 | border-radius: 36px; 76 | 77 | :hover, 78 | :focus { 79 | opacity: 0.7; 80 | } 81 | ` 82 | 83 | const HelpCircleStyled = styled.img` 84 | height: 18px; 85 | width: 18px; 86 | ` 87 | 88 | const fadeIn = keyframes` 89 | from { 90 | opacity : 0; 91 | } 92 | 93 | to { 94 | opacity : 1; 95 | } 96 | ` 97 | 98 | const Popup = styled(Flex)` 99 | position: absolute; 100 | width: 228px; 101 | right: 110px; 102 | top: 4px; 103 | z-index: 10; 104 | flex-direction: column; 105 | align-items: center; 106 | padding: 0.6rem 1rem; 107 | line-height: 150%; 108 | background: ${({ theme }) => theme.backgroundColor}; 109 | border: 1px solid ${({ theme }) => theme.mercuryGray}; 110 | border-radius: 8px; 111 | animation: ${fadeIn} 0.15s linear; 112 | color: ${({ theme }) => theme.textColor}; 113 | font-style: italic; 114 | 115 | box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.04), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), 116 | 0px 24px 32px rgba(0, 0, 0, 0.04); 117 | 118 | ${({ theme }) => theme.mediaWidth.upToSmall` 119 | left: 2px; 120 | top: 50px; 121 | `} 122 | ` 123 | 124 | const Text = styled.div` 125 | color: ${({ theme }) => theme.textColor}; 126 | ` 127 | 128 | function WarningCard({ onDismiss, urlAddedTokens, currency }) { 129 | const [showPopup, setPopup] = useState() 130 | const { chainId } = useWeb3React() 131 | const { symbol: inputSymbol, name: inputName } = useTokenDetails(currency) 132 | const fromURL = urlAddedTokens.hasOwnProperty(currency) 133 | 134 | return ( 135 | 136 | onDismiss()}> 137 | 138 | 139 | 140 | {fromURL ? 'Token imported by URL ' : 'Token imported by user'} 141 | { 143 | setPopup(!showPopup) 144 | }} 145 | onMouseEnter={() => { 146 | setPopup(true) 147 | }} 148 | onMouseLeave={() => { 149 | setPopup(false) 150 | }} 151 | > 152 | 153 | 154 | {showPopup ? ( 155 | 156 | 157 | The Uniswap smart contracts are designed to support any ERC20 token on Ethereum. Any token can be loaded 158 | into the interface by entering its Ethereum address into the search field or passing it as a URL 159 | parameter. Be careful when interacting with imported tokens as they have not been verified. 160 | 161 | 162 | ) : ( 163 | '' 164 | )} 165 | 166 | 167 | 168 |
{inputName && inputSymbol ? inputName + ' (' + inputSymbol + ')' : ''}
169 | 170 | (View on Etherscan) 171 | 172 |
173 | 174 | Please verify the legitimacy of this token before making any transactions. 175 | 176 |
177 | ) 178 | } 179 | 180 | export default WarningCard 181 | -------------------------------------------------------------------------------- /src/contexts/Tokens.js: -------------------------------------------------------------------------------- 1 | import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react' 2 | 3 | import { useWeb3React } from '../hooks' 4 | import { 5 | isAddress, 6 | getTokenName, 7 | getTokenSymbol, 8 | getTokenDecimals, 9 | getTokenExchangeAddressFromFactory, 10 | safeAccess 11 | } from '../utils' 12 | import { stat } from 'fs' 13 | 14 | const NAME = 'name' 15 | const SYMBOL = 'symbol' 16 | const DECIMALS = 'decimals' 17 | const EXCHANGE_ADDRESS = 'exchangeAddress' 18 | 19 | const UPDATE = 'UPDATE' 20 | 21 | const ETH = { 22 | ETH: { 23 | [NAME]: 'Ethereum', 24 | [SYMBOL]: 'ETH', 25 | [DECIMALS]: 18, 26 | [EXCHANGE_ADDRESS]: null 27 | } 28 | } 29 | 30 | export const INITIAL_TOKENS_CONTEXT = { 31 | 8995: { 32 | '0xf8c1459e7cf95811f6748a34c9e79ab1c978664a': { 33 | [NAME]: 'DaiStable Coin', 34 | [SYMBOL]: 'DAI', 35 | [DECIMALS]: 8, 36 | [EXCHANGE_ADDRESS]: '0x389212ea4899D5e03F18C50556288EAE978FA965' 37 | }, 38 | '0xc1D19fB05Cd9CbFeF48985aBf074aFB02681Bf4a': { 39 | [NAME]: 'Mana', 40 | [SYMBOL]: 'MANA', 41 | [DECIMALS]: 8, 42 | [EXCHANGE_ADDRESS]: '0x9b22aeAA356fBa6bF2a782287DBeee426D2F077E' 43 | }, 44 | // Using DAI EXCHANGE IN CDAI SINCE IT'S MANDATORY 45 | '0xf190929858f5f6dC55A03B40C25545fE9c79CF8c': { 46 | [NAME]: 'Cdai', 47 | [SYMBOL]: 'CDAI', 48 | [DECIMALS]: 8, 49 | [EXCHANGE_ADDRESS]: '0x389212ea4899D5e03F18C50556288EAE978FA965' 50 | }, 51 | // Using DAI EXCHANGE IN LAND SINCE IT'S MANDATORY THE EXCHANGE ADDRESS IS NEVER USED IN THIS CASE 52 | '0xd1981d74abae2ec51e045242d8bb86a9daaa76b5': { 53 | [NAME]: 'Land Token', 54 | [SYMBOL]: 'LAND', 55 | [DECIMALS]: 8, 56 | [EXCHANGE_ADDRESS]: '0x389212ea4899D5e03F18C50556288EAE978FA965' 57 | } 58 | } 59 | } 60 | 61 | const TokensContext = createContext() 62 | 63 | function useTokensContext() { 64 | return useContext(TokensContext) 65 | } 66 | 67 | function reducer(state, { type, payload }) { 68 | switch (type) { 69 | case UPDATE: { 70 | const { networkId, tokenAddress, name, symbol, decimals, exchangeAddress } = payload 71 | return { 72 | ...state, 73 | [networkId]: { 74 | ...(safeAccess(state, [networkId]) || {}), 75 | [tokenAddress]: { 76 | [NAME]: name, 77 | [SYMBOL]: symbol, 78 | [DECIMALS]: decimals, 79 | [EXCHANGE_ADDRESS]: exchangeAddress 80 | } 81 | } 82 | } 83 | } 84 | default: { 85 | throw Error(`Unexpected action type in TokensContext reducer: '${type}'.`) 86 | } 87 | } 88 | } 89 | 90 | export default function Provider({ children }) { 91 | const [state, dispatch] = useReducer(reducer, INITIAL_TOKENS_CONTEXT) 92 | 93 | const update = useCallback((networkId, tokenAddress, name, symbol, decimals, exchangeAddress) => { 94 | dispatch({ type: UPDATE, payload: { networkId, tokenAddress, name, symbol, decimals, exchangeAddress } }) 95 | }, []) 96 | 97 | return ( 98 | [state, { update }], [state, update])}> 99 | {children} 100 | 101 | ) 102 | } 103 | 104 | export function useTokenDetails(tokenAddress) { 105 | const { library, chainId } = useWeb3React() 106 | 107 | const [state, { update }] = useTokensContext() 108 | const allTokensInNetwork = { ...ETH, ...(safeAccess(state, [chainId]) || {}) } 109 | const { [NAME]: name, [SYMBOL]: symbol, [DECIMALS]: decimals, [EXCHANGE_ADDRESS]: exchangeAddress } = 110 | safeAccess(allTokensInNetwork, [tokenAddress]) || {} 111 | 112 | useEffect(() => { 113 | if ( 114 | isAddress(tokenAddress) && 115 | (name === undefined || symbol === undefined || decimals === undefined || exchangeAddress === undefined) && 116 | (chainId || chainId === 0) && 117 | library 118 | ) { 119 | let stale = false 120 | const namePromise = getTokenName(tokenAddress, library).catch(() => null) 121 | const symbolPromise = getTokenSymbol(tokenAddress, library).catch(() => null) 122 | const decimalsPromise = getTokenDecimals(tokenAddress, library).catch(() => null) 123 | const exchangeAddressPromise = getTokenExchangeAddressFromFactory(tokenAddress, chainId, library).catch( 124 | () => null 125 | ) 126 | 127 | Promise.all([namePromise, symbolPromise, decimalsPromise, exchangeAddressPromise]).then( 128 | ([resolvedName, resolvedSymbol, resolvedDecimals, resolvedExchangeAddress]) => { 129 | if (!stale) { 130 | update(chainId, tokenAddress, resolvedName, resolvedSymbol, resolvedDecimals, resolvedExchangeAddress) 131 | } 132 | } 133 | ) 134 | return () => { 135 | stale = true 136 | } 137 | } 138 | }, [tokenAddress, name, symbol, decimals, exchangeAddress, chainId, library, update]) 139 | 140 | return { name, symbol, decimals, exchangeAddress } 141 | } 142 | 143 | export function useAllTokenDetails() { 144 | const { chainId } = useWeb3React() 145 | 146 | const [state] = useTokensContext() 147 | 148 | return useMemo(() => ({ ...ETH, ...(safeAccess(state, [chainId]) || {}) }), [state, chainId]) 149 | } -------------------------------------------------------------------------------- /src/pages/Pool/CreateExchange.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import { withRouter } from 'react-router' 3 | import { createBrowserHistory } from 'history' 4 | import { ethers } from 'ethers' 5 | import styled from 'styled-components' 6 | import { useTranslation } from 'react-i18next' 7 | import ReactGA from 'react-ga' 8 | 9 | import { useWeb3React, useFactoryContract } from '../../hooks' 10 | import { Button } from '../../theme' 11 | import AddressInputPanel from '../../components/AddressInputPanel' 12 | import OversizedPanel from '../../components/OversizedPanel' 13 | import { useTokenDetails } from '../../contexts/Tokens' 14 | import { useTransactionAdder } from '../../contexts/Transactions' 15 | 16 | const SummaryPanel = styled.div` 17 | ${({ theme }) => theme.flexColumnNoWrap}; 18 | padding: 1rem 0; 19 | ` 20 | 21 | const ExchangeRateWrapper = styled.div` 22 | ${({ theme }) => theme.flexRowNoWrap}; 23 | align-items: center; 24 | color: ${({ theme }) => theme.doveGray}; 25 | font-size: 0.75rem; 26 | padding: 0.25rem 1rem 0; 27 | ` 28 | 29 | const ExchangeRate = styled.span` 30 | flex: 1 1 auto; 31 | width: 0; 32 | color: ${({ theme }) => theme.doveGray}; 33 | ` 34 | 35 | const CreateExchangeWrapper = styled.div` 36 | color: ${({ theme }) => theme.doveGray}; 37 | text-align: center; 38 | margin-top: 1rem; 39 | padding-top: 1rem; 40 | ` 41 | 42 | const SummaryText = styled.div` 43 | font-size: 0.75rem; 44 | color: ${({ error, theme }) => error && theme.salmonRed}; 45 | ` 46 | 47 | const Flex = styled.div` 48 | display: flex; 49 | justify-content: center; 50 | padding: 2rem; 51 | 52 | button { 53 | max-width: 20rem; 54 | } 55 | ` 56 | 57 | function CreateExchange({ location, params }) { 58 | const { t } = useTranslation() 59 | const { account } = useWeb3React() 60 | 61 | var [brokenTokenWarning] = useState() 62 | 63 | const factory = useFactoryContract() 64 | 65 | const [tokenAddress, setTokenAddress] = useState({ 66 | address: params.tokenAddress ? params.tokenAddress : '', 67 | name: '' 68 | }) 69 | const [tokenAddressError, setTokenAddressError] = useState() 70 | 71 | const { name, symbol, decimals, exchangeAddress } = useTokenDetails(tokenAddress.address) 72 | const addTransaction = useTransactionAdder() 73 | 74 | // LAND & CDAI NOT SUPPORTED 75 | if (symbol === 'CDAI' || symbol === 'LAND') { 76 | brokenTokenWarning = true; 77 | } 78 | 79 | // clear url of query 80 | useEffect(() => { 81 | const history = createBrowserHistory() 82 | history.push(window.location.pathname + '') 83 | }, []) 84 | 85 | // validate everything 86 | const [errorMessage, setErrorMessage] = useState(!account && t('noWallet')) 87 | useEffect(() => { 88 | if (tokenAddressError) { 89 | setErrorMessage(t('invalidTokenAddress')) 90 | } else if (symbol === undefined || decimals === undefined || exchangeAddress === undefined) { 91 | setErrorMessage() 92 | } else if (symbol === null) { 93 | setErrorMessage(t('invalidSymbol')) 94 | } else if (decimals === null) { 95 | setErrorMessage(t('invalidDecimals')) 96 | } else if (exchangeAddress !== ethers.constants.AddressZero) { 97 | setErrorMessage(t('exchangeExists')) 98 | } else if (!account) { 99 | setErrorMessage(t('noWallet')) 100 | } else { 101 | setErrorMessage(null) 102 | } 103 | 104 | return () => { 105 | setErrorMessage() 106 | } 107 | }, [tokenAddress.address, symbol, decimals, exchangeAddress, account, t, tokenAddressError]) 108 | 109 | async function createExchange() { 110 | const estimatedGasLimit = await factory.estimate.createExchange(tokenAddress.address) 111 | 112 | factory.createExchange(tokenAddress.address, { gasLimit: estimatedGasLimit }).then(response => { 113 | ReactGA.event({ 114 | category: 'Transaction', 115 | action: 'Create Exchange' 116 | }) 117 | 118 | addTransaction(response) 119 | }) 120 | } 121 | 122 | const isValid = errorMessage === null 123 | 124 | return ( 125 | <> 126 | 136 | 137 | 138 | 139 | {t('name')} 140 | {name ? name : ' - '} 141 | 142 | 143 | {t('symbol')} 144 | {symbol ? symbol : ' - '} 145 | 146 | 147 | {t('decimals')} 148 | {decimals || decimals === 0 ? decimals : ' - '} 149 | 150 | 151 | 152 | 153 | {errorMessage ? errorMessage : t('enterTokenCont')} 154 | 155 | 156 | 159 | 160 | 161 | ) 162 | } 163 | 164 | export default withRouter(CreateExchange) 165 | -------------------------------------------------------------------------------- /src/components/AddressInputPanel/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import styled from 'styled-components' 3 | import { useTranslation } from 'react-i18next' 4 | import { transparentize } from 'polished' 5 | 6 | import { isAddress } from '../../utils' 7 | import { useWeb3React, useDebounce } from '../../hooks' 8 | 9 | const InputPanel = styled.div` 10 | ${({ theme }) => theme.flexColumnNoWrap} 11 | box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.95, theme.shadowColor)}; 12 | position: relative; 13 | border-radius: 1.25rem; 14 | background-color: ${({ theme }) => theme.inputBackground}; 15 | z-index: 1; 16 | ` 17 | 18 | const ContainerRow = styled.div` 19 | display: flex; 20 | justify-content: center; 21 | align-items: center; 22 | border-radius: 1.25rem; 23 | border: 1px solid ${({ error, theme }) => (error ? theme.salmonRed : theme.mercuryGray)}; 24 | 25 | background-color: ${({ theme }) => theme.inputBackground}; 26 | ` 27 | 28 | const InputContainer = styled.div` 29 | flex: 1; 30 | ` 31 | 32 | const LabelRow = styled.div` 33 | ${({ theme }) => theme.flexRowNoWrap} 34 | align-items: center; 35 | color: ${({ theme }) => theme.doveGray}; 36 | font-size: 0.75rem; 37 | line-height: 1rem; 38 | padding: 0.75rem 1rem; 39 | ` 40 | 41 | const LabelContainer = styled.div` 42 | flex: 1 1 auto; 43 | width: 0; 44 | overflow: hidden; 45 | white-space: nowrap; 46 | text-overflow: ellipsis; 47 | ` 48 | 49 | const InputRow = styled.div` 50 | ${({ theme }) => theme.flexRowNoWrap} 51 | align-items: center; 52 | padding: 0.25rem 0.85rem 0.75rem; 53 | ` 54 | 55 | const Input = styled.input` 56 | font-size: 1rem; 57 | outline: none; 58 | border: none; 59 | flex: 1 1 auto; 60 | width: 0; 61 | background-color: ${({ theme }) => theme.inputBackground}; 62 | 63 | color: ${({ error, theme }) => (error ? theme.salmonRed : theme.royalBlue)}; 64 | overflow: hidden; 65 | text-overflow: ellipsis; 66 | 67 | ::placeholder { 68 | color: ${({ theme }) => theme.placeholderGray}; 69 | } 70 | ` 71 | 72 | export default function AddressInputPanel({ title, initialInput = '', onChange = () => {}, onError = () => {} }) { 73 | const { t } = useTranslation() 74 | 75 | const { library } = useWeb3React() 76 | 77 | const [input, setInput] = useState(initialInput.address ? initialInput.address : '') 78 | 79 | const debouncedInput = useDebounce(input, 150) 80 | 81 | const [data, setData] = useState({ address: undefined, name: undefined }) 82 | const [error, setError] = useState(false) 83 | 84 | // keep data and errors in sync 85 | useEffect(() => { 86 | onChange({ address: data.address, name: data.name }) 87 | }, [onChange, data.address, data.name]) 88 | useEffect(() => { 89 | onError(error) 90 | }, [onError, error]) 91 | 92 | // run parser on debounced input 93 | useEffect(() => { 94 | let stale = false 95 | 96 | if (isAddress(debouncedInput)) { 97 | try { 98 | library 99 | .lookupAddress(debouncedInput) 100 | .then(name => { 101 | if (!stale) { 102 | // if an ENS name exists, set it as the destination 103 | if (name) { 104 | setInput(name) 105 | } else { 106 | setData({ address: debouncedInput, name: '' }) 107 | setError(null) 108 | } 109 | } 110 | }) 111 | .catch(() => { 112 | if (!stale) { 113 | setData({ address: debouncedInput, name: '' }) 114 | setError(null) 115 | } 116 | }) 117 | } catch { 118 | setData({ address: debouncedInput, name: '' }) 119 | setError(null) 120 | } 121 | } else { 122 | if (debouncedInput !== '') { 123 | try { 124 | library 125 | .resolveName(debouncedInput) 126 | .then(address => { 127 | if (!stale) { 128 | // if the debounced input name resolves to an address 129 | if (address) { 130 | setData({ address: address, name: debouncedInput }) 131 | setError(null) 132 | } else { 133 | setError(true) 134 | } 135 | } 136 | }) 137 | .catch(() => { 138 | if (!stale) { 139 | setError(true) 140 | } 141 | }) 142 | } catch { 143 | setError(true) 144 | } 145 | } 146 | } 147 | 148 | return () => { 149 | stale = true 150 | } 151 | }, [debouncedInput, library, onChange, onError]) 152 | 153 | function onInput(event) { 154 | if (data.address !== undefined || data.name !== undefined) { 155 | setData({ address: undefined, name: undefined }) 156 | } 157 | if (error !== undefined) { 158 | setError() 159 | } 160 | const input = event.target.value 161 | const checksummedInput = isAddress(input) 162 | setInput(checksummedInput || input) 163 | } 164 | 165 | return ( 166 | 167 | 168 | 169 | 170 | 171 | {title || t('recipientAddress')} 172 | 173 | 174 | 175 | 186 | 187 | 188 | 189 | 190 | ) 191 | } 192 | -------------------------------------------------------------------------------- /src/constants/index.js: -------------------------------------------------------------------------------- 1 | import { injected, walletconnect, walletlink, fortmatic, portis, torus } from '../connectors' 2 | 3 | export const FACTORY_ADDRESSES = { 4 | 1: '0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95', 5 | 3: '0x9c83dCE8CA20E9aAF9D3efc003b2ea62aBC08351', 6 | 4: '0xf5D915570BC477f9B8D6C0E980aA81757A3AaC36', 7 | 42: '0xD3E51Ef092B2845f10401a0159B2B96e8B6c3D30', 8 | 8995: '0x24A511435E0b5dE3AA1aD6f773A3D7e03A08dE00' 9 | } 10 | 11 | export const SUPPORTED_THEMES = { 12 | DARK: 'DARK', 13 | LIGHT: 'LIGHT' 14 | } 15 | 16 | const MAINNET_WALLETS = { 17 | INJECTED: { 18 | connector: injected, 19 | name: 'Injected', 20 | iconName: 'arrow-right.svg', 21 | description: 'Injected web3 provider.', 22 | href: null, 23 | color: '#010101', 24 | primary: true 25 | }, 26 | METAMASK: { 27 | connector: injected, 28 | name: 'MetaMask', 29 | iconName: 'metamask.png', 30 | description: 'Easy-to-use browser extension.', 31 | href: null, 32 | color: '#E8831D' 33 | }, 34 | FORTMATIC: { 35 | connector: fortmatic, 36 | name: 'Fortmatic', 37 | iconName: 'fortmaticIcon.png', 38 | description: 'Login using Fortmatic hosted wallet', 39 | href: null, 40 | color: '#6748FF', 41 | mobile: true 42 | }, 43 | Portis: { 44 | connector: portis, 45 | name: 'Portis', 46 | iconName: 'portisIcon.png', 47 | description: 'Login using Portis hosted wallet', 48 | href: null, 49 | color: '#4A6C9B', 50 | mobile: true 51 | }, 52 | Torus: { 53 | connector: torus, 54 | name: 'Torus', 55 | iconName: 'torus.png', 56 | description: 'Login via Google, Facebook and others', 57 | href: null, 58 | color: '#5495F7', 59 | mobile: true 60 | }, 61 | WALLET_CONNECT: { 62 | connector: walletconnect, 63 | name: 'WalletConnect', 64 | iconName: 'walletConnectIcon.svg', 65 | description: 'Connect to Trust Wallet, Rainbow Wallet and more...', 66 | href: null, 67 | color: '#4196FC' 68 | }, 69 | WALLET_LINK: { 70 | connector: walletlink, 71 | name: 'Coinbase Wallet', 72 | iconName: 'coinbaseWalletIcon.svg', 73 | description: 'Use Coinbase Wallet app on mobile device', 74 | href: null, 75 | color: '#315CF5' 76 | }, 77 | COINBASE_LINK: { 78 | name: 'Open in Coinbase Wallet', 79 | iconName: 'coinbaseWalletIcon.svg', 80 | description: 'Open in Coinbase Wallet app.', 81 | href: 'https://go.cb-w.com/mtUDhEZPy1', 82 | color: '#315CF5', 83 | mobile: true, 84 | mobileOnly: true 85 | }, 86 | TRUST_WALLET_LINK: { 87 | name: 'Open in Trust Wallet', 88 | iconName: 'trustWallet.png', 89 | description: 'iOS and Android app.', 90 | href: 'https://link.trustwallet.com/open_url?coin_id=60&url=https://uniswap.exchange/swap', 91 | color: '#1C74CC', 92 | mobile: true, 93 | mobileOnly: true 94 | } 95 | } 96 | 97 | export const SUPPORTED_WALLETS = 98 | '8995' !== '1' 99 | ? MAINNET_WALLETS 100 | : { 101 | ...MAINNET_WALLETS, 102 | ...{ 103 | WALLET_CONNECT: { 104 | connector: walletconnect, 105 | name: 'WalletConnect', 106 | iconName: 'walletConnectIcon.svg', 107 | description: 'Connect to Trust Wallet, Rainbow Wallet and more...', 108 | href: null, 109 | color: '#4196FC' 110 | }, 111 | WALLET_LINK: { 112 | connector: walletlink, 113 | name: 'Coinbase Wallet', 114 | iconName: 'coinbaseWalletIcon.svg', 115 | description: 'Use Coinbase Wallet app on mobile device', 116 | href: null, 117 | color: '#315CF5' 118 | }, 119 | COINBASE_LINK: { 120 | name: 'Open in Coinbase Wallet', 121 | iconName: 'coinbaseWalletIcon.svg', 122 | description: 'Open in Coinbase Wallet app.', 123 | href: 'https://go.cb-w.com/mtUDhEZPy1', 124 | color: '#315CF5', 125 | mobile: true, 126 | mobileOnly: true 127 | }, 128 | TRUST_WALLET_LINK: { 129 | name: 'Open in Trust Wallet', 130 | iconName: 'trustWallet.png', 131 | description: 'iOS and Android app.', 132 | href: 'https://link.trustwallet.com/open_url?coin_id=60&url=https://uniswap.exchange/swap', 133 | color: '#1C74CC', 134 | mobile: true, 135 | mobileOnly: true 136 | }, 137 | FORTMATIC: { 138 | connector: fortmatic, 139 | name: 'Fortmatic', 140 | iconName: 'fortmaticIcon.png', 141 | description: 'Login using Fortmatic hosted wallet', 142 | href: null, 143 | color: '#6748FF', 144 | mobile: true 145 | }, 146 | Portis: { 147 | connector: portis, 148 | name: 'Portis', 149 | iconName: 'portisIcon.png', 150 | description: 'Login using Portis hosted wallet', 151 | href: null, 152 | color: '#4A6C9B', 153 | mobile: true 154 | } 155 | } 156 | } 157 | 158 | // list of tokens that lock fund on adding liquidity - used to disable button 159 | export const brokenTokens = [ 160 | '0xB8c77482e45F1F44dE1745F52C74426C631bDD52', 161 | '0x95dAaaB98046846bF4B2853e23cba236fa394A31', 162 | '0x55296f69f40Ea6d20E478533C15A6B08B654E758', 163 | '0xc3761EB917CD790B30dAD99f6Cc5b4Ff93C4F9eA', 164 | '0x5C406D99E04B8494dc253FCc52943Ef82bcA7D75' 165 | ] 166 | 167 | export const NetworkContextName = 'NETWORK' -------------------------------------------------------------------------------- /src/contexts/Transactions.js: -------------------------------------------------------------------------------- 1 | import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react' 2 | 3 | import { useWeb3React } from '../hooks' 4 | import { safeAccess } from '../utils' 5 | import { useBlockNumber } from './Application' 6 | 7 | const RESPONSE = 'response' 8 | const CUSTOM_DATA = 'CUSTOM_DATA' 9 | const BLOCK_NUMBER_CHECKED = 'BLOCK_NUMBER_CHECKED' 10 | const RECEIPT = 'receipt' 11 | 12 | const ADD = 'ADD' 13 | const CHECK = 'CHECK' 14 | const FINALIZE = 'FINALIZE' 15 | 16 | const TransactionsContext = createContext() 17 | 18 | export function useTransactionsContext() { 19 | return useContext(TransactionsContext) 20 | } 21 | 22 | function reducer(state, { type, payload }) { 23 | switch (type) { 24 | case ADD: { 25 | const { networkId, hash, response } = payload 26 | 27 | if (safeAccess(state, [networkId, hash]) !== null) { 28 | throw Error('Attempted to add existing transaction.') 29 | } 30 | 31 | return { 32 | ...state, 33 | [networkId]: { 34 | ...(safeAccess(state, [networkId]) || {}), 35 | [hash]: { 36 | [RESPONSE]: response 37 | } 38 | } 39 | } 40 | } 41 | case CHECK: { 42 | const { networkId, hash, blockNumber } = payload 43 | 44 | if (safeAccess(state, [networkId, hash]) === null) { 45 | throw Error('Attempted to check non-existent transaction.') 46 | } 47 | 48 | return { 49 | ...state, 50 | [networkId]: { 51 | ...(safeAccess(state, [networkId]) || {}), 52 | [hash]: { 53 | ...(safeAccess(state, [networkId, hash]) || {}), 54 | [BLOCK_NUMBER_CHECKED]: blockNumber 55 | } 56 | } 57 | } 58 | } 59 | case FINALIZE: { 60 | const { networkId, hash, receipt } = payload 61 | 62 | if (safeAccess(state, [networkId, hash]) === null) { 63 | throw Error('Attempted to finalize non-existent transaction.') 64 | } 65 | 66 | return { 67 | ...state, 68 | [networkId]: { 69 | ...(safeAccess(state, [networkId]) || {}), 70 | [hash]: { 71 | ...(safeAccess(state, [networkId, hash]) || {}), 72 | [RECEIPT]: receipt 73 | } 74 | } 75 | } 76 | } 77 | default: { 78 | throw Error(`Unexpected action type in TransactionsContext reducer: '${type}'.`) 79 | } 80 | } 81 | } 82 | 83 | export default function Provider({ children }) { 84 | const [state, dispatch] = useReducer(reducer, {}) 85 | 86 | const add = useCallback((networkId, hash, response) => { 87 | dispatch({ type: ADD, payload: { networkId, hash, response } }) 88 | }, []) 89 | const check = useCallback((networkId, hash, blockNumber) => { 90 | dispatch({ type: CHECK, payload: { networkId, hash, blockNumber } }) 91 | }, []) 92 | const finalize = useCallback((networkId, hash, receipt) => { 93 | dispatch({ type: FINALIZE, payload: { networkId, hash, receipt } }) 94 | }, []) 95 | 96 | return ( 97 | [state, { add, check, finalize }], [state, add, check, finalize])} 99 | > 100 | {children} 101 | 102 | ) 103 | } 104 | 105 | export function Updater() { 106 | const { chainId, library } = useWeb3React() 107 | 108 | const globalBlockNumber = useBlockNumber() 109 | 110 | const [state, { check, finalize }] = useTransactionsContext() 111 | const allTransactions = safeAccess(state, [chainId]) || {} 112 | 113 | useEffect(() => { 114 | if ((chainId || chainId === 0) && library) { 115 | let stale = false 116 | Object.keys(allTransactions) 117 | .filter( 118 | hash => !allTransactions[hash][RECEIPT] && allTransactions[hash][BLOCK_NUMBER_CHECKED] !== globalBlockNumber 119 | ) 120 | .forEach(hash => { 121 | library 122 | .getTransactionReceipt(hash) 123 | .then(receipt => { 124 | if (!stale) { 125 | if (!receipt) { 126 | check(chainId, hash, globalBlockNumber) 127 | } else { 128 | finalize(chainId, hash, receipt) 129 | } 130 | } 131 | }) 132 | .catch(() => { 133 | check(chainId, hash, globalBlockNumber) 134 | }) 135 | }) 136 | 137 | return () => { 138 | stale = true 139 | } 140 | } 141 | }, [chainId, library, allTransactions, globalBlockNumber, check, finalize]) 142 | 143 | return null 144 | } 145 | 146 | export function useTransactionAdder() { 147 | const { chainId } = useWeb3React() 148 | 149 | const [, { add }] = useTransactionsContext() 150 | 151 | return useCallback( 152 | (response, customData = {}) => { 153 | if (!(chainId || chainId === 0)) { 154 | throw Error(`Invalid networkId '${chainId}`) 155 | } 156 | 157 | const hash = safeAccess(response, ['hash']) 158 | 159 | if (!hash) { 160 | throw Error('No transaction hash found.') 161 | } 162 | add(chainId, hash, { ...response, [CUSTOM_DATA]: customData }) 163 | }, 164 | [chainId, add] 165 | ) 166 | } 167 | 168 | export function useAllTransactions() { 169 | const { chainId } = useWeb3React() 170 | 171 | const [state] = useTransactionsContext() 172 | 173 | return safeAccess(state, [chainId]) || {} 174 | } 175 | 176 | export function usePendingApproval(tokenAddress) { 177 | const allTransactions = useAllTransactions() 178 | 179 | return ( 180 | Object.keys(allTransactions).filter(hash => { 181 | if (allTransactions[hash][RECEIPT]) { 182 | return false 183 | } else if (!allTransactions[hash][RESPONSE]) { 184 | return false 185 | } else if (allTransactions[hash][RESPONSE][CUSTOM_DATA].approval !== tokenAddress) { 186 | return false 187 | } else { 188 | return true 189 | } 190 | }).length >= 1 191 | ) 192 | } 193 | -------------------------------------------------------------------------------- /src/components/Modal/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled, { css } from 'styled-components' 3 | import { animated, useTransition, useSpring } from 'react-spring' 4 | import { Spring } from 'react-spring/renderprops' 5 | 6 | import { DialogOverlay, DialogContent } from '@reach/dialog' 7 | import { isMobile } from 'react-device-detect' 8 | import '@reach/dialog/styles.css' 9 | import { transparentize } from 'polished' 10 | import { useGesture } from 'react-use-gesture' 11 | 12 | const AnimatedDialogOverlay = animated(DialogOverlay) 13 | const WrappedDialogOverlay = ({ suppressClassNameWarning, mobile, ...rest }) => 14 | const StyledDialogOverlay = styled(WrappedDialogOverlay).attrs({ 15 | suppressClassNameWarning: true 16 | })` 17 | &[data-reach-dialog-overlay] { 18 | z-index: 2; 19 | display: flex; 20 | align-items: center; 21 | justify-content: center; 22 | background-color: ${({ theme }) => 'transparent'}; 23 | 24 | ${({ mobile }) => 25 | mobile && 26 | css` 27 | align-items: flex-end; 28 | `} 29 | 30 | &::after { 31 | content: ''; 32 | background-color: ${({ theme }) => theme.modalBackground}; 33 | opacity: 0.5; 34 | top: 0; 35 | left: 0; 36 | bottom: 0; 37 | right: 0; 38 | /* position: absolute; */ 39 | position: fixed; 40 | z-index: -1; 41 | } 42 | } 43 | ` 44 | 45 | const FilteredDialogContent = ({ minHeight, maxHeight, isOpen, slideInAnimation, mobile, ...rest }) => ( 46 | 47 | ) 48 | const StyledDialogContent = styled(FilteredDialogContent)` 49 | &[data-reach-dialog-content] { 50 | margin: 0 0 2rem 0; 51 | border: 1px solid ${({ theme }) => theme.concreteGray}; 52 | background-color: ${({ theme }) => theme.inputBackground}; 53 | box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.95, theme.shadowColor)}; 54 | padding: 0px; 55 | width: 50vw; 56 | 57 | max-width: 650px; 58 | ${({ maxHeight }) => 59 | maxHeight && 60 | css` 61 | max-height: ${maxHeight}vh; 62 | `} 63 | ${({ minHeight }) => 64 | minHeight && 65 | css` 66 | min-height: ${minHeight}vh; 67 | `} 68 | display: flex; 69 | overflow: hidden; 70 | border-radius: 10px; 71 | ${({ theme }) => theme.mediaWidth.upToMedium` 72 | width: 65vw; 73 | max-height: 65vh; 74 | margin: 0; 75 | `} 76 | ${({ theme, mobile, isOpen }) => theme.mediaWidth.upToSmall` 77 | width: 85vw; 78 | max-height: 66vh; 79 | ${mobile && 80 | css` 81 | width: 100vw; 82 | border-radius: 20px; 83 | border-bottom-left-radius: 0; 84 | border-bottom-right-radius: 0; 85 | `} 86 | `} 87 | } 88 | ` 89 | 90 | const HiddenCloseButton = styled.button` 91 | margin: 0; 92 | padding: 0; 93 | width: 0; 94 | height: 0; 95 | border: none; 96 | ` 97 | 98 | export default function Modal({ isOpen, onDismiss, minHeight = false, maxHeight = 50, initialFocusRef, children }) { 99 | const transitions = useTransition(isOpen, null, { 100 | config: { duration: 200 }, 101 | from: { opacity: 0 }, 102 | enter: { opacity: 1 }, 103 | leave: { opacity: 0 } 104 | }) 105 | 106 | const [{ xy }, set] = useSpring(() => ({ xy: [0, 0] })) 107 | const bind = useGesture({ 108 | onDrag: state => { 109 | let velocity = state.velocity 110 | if (velocity < 1) { 111 | velocity = 1 112 | } 113 | if (velocity > 8) { 114 | velocity = 8 115 | } 116 | set({ 117 | xy: state.down ? state.movement : [0, 0], 118 | config: { mass: 1, tension: 210, friction: 20 } 119 | }) 120 | if (velocity > 3 && state.direction[1] > 0) { 121 | onDismiss() 122 | } 123 | } 124 | }) 125 | 126 | if (isMobile) { 127 | return transitions.map( 128 | ({ item, key, props }) => 129 | item && ( 130 | 137 | 145 | {props => ( 146 | `translate3d(${0}px,${y > 0 ? y : 0}px,0)`) }} 149 | > 150 | 160 | 161 | )} 162 | 163 | 164 | ) 165 | ) 166 | } else { 167 | return transitions.map( 168 | ({ item, key, props }) => 169 | item && ( 170 | 177 | 187 | 188 | ) 189 | ) 190 | } 191 | } 192 | --------------------------------------------------------------------------------