├── .babelrc ├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── .gitmodules ├── .prettierrc ├── Participants Agreement V1.pdf ├── README.md ├── build ├── webpack-dev.config.js └── webpack-prod.config.js ├── nec.png ├── package.json ├── proposal.jpg ├── proposed_tokens ├── ABYSS.json ├── ARN.json ├── ATN.json ├── CRAD.json ├── EnergiToken.json ├── LOOM.json ├── Labtorum.json ├── MINDEXCOIN.json ├── Pinkiaton.json ├── WePower.json ├── airswap.json ├── axpire.json ├── daostack.json ├── fetch.json ├── foam.json ├── gnosis.json ├── hydro.json ├── index.js ├── ink.json ├── iotex.json ├── pumapay.json ├── scroll.json ├── solve.json ├── spacechain.json ├── swarm.json ├── tolar.json ├── tripio.json ├── upfiring.json ├── upfiringNEW.json ├── wibson.json └── zrx.json ├── screenshots └── traderboard_screenshot.png ├── server ├── config.dist.js ├── create.sql ├── crud-service.js ├── index.js └── wasteland │ ├── .gitignore │ ├── .travis.yml │ ├── README.md │ ├── backends │ ├── Grenache.js │ ├── Memory.js │ └── index.js │ ├── examples │ ├── grenache.js │ └── memory.js │ ├── index.js │ ├── lib │ ├── Wasteland.js │ └── data-helper.js │ ├── package.json │ └── test │ ├── helper.js │ ├── integration.grenache-backend.js │ ├── integration.memory-backend.js │ ├── integration.wasteland.js │ └── unit.data-helper.js ├── solidity ├── build │ └── contracts │ │ ├── ApproveAndCallFallBack.json │ │ ├── Controlled.json │ │ ├── DestructibleMiniMeToken.json │ │ ├── DestructibleMiniMeTokenFactory.json │ │ ├── Migrations.json │ │ ├── MiniMeToken.json │ │ ├── MiniMeTokenFactory.json │ │ ├── NEC.json │ │ ├── NectarController.json │ │ ├── Ownable.json │ │ ├── Owned.json │ │ ├── ProposalManager.json │ │ ├── SafeMath.json │ │ ├── TokenController.json │ │ ├── TokenListingManager.json │ │ ├── TokenListingManagerAdvanced.json │ │ └── Whitelist.json ├── contracts │ ├── Controlled.sol │ ├── DestructibleMiniMeToken.sol │ ├── DestructibleMiniMeTokenFactory.sol │ ├── Migrations.sol │ ├── MiniMeToken.sol │ ├── NEC.sol │ ├── NectarController.sol │ ├── Ownable.sol │ ├── ProposalManager.sol │ ├── TokenController.sol │ ├── TokenListingManager.sol │ ├── TokenListingManagerAdvanced.sol │ └── Whitelist.sol ├── migrations │ ├── 1_initial_migration.js │ └── 2_deploy_contracts.js ├── scripts │ ├── data.json │ └── get_config.py ├── test │ ├── helpers │ │ └── advanceToBlock.js │ ├── testTokenListingManager.js │ └── testTokenListingManagerAdvanced.js ├── truffle-config.js └── truffle.js ├── src ├── actions │ ├── accountActions.js │ ├── actionTypes.js │ ├── auctionActions.js │ ├── competitionActions.js │ ├── delegateActions.js │ ├── notificationActions.js │ ├── proposalActions.js │ ├── tokenActions.js │ ├── tokenData.js │ └── traderAction.js ├── common │ ├── components.scss │ ├── header-desc-container.scss │ ├── text.scss │ └── variables.scss ├── components │ ├── Admin │ │ ├── Admin.jsx │ │ └── Admin.scss │ ├── AllProposals │ │ ├── AllProposals.jsx │ │ └── AllProposals.scss │ ├── App │ │ ├── App.jsx │ │ ├── App.scss │ │ └── assets │ │ │ ├── deversifi-logo-dark.png │ │ │ └── neclogo.svg │ ├── Auction │ │ ├── Auction.jsx │ │ ├── Auction.scss │ │ ├── Description │ │ │ └── index.js │ │ └── Diagrams │ │ │ ├── BarDiagram.jsx │ │ │ ├── Circle.jsx │ │ │ └── Diagram.jsx │ ├── Calculator │ │ ├── Calculator.jsx │ │ └── Calculator.scss │ ├── DAO │ │ ├── DAO.jsx │ │ ├── DAO.scss │ │ ├── Eth_cand.gif │ │ ├── Eth_draw.gif │ │ ├── Eth_pool.gif │ │ ├── Eth_vote.gif │ │ ├── Eth_winners.gif │ │ ├── TermsModal │ │ │ ├── Terms.jsx │ │ │ ├── Terms.scss │ │ │ └── TermsModal.jsx │ │ ├── assets │ │ │ └── arrow.svg │ │ └── free-thinking-icon.png │ ├── DelegateVotes │ │ ├── DelegateVotes.jsx │ │ └── DelegateVotes.scss │ ├── Help │ │ ├── Help.jsx │ │ └── Help.scss │ ├── Landing │ │ ├── CircleButton │ │ │ ├── CircleButton.jsx │ │ │ ├── CircleButton.scss │ │ │ └── index.js │ │ ├── Landing.jsx │ │ ├── Landing.scss │ │ ├── scrollHelper.js │ │ ├── sections │ │ │ ├── DaoGovernance │ │ │ │ ├── DaoGovernance.jsx │ │ │ │ └── index.js │ │ │ ├── Ecosystem │ │ │ │ ├── Ecosystem.jsx │ │ │ │ ├── Ecosystem.scss │ │ │ │ └── index.js │ │ │ ├── Nec │ │ │ │ ├── Nec.jsx │ │ │ │ ├── Nec.scss │ │ │ │ └── index.js │ │ │ ├── Neconomics │ │ │ │ ├── Neconomics.jsx │ │ │ │ ├── Neconomics.scss │ │ │ │ └── index.js │ │ │ └── index.js │ │ └── volumeHelpers.js │ ├── Loading │ │ └── index.jsx │ ├── Login │ │ ├── Login.jsx │ │ └── Login.scss │ ├── MissionProposal │ │ ├── MissionProposal.jsx │ │ └── MissionProposal.scss │ ├── PreviousTokenListing │ │ ├── PreviousTokenListing.jsx │ │ ├── PreviousTokenListing.scss │ │ └── previousTokenData.js │ ├── Proposal │ │ ├── Proposal.jsx │ │ └── Proposal.scss │ ├── ProposalCountdown │ │ ├── ProposalCountdown.jsx │ │ └── ProposalCountdown.scss │ ├── Stats │ │ ├── Stats.jsx │ │ └── Stats.scss │ ├── Submit │ │ ├── Submit.jsx │ │ └── Submit.scss │ ├── TokenAbout │ │ ├── Eth_cand.gif │ │ ├── Eth_draw.gif │ │ ├── Eth_pool.gif │ │ ├── Eth_vote.gif │ │ ├── Eth_winners.gif │ │ ├── TokenAbout.jsx │ │ └── TokenAbout.scss │ ├── TokenListingVoteModal │ │ ├── TokenListingVoteModal.jsx │ │ └── TokenListingVoteModal.scss │ ├── TokenListings │ │ ├── TokenListings.jsx │ │ ├── TokenListings.scss │ │ └── currentLeaderboard.js │ ├── TokenPool │ │ ├── TokenPool.jsx │ │ ├── TokenPool.scss │ │ └── check-circle.svg │ ├── Tokens │ │ ├── Tokens.jsx │ │ └── Tokens.scss │ ├── Traderboard │ │ ├── Traderboard.jsx │ │ ├── Traderboard.scss │ │ └── arrow.svg │ ├── TradingReward │ │ ├── TradingReward.jsx │ │ └── TradingReward.scss │ ├── Voting │ │ ├── Current │ │ │ ├── Current.jsx │ │ │ └── Current.scss │ │ ├── Past │ │ │ ├── Past.jsx │ │ │ └── Past.scss │ │ ├── Voting.jsx │ │ └── Voting.scss │ └── WhitePaper │ │ ├── NEC 2.0 WP V1.4.pdf │ │ ├── WhitePaper.jsx │ │ └── WhitePaper.scss ├── constants │ ├── abis.json │ ├── config.dev.json │ ├── config.dist.json │ ├── images │ │ ├── Ethfinex_voEDIT.png │ │ ├── delegate-icon.svg │ │ ├── delegators-icon.svg │ │ ├── dolphin.svg │ │ ├── down.svg │ │ ├── landing-bg.png │ │ ├── landingBackgrounds │ │ │ ├── buy-bg.png │ │ │ ├── buy-bg@2x.png │ │ │ ├── dao-bg.png │ │ │ ├── dao-bg@2x.png │ │ │ ├── exchanges-bg.png │ │ │ ├── exchanges-bg@2x.png │ │ │ ├── fee-bg.png │ │ │ ├── fee-bg@2x.png │ │ │ ├── nec-bg.png │ │ │ └── nec-bg@2x.png │ │ ├── landingIcons │ │ │ ├── DeversiFi.png │ │ │ ├── arrow-bottom.svg │ │ │ ├── badge.svg │ │ │ ├── balancer.png │ │ │ ├── bitfinex.svg │ │ │ ├── buy-active.svg │ │ │ ├── buy-mono.svg │ │ │ ├── buy.svg │ │ │ ├── dao-active.svg │ │ │ ├── dao.svg │ │ │ ├── daxag.svg │ │ │ ├── deversifi-logo-dark.png │ │ │ ├── dexag.svg │ │ │ ├── discord.jsx │ │ │ ├── exchanges-active.svg │ │ │ ├── exchanges.svg │ │ │ ├── fee-active.svg │ │ │ ├── fee.svg │ │ │ ├── icon-arrows.svg │ │ │ ├── neconomics-active.svg │ │ │ ├── neconomics.svg │ │ │ ├── new-logo-long.svg │ │ │ ├── twitter.jsx │ │ │ └── uniswap.svg │ │ ├── logo.svg │ │ ├── minnow.svg │ │ ├── nectar.png │ │ ├── new-logo-v.svg │ │ ├── new-logo-wh.svg │ │ ├── shark.svg │ │ ├── up.svg │ │ ├── wave1.png │ │ ├── wave2.png │ │ ├── whale.svg │ │ └── x.svg │ └── videos │ │ └── Ethfinex_voEDIT.mp4 ├── index.html ├── index.jsx ├── reducers │ ├── accountReducer.js │ ├── auctionReducer.js │ ├── competitionReducer.js │ ├── delegateReducer.js │ ├── index.js │ ├── notificationReducer.js │ ├── proposalReducer.js │ ├── tokenReducer.js │ └── traderReducer.js ├── routes.jsx ├── services │ ├── ethereumService.js │ ├── grenacheService.js │ ├── keystoreService.js │ ├── klerosService.js │ ├── listedTokens.js │ ├── scrollAnimation.js │ └── utils.js └── store.js └── static.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env", "@babel/preset-react"], 3 | "plugins": [ 4 | "@babel/plugin-transform-runtime", 5 | "@babel/plugin-proposal-class-properties", 6 | "@babel/plugin-proposal-optional-chaining", 7 | "@babel/plugin-proposal-export-default-from" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['eslint-config-airbnb', 'prettier'], 3 | plugins: ['react', 'import', 'prettier'], 4 | parser: 'babel-eslint', 5 | env: { 6 | es6: true, 7 | browser: true, 8 | }, 9 | rules: { 10 | 'prettier/prettier': ['warn'], 11 | 'max-len': [2, 120, 2, { ignoreComments: true }], 12 | 'class-methods-use-this': 0, 13 | 'jsx-a11y/href-no-hash': 'off', 14 | 'react/jsx-tag-spacing': 1, 15 | 'react/no-danger': 0, 16 | 'no-underscore-dangle': 0, 17 | 'global-require': 0, 18 | 'no-console': 0, 19 | 'new-cap': 0, 20 | 'eol-last': 0, 21 | 'jsx-a11y/label-has-for': 0, 22 | 'linebreak-style': 0, 23 | 'consistent-return': 0, 24 | 'react/forbid-prop-types': 0, 25 | 'import/prefer-default-export': 0, 26 | 'no-unescaped-entities': 0, 27 | 'jsx-a11y/no-static-element-interactions': 0, 28 | 'jsx-a11y/click-events-have-key-events': 0, 29 | 'jsx-a11y/anchor-is-valid': 0, 30 | 'no-shadow': 0, 31 | 'prefer-destructuring': 0, 32 | 'function-paren-newline': 0, 33 | 'no-restricted-globals': 0, 34 | 'no-buffer-constructor': 0, 35 | 'no-plusplus': 0, 36 | 'no-unused-vars': 1, 37 | 'react/no-unused-prop-types': 1, 38 | 'react/prop-types': ['warn', { ignore: ['className', 'children', 'history'] }], 39 | }, 40 | globals: { 41 | web3: true, 42 | window: true, 43 | document: true, 44 | fetch: true, 45 | localStorage: true, 46 | }, 47 | parserOptions: { 48 | ecmaVersion: 10, 49 | sourceType: 'module', 50 | ecmaFeatures: { 51 | jsx: true, 52 | }, 53 | }, 54 | }; 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | package-lock.json 4 | yarn.lock 5 | yarn-error.log 6 | src/constants/config.json 7 | server/config.js 8 | server/*.db 9 | dist/ 10 | .env 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "server/wasteland"] 2 | path = server/wasteland 3 | url = https://github.com/bitfinexcom/wasteland.git 4 | branch = master 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "trailingComma": "es5", 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "bracketSpacing": true, 8 | "jsxBracketSameLine": false, 9 | "tabWidth": 2, 10 | "printWidth": 100 11 | } 12 | -------------------------------------------------------------------------------- /Participants Agreement V1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/Participants Agreement V1.pdf -------------------------------------------------------------------------------- /build/webpack-dev.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const FaviconsWebpackPlugin = require('favicons-webpack-plugin'); 5 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 6 | const WriteFilePlugin = require('write-file-webpack-plugin'); 7 | require('@babel/polyfill'); 8 | 9 | const HtmlWebpackPluginConfig = new HtmlWebpackPlugin({ 10 | template: './src/index.html', 11 | filename: 'index.html', 12 | inject: 'body', 13 | }); 14 | 15 | module.exports = { 16 | entry: ['@babel/polyfill', './src/index.jsx'], 17 | devtool: 'inline-source-map', 18 | node: { 19 | fs: 'empty', 20 | tls: 'empty', 21 | net: 'empty', 22 | }, 23 | devServer: { 24 | hot: true, 25 | // disableHostCheck: true, 26 | // port: 80, 27 | }, 28 | output: { 29 | path: path.resolve('dist'), 30 | filename: 'index_bundle.js', 31 | }, 32 | module: { 33 | loaders: [ 34 | { 35 | test: /\.(js|jsx)$/, 36 | loaders: [ 37 | 'babel-loader', 38 | { 39 | loader: 'eslint-loader', 40 | options: { emitWarning: true, emitError: false }, 41 | }, 42 | ], 43 | exclude: /node_modules/, 44 | }, 45 | { 46 | test: /\.(jpe?g|png|gif|svg|pdf)$/i, 47 | loaders: [ 48 | 'file-loader?hash=sha512&digest=hex&name=[hash].[ext]', 49 | { 50 | loader: 'image-webpack-loader', 51 | options: { 52 | bypassOnDebug: true, 53 | optipng: { 54 | optimizationLevel: 7, 55 | interlaced: false, 56 | }, 57 | }, 58 | }, 59 | ], 60 | }, 61 | { 62 | test: /\.(scss|css)$/, 63 | loader: 64 | 'style-loader!css-loader?modules&importLoaders=2&sourceMap&localIdentName=[local]!sass-loader?outputStyle=expanded&sourceMap', 65 | }, 66 | ], 67 | }, 68 | resolve: { 69 | extensions: ['.js', '.jsx'], 70 | }, 71 | plugins: [ 72 | HtmlWebpackPluginConfig, 73 | new webpack.DefinePlugin({ 74 | 'process.env': { 75 | env: '"development"', 76 | PROVIDER_URL: JSON.stringify(process.env.PROVIDER_URL), 77 | }, 78 | }), 79 | new webpack.NamedModulesPlugin(), 80 | new webpack.HotModuleReplacementPlugin(), 81 | new FaviconsWebpackPlugin(path.resolve('nec.png')), 82 | new WriteFilePlugin({ 83 | test: /^(images|videos)/, 84 | }), 85 | new CopyWebpackPlugin([ 86 | { 87 | from: './src/constants/images/', 88 | to: 'images/', 89 | }, 90 | { 91 | from: './src/constants/videos/', 92 | to: 'videos/', 93 | }, 94 | ]), 95 | ], 96 | }; 97 | -------------------------------------------------------------------------------- /build/webpack-prod.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const CleanPlugin = require('clean-webpack-plugin'); 5 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 6 | const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); 7 | const FaviconsWebpackPlugin = require('favicons-webpack-plugin'); 8 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 9 | require('@babel/polyfill'); 10 | 11 | const HtmlWebpackPluginConfig = new HtmlWebpackPlugin({ 12 | template: './src/index.html', 13 | filename: 'index.html', 14 | inject: 'body', 15 | }); 16 | 17 | module.exports = { 18 | entry: ['@babel/polyfill', './src/index.jsx'], 19 | output: { 20 | path: path.resolve('dist'), 21 | filename: '[name].js', 22 | chunkFilename: '[chunkhash]-[chunkhash].js', 23 | }, 24 | module: { 25 | loaders: [ 26 | { test: /\.(js|jsx)$/, loaders: ['babel-loader'], exclude: /node_modules/ }, 27 | { 28 | test: /\.(jpe?g|png|gif|svg|pdf)$/i, 29 | loaders: [ 30 | 'file-loader?hash=sha512&digest=hex&name=[hash].[ext]', 31 | { 32 | loader: 'image-webpack-loader', 33 | options: { 34 | bypassOnDebug: true, 35 | optipng: { 36 | optimizationLevel: 7, 37 | interlaced: false, 38 | }, 39 | }, 40 | }, 41 | ], 42 | }, 43 | { 44 | test: /\.(scss|css)$/, 45 | use: ExtractTextPlugin.extract({ 46 | fallback: 'style-loader', 47 | use: [ 48 | 'css-loader?modules&importLoaders=2&localIdentName=[local]&sourceMap&minimize=true', 49 | 'sass-loader?outputStyle=expanded&sourceMap=true&sourceMapContents=true', 50 | ], 51 | }), 52 | }, 53 | ], 54 | }, 55 | resolve: { 56 | extensions: ['.js', '.jsx'], 57 | }, 58 | plugins: [ 59 | new CleanPlugin([path.resolve('dist')], { root: path.resolve(__dirname, '../') }), 60 | new ExtractTextPlugin('[name].css', { allChunks: true }), 61 | HtmlWebpackPluginConfig, 62 | // optimizations 63 | new OptimizeCssAssetsPlugin({ 64 | assetNameRegExp: /\.optimize\.css$/g, 65 | cssProcessor: require('cssnano'), 66 | cssProcessorOptions: { discardComments: { removeAll: true } }, 67 | canPrint: true, 68 | }), 69 | new webpack.optimize.OccurrenceOrderPlugin(), 70 | new webpack.optimize.UglifyJsPlugin({ 71 | compress: { 72 | warnings: false, 73 | }, 74 | minimize: true, 75 | include: /\.min\.js$/ 76 | }), 77 | new FaviconsWebpackPlugin(path.resolve('nec.png')), 78 | new webpack.DefinePlugin({ 79 | 'process.env': { 80 | env: '"production"', 81 | NODE_ENV: '"production"', 82 | PROVIDER_URL: JSON.stringify(process.env.PROVIDER_URL), 83 | }, 84 | }), 85 | new CopyWebpackPlugin([ 86 | { 87 | from: './src/constants/images/', 88 | to: 'images/', 89 | }, 90 | { 91 | from: './src/constants/videos/', 92 | to: 'videos/', 93 | }, 94 | ]), 95 | ], 96 | }; 97 | -------------------------------------------------------------------------------- /nec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/nec.png -------------------------------------------------------------------------------- /proposal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/proposal.jpg -------------------------------------------------------------------------------- /proposed_tokens/ABYSS.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0x0e8d6b471e332f140e7d9dbb99e5e3822f728da6", 3 | "symbol": "ABYSS", 4 | "decimals": "18", 5 | "token": "ABYSS", 6 | "description": "The Abyss is a next-generation digital distribution platform, delivering all types of video games to the fast-growing global game community. Unlike other platforms, The Abyss offers a groundbreaking motivational and multilevel referral system, allowing gamers to earn from in-game and social activities, and other gamers' payments as well. By joining The Abyss, developers will reduce their marketing expenses and receive an extra income from referral payments made in other games on the platform.", 7 | "logo": "https://www.theabyss.com/static/icon/theabyss_logo200.png", 8 | "discussions": "https://telegram.theabyss.com/", 9 | "website": "https://www.theabyss.com/" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/ARN.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0xBA5F11b16B155792Cf3B2E6880E8706859A8AEB6", 3 | "symbol": "ARN", 4 | "decimals": "8", 5 | "token": "Aeron", 6 | "description": "Aeron is the new standard of aviation safety powered by blockchain.", 7 | "logo": "https://aeron.aero/img/ARN-token-logo-256x256.png", 8 | "discussions": "https://t.me/aeronaero", 9 | "website": "https://aeron.aero" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/ATN.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0x461733c17b0755CA5649B6DB08B3E213FCf22546", 3 | "symbol": "ATN", 4 | "decimals": "18", 5 | "token": "ATN", 6 | "description": "A.I. Technology Network (ATN) is the first incepted vision of a blockchain-based global Artificial Intelligence as a Service (AIaaS) marketplace. Developers, technology suppliers and buyers come together to access and develop new and innovative forms of A.I. technology in a secure and collaborative environment. ATN is an enabling marketplace for the sharing of data, computing resources, AI algorithms, models and applications. The platform significantly lowers the barriers for anyone, such as SMEs or developers, to participate in the A.I. space, while incentivizing collaboration and exchange through the ATN token. ATN boasts a strong technical team and its long-term vision is to become the hub for world A.I..", 7 | "logo": "https://i.imgur.com/V8pjot1.png", 8 | "discussions": "https://twitter.com/atn_io", 9 | "website": "https://atn.io/" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/CRAD.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0x3637fcd710b99d2b643af653d60b762d6c310a8b", 3 | "symbol": "CRAD", 4 | "decimals": "18", 5 | "token": "CryptoAds token", 6 | "description": "CryptoAds Marketplace is a powerful decentralized marketing and advertising platform built on Ethereum Blockchain. CFC Company developed a cloud based software system for internal use and B2C sector; and now it’s time for expanding into the growing B2B marketplace. The platform is completely transparent and provides secure solutions for brands, digital advertisers, marketers, content publishers and everyday Internet users, who receive the opportunity to trade directly without the need for centralized ad exchanges. CryptoAds Marketplace will give to advertisers an opportunity to place a variety of ad units (native video, live-casting ads, 360 ads, “in the round”, vertical video, in-app, and walls) at an unlimited number of traffic sources with AI and Blockchain technologies and without having to worry about proper execution. CryptoAds model is already implemented inside CFC.io project (launched in 2005) with 7M registered users and profit-generating returns. This project will be base to growing platform worldwide.", 7 | "logo": "https://drive.google.com/file/d/1xsN2lccgGXl9UNVqhjdSykf2msCZDoAz/view?usp=sharing", 8 | "discussions": "https://t.me/cryptoads_marketplace_en", 9 | "website": "https://cryptoads.cfc.io/" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/EnergiToken.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0x3c4a3ffd813a107febd57b2f01bc344264d90fde", 3 | "symbol": "ETK", 4 | "decimals": "2", 5 | "token": "EnergiToken", 6 | "description": "We seek to empower individuals and provide the tools to manage their energy in a co-operative, efficient and sustainable way. To achieve this we have created an ecosystem built on Blockchain driven by Artificial Intelligence whereby users are rewarded for energy efficient behaviour. We seek to empower individuals and provide the tools to manage their energy in a co-operative, efficient and sustainable way. To achieve this we have created an ecosystem built on Blockchain driven by Artificial Intelligence whereby users are rewarded for energy efficient behaviour.", 7 | "logo": "https://energitoken.com/images/e.png", 8 | "discussions": "https://t.me/energitoken", 9 | "website": "https://EnergiToken.com/" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/LOOM.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0xa4e8c3ec456107ea67d3075bf9e3df3a75823db0", 3 | "symbol": "LOOM", 4 | "decimals": "18", 5 | "token": "Loom", 6 | "description": "Loom is a Layer Two solution using DPoS side-chains allowing for highly scalable blockchain games and DApps, with the security of the Ethereum network. ", 7 | "logo": "https://cdn-images-1.medium.com/max/1200/1*K76UVoLq-FOL7l-_Fag-Qw@2x.png", 8 | "discussions": "https://www.reddit.com/r/loomnetwork/", 9 | "website": "https://loomx.io/" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/Labtorum.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0x1bD4E709a076Fb71ea1014293a739f2b19CA565D", 3 | "symbol": "LTR", 4 | "decimals": "8", 5 | "token": "Labtorum", 6 | "description": "Labtorum is an ERC20 wallet application that provides information about profit on trading, so that users can get profit while trading. With the user experience that we currently apply to the application view to facilitate the creation, recovery, import / export wallet and transaction in this application.", 7 | "logo": "https://www.labtorum.com/image/squarelogo.png", 8 | "discussions": "https://t.me/official_labtorum_global", 9 | "website": "https://www.labtorum.com/" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/MINDEXCOIN.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0x3a1237d38d0fb94513f85d61679cad7f38507242", 3 | "symbol": "MIC", 4 | "decimals": "18", 5 | "token": "Mindexcoin", 6 | "description": "Mindexcoin come to revolutionize cryptocurrency payment platforms. New Cryptocurrency Chapter.", 7 | "logo": "https://mindexcoin.org/assets/img/mindex.png", 8 | "discussions": "https://t.me/mindexcoin", 9 | "website": "https://mindexcoin.com/" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/Pinkiaton.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0x93ED3FBe21207Ec2E8f2d3c3de6e058Cb73Bc04d", 3 | "decimals": "18", 4 | "token": "Kleros", 5 | "symbol": "PNK", 6 | "description": "Blockchain dispute resolution layer that works as decentralised third party to arbitrate disputes, relying on game theoretic incentives to have jurors rule cases correctly in a fast, cheap, reliable and decentralized way.", 7 | "logo": "https://slack.kleros.io/images/pinakion.png", 8 | "discussion": "https://t.me/kleros", 9 | "website": "https://kleros.io/#" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/WePower.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0x4CF488387F035FF08c371515562CBa712f9015d4", 3 | "symbol": "WPR", 4 | "decimals": "18", 5 | "token": "WePower", 6 | "description": "WePower is a blockchain-based green energy financing and trading platform. It connects energy buyers directly with the green energy producers and creates an opportunity to purchase energy upfront at below market rates. WePower uses Smart Energy Contracts to standardize, simplify and open globally the currently existing energy investment ecosystem. Smart Energy Contracts ensures liquidity and extends access to capital. Moreover, it provides the first access to live trade in renewable energy globally for everyone.", 7 | "logo": "https://image.ibb.co/dzfCXJ/Wepower_logo_256x256.png", 8 | "discussions": "https://t.me/WePowerNetwork", 9 | "website": "https://wepower.network/" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/airswap.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0x27054b13b1B798B345b591a4d22e6562d47eA75a", 3 | "symbol": "AST", 4 | "decimals": "", 5 | "token": "AirSwap", 6 | "description": "Is a decentralised market-place allowing traders to connect and interact, wallet to wallet, conveniently and efficiently from their mobile or browser.", 7 | "logo": "", 8 | "discussions": "", 9 | "website": "https://www.airswap.io/" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/axpire.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0xC39E626A04C5971D770e319760D7926502975e47", 3 | "symbol": "AXPR", 4 | "decimals": "", 5 | "token": "aXpire", 6 | "description": "aXpire is a multi-purpose ERP SaaS platform catering to Funds, Businesses, Entrepreneurs and Consumers with a range of purpose built blockchain solutions.", 7 | "logo": "", 8 | "discussions": "", 9 | "website": "https://axpr.io/" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/daostack.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0x543Ff227F64Aa17eA132Bf9886cAb5DB55DCAddf", 3 | "symbol": "GEN", 4 | "decimals": "", 5 | "token": "DAOstack", 6 | "description": "Allows for the creation of distributed, self-governing organisations whilst facilitating their growth in the Ethereum ecosystem. ", 7 | "logo": "", 8 | "discussions": "", 9 | "website": "https://daostack.io/" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/fetch.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0x1D287CC25dAD7cCaF76a26bc660c5F7C8E2a05BD", 3 | "symbol": "FET", 4 | "decimals": "", 5 | "token": "Fetch", 6 | "description": "Fetch is a decentralised, Ai-enabled ecosystem comprising Autonomous Agents, an Open Economic Framework and Smart Ledger to conducting sophisticated economic activities.", 7 | "logo": "", 8 | "discussions": "", 9 | "website": "https://fetch.ai" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/foam.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0x4946Fcea7C692606e8908002e55A582af44AC121", 3 | "symbol": "FOAM", 4 | "decimals": "", 5 | "token": "FOAM", 6 | "description": "The FOAM 'Proof of Location' protocol for geospatial data markets allows users to build a consensus driven, trusted map of the world by securing physical space on the blockchain. ", 7 | "logo": "", 8 | "discussions": "", 9 | "website": "https://www.foam.space/" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/gnosis.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0x6810e776880C02933D47DB1b9fc05908e5386b96", 3 | "symbol": "GNO", 4 | "decimals": "", 5 | "token": "GNOSIS", 6 | "description": "Gnosis, a dapp on Ethereum, sets out to build a fully fledged ecosystem, comprising a multi-sig wallet, Dutch Exchange as well as providing the infrastructure to create your very own prediction markets.", 7 | "logo": "", 8 | "discussions": "", 9 | "website": "https://gnosis.pm/" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/hydro.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0xEBBdf302c940c6bfd49C6b165f457fdb324649bc", 3 | "symbol": "HYDRO", 4 | "decimals": "", 5 | "token": "Hydro", 6 | "description": "Managed and maintained by a decentralised community, Hydro comprises a DApp store and multiple smart-contracts allowing existing private systems to leverage the benefits of public blockchains.", 7 | "logo": "", 8 | "discussions": "", 9 | "website": "https://projecthydro.org/" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/index.js: -------------------------------------------------------------------------------- 1 | import ABYSS from './ABYSS.json'; 2 | import ARN from './ARN.json'; 3 | import ATN from './ATN.json'; 4 | import CRAD from './CRAD.json'; 5 | import EnergiToken from './EnergiToken.json'; 6 | import LOOM from './LOOM.json'; 7 | import Labtorum from './Labtorum.json'; 8 | import MINDEXCOIN from './MINDEXCOIN.json'; 9 | import WePower from './WePower.json'; 10 | import upfiring from './upfiringNEW.json'; 11 | import Pinkiaton from './Pinkiaton.json'; 12 | import Gnosis from './gnosis.json'; 13 | import ZRX from './zrx.json'; 14 | import AirSwap from './airswap.json'; 15 | import Hydro from './hydro.json'; 16 | import Scroll from './scroll.json'; 17 | import DAOstack from './daostack.json'; 18 | import FOAM from './foam.json'; 19 | import IOTX from './iotex.json'; 20 | import AXPR from './axpire.json'; 21 | import WIB from './wibson.json'; 22 | import TOL from './tolar.json'; 23 | import FET from './fetch.json'; 24 | import INK from './ink.json'; 25 | import PMA from './pumapay.json'; 26 | import TRIO from './tripio.json'; 27 | import SOLVE from './solve.json'; 28 | import SPC from './spacechain.json'; 29 | import SWM from './swarm.json'; 30 | 31 | export default [ 32 | ABYSS, 33 | ARN, 34 | ATN, 35 | CRAD, 36 | EnergiToken, 37 | LOOM, 38 | Labtorum, 39 | MINDEXCOIN, 40 | WePower, 41 | upfiring, 42 | Pinkiaton, 43 | Gnosis, 44 | FOAM, 45 | DAOstack, 46 | Scroll, 47 | Hydro, 48 | AirSwap, 49 | ZRX, 50 | IOTX, 51 | AXPR, 52 | WIB, 53 | TOL, 54 | FET, 55 | TRIO, 56 | INK, 57 | PMA, 58 | SOLVE, 59 | SWM, 60 | SPC, 61 | ].reduce((tmp, token) => { 62 | tmp[token.address.toLowerCase()] = token; 63 | return tmp; 64 | }, {}); 65 | -------------------------------------------------------------------------------- /proposed_tokens/ink.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0xf4c90e18727C5C76499eA6369C856A6d61D3E92E", 3 | "symbol": "INK", 4 | "decimals": "", 5 | "token": "Ink", 6 | "description": "Ink harnesses blockchain to achieve fast IP-rights verification, free trade of cultural assets and more, with the aim to build a new ecosystem for the global cultural industry. ", 7 | "logo": "", 8 | "discussions": "", 9 | "website": "https://ink.one" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/iotex.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0x6fB3e0A217407EFFf7Ca062D46c26E5d60a14d69", 3 | "symbol": "IOTX", 4 | "decimals": "", 5 | "token": "IoTeX", 6 | "description": "IoTeX is bringing the new token economy to the IoT space, leveraging crypto incentives to increase scalability, privacy and innovation in IoT.", 7 | "logo": "", 8 | "discussions": "", 9 | "website": "https://iotex.io/" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/pumapay.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0x846C66cf71C43f80403B51fE3906B3599D63336f", 3 | "symbol": "PMA", 4 | "decimals": "", 5 | "token": "PumaPay", 6 | "description": "With business and users in mind, PumaPay is building a blockchain enabled payments system for day to day transactions.", 7 | "logo": "", 8 | "discussions": "", 9 | "website": "https://pumapay.io/#/home " 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/scroll.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0x24DCc881E7Dd730546834452F21872D5cb4b5293", 3 | "symbol": "SCRL", 4 | "decimals": "", 5 | "token": "Scroll", 6 | "description": "Scroll is a decentralised network built atop the Ethereum blockchain that uses peer to peer technology to enhance data security and privacy control.", 7 | "logo": "", 8 | "discussions": "", 9 | "website": "https://scroll.network/" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/solve.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0x446C9033E7516D820cc9a2ce2d0B7328b579406F", 3 | "symbol": "SOLVE", 4 | "decimals": "18", 5 | "token": "SOLVE", 6 | "description": "Solve.Care, a decentralized healthcare platform, is designed to be used for care coordination, administration, and payments of healthcare and benefits around the world.", 7 | "website": "https://solve.care/" 8 | } 9 | -------------------------------------------------------------------------------- /proposed_tokens/spacechain.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0x8069080a922834460C3A092FB2c1510224dc066b", 3 | "symbol": "SPC", 4 | "decimals": "18", 5 | "token": "SpaceChain", 6 | "description": "Spacechain combines space and blockchain technology to establish the world's first open-source, blockchain enabled satellite network.", 7 | "website": "https://spacechain.com/" 8 | } 9 | -------------------------------------------------------------------------------- /proposed_tokens/swarm.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0x9e88613418cF03dCa54D6a2cf6Ad934A78C7A17A", 3 | "symbol": "SWM", 4 | "decimals": "", 5 | "token": "Swarm", 6 | "description": "Swarm is a decentralised, open source infrastructure built for digital securities, making issuance easy whilst providing market accessibility and interoperability. ", 7 | "logo": "", 8 | "discussions": "", 9 | "website": "https://www.swarm.fund/" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/tolar.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0xd07D9Fe2d2cc067015E2b4917D24933804f42cFA", 3 | "symbol": "TOL", 4 | "decimals": "", 5 | "token": "Tolar", 6 | "description": "Tolar is a high through-put, community-governed, cryptocurrency allowing for fast, secure and publicallyverifiable transactions.", 7 | "logo": "", 8 | "discussions": "", 9 | "website": "https://www.tolar.io/" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/tripio.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0x8B40761142B9aa6dc8964e61D0585995425C3D94", 3 | "symbol": "TRIO", 4 | "decimals": "", 5 | "token": "Tripio", 6 | "description": "Tripio is a decentralised marketplace for the travel industry, connecting customers and service providers in a transparent and tamper-resistant ecosystem.", 7 | "logo": "", 8 | "discussions": "", 9 | "website": "http://trip.io" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/upfiring.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0xea097a2b1db00627b2fa17460ad260c016016977", 3 | "symbol": "UFR", 4 | "decimals": "18", 5 | "token": "Upfiring", 6 | "description": "Upfiring is a fully decentralized, open-source, P2P file-sharing desktop application for Windows, MacOS, and Linux that encrypts and distributes files between peers using a modified bittorrent protocol. The application allows users to generate custom encrypted .ufr files with embedded price data and seed them on the Upfiring network. Users earn UFR tokens in exchange for seeding and keeping files online. Seeder smart contracts deployed to the Ethereum network mediate the movement of files across the network, maintain seeder UFR balances, and allow for the decryption of files when warranted.", 7 | "logo": "https://i.ibb.co/QPDZSGm/upfiringlogo1000x1000.png", 8 | "discussions": "https://t.me/UpfiringCommunity", 9 | "website": "https://www.upfiring.com" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/upfiringNEW.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0xEA097A2b1dB00627B2Fa17460Ad260c016016977", 3 | "symbol": "UFR", 4 | "decimals": "", 5 | "token": "Upfiring", 6 | "description": "Upfiring is a decentralised exchange for file-sharing that uses P2P and blockchain technology to incentivise seeders and downloaders with UFR tokens.", 7 | "logo": "", 8 | "discussions": "", 9 | "website": "https://www.upfiring.com/" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/wibson.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0x3F17Dd476faF0a4855572F0B6ed5115D9bBA22AD", 3 | "symbol": "WIB", 4 | "decimals": "", 5 | "token": "Wibson", 6 | "description": "Wibson is a decentralised data market-place empowering users with the tools and trust needed to securely sell their private data.", 7 | "logo": "", 8 | "discussions": "", 9 | "website": "https://wibson.org/" 10 | } 11 | -------------------------------------------------------------------------------- /proposed_tokens/zrx.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0xE41d2489571d322189246DaFA5ebDe1F4699F498", 3 | "symbol": "ZRX", 4 | "decimals": "", 5 | "token": "0x", 6 | "description": "0x is a multi-purpose, open protocol built atop Ethereum that allows for the peer to peer exchange of various assets over the blockchain.", 7 | "logo": "", 8 | "discussions": "", 9 | "website": "https://0x.org/" 10 | } 11 | -------------------------------------------------------------------------------- /screenshots/traderboard_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/screenshots/traderboard_screenshot.png -------------------------------------------------------------------------------- /server/config.dist.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | GRAPE_URL: 'http://139.59.146.81:30001', 3 | SERVICE_PORT: 2008, 4 | SERVER_PORT: 3000, 5 | DB_PATH: 'backup.db', 6 | } 7 | -------------------------------------------------------------------------------- /server/create.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE proposals ( 2 | id INTEGER PRIMARY KEY AUTOINCREMENT, 3 | email TEXT, 4 | description TEXT, 5 | hash VARCHAR(40), 6 | timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP 7 | ); 8 | -------------------------------------------------------------------------------- /server/crud-service.js: -------------------------------------------------------------------------------- 1 | const Grenache = require('grenache-nodejs-http'); 2 | const Link = require('grenache-nodejs-link'); 3 | const Peer = Grenache.PeerRPCServer; 4 | const Wasteland = require('./wasteland'); 5 | const GrenacheBackend = require('./wasteland/backends/Grenache'); 6 | const config = require('./config'); 7 | 8 | const link = new Link({ grape: config.GRAPE_URL }); 9 | link.start(); 10 | 11 | const peer = new Peer(link, { 12 | timeout: 300000 13 | }); 14 | peer.init(); 15 | 16 | const gb = new GrenacheBackend({ 17 | transport: link, 18 | }); 19 | 20 | const wl = new Wasteland({ backend: gb }); 21 | 22 | const service = peer.transport('server'); 23 | service.listen(config.SERVICE_PORT); 24 | 25 | console.log(`nectarcommunitycrud service listening on ${config.SERVICE_PORT}`); 26 | 27 | setInterval(function () { 28 | link.announce('nectarcommunitycrud', service.port, {}); 29 | }, 1000); 30 | 31 | service.on('request', (rid, key, payload, handler) => { 32 | if (typeof payload === 'string') { 33 | try { 34 | payload = JSON.parse(payload); 35 | } catch (e) { 36 | return handler.reply(new Error('Invalid JSON sent')); 37 | } 38 | } 39 | if (payload.action === 'put') { 40 | wl.put(payload.text, {}, (err, hash) => { 41 | if (err) return handler.reply(err); 42 | handler.reply(null, hash); 43 | }); 44 | } else { 45 | wl.get(payload.hash, {}, (err, data) => { 46 | if (err) return handler.reply(err); 47 | handler.reply(null, data); 48 | }); 49 | } 50 | }); 51 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | const sqlite3 = require('sqlite3').verbose(); 2 | const express = require('express'); 3 | const app = express(); 4 | const config = require('./config'); 5 | 6 | const bodyParser = require('body-parser'); 7 | app.use(bodyParser.json()); 8 | app.use(bodyParser.urlencoded({ extended: true })); 9 | app.use(bodyParser.raw()); 10 | 11 | app.use(function(req, res, next) { 12 | res.set('Access-Control-Allow-Origin', '*'); 13 | next(); 14 | }); 15 | 16 | const path = require('path'); 17 | app.use('/', express.static(path.resolve(__dirname, '../dist'))); 18 | 19 | const Grenache = require('grenache-nodejs-http'); 20 | const Link = require('grenache-nodejs-link'); 21 | const Peer = Grenache.PeerRPCClient; 22 | 23 | const link = new Link({ grape: config.GRAPE_URL }); 24 | link.start(); 25 | 26 | const peer = new Peer(link, {}); 27 | peer.init(); 28 | 29 | app.post('/put', (req, res) => { 30 | console.log(req.body); 31 | const data = req.body.data; 32 | if (!data) return res.status(400).send('Data not sent'); 33 | peer.request( 34 | 'nectarcommunitycrud', 35 | { 36 | action: 'put', 37 | text: data, 38 | }, 39 | { timeout: 10000 }, 40 | (err, data) => { 41 | res.set('Access-Control-Allow-Origin', '*'); 42 | if (err) { 43 | console.error(err); 44 | return res.status(500).send(err.message || err); 45 | } 46 | res.send(data); 47 | const db = new sqlite3.Database(config.DB_PATH); 48 | db.run('INSERT INTO proposals(email, description, hash) VALUES (?, ?, ?)', [ 49 | req.body.email, 50 | req.body.data, 51 | data, 52 | ]); 53 | db.close(); 54 | } 55 | ); 56 | }); 57 | 58 | app.post('/get', (req, res) => { 59 | console.log(req.body); 60 | const hash = req.body.hash; 61 | if (!hash) return res.status(400).send('Hash not sent'); 62 | peer.request( 63 | 'nectarcommunitycrud', 64 | { 65 | action: 'get', 66 | hash, 67 | }, 68 | { timeout: 10000 }, 69 | (err, data) => { 70 | res.set('Access-Control-Allow-Origin', '*'); 71 | if (err) { 72 | console.error(err); 73 | return res.status(500).send(err.message || err); 74 | } 75 | if (data) return res.send(data.v); 76 | console.error(`Data for hash ${hash} not found on grenache`); 77 | const db = new sqlite3.Database(config.DB_PATH); 78 | db.get('SELECT description FROM proposals WHERE hash = ?', [hash], (err, row) => { 79 | if (row) return res.send(row.description); 80 | res.send('Data for hash not found'); 81 | console.error(`Data for hash ${hash} not found in backup db`); 82 | }); 83 | db.close(); 84 | } 85 | ); 86 | }); 87 | 88 | app.listen(config.SERVER_PORT, () => console.log(`Server listening on ${config.SERVER_PORT}`)); 89 | -------------------------------------------------------------------------------- /server/wasteland/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | npm-debug.log 4 | !config/*.json.example 5 | config/*.json 6 | !config/facs/*.json.example 7 | config/facs/*.json 8 | logs 9 | -------------------------------------------------------------------------------- /server/wasteland/.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: node_js 4 | node_js: 5 | - "stable" 6 | -------------------------------------------------------------------------------- /server/wasteland/backends/Memory.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const _ = require('lodash') 4 | const bencode = require('bencode') 5 | const ed = require('ed25519-supercop') 6 | const crypto = require('crypto') 7 | 8 | class MemoryBackend { 9 | constructor (conf = {}) { 10 | this.store = {} 11 | 12 | this.conf = { 13 | keys: null 14 | } 15 | 16 | _.extend(this.conf, conf) 17 | } 18 | 19 | put (data, opts, cb) { 20 | if (!data) return cb(new Error('no data provided')) 21 | 22 | const isMutable = !!opts.seq || opts.seq === 0 23 | const payload = this.getPayload(data, opts) 24 | 25 | let key 26 | if (isMutable) { 27 | key = Buffer.concat([ 28 | Buffer.from(payload.k), Buffer.from(payload.salt) 29 | ]).toString('hex') 30 | } else { 31 | const tmpKey = bencode.encode(payload.v) 32 | let sha = crypto.createHash('sha1').update(tmpKey) 33 | key = sha.digest('hex') 34 | } 35 | 36 | if (!this.store[key]) { 37 | this.store[key] = payload 38 | return cb(null, key) 39 | } 40 | 41 | if (typeof this.store[key].seq === 'undefined') { 42 | return cb(null, key) 43 | } 44 | 45 | if (!this.conf.keys) { 46 | return cb(new Error('no keys set')) 47 | } 48 | 49 | const nextSeq = this.store[key].seq + 1 50 | if (nextSeq !== payload.seq) { 51 | return cb(new Error('ERR_CONFLICT_SEQ')) 52 | } 53 | 54 | this.store[key] = payload 55 | return cb(null, key) 56 | } 57 | 58 | getSha (data) { 59 | let sha = crypto.createHash('sha1') 60 | return sha.update(data + Math.random()).digest('hex') 61 | } 62 | 63 | getPayload (data, opts) { 64 | const salt = opts.salt || this.getSha(data) 65 | 66 | if (!opts.seq) return { v: data } 67 | 68 | const res = { 69 | v: data, 70 | seq: opts.seq, 71 | salt: salt 72 | } 73 | 74 | const { publicKey, secretKey } = this.conf.keys 75 | 76 | res.k = publicKey.toString('hex') 77 | 78 | const toEncode = { seq: res.seq, v: res.v } 79 | toEncode.salt = opts.salt 80 | 81 | const encoded = bencode 82 | .encode(toEncode) 83 | .slice(1, -1) 84 | .toString() 85 | 86 | res.sig = ed 87 | .sign(encoded, publicKey, secretKey) 88 | .toString('hex') 89 | 90 | return res 91 | } 92 | 93 | get (key, opts, cb) { 94 | if (!key) return cb(new Error('no key provided')) 95 | 96 | const id = { id: 'memory' } 97 | 98 | if (this.store[key]) { 99 | const res = Object.assign({}, this.store[key], id) 100 | return cb(null, res) 101 | } 102 | 103 | return cb(null, id) 104 | } 105 | } 106 | 107 | module.exports = MemoryBackend 108 | -------------------------------------------------------------------------------- /server/wasteland/backends/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Backends = { 4 | Grenache: require('./Grenache.js'), 5 | Memory: require('./Memory.js') 6 | } 7 | 8 | module.exports = Backends 9 | -------------------------------------------------------------------------------- /server/wasteland/examples/grenache.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Wasteland = require('../') 4 | const GrenacheBackend = require('../backends/Grenache.js') 5 | 6 | const Link = require('grenache-nodejs-link') 7 | const ed = require('ed25519-supercop') 8 | 9 | const link = new Link({ 10 | grape: 'http://127.0.0.1:30001' 11 | }) 12 | link.start() 13 | 14 | const { publicKey, secretKey } = ed.createKeyPair(ed.createSeed()) 15 | 16 | const gb = new GrenacheBackend({ 17 | transport: link, 18 | keys: { publicKey, secretKey } 19 | }) 20 | 21 | const wl = new Wasteland({ backend: gb }) 22 | 23 | function storeMutable (cb) { 24 | console.log('storing mutable data') 25 | 26 | const opts = { seq: 1, salt: 'pineapple-salt' } 27 | wl.put('unchunked-data', opts, (err, hash) => { 28 | if (err) throw err 29 | 30 | wl.get({ hash: hash, salt: 'pineapple-salt' }, {}, (err, data) => { 31 | if (err) throw err 32 | 33 | console.log(data) 34 | cb(null) 35 | }) 36 | }) 37 | } 38 | 39 | function storeImmutable () { 40 | console.log('storing immutable data') 41 | 42 | const opts = {} 43 | wl.put('unchunked-data', opts, (err, hash) => { 44 | if (err) throw err 45 | 46 | wl.get(hash, {}, (err, data) => { 47 | if (err) throw err 48 | 49 | console.log(data) 50 | link.stop() 51 | }) 52 | }) 53 | } 54 | 55 | storeMutable(() => storeImmutable()) 56 | -------------------------------------------------------------------------------- /server/wasteland/examples/memory.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Wasteland = require('../') 4 | const MemoryBackend = require('../backends/Memory.js') 5 | 6 | const ed = require('ed25519-supercop') 7 | const { publicKey, secretKey } = ed.createKeyPair(ed.createSeed()) 8 | 9 | const mb = new MemoryBackend({ 10 | keys: { publicKey, secretKey } 11 | }) 12 | 13 | const wl = new Wasteland({ backend: mb }) 14 | 15 | function storeMutable (cb) { 16 | console.log('storing mutable data') 17 | 18 | const opts = { seq: 1, salt: 'pineapple-salt' } 19 | wl.put('unchunked-data', opts, (err, hash) => { 20 | if (err) throw err 21 | 22 | wl.get(hash, {}, (err, data) => { 23 | if (err) throw err 24 | 25 | console.log(data) 26 | cb(null) 27 | }) 28 | }) 29 | } 30 | 31 | function storeImmutable () { 32 | console.log('storing immutable data') 33 | 34 | const opts = {} 35 | wl.put('unchunked-data', opts, (err, hash) => { 36 | if (err) throw err 37 | 38 | wl.get(hash, {}, (err, data) => { 39 | if (err) throw err 40 | 41 | console.log(data) 42 | }) 43 | }) 44 | } 45 | 46 | storeMutable(() => storeImmutable()) 47 | -------------------------------------------------------------------------------- /server/wasteland/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Wasteland = require('./lib/Wasteland.js') 4 | module.exports = (exports = Wasteland) 5 | 6 | exports.Backends = require('./backends') 7 | -------------------------------------------------------------------------------- /server/wasteland/lib/Wasteland.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const _ = require('lodash') 4 | 5 | class Wasteland { 6 | constructor (conf = {}) { 7 | this.conf = { backend: null } 8 | 9 | _.extend(this.conf, conf) 10 | 11 | if (!this.conf.backend) { 12 | throw new Error('no backend set') 13 | } 14 | 15 | this.backend = this.conf.backend 16 | } 17 | 18 | start (cb = () => {}) { 19 | this.backend.start(cb) 20 | } 21 | 22 | stop (cb = () => {}) { 23 | this.backend.stop(cb) 24 | } 25 | 26 | put (data, opts, cb) { 27 | // if (typeof data !== 'string') { 28 | // data = JSON.stringify(data) 29 | // } 30 | 31 | this.backend.put(data, opts, cb) 32 | } 33 | 34 | get (key, opts, cb) { 35 | if (!key) return cb(new Error('no key provided')) 36 | 37 | // JSON.parse 38 | 39 | this.backend.get(key, opts, cb) 40 | } 41 | } 42 | 43 | module.exports = Wasteland 44 | -------------------------------------------------------------------------------- /server/wasteland/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wasteland", 3 | "version": "2.0.0", 4 | "description": "Slice and store data", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "npm run lint && npm run unit", 8 | "lint": "standard", 9 | "unit": "mocha" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/bitfinexcom/wasteland.git" 14 | }, 15 | "keywords": [ 16 | "DHT", 17 | "grenache", 18 | "kademlia" 19 | ], 20 | "author": "Robert Kowalski ", 21 | "license": "MIT", 22 | "devDependencies": { 23 | "grenache-grape": "git+https://github.com/bitfinexcom/grenache-grape.git", 24 | "mocha": "^5.2.0", 25 | "standard": "^11.0.1" 26 | }, 27 | "dependencies": { 28 | "async": "^2.6.0", 29 | "bencode": "^1.0.0", 30 | "ed25519-supercop": "^1.0.2", 31 | "grenache-nodejs-link": "git+https://github.com/bitfinexcom/grenache-nodejs-link.git", 32 | "lodash": "^4.17.4" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /server/wasteland/test/helper.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { Grape } = require('grenache-grape') 4 | const waterfall = require('async/waterfall') 5 | 6 | exports.bootTwoGrapes = bootTwoGrapes 7 | function bootTwoGrapes (cb) { 8 | const grape1 = new Grape({ 9 | dht_port: 20002, 10 | dht_bootstrap: [ '127.0.0.1:20001' ], 11 | api_port: 40001 12 | }) 13 | const grape2 = new Grape({ 14 | dht_port: 20001, 15 | dht_bootstrap: [ '127.0.0.1:20002' ], 16 | api_port: 30001 17 | }) 18 | 19 | waterfall([ 20 | (cb) => { 21 | grape1.start() 22 | grape1.once('ready', cb) 23 | }, 24 | (cb) => { 25 | grape2.start() 26 | grape2.once('node', cb) 27 | } 28 | ], () => { 29 | cb(null, [ grape1, grape2 ]) 30 | }) 31 | } 32 | 33 | exports.killGrapes = killGrapes 34 | function killGrapes (grapes, done) { 35 | grapes[0].stop((err) => { 36 | if (err) throw err 37 | grapes[1].stop((err) => { 38 | if (err) throw err 39 | done() 40 | }) 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /server/wasteland/test/integration.memory-backend.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | 'use strict' 4 | 5 | const assert = require('assert') 6 | 7 | const MemoryBackend = require('../backends/Memory.js') 8 | 9 | const ed = require('ed25519-supercop') 10 | const { publicKey, secretKey } = ed.createKeyPair(ed.createSeed()) 11 | 12 | describe('Plain Memory Storage Backend', () => { 13 | it('stores data', (done) => { 14 | const mb = new MemoryBackend({ 15 | keys: { publicKey, secretKey } 16 | }) 17 | 18 | const opts = { seq: 1, salt: 'pineapple-salt' } 19 | 20 | mb.put('furbie', opts, (err, hash) => { 21 | if (err) throw err 22 | 23 | assert.ok(hash) 24 | mb.get(hash, {}, (err, data) => { 25 | if (err) throw err 26 | assert.equal(data.v, 'furbie') 27 | assert.ok(data.id) 28 | assert.ok(data.seq) 29 | assert.equal(data.salt, 'pineapple-salt') 30 | assert.equal(data.k, publicKey.toString('hex')) 31 | 32 | done() 33 | }) 34 | }) 35 | }) 36 | 37 | it('verifies sequences', (done) => { 38 | const mb = new MemoryBackend({ 39 | keys: { publicKey, secretKey } 40 | }) 41 | 42 | const opts = { seq: 1, salt: 'pineapple-salt' } 43 | 44 | mb.put('furbie', opts, (err, hash) => { 45 | if (err) throw err 46 | mb.put('furbie', opts, (err, hash) => { 47 | assert.ok(err) // outdated sequence 48 | assert.equal(err.message, 'ERR_CONFLICT_SEQ') 49 | 50 | opts.seq = 2 51 | mb.put('furbie-foo', opts, (err, hash) => { 52 | if (err) throw err 53 | mb.get(hash, {}, (err, data) => { 54 | if (err) throw err 55 | assert.equal(data.seq, 2) 56 | assert.equal(data.v, 'furbie-foo') 57 | done() 58 | }) 59 | }) 60 | }) 61 | }) 62 | }) 63 | 64 | it('stores immutable data', (done) => { 65 | const mb = new MemoryBackend() 66 | 67 | const opts = {} // no sequence provided, data is stored immutable 68 | mb.put('furbie', opts, (err, hash) => { 69 | if (err) throw err 70 | 71 | mb.put('furbie', opts, (err, hash2) => { 72 | if (err) throw err 73 | 74 | assert.equal(hash, hash2) // hash/key stays same 75 | 76 | mb.put('furbie-foo', opts, (err, hash3) => { 77 | if (err) throw err 78 | // different content, different hash/key 79 | assert.notEqual(hash2, hash3) 80 | assert.notEqual(hash, hash3) 81 | 82 | done() 83 | }) 84 | }) 85 | }) 86 | }).timeout(20000) 87 | }) 88 | -------------------------------------------------------------------------------- /server/wasteland/test/integration.wasteland.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | 'use strict' 4 | 5 | const assert = require('assert') 6 | const { bootTwoGrapes, killGrapes } = require('./helper') 7 | 8 | const Wasteland = require('../') 9 | const MemoryBackend = require('../backends/Memory.js') 10 | const GrenacheBackend = require('../backends/Grenache.js') 11 | const Link = require('grenache-nodejs-link') 12 | 13 | const ed = require('ed25519-supercop') 14 | const { publicKey, secretKey } = ed.createKeyPair(ed.createSeed()) 15 | 16 | let grapes 17 | describe('Wasteland with Grenache Storage Backend', () => { 18 | before(function (done) { 19 | this.timeout(20000) 20 | 21 | bootTwoGrapes((err, g) => { 22 | if (err) throw err 23 | 24 | grapes = g 25 | done() 26 | }) 27 | }) 28 | 29 | after(function (done) { 30 | this.timeout(5000) 31 | 32 | killGrapes(grapes, done) 33 | }) 34 | 35 | it('stores data unchunked', (done) => { 36 | const link = new Link({ 37 | grape: 'http://127.0.0.1:30001' 38 | }) 39 | link.start() 40 | 41 | const { publicKey, secretKey } = ed.createKeyPair(ed.createSeed()) 42 | 43 | const gb = new GrenacheBackend({ 44 | transport: link, 45 | keys: { publicKey, secretKey } 46 | }) 47 | 48 | const wl = new Wasteland({ backend: gb }) 49 | 50 | const opts = { seq: 1, salt: 'pineapple-salt' } 51 | 52 | wl.put('unchunked-data', opts, (err, hash) => { 53 | if (err) throw err 54 | 55 | assert.ok(hash) 56 | wl.get({ hash: hash, salt: 'pineapple-salt' }, {}, (err, data) => { 57 | if (err) throw err 58 | 59 | assert.equal(data.v, 'unchunked-data') 60 | assert.ok(data.id) 61 | assert.ok(data.seq) 62 | assert.equal(data.k, publicKey.toString('hex')) 63 | 64 | link.stop() 65 | done() 66 | }) 67 | }) 68 | }).timeout(7000) 69 | }) 70 | 71 | describe('Wasteland with Memory Storage Backend', () => { 72 | it('stores data ', (done) => { 73 | const mb = new MemoryBackend({ 74 | keys: { publicKey, secretKey } 75 | }) 76 | 77 | const wl = new Wasteland({ backend: mb }) 78 | 79 | const opts = { seq: 2, salt: 'tomato-salt' } 80 | 81 | wl.put('furbie', opts, (err, hash) => { 82 | if (err) throw err 83 | 84 | assert.ok(hash) 85 | 86 | wl.get(hash, opts, (err, data) => { 87 | if (err) throw err 88 | assert.equal(data.v, 'furbie') 89 | assert.ok(data.id) 90 | assert.equal(data.seq, 2) 91 | assert.equal(data.salt, 'tomato-salt') 92 | assert.equal(data.k, publicKey.toString('hex')) 93 | 94 | done() 95 | }) 96 | }) 97 | }) 98 | }) 99 | -------------------------------------------------------------------------------- /solidity/contracts/Controlled.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | contract Controlled { 4 | /// @notice The address of the controller is the only address that can call 5 | /// a function with this modifier 6 | modifier onlyController { require(msg.sender == controller); _; } 7 | 8 | address public controller; 9 | 10 | function Controlled() public { controller = msg.sender;} 11 | 12 | /// @notice Changes the controller of the contract 13 | /// @param _newController The new controller of the contract 14 | function changeController(address _newController) public onlyController { 15 | controller = _newController; 16 | } 17 | } -------------------------------------------------------------------------------- /solidity/contracts/DestructibleMiniMeToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "./MiniMeToken.sol"; 4 | 5 | /* 6 | Copyright 2017, Will Harborne (Ethfinex) 7 | */ 8 | 9 | contract DestructibleMiniMeToken is MiniMeToken { 10 | 11 | address public terminator; 12 | 13 | constructor( 14 | address _tokenFactory, 15 | address _parentToken, 16 | uint _parentSnapShotBlock, 17 | string _tokenName, 18 | uint8 _decimalUnits, 19 | string _tokenSymbol, 20 | bool _transfersEnabled, 21 | address _terminator 22 | ) public MiniMeToken( 23 | _tokenFactory, 24 | _parentToken, 25 | _parentSnapShotBlock, 26 | _tokenName, 27 | _decimalUnits, 28 | _tokenSymbol, 29 | _transfersEnabled 30 | ) { 31 | terminator = _terminator; 32 | } 33 | 34 | function recycle() public { 35 | require(msg.sender == terminator); 36 | selfdestruct(terminator); 37 | } 38 | } -------------------------------------------------------------------------------- /solidity/contracts/DestructibleMiniMeTokenFactory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "./DestructibleMiniMeToken.sol"; 4 | 5 | 6 | contract DestructibleMiniMeTokenFactory { 7 | 8 | /// @notice Update the DApp by creating a new token with new functionalities 9 | /// the msg.sender becomes the controller of this clone token 10 | /// @param _parentToken Address of the token being cloned 11 | /// @param _snapshotBlock Block of the parent token that will 12 | /// determine the initial distribution of the clone token 13 | /// @param _tokenName Name of the new token 14 | /// @param _decimalUnits Number of decimals of the new token 15 | /// @param _tokenSymbol Token Symbol for the new token 16 | /// @param _transfersEnabled If true, tokens will be able to be transferred 17 | /// @return The address of the new token contract 18 | function createDestructibleCloneToken( 19 | address _parentToken, 20 | uint _snapshotBlock, 21 | string _tokenName, 22 | uint8 _decimalUnits, 23 | string _tokenSymbol, 24 | bool _transfersEnabled 25 | ) public returns (DestructibleMiniMeToken) { 26 | DestructibleMiniMeToken newToken = new DestructibleMiniMeToken( 27 | this, 28 | _parentToken, 29 | _snapshotBlock, 30 | _tokenName, 31 | _decimalUnits, 32 | _tokenSymbol, 33 | _transfersEnabled, 34 | msg.sender 35 | ); 36 | 37 | newToken.changeController(msg.sender); 38 | return newToken; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /solidity/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.17; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() public { 12 | owner = msg.sender; 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 | -------------------------------------------------------------------------------- /solidity/contracts/NEC.sol: -------------------------------------------------------------------------------- 1 | import "./MiniMeToken.sol"; 2 | 3 | 4 | contract NEC is MiniMeToken { 5 | 6 | function NEC( 7 | address _tokenFactory, 8 | address efxVaultWallet 9 | ) public MiniMeToken( 10 | _tokenFactory, 11 | 0x0, // no parent token 12 | 0, // no snapshot block number from parent 13 | "Ethfinex Nectar Token", // Token name 14 | 18, // Decimals 15 | "NEC", // Symbol 16 | true // Enable transfers 17 | ) { 18 | generateTokens(efxVaultWallet, 1000000000000000000000000000); 19 | enableBurning(false); 20 | } 21 | 22 | // Flag that determines if the token can be burned for rewards or not 23 | bool public burningEnabled; 24 | 25 | 26 | //////////////// 27 | // Enable token burning by users 28 | //////////////// 29 | 30 | function enableBurning(bool _burningEnabled) public onlyController { 31 | burningEnabled = _burningEnabled; 32 | } 33 | 34 | function burnAndRetrieve(uint256 _tokensToBurn) public returns (bool success) { 35 | require(burningEnabled); 36 | 37 | var previousBalanceFrom = balanceOfAt(msg.sender, block.number); 38 | if (previousBalanceFrom < _tokensToBurn) { 39 | return false; 40 | } 41 | 42 | // Alerts the token controller of the burn function call 43 | // If enabled, controller will distribute fees and destroy tokens 44 | // Or any other logic chosen by controller 45 | if (isContract(controller)) { 46 | require(TokenController(controller).onBurn(msg.sender, _tokensToBurn)); 47 | } 48 | 49 | Burned(msg.sender, _tokensToBurn); 50 | return true; 51 | } 52 | 53 | event Burned(address indexed who, uint256 _amount); 54 | 55 | } -------------------------------------------------------------------------------- /solidity/contracts/Ownable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | contract Ownable { 4 | 5 | address public owner; 6 | 7 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 8 | 9 | function Ownable() public { 10 | owner = msg.sender; 11 | } 12 | 13 | modifier onlyOwner() { 14 | require(msg.sender == owner); 15 | _; 16 | } 17 | 18 | function transferOwnership(address newOwner) public onlyOwner { 19 | require(newOwner != address(0)); 20 | emit OwnershipTransferred(owner, newOwner); 21 | owner = newOwner; 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /solidity/contracts/TokenController.sol: -------------------------------------------------------------------------------- 1 | contract TokenController { 2 | /// @notice Called when `_owner` sends ether to the MiniMe Token contract 3 | /// @param _owner The address that sent the ether to create tokens 4 | /// @return True if the ether is accepted, false if it throws 5 | function proxyPayment(address _owner) public payable returns(bool); 6 | 7 | /// @notice Notifies the controller about a token transfer allowing the 8 | /// controller to react if desired 9 | /// @param _from The origin of the transfer 10 | /// @param _to The destination of the transfer 11 | /// @param _amount The amount of the transfer 12 | /// @return False if the controller does not authorize the transfer 13 | function onTransfer(address _from, address _to, uint _amount) public returns(bool); 14 | 15 | /// @notice Notifies the controller about an approval allowing the 16 | /// controller to react if desired 17 | /// @param _owner The address that calls `approve()` 18 | /// @param _spender The spender in the `approve()` call 19 | /// @param _amount The amount in the `approve()` call 20 | /// @return False if the controller does not authorize the approval 21 | function onApprove(address _owner, address _spender, uint _amount) public 22 | returns(bool); 23 | 24 | /// @notice Notifies the controller about a token burn 25 | /// @param _owner The address of the burner 26 | /// @param _amount The amount to burn 27 | /// @return False if the controller does not authorize the burn 28 | function onBurn(address _owner, uint _amount) public returns(bool); 29 | } -------------------------------------------------------------------------------- /solidity/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /solidity/migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | const TLM = artifacts.require("./TokenListingManager.sol"); 2 | const TLMA = artifacts.require("./TokenListingManagerAdvanced.sol"); 3 | const NectarToken = artifacts.require("./NEC.sol"); 4 | const DestructibleMiniMeFactory = artifacts.require("./DestructibleMiniMeTokenFactory.sol"); 5 | const NectarController = artifacts.require("./NectarController.sol"); 6 | const ProposalManager = artifacts.require("./ProposalManager.sol"); 7 | 8 | 9 | // to use only for developing purposes!!! 10 | module.exports = function(deployer) { 11 | 12 | deployer.then(async () => { 13 | let amount = 1000000000000000000; 14 | 15 | await deployer.deploy(DestructibleMiniMeFactory); 16 | let factory = await DestructibleMiniMeFactory.deployed(); 17 | 18 | await deployer.deploy(NectarToken, factory.address, '0x6c259ea1fca0d1883e3fffddeb8a0719e1d7265f'); 19 | let token = await NectarToken.deployed(); 20 | 21 | await deployer.deploy(NectarController, '0x6c259ea1fca0d1883e3fffddeb8a0719e1d7265f', token.address); 22 | let controller = await NectarController.deployed(); 23 | 24 | await controller.register(['0x11b6c106d52f0bb8adb75a577076d33f33fa9c40', '0x00158a74921620b39e5c3afe4dca79feb2c2c143', '0xc26bf0fa0413d9a81470353589a50d4fb3f92a30']); 25 | 26 | // generate tokens to those two addresses for testing purposes 27 | await token.generateTokens('0x11b6c106d52f0bb8adb75a577076d33f33fa9c40', 2 * amount); 28 | await token.generateTokens('0x00158a74921620b39e5c3afe4dca79feb2c2c143', 2 * amount); 29 | await token.generateTokens('0xc26bf0fa0413d9a81470353589a50d4fb3f92a30', amount); 30 | 31 | await token.changeController(controller.address); 32 | 33 | await deployer.deploy(ProposalManager, factory.address, token.address); 34 | await deployer.deploy(TLM, factory.address, token.address); 35 | return deployer.deploy(TLMA, factory.address, token.address); 36 | }); 37 | }; 38 | -------------------------------------------------------------------------------- /solidity/scripts/get_config.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import collections 4 | 5 | """ 6 | ============== 7 | This script is going to fill config.json file with addresses and abi's from last build. 8 | This means whenever you do "truffle migrate", you need to run this script and update config file. 9 | ============== 10 | 11 | ============== 12 | To run: $ python update_config.py 13 | ============== 14 | 15 | """ 16 | directory = "../build/contracts" 17 | dict = {} 18 | abi = {} 19 | network_id = 42 20 | 21 | for contract in os.listdir(directory): 22 | if contract.endswith(".json"): 23 | with open(os.path.join(directory,contract)) as json_contract: 24 | dictdump = json.loads(json_contract.read()) 25 | if(dictdump.get("networks") == {}): 26 | continue 27 | 28 | if(dictdump["abi"] != None): 29 | abi[contract] = dictdump["abi"] 30 | if(dictdump["networks"].get(str(network_id)) != None ): 31 | dict[contract] = dictdump["networks"][str(network_id)]["address"] 32 | else: 33 | print("{} doesn't have address on network id: {}".format(contract, network_id)) 34 | 35 | with open("./data.json","r+") as jsonFile: 36 | data = {} 37 | 38 | for contract in dict: 39 | c = contract[:-5] 40 | data[c] = {} 41 | data[c]['abi'] = abi[contract] 42 | data[c]['address'] = dict[contract] 43 | print("Added {} with address: {}".format(c, dict[contract])) 44 | 45 | jsonFile.seek(0) 46 | json.dump(data, jsonFile) 47 | jsonFile.truncate() 48 | -------------------------------------------------------------------------------- /solidity/test/helpers/advanceToBlock.js: -------------------------------------------------------------------------------- 1 | function advanceBlock () { 2 | return new Promise((resolve, reject) => { 3 | web3.currentProvider.sendAsync({ 4 | jsonrpc: '2.0', 5 | method: 'evm_mine', 6 | id: Date.now(), 7 | }, (err, res) => { 8 | return err ? reject(err) : resolve(res); 9 | }); 10 | }); 11 | } 12 | 13 | // Advances the block number so that the last mined block is `number`. 14 | async function advanceToBlock (number) { 15 | if (web3.eth.blockNumber > number) { 16 | return; 17 | // throw Error(`block number ${number} is in the past (current is ${web3.eth.blockNumber})`); 18 | } 19 | 20 | while (web3.eth.blockNumber < number) { 21 | await advanceBlock(); 22 | } 23 | } 24 | 25 | module.exports = { 26 | advanceToBlock 27 | }; -------------------------------------------------------------------------------- /solidity/truffle-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // See 3 | // to customize your Truffle configuration! 4 | }; 5 | -------------------------------------------------------------------------------- /solidity/truffle.js: -------------------------------------------------------------------------------- 1 | const dotenv = require('dotenv').config(); 2 | const HDWalletProvider = require('truffle-hdwallet-provider-privkey'); 3 | 4 | 5 | const mnemonic = process.env.ETHEREUM_ACCOUNT_MNEMONIC; 6 | 7 | module.exports = { 8 | solc: { 9 | optimizer: { 10 | enabled: true, 11 | runs: 200 12 | } 13 | }, 14 | networks: { 15 | development: { 16 | host: "localhost", 17 | port: 8545, 18 | network_id: "*" // Match any network id 19 | }, 20 | ropsten: { 21 | network_id: 3, 22 | provider: function() { 23 | return new HDWalletProvider(mnemonic, "https://ropsten.infura.io/nGIfOm1FglutIhKxiq6T") 24 | }, 25 | gas: 4698712 26 | }, 27 | testnet: { 28 | network_id: 666, 29 | provider: function() { 30 | return new HDWalletProvider(mnemonic, "https://testnet.decenter.com") 31 | }, 32 | gas: 4698712 33 | }, 34 | kovan: { 35 | provider: function() { 36 | return new HDWalletProvider(mnemonic, `https://kovan.decenter.com/`); 37 | }, 38 | network_id: 42, 39 | gasPrice: 2000000000, // 2 GWei 40 | gas: 5400000, 41 | }, 42 | rinkeby: { 43 | provider: function() { 44 | return new HDWalletProvider(mnemonic, `https://rinkeby.decenter.com/`); 45 | }, 46 | network_id: 4, 47 | gasPrice: 2000000000, // 2 GWei 48 | gas: 5400000, 49 | } 50 | }, 51 | }; 52 | 53 | -------------------------------------------------------------------------------- /src/actions/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const SET_VALUE = 'set_value'; 2 | 3 | export const GET_ACCOUNT_SUCCESS = 'get_account_success'; 4 | export const GET_ACCOUNT_ERROR = 'get_account_error'; 5 | 6 | export const SHOW_NOTIF = 'show_notif'; 7 | export const HIDE_NOTIF = 'hide_notif'; 8 | 9 | export const TOKEN_BALANCE = 'token_balance'; 10 | export const VOTING_TOKEN_BALANCE = 'voting_token_balance'; 11 | export const VOTES_SPENT_BALANCE = 'voting_token_balance'; 12 | 13 | export const FETCHED_PROPOSALS = 'fetched_proposals'; 14 | export const FETCHED_ACTIVE_PROPOSALS = 'fetched_active_proposals'; 15 | export const FETCHED_NONAPPROVED_PROPOSALS = 'fetched_nonapproved_proposals'; 16 | 17 | export const FETCHED_DELEGATES = 'fetched_delegates'; 18 | export const MY_DELEGATE = 'MY_DELEGATE'; 19 | 20 | export const FETCHED_TOKENS = 'fetched_tokens'; 21 | 22 | export const UPDATE_ETHFINEX_DATA = 'update_ethfinex_data'; 23 | 24 | export const OPEN_LOGIN = 'open_login'; 25 | export const CLOSE_LOGIN = 'close_login'; 26 | 27 | export const OPEN_HELP = 'open_help'; 28 | export const CLOSE_HELP = 'close_help'; 29 | 30 | export const FETCHED_POOL_TOKENS = 'fetched_pool_tokens'; 31 | 32 | export const FETCH_TRADERS_START = 'fetch_traders_start'; 33 | export const FETCH_TRADERS_BY_TOKEN = 'fetch_traders_by_token'; 34 | export const FETCH_TRADERS_ERROR = 'fetch_traders_error'; 35 | 36 | export const FETCH_COMPETITIONS_BY_TAG = 'fetch_competitions_by_tag'; 37 | 38 | export const FETCH_CIRCULATING_NEC_DATA = 'fetch_circulating_nec_data'; 39 | export const FETCH_BURNED_NEC = 'fetch_burned_nec'; 40 | export const FETCH_DEVERSIFI_NEC_ETH_DATA = 'fetch_deversifi_nec_eth_data'; 41 | export const FETCH_DEVERSIFI_NEC_USD_DATA = 'fetch_deversifi_nec_usd_data'; 42 | export const FETCH_NEXT_AUCTION_ETH_DATA = 'fetch_next_auction_eth_data'; 43 | 44 | export const FETCH_CURRENT_AUCTION_SUMMARY = 'fetch_current_auction_summary'; 45 | export const FETCH_AUCTION_INTERVAL_DATA = 'fetch_auction_interval_data'; 46 | export const SELL_IN_AUCTION_START = 'sell_in_action_start'; 47 | export const FETCH_AUCTION_TRANSACTIONS = 'fetch_auction_transactions'; 48 | export const FETCH_ETH_PRICE = 'fetch_eth_price'; 49 | export const FETCH_NEC_PRICE = 'fetch_nec_price'; 50 | export const SELL_AND_BURN_NEC = 'sell_and_burn_nec'; 51 | export const FETCH_NEXT_AUCTION_DATE = 'fetch_next_auction_date'; 52 | -------------------------------------------------------------------------------- /src/actions/competitionActions.js: -------------------------------------------------------------------------------- 1 | import { FETCH_COMPETITIONS_BY_TAG } from './actionTypes'; 2 | 3 | const GHOST_API_URL = 'https://deversifi-2.ghost.io/'; 4 | const GHOST_CONTENT_API_KEY = '1b9232f0014d858f227e5d6b11'; 5 | const API = 'ghost/api/v2/content/'; 6 | 7 | const fetchedPosts = payload => ({ 8 | type: FETCH_COMPETITIONS_BY_TAG, 9 | posts: payload.posts, 10 | }); 11 | 12 | export const fetchPostsByTag = () => async dispatch => { 13 | const endpoint = `${GHOST_API_URL}${API}posts/?key=${GHOST_CONTENT_API_KEY}&limit=all&include=tags`; 14 | 15 | try { 16 | const promiseResp = await fetch(endpoint); 17 | const resp = await promiseResp.json(); 18 | 19 | const formattedPosts = resp.posts 20 | .filter(el => el.title.includes('Competition')) 21 | .sort((a, b) => b.published_at - a.published_at) 22 | .slice(0, 3); 23 | 24 | dispatch( 25 | fetchedPosts({ 26 | posts: formattedPosts, 27 | }) 28 | ); 29 | } catch (err) { 30 | console.log(err); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /src/actions/delegateActions.js: -------------------------------------------------------------------------------- 1 | import { FETCHED_DELEGATES, MY_DELEGATE } from './actionTypes'; 2 | import eth from '../services/ethereumService'; 3 | import grenache from '../services/grenacheService'; 4 | import { notify, notifyError } from './notificationActions'; 5 | import { openLogin } from './accountActions'; 6 | import { log } from '../services/utils'; 7 | 8 | const fetchedDelegates = delegates => ({ 9 | type: FETCHED_DELEGATES, 10 | delegates, 11 | }); 12 | 13 | export const getDelegates = () => async dispatch => { 14 | const delegates = await eth.getDelegates(); 15 | const promises = delegates.map(delegate => grenache.get(delegate.storageHash.substr(2, 40))); 16 | 17 | Promise.all(promises) 18 | .then(descriptions => { 19 | const parsedDelegates = delegates.map((delegate, i) => ({ 20 | ...delegate, 21 | description: descriptions[i], 22 | })); 23 | log(delegates); 24 | dispatch(fetchedDelegates(parsedDelegates)); 25 | }) 26 | .catch(error => { 27 | log(error); 28 | }); 29 | }; 30 | 31 | export const becomeDelegate = description => async (dispatch, getState) => { 32 | if (!getState().account.accountType) return dispatch(openLogin()); 33 | try { 34 | if (await eth.hasVotedOnTokenListing(getState().account.account)) { 35 | throw new Error("You've already voted and can not become a delegate in this round. "); 36 | } 37 | const descriptionHash = await grenache.put(description); 38 | await eth.becomeDelegate(descriptionHash, getState().account.accountType); 39 | notify('Your request to become a delegate has been submitted', 'success')(dispatch); 40 | } catch (err) { 41 | notifyError(err)(dispatch); 42 | } 43 | }; 44 | 45 | export const delegateVote = to => async (dispatch, getState) => { 46 | if (!getState().account.accountType) return dispatch(openLogin()); 47 | if (await eth.hasVotedOnTokenListing(getState().account.account)) { 48 | throw new Error("You've already voted and can not delegate your vote in this round."); 49 | } 50 | if (await eth.hasVotedOnTokenListing(to)) { 51 | throw new Error( 52 | 'The chosen address has already voted in this round and currently can not be a delegate.' 53 | ); 54 | } 55 | try { 56 | await eth.delegateVote(to, getState().account.accountType); 57 | notify('Your votes have been delegated', 'success')(dispatch); 58 | } catch (err) { 59 | notifyError(err)(dispatch); 60 | } 61 | }; 62 | 63 | export const undelegate = () => async (dispatch, getState) => { 64 | if (!getState().account.accountType) return dispatch(openLogin()); 65 | try { 66 | await eth.undelegate(getState().account.accountType); 67 | notify('Your votes have been undelegated', 'success')(dispatch); 68 | } catch (err) { 69 | notifyError(err)(dispatch); 70 | } 71 | }; 72 | 73 | const myDelegate = (myDelegate, userHasVoted) => ({ 74 | type: MY_DELEGATE, 75 | myDelegate, 76 | userHasVoted, 77 | }); 78 | 79 | export const getMyDelegate = () => async (dispatch, getState) => { 80 | const delegate = await eth.getDelegate(getState().account.account); 81 | const userHasVoted = await eth.hasVotedOnTokenListing(getState().account.account); 82 | dispatch(myDelegate(delegate, userHasVoted)); 83 | }; 84 | -------------------------------------------------------------------------------- /src/actions/notificationActions.js: -------------------------------------------------------------------------------- 1 | import { SHOW_NOTIF, HIDE_NOTIF } from './actionTypes'; 2 | 3 | let timeout; 4 | 5 | export const showNotification = (message, notifType) => ({ 6 | type: SHOW_NOTIF, 7 | message, 8 | notifType, 9 | }); 10 | 11 | export const hideNotification = () => ({ 12 | type: HIDE_NOTIF, 13 | }); 14 | 15 | export const notify = (message, type = 'success', length = 5000) => dispatch => { 16 | dispatch(showNotification(message, type)); 17 | clearTimeout(timeout); 18 | timeout = setTimeout(() => dispatch(hideNotification()), length); 19 | }; 20 | 21 | export const notifyError = err => dispatch => { 22 | let message = err.message ? err.message : err; 23 | if (message.indexOf('MetaMask') !== -1 && message.indexOf('\n') > -1) { 24 | message = message.substr(message.indexOf('MetaMask')); 25 | message = message.substr(0, message.indexOf('\n')); 26 | } 27 | if (message === 'Invalid status 6985') message += ' - User denied tx'; 28 | notify(message, 'error')(dispatch); 29 | }; 30 | -------------------------------------------------------------------------------- /src/actions/proposalActions.js: -------------------------------------------------------------------------------- 1 | import { 2 | FETCHED_PROPOSALS, 3 | FETCHED_ACTIVE_PROPOSALS, 4 | FETCHED_NONAPPROVED_PROPOSALS, 5 | } from './actionTypes'; 6 | import eth from '../services/ethereumService'; 7 | import grenache from '../services/grenacheService'; 8 | import { openLogin } from './accountActions'; 9 | import { notify, notifyError } from './notificationActions'; 10 | import { log } from '../services/utils'; 11 | 12 | const fetchedProposals = proposals => ({ 13 | type: FETCHED_PROPOSALS, 14 | proposals, 15 | }); 16 | 17 | export const getProposals = () => async dispatch => { 18 | const proposals = await Promise.all(await eth.getProposals()); 19 | log(proposals); 20 | dispatch(fetchedProposals(proposals)); 21 | }; 22 | 23 | const fetchedActiveProposals = proposals => ({ 24 | type: FETCHED_ACTIVE_PROPOSALS, 25 | proposals, 26 | }); 27 | 28 | export const getActiveProposals = () => async dispatch => { 29 | const proposals = await Promise.all(await eth.getActiveProposals()); 30 | log(proposals); 31 | dispatch(fetchedActiveProposals(proposals)); 32 | }; 33 | 34 | const fetchedNonApprovedProposals = proposals => ({ 35 | type: FETCHED_NONAPPROVED_PROPOSALS, 36 | proposals, 37 | }); 38 | 39 | export const getNonApprovedProposals = () => async dispatch => { 40 | const proposals = await Promise.all(await eth.getNonApprovedProposals()); 41 | log(proposals); 42 | dispatch(fetchedNonApprovedProposals(proposals)); 43 | }; 44 | 45 | export const submitProposal = (duration, description, email) => async (dispatch, getState) => { 46 | if (!getState().account.accountType) return dispatch(openLogin()); 47 | try { 48 | const descriptionHash = await grenache.put(description, email); 49 | await eth.submitProposal(duration, descriptionHash, getState().account.accountType); 50 | notify( 51 | `Your proposal has been submitted and will need to be approved 52 | by the Ethfinex moderation team before a vote can begin`, 53 | 'success' 54 | )(dispatch); 55 | // go to home 56 | } catch (err) { 57 | notifyError(err)(dispatch); 58 | } 59 | }; 60 | 61 | export const voteForProposal = (id, vote) => async (dispatch, getState) => { 62 | if (!getState().account.accountType) return dispatch(openLogin()); 63 | try { 64 | await eth.vote(id, vote, getState().account.accountType); 65 | notify('Thanks for voting!', 'success')(dispatch); 66 | } catch (err) { 67 | notifyError(err)(dispatch); 68 | } 69 | }; 70 | 71 | export const voteForMission = vote => async (dispatch, getState) => { 72 | notify('Voting already finished!', 'success')(dispatch); 73 | /* if (!getState().account.accountType) return dispatch(openLogin()); 74 | try { 75 | await eth.voteMission(vote, getState().account.accountType); 76 | notify('Thanks for voting!', 'success')(dispatch); 77 | } catch (err) { 78 | notifyError(err)(dispatch); 79 | } 80 | */ 81 | }; 82 | -------------------------------------------------------------------------------- /src/actions/tokenActions.js: -------------------------------------------------------------------------------- 1 | import { FETCHED_TOKENS, FETCHED_POOL_TOKENS } from './actionTypes'; 2 | import eth from '../services/ethereumService'; 3 | import { getTokenInfo, getApprovedTokens } from '../services/klerosService'; 4 | import { notify, notifyError } from './notificationActions'; 5 | import { getVotingTokenBalance } from './accountActions'; 6 | import proposedTokenData from '../../proposed_tokens'; 7 | import { log } from '../services/utils'; 8 | import currentLeaderboard from '../components/TokenListings/currentLeaderboard'; 9 | 10 | const fetchedTokens = (tokens, endingTime, isActive, evtAddress) => ({ 11 | type: FETCHED_TOKENS, 12 | tokens, 13 | endingTime, 14 | isActive, 15 | evtAddress, 16 | }); 17 | 18 | export const getTokenVotes = () => async dispatch => { 19 | const proposalData = await eth.getTokenProposalDetails(); 20 | if (!proposalData._tokens) { 21 | log('No active token proposal'); 22 | const tokens = (await Promise.all(currentLeaderboard.map(getTokenInfo))).map(token => { 23 | const extraData = proposedTokenData[token.address.toLowerCase()]; 24 | return { 25 | ...token, 26 | description: extraData && extraData.description, 27 | website: extraData && extraData.website, 28 | }; 29 | }); 30 | dispatch(fetchedTokens(tokens, new Date(), false, '0x0')); 31 | return; 32 | } 33 | const tokens = (await Promise.all(proposalData._tokens.map(getTokenInfo))) 34 | .map((token, i) => ({ 35 | ...token, 36 | id: i, 37 | totalYes: proposalData.yesVotes[i], 38 | total: proposalData.totalVotes, 39 | })) 40 | .filter(token => token.address !== '0x0000000000000000000000000000000000000000'); 41 | dispatch( 42 | fetchedTokens(tokens, proposalData.endingTime, proposalData._active, proposalData._votingToken) 43 | ); 44 | }; 45 | 46 | export const voteForToken = (id, amount) => async (dispatch, getState) => { 47 | if (!getState().account.votingTokenBalance || getState().account.votingTokenBalance < 0.1) { 48 | return notifyError('You first need voting tokens!')(dispatch); 49 | } 50 | 51 | try { 52 | await eth.voteTokens(id, amount, getState().account.accountType); 53 | notify('Thanks for voting!', 'success')(dispatch); 54 | getTokenVotes()(dispatch); 55 | getVotingTokenBalance()(dispatch, getState); 56 | } catch (err) { 57 | notifyError(err)(dispatch); 58 | } 59 | }; 60 | 61 | const fetchedPoolTokens = poolTokens => ({ 62 | type: FETCHED_POOL_TOKENS, 63 | poolTokens, 64 | }); 65 | 66 | export const getPoolTokens = () => async dispatch => { 67 | const tokens = await getApprovedTokens(); 68 | dispatch(fetchedPoolTokens(tokens)); 69 | }; 70 | -------------------------------------------------------------------------------- /src/common/header-desc-container.scss: -------------------------------------------------------------------------------- 1 | .header-desc-container { 2 | margin: 20px auto 30px; 3 | p { 4 | margin-bottom: 20px; 5 | font-size: 16px; 6 | font-weight: 300; 7 | line-height: 1.4em; 8 | } 9 | .right-header { 10 | .side-badge { 11 | display: block; 12 | background-color: #4800FF; 13 | padding: 25px; 14 | font-size: 20px; 15 | width: 280px; 16 | text-align: center; 17 | position: relative; 18 | transition: background-color .2s; 19 | &:before { 20 | content: ''; 21 | position: absolute; 22 | height: 100%; 23 | width: 100%; 24 | background-color: #9972FF; 25 | right: -10px; 26 | bottom: -10px; 27 | display: block; 28 | z-index: -1; 29 | } 30 | &:hover { 31 | background-color: lighten(#4800FF, 2%); 32 | } 33 | } 34 | } 35 | @media screen and (min-width: 768px) { 36 | .left-header { 37 | vertical-align: top; 38 | display: inline-block; 39 | width: 70%; 40 | margin-bottom: 0; 41 | p { 42 | margin-top: 0; 43 | } 44 | } 45 | .right-header { 46 | vertical-align: top; 47 | display: inline-block; 48 | width: 27%; 49 | margin-left: 3%; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/common/text.scss: -------------------------------------------------------------------------------- 1 | //FONT SIZES ( 1rem = 10px ) 2 | $font-size-giant: 2.2rem; 3 | $font-size-extra-large: 2rem; 4 | $font-size-large: 1.8rem; 5 | $font-size-medium: 1.6rem; 6 | $font-size-regular: 1.4rem; 7 | $font-size-small: 1.2rem; 8 | $font-size-extra-small: 1rem; 9 | $font-size-tiny: 0.8rem; 10 | 11 | //FONT WEIGHTS 12 | $font-weight-bold: 700; 13 | $font-weight-semi-bold: 500; 14 | $font-weight-normal: 400; 15 | $font-weight-thin: 300; 16 | 17 | .text-giant{ 18 | font-size: $font-size-giant; 19 | } 20 | .text-extra-large{ 21 | font-size: $font-size-extra-large; 22 | } 23 | .text-large{ 24 | font-size: $font-size-large; 25 | } 26 | .text-medium{ 27 | font-size: $font-size-medium; 28 | } 29 | .text-regular{ 30 | font-size: $font-size-regular; 31 | } 32 | .text-small{ 33 | font-size: $font-size-small; 34 | } 35 | .text-extra-small{ 36 | font-size: $font-size-extra-small; 37 | } 38 | .text-tiny{ 39 | font-size: $font-size-tiny; 40 | } 41 | 42 | .text-bold{ 43 | font-weight: $font-weight-bold; 44 | } 45 | .text-semi-bold{ 46 | font-weight: $font-weight-semi-bold; 47 | } 48 | .text-normal{ 49 | font-weight: $font-weight-normal; 50 | } 51 | .text-light{ 52 | font-weight: $font-weight-thin; 53 | } 54 | -------------------------------------------------------------------------------- /src/common/variables.scss: -------------------------------------------------------------------------------- 1 | $color1: #222a43; 2 | $color2: #041824; 3 | $color3: #191F32; 4 | $color4: #757891; 5 | 6 | $accent: #9972FF; 7 | 8 | $bg-text-color-1: #4D5475; 9 | 10 | $font-family-bg-text: 'Old Standard TT', serif; 11 | 12 | $dark-gray: #28324A; 13 | 14 | $primary-text: #fff; 15 | $secondary-text: #A9ABCB; 16 | $dark-text: #212A41; 17 | 18 | 19 | $font-sen: 'Sen', sans-serif; 20 | $font-nunito: 'Nunito', sans-serif; 21 | $font-montserrat: 'Montserrat', sans-serif; 22 | 23 | $component-background: #28324A; 24 | 25 | $violet-inactive: #4100C0; 26 | $violet-active: #5500FC; 27 | 28 | $input-color: rgba(169, 171, 203, 0.1); 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/components/Admin/Admin.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { connect } from 'react-redux'; 4 | import { Redirect } from 'react-router-dom'; 5 | import { getNonApprovedProposals } from '../../actions/proposalActions'; 6 | import './Admin.scss'; 7 | import eth from '../../services/ethereumService'; 8 | 9 | class Admin extends Component { 10 | componentDidMount() { 11 | this.props.getNonApprovedProposals(); 12 | } 13 | 14 | render() { 15 | return ( 16 |
17 |
18 |

Submitted Proposals

19 |
20 | {this.props.proposals.map(proposal => ( 21 |
22 |
23 |

{proposal.title}

24 |

Duration: {proposal.duration} days

25 |

Submitted by: {proposal._proposer}

26 |

{proposal.description}

27 | {!this.props.isAdmin && ( 28 |

29 | This proposal is pending approval by the administrators 30 |

31 | )} 32 | {this.props.isAdmin && ( 33 | 42 | )} 43 |
44 |
45 | ))} 46 | 47 | {!this.props.proposals.length && ( 48 |
No proposals are currently pending.
49 | )} 50 |
51 |
52 |
53 | ); 54 | } 55 | } 56 | 57 | Admin.propTypes = { 58 | getNonApprovedProposals: PropTypes.func.isRequired, 59 | proposals: PropTypes.array.isRequired, 60 | account: PropTypes.string.isRequired, 61 | accountType: PropTypes.string.isRequired, 62 | isAdmin: PropTypes.bool.isRequired, 63 | }; 64 | 65 | const mapStateToProps = state => ({ 66 | proposals: state.proposal.nonApprovedProposals, 67 | account: state.account.account, 68 | accountType: state.account.accountType, 69 | isAdmin: state.account.isAdmin, 70 | }); 71 | 72 | export default connect( 73 | mapStateToProps, 74 | { 75 | getNonApprovedProposals, 76 | } 77 | )(Admin); 78 | -------------------------------------------------------------------------------- /src/components/Admin/Admin.scss: -------------------------------------------------------------------------------- 1 | @import '../../common/variables'; 2 | 3 | .admin { 4 | margin: auto; 5 | padding: 10px 0; 6 | min-height: calc(100vh - 177px); 7 | h1 { 8 | font-size: 70px; 9 | margin: 100px 0 100px; 10 | font-weight: 300; 11 | } 12 | .empty { 13 | margin: 50px 0; 14 | } 15 | .proposal-wrapper { 16 | &:first-child { 17 | .remaining { 18 | padding-top: 0; 19 | } 20 | .details-wrapper { 21 | border: 0 !important; 22 | padding-top: 0; 23 | } 24 | .results-wrapper { 25 | border: 0 !important; 26 | padding-top: 0; 27 | } 28 | } 29 | .details-wrapper { 30 | flex: 1 1 100%; 31 | padding: 40px 0; 32 | border-top: 1px solid white; 33 | p { 34 | margin: 10px 0; 35 | line-height: 1.5em; 36 | font-size: 14px; 37 | letter-spacing: 0.02em; 38 | } 39 | .started { 40 | color: $color4; 41 | } 42 | .title { 43 | font-weight: bold; 44 | line-height: 38px; 45 | text-decoration: none; 46 | font-size: 18px; 47 | letter-spacing: 0.02em; 48 | } 49 | .description { 50 | white-space: pre-wrap; 51 | font-size: 14px; 52 | overflow: hidden; 53 | position: relative; 54 | } 55 | a { 56 | color: white; 57 | text-decoration: none; 58 | line-height: 58px; 59 | font-size: 35px; 60 | letter-spacing: 0.02em; 61 | margin-top: 20px; 62 | display: inline-block; 63 | cursor: pointer; 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/components/App/assets/deversifi-logo-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/components/App/assets/deversifi-logo-dark.png -------------------------------------------------------------------------------- /src/components/Auction/Description/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import '../Auction.scss'; 3 | 4 | const AuctionDescription = ({ closeDescription, visible }) => { 5 | return visible && ( 6 |
7 |

8 | Every week the necBurn smart contract facilitates an auction the ‘necBurn’ whereby it sells 9 | off a percentage of the revenues gathered from trading fees on DeversiFi (in ETH) in the 10 | previous week in exchange for NEC tokens which then get destroyed - ‘burnt’. 11 |

12 | 13 |

14 | The auctions start on Thursday of each week and last 7 days or until the accumulated funds 15 | are sold out. The opening NEC/ETH price of the auction is half the price at which the 16 | previous auction ended and it increases incrementally 5 times per day. 17 |

18 | 19 |

20 | The portion of DeversiFi trading fee revenues dedicated to the auction is determined by the 21 | following schedule. Click 22 | here 23 |

24 | 25 |

26 | For the purposes of the necBurn auction revenues from DeversiFi trading fees are defined as 27 | trading fees collected from users minus on-chain settlement costs minus external liquidity 28 | provider costs if one exists. 29 |

30 |
31 | 32 |
33 |
34 | ); 35 | }; 36 | 37 | export default AuctionDescription; 38 | -------------------------------------------------------------------------------- /src/components/Auction/Diagrams/BarDiagram.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Cell } from 'recharts'; 3 | import '../Auction.scss'; 4 | import { renderTooltip } from './Diagram'; 5 | import { formatNumber } from '../../../services/utils'; 6 | 7 | export default class BarDiagram extends Component { 8 | constructor(props) { 9 | super(props); 10 | } 11 | 12 | render() { 13 | const { data, XAxisKey, YAxisKey, highlightValue } = this.props; 14 | 15 | return ( 16 |
17 |

Current Auction Price NEC/ETH

18 | 24 | 25 | 34 | 35 | 36 | 37 | { 38 | this.props.data && this.props.data.map((entry, index) => ( 39 | 40 | )) 41 | } 42 | 43 | 44 |
45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/components/Auction/Diagrams/Circle.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function Circle({ children, title, percentage, soldEthVal, text }) { 4 | const sold = `${percentage}, 100`; 5 | return ( 6 |
7 |

{title}

8 |
9 |
{text}
10 | 11 | 17 | 24 | {children} 25 | 26 |
27 |
28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /src/components/Auction/Diagrams/Diagram.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip } from 'recharts'; 3 | import '../Auction.scss'; 4 | import { formatNumber } from '../../../services/utils'; 5 | 6 | export function renderTooltip({ active, payload }) { 7 | if (!active) { 8 | return null; 9 | } 10 | 11 | return ( 12 |
13 |

{payload && payload[0] && `${formatNumber(payload[0].value)}`}

14 | {/* */} 15 |
16 | ); 17 | } 18 | 19 | export default class Diagram extends Component { 20 | constructor(props) { 21 | super(props); 22 | } 23 | 24 | render() { 25 | return ( 26 |
27 |

{this.props.tabContent.name}

28 | 34 | 35 | 43 | 50 | 51 | 58 | 59 |
60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/components/Calculator/Calculator.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Tokens from '../Tokens/Tokens'; 3 | import TradingReward from '../TradingReward/TradingReward'; 4 | 5 | import './Calculator.scss'; 6 | 7 | class Calculator extends Component { 8 | constructor() { 9 | super(); 10 | 11 | this.state = { 12 | shown: 'trading', 13 | }; 14 | 15 | this.switch = this.switch.bind(this); 16 | } 17 | 18 | switch(slug) { 19 | this.setState({ 20 | shown: slug, 21 | }); 22 | } 23 | 24 | render() { 25 | return ( 26 |
27 |
calculator
28 | 29 |
30 | this.switch('trading')} 33 | > 34 | Trading Reward
Calculator 35 |
36 | this.switch('tokens')} 39 | > 40 | Redeem Value
Calculator 41 |
42 |
43 | { 44 | this.state.shown === 'tokens' && 45 | 46 | } 47 | { 48 | this.state.shown === 'trading' && 49 | 50 | } 51 |
52 | ); 53 | } 54 | } 55 | 56 | export default Calculator; 57 | -------------------------------------------------------------------------------- /src/components/Calculator/Calculator.scss: -------------------------------------------------------------------------------- 1 | @import "../../common/variables"; 2 | 3 | .calculator-wrapper { 4 | position: relative; 5 | 6 | .calculator-bg-text { 7 | right: 0; 8 | top: 189px; 9 | } 10 | 11 | .calculator-nav { 12 | z-index: 2; 13 | height: 120px; 14 | margin-top: -60px; 15 | display: flex; 16 | position: relative; 17 | 18 | @media screen and (max-width: 767px) { 19 | display: block; 20 | margin: -60px 0 30px; 21 | height: auto; 22 | a { 23 | display: block; 24 | } 25 | br { 26 | display: none; 27 | } 28 | } 29 | 30 | a { 31 | padding: 32px 50px; 32 | background: $color3; 33 | flex: 1 1 100%; 34 | line-height: normal; 35 | font-size: 24px; 36 | cursor: pointer; 37 | &.active { 38 | font-weight: bold; 39 | background: linear-gradient(90deg, #757891 0%, #505266 100%); 40 | box-shadow: 0px 0px 100px rgba(0, 0, 0, 0.68); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/components/DAO/Eth_cand.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/components/DAO/Eth_cand.gif -------------------------------------------------------------------------------- /src/components/DAO/Eth_draw.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/components/DAO/Eth_draw.gif -------------------------------------------------------------------------------- /src/components/DAO/Eth_pool.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/components/DAO/Eth_pool.gif -------------------------------------------------------------------------------- /src/components/DAO/Eth_vote.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/components/DAO/Eth_vote.gif -------------------------------------------------------------------------------- /src/components/DAO/Eth_winners.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/components/DAO/Eth_winners.gif -------------------------------------------------------------------------------- /src/components/DAO/TermsModal/Terms.scss: -------------------------------------------------------------------------------- 1 | 2 | 3 | .terms-content { 4 | ol { 5 | counter-reset: section; 6 | list-style-type: none; 7 | text-indent: -10px; 8 | 9 | li::before { 10 | counter-increment: section; 11 | content: counters(section,".") " "; 12 | font-weight: bold; 13 | } 14 | li { 15 | padding-right: .6em; 16 | margin-bottom: 10px; 17 | } 18 | } 19 | p { 20 | font-family: 'Montserrat', sans-serif; 21 | font-size: 13px !important ; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/components/DAO/assets/arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/DAO/free-thinking-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/components/DAO/free-thinking-icon.png -------------------------------------------------------------------------------- /src/components/DelegateVotes/DelegateVotes.scss: -------------------------------------------------------------------------------- 1 | .delegates { 2 | margin: auto; 3 | padding: 10px 0; 4 | 5 | .container.flex { 6 | display: flex; 7 | align-items: flex-start; 8 | img { 9 | padding-right: 40px; 10 | } 11 | h2 { 12 | margin-top: 0; 13 | } 14 | } 15 | 16 | h1 { 17 | font-size: 70px; 18 | margin: 100px 0 20px; 19 | font-weight: 300; 20 | } 21 | 22 | .small-title { 23 | font-size: 24px; 24 | line-height: 29px; 25 | margin-bottom: 22px; 26 | } 27 | 28 | .page-description { 29 | margin-bottom: 30px; 30 | } 31 | .section-description { 32 | font-size: 18px; 33 | line-height: 29px; 34 | font-weight: 300; 35 | } 36 | .undelegate { 37 | margin: 30px 0; 38 | a { 39 | text-decoration: underline; 40 | margin-top: 5px; 41 | cursor: pointer; 42 | display: inline-block; 43 | } 44 | } 45 | .votes-section { 46 | background-color: #222A43; 47 | 48 | .section-description { 49 | margin-bottom: 50px; 50 | } 51 | 52 | .single-delegate { 53 | display: flex; 54 | justify-content: space-between; 55 | margin-bottom: 60px; 56 | 57 | @media screen and (max-width: 767px) { 58 | flex-direction: column; 59 | } 60 | 61 | .delegate-information { 62 | white-space: pre-line; 63 | 64 | .delegate-address { 65 | color: #fff; 66 | font-size: 18px; 67 | line-height: 22px; 68 | margin: 10px 0; 69 | } 70 | 71 | .delegate-mission { 72 | font-size: 15px; 73 | line-height: normal; 74 | font-weight: 300; 75 | } 76 | } 77 | 78 | .vote-for-delegate { 79 | .checkbox-text { 80 | font-size: 15px; 81 | color: #757891; 82 | text-shadow: 0 0 100px rgba(0, 0, 0, 0.75); 83 | } 84 | &.submit-wrapper { 85 | padding: 0; 86 | text-align: left; 87 | } 88 | } 89 | } 90 | } 91 | } 92 | 93 | .checkbox { 94 | display: inline !important; 95 | cursor: pointer; 96 | font-size: 12px; 97 | position: relative; 98 | margin: 0 !important; 99 | input { 100 | display: none; 101 | &:checked { 102 | & + .label:before { 103 | -webkit-box-shadow: inset 0 0 0 3px #222A43; 104 | -moz-box-shadow: inset 0 0 0 3px #222A43; 105 | box-shadow: inset 0 0 0 3px #222A43; 106 | background-color: #fff; 107 | } 108 | } 109 | } 110 | .label { 111 | font-size: 12px; 112 | margin: 0; 113 | &:before { 114 | content: ""; 115 | position: relative; 116 | bottom: -2px; 117 | display: inline-flex; 118 | width: 13px; 119 | height: 13px; 120 | text-align: center; 121 | border: 1px solid #757891; 122 | margin-right: 8px; 123 | background-color: transparent; 124 | transition: background-color .2s ease-in-out; 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/components/Help/Help.scss: -------------------------------------------------------------------------------- 1 | @import "../../common/variables"; 2 | 3 | .help-fab { 4 | position: fixed; 5 | bottom: 100px; 6 | right: 40px; 7 | height: 80px; 8 | width: 80px; 9 | border-radius: 80px; 10 | background: darken($color4, 5%); 11 | cursor: pointer; 12 | line-height: 11px; 13 | font-size: 25px; 14 | text-align: center; 15 | text-transform: uppercase; 16 | display: flex; 17 | justify-content: center; 18 | flex-direction: column; 19 | transition: all .2s; 20 | z-index: 1000; 21 | &:hover { 22 | background-color: darken($color4, 8%); 23 | } 24 | &:active { 25 | background-color: darken($color4, 10%); 26 | } 27 | } 28 | 29 | .help-wrapper { 30 | position: fixed; 31 | width: 100%; 32 | padding: 80px 140px; 33 | top: 0; 34 | left: 0; 35 | z-index: 300; 36 | max-height: 100vh; 37 | overflow: auto; 38 | @media screen and (max-width: 1100px) { 39 | padding: 40px; 40 | } 41 | .help-inner-wrapper { 42 | box-shadow: 0px 0px 100px rgba(0, 0, 0, 0.82); 43 | background-color: $color2; 44 | border-radius: 5px; 45 | padding: 30px; 46 | position: relative; 47 | .close-button { 48 | display: block; 49 | position: absolute; 50 | top: 20px; 51 | right: 20px; 52 | background: url('/images/x.svg') 100% center no-repeat; 53 | height: 34px; 54 | width: 34px; 55 | border: 0; 56 | outline: 0; 57 | cursor: pointer; 58 | } 59 | .left-bullet-wrapper { 60 | position: left; 61 | display: inline-block; 62 | vertical-align: middle; 63 | width: 50%; 64 | ul li, ol li { 65 | padding: 2px 0px; 66 | } 67 | } 68 | .video-wrapper { 69 | box-shadow: 0px 0px 100px rgba(0, 0, 0, 0.82); 70 | border-radius: 5px; 71 | padding: 5px; 72 | position: right; 73 | display: inline-block; 74 | vertical-align: middle; 75 | width: 45%; 76 | margin-left: 5%; 77 | bottom: 10%; 78 | } 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/components/Landing/CircleButton/CircleButton.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import './CircleButton.scss'; 5 | 6 | export default function CircleButton(props) { 7 | const { icon, activeTab, desc, onCircleButtonClick } = props; 8 | 9 | if (!icon || !activeTab || !desc || !onCircleButtonClick) { 10 | return null; 11 | } 12 | 13 | return ( 14 | 41 | ); 42 | } 43 | 44 | CircleButton.propTypes = { 45 | icon: PropTypes.string.isRequired, 46 | activeTab: PropTypes.string.isRequired, 47 | desc: PropTypes.string.isRequired, 48 | 49 | onCircleButtonClick: PropTypes.func.isRequired, 50 | }; 51 | -------------------------------------------------------------------------------- /src/components/Landing/CircleButton/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './CircleButton'; 2 | -------------------------------------------------------------------------------- /src/components/Landing/scrollHelper.js: -------------------------------------------------------------------------------- 1 | const WHEEL_STEP = 80; 2 | 3 | let counterDeltaTop = 0; 4 | let counterDeltaBottom = 0; 5 | export function countWheels(delta) { 6 | if (delta > 0) { 7 | counterDeltaTop += delta; 8 | counterDeltaBottom = 0; 9 | } 10 | if (delta < 0) { 11 | counterDeltaBottom += delta; 12 | counterDeltaTop = 0; 13 | } 14 | if (counterDeltaTop >= WHEEL_STEP) { 15 | counterDeltaTop = 0; 16 | return 'top'; 17 | } 18 | if (counterDeltaBottom <= -WHEEL_STEP) { 19 | counterDeltaBottom = 0; 20 | return 'bottom'; 21 | } 22 | return 'not changed'; 23 | } 24 | -------------------------------------------------------------------------------- /src/components/Landing/sections/DaoGovernance/DaoGovernance.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | export default function DaoGovernance() { 5 | return ( 6 |
7 |
8 | DAO Governance 9 |
10 |

11 | Through the necDAO, NEC holders can govern the utility of the Nectar token itself, but 12 | they’re also able to control how necDAO’s 17,000 ETH are allocated. 13 |

14 | 15 |
16 | 17 | Find out more 18 | 19 | 20 | 21 | Visit DAO page 22 | 23 |
24 | 25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /src/components/Landing/sections/DaoGovernance/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './DaoGovernance'; 2 | -------------------------------------------------------------------------------- /src/components/Landing/sections/Ecosystem/Ecosystem.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './Ecosystem.scss'; 3 | 4 | export default function Ecosystem() { 5 | return ( 6 |
7 |
8 | Trade NEC 9 |
10 |

11 | Nectar exists as a token on the Ethereum blockchain, allowing it to interact with the wider 12 | Decentralised Finance ecosystem, and is listed on multiple exchanges to promote discovery 13 | and liquidity. 14 |

15 |
16 | 17 | 18 | DeversiFi 19 | 20 | 21 | 22 | Bitfinex 23 | 24 | 25 | 26 | Uniswap 27 | 28 | 29 | 30 | Balancer 31 | 32 |
33 |
34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /src/components/Landing/sections/Ecosystem/Ecosystem.scss: -------------------------------------------------------------------------------- 1 | .exchanges__links-wrapper { 2 | display: flex; 3 | flex-direction: column; 4 | margin-top: 34px; 5 | 6 | & > * { 7 | margin-bottom: 30px; 8 | 9 | img { 10 | max-height: 32px; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/components/Landing/sections/Ecosystem/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Ecosystem'; 2 | -------------------------------------------------------------------------------- /src/components/Landing/sections/Nec/Nec.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './Nec.scss'; 3 | 4 | export default function Nec() { 5 | return ( 6 |
7 |

8 | Welcome to the home of Nectar 9 |

10 |

11 | Nectar ($NEC) is a token governed by a DAO. 12 |

13 |

14 |

15 | Holders of $NEC enjoy voting rights in necDAO (which is one of the most well-funded DAOs in the world). necDAO has closely collaborated in the past with several of the top projects in DeFi, including DeversiFi, Kleros and DAOstack. 16 |

17 | 18 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /src/components/Landing/sections/Nec/Nec.scss: -------------------------------------------------------------------------------- 1 | @import "../../../../common/variables"; 2 | 3 | .nec__header { 4 | margin: 0 0 32px; 5 | font-weight: normal; 6 | font-size: 48px; 7 | line-height: 140%; 8 | font-family: $font-montserrat; 9 | 10 | span { 11 | display: inline-block; 12 | position: relative; 13 | font-weight: bold; 14 | 15 | &::after { 16 | content: " "; 17 | position: absolute; 18 | z-index: -1; 19 | height: 32px; 20 | width: calc(100% + 20px); 21 | bottom: 6px; 22 | right: -10px; 23 | display: block; 24 | background: #FCAE16; 25 | opacity: 0.3; 26 | } 27 | } 28 | 29 | @media all and (max-width: 868px) { 30 | font-size: 38px; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/components/Landing/sections/Nec/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Nec'; 2 | -------------------------------------------------------------------------------- /src/components/Landing/sections/Neconomics/Neconomics.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AnimatedNumber from 'animated-number-react'; 3 | import eth from '../../../../services/ethereumService'; 4 | 5 | import './Neconomics.scss'; 6 | import { numberWithCommas } from '../../../../services/utils'; 7 | 8 | class Neconomics extends React.Component { 9 | constructor(props) { 10 | super(props); 11 | this.state = { 12 | circulatingSupply: 0, 13 | totalSupply: 0, 14 | marketCap: 0, 15 | necPrice: 0, 16 | }; 17 | } 18 | 19 | async componentWillMount() { 20 | const { necPrice, totalSupply, marketCap, circulatingSupply } = await eth.getNeconomicsData(); 21 | this.setState({ 22 | necPrice, 23 | totalSupply, 24 | marketCap, 25 | circulatingSupply, 26 | }); 27 | } 28 | 29 | render() { 30 | const { necPrice, totalSupply, marketCap, circulatingSupply } = this.state; 31 | 32 | return ( 33 |
34 |
35 | Neconomics 36 |
37 |
38 |
39 |
40 | 45 |
46 |
Circulating supply
47 |
48 |
49 |
50 |
51 | 56 |
57 |
Total supply
58 |
59 |
60 |
61 |
62 | $ 67 |
68 |
Market cap
69 |
70 |
71 |
72 |
73 | $ value.toFixed(5)} 77 | /> 78 |
79 |
Token price
80 |
81 |
82 | ); 83 | } 84 | } 85 | 86 | export default Neconomics; 87 | -------------------------------------------------------------------------------- /src/components/Landing/sections/Neconomics/Neconomics.scss: -------------------------------------------------------------------------------- 1 | @import "../../../../common/variables"; 2 | 3 | .neconomics__section { 4 | .separator { 5 | background: linear-gradient(90deg, #FF8800 8.75%, #E2A907 100%); 6 | height: 3px; 7 | width: 60px; 8 | margin-bottom: 24px; 9 | } 10 | 11 | 12 | .main__text { 13 | font-family: $font-montserrat; 14 | font-weight: bold; 15 | font-size: 40px; 16 | line-height: 140%; 17 | } 18 | 19 | .side__text { 20 | font-family: $font-montserrat; 21 | font-size: 18px; 22 | line-height: 140%; 23 | } 24 | margin-bottom: 24px; 25 | } 26 | -------------------------------------------------------------------------------- /src/components/Landing/sections/Neconomics/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Neconomics'; 2 | -------------------------------------------------------------------------------- /src/components/Landing/sections/index.js: -------------------------------------------------------------------------------- 1 | export { default as DaoGovernance } from './DaoGovernance'; 2 | export { default as Ecosystem } from './Ecosystem'; 3 | export { default as Nec } from './Nec'; 4 | export { default as Neconomics } from './Neconomics'; 5 | -------------------------------------------------------------------------------- /src/components/Landing/volumeHelpers.js: -------------------------------------------------------------------------------- 1 | export const calculateVolumeDiscount = volume => { 2 | const LIMITS = [100000, 1000000, 10000000, 100000000, 1000000000]; 3 | const FRACTION = 1 / 5; 4 | if (volume >= LIMITS[4]) { 5 | return { 6 | volumePercentage: 100, 7 | volumeDiscount: 50, 8 | }; 9 | } 10 | if (volume < LIMITS[0]) { 11 | return { 12 | volumePercentage: (volume / LIMITS[0]) * FRACTION * 100, 13 | volumeDiscount: undefined, 14 | }; 15 | } 16 | for (let i = 0; i < LIMITS.length; i++) { 17 | if (volume >= LIMITS[i] && volume < LIMITS[i + 1]) { 18 | return { 19 | volumePercentage: 20 | (FRACTION * (i + 1) + ((volume - LIMITS[i]) / (LIMITS[i + 1] - LIMITS[i])) * FRACTION) * 21 | 100, 22 | volumeDiscount: 10 * (i + 1), 23 | }; 24 | } 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /src/components/Loading/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Loader from 'react-loader-spinner'; 3 | 4 | const Loading = () => ( 5 | 11 | ) 12 | 13 | export default Loading; 14 | -------------------------------------------------------------------------------- /src/components/PreviousTokenListing/PreviousTokenListing.scss: -------------------------------------------------------------------------------- 1 | @import '../../common/variables'; 2 | 3 | .previouslistings { 4 | h5 { 5 | margin: 0px 0 30px; 6 | } 7 | .header-desc-container { 8 | margin: 0 auto 30px; 9 | } 10 | .description { 11 | color: green; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/components/ProposalCountdown/ProposalCountdown.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { timeUntilDate } from '../../services/utils'; 4 | 5 | import './ProposalCountdown.scss'; 6 | 7 | class ProposalCountdown extends Component { 8 | constructor() { 9 | super(); 10 | this.state = { 11 | count: 0, 12 | unit: 'days', 13 | }; 14 | this.parseETA = this.parseETA.bind(this); 15 | } 16 | componentDidMount() { 17 | this.parseETA(); 18 | setInterval(() => this.parseETA(), 1000); 19 | } 20 | parseETA() { 21 | this.setState({ 22 | ...timeUntilDate(this.props.endTime), 23 | }); 24 | } 25 | render() { 26 | if (this.state.count === 0) 27 | return ( 28 |
29 |
Finished
30 |
31 | ); 32 | 33 | return ( 34 |
35 |
{this.state.count}
36 |
37 |
{this.state.unit}
38 |
remaining
39 |
40 |
41 | ); 42 | } 43 | } 44 | 45 | ProposalCountdown.propTypes = { 46 | endTime: PropTypes.object.isRequired, 47 | }; 48 | 49 | export default ProposalCountdown; 50 | -------------------------------------------------------------------------------- /src/components/ProposalCountdown/ProposalCountdown.scss: -------------------------------------------------------------------------------- 1 | .proposal-countdown-wrapper { 2 | width: 200px; 3 | display: flex; 4 | .number { 5 | font-weight: bold; 6 | font-size: 60px; 7 | line-height: 60px; 8 | margin-right: 5px; 9 | } 10 | .unit { 11 | font-size: 32px; 12 | font-weight: 300; 13 | } 14 | .remaining { 15 | font-size: 12px; 16 | font-weight: 300; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/components/Stats/Stats.scss: -------------------------------------------------------------------------------- 1 | @import "../../common/variables"; 2 | 3 | .stats-wrapper { 4 | position: relative; 5 | 6 | .stats-bg-text { 7 | top: -20px; 8 | right: 210px; 9 | } 10 | 11 | h2 { 12 | font-size: 48px; 13 | margin: 0; 14 | @media screen and (max-width: 1365px) { 15 | font-size: 40px; 16 | } 17 | @media screen and (max-width: 767px) { 18 | font-size: 32px; 19 | } 20 | } 21 | h3 { 22 | font-size: 18px; 23 | line-height: 29px; 24 | font-weight: normal; 25 | margin: 25px 50px; 26 | max-width: 442px; 27 | @media screen and (max-width: 767px) { 28 | font-size: 12px; 29 | } 30 | } 31 | .stats-grid { 32 | display: grid; 33 | grid-template-columns: 1fr 1fr 1fr 1fr; 34 | grid-gap: 10px; 35 | line-height: 22px; 36 | font-size: 14px; 37 | font-weight: normal; 38 | .token-supply { 39 | grid-column: 3 / span 2; 40 | } 41 | .current-token { 42 | grid-column: 3; 43 | } 44 | .trading-volume { 45 | grid-column: 4; 46 | } 47 | .breaker { 48 | display: none; 49 | } 50 | @media screen and (max-width: 1000px) { 51 | display: block; 52 | div { 53 | display: inline-flex; 54 | flex-direction: column; 55 | } 56 | .breaker { 57 | display: block; 58 | } 59 | } 60 | > div { 61 | padding: 20px; 62 | } 63 | .stat-wrapper { 64 | border-top: 2px solid white; 65 | display: inline-flex; 66 | padding: 15px 0; 67 | margin: 20px 0; 68 | @media screen and (max-width: 1365px) { 69 | padding: 5px 0; 70 | margin: 10px 0; 71 | flex-direction: row; 72 | } 73 | .stat { 74 | font-weight: bold; 75 | font-size: 40px; 76 | line-height: 40px; 77 | @media screen and (max-width: 1365px) { 78 | font-size: 26px; 79 | } 80 | @media screen and (max-width: 767px) { 81 | font-size: 28px; 82 | } 83 | } 84 | .stat-addon { 85 | font-size: 14px; 86 | line-height: 18px; 87 | margin-left: 5px; 88 | margin-top: 2px; 89 | 90 | @media screen and (max-width: 1365px) { 91 | font-size: 12px; 92 | line-height: 14px; 93 | margin-top: 6px; 94 | } 95 | .secondary { 96 | color: $color4; 97 | } 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/components/Submit/Submit.scss: -------------------------------------------------------------------------------- 1 | @import "../../common/variables"; 2 | 3 | .submit-proposal { 4 | padding: 10px 0 0; 5 | h1 { 6 | font-size: 70px; 7 | margin: 100px 0 50px; 8 | font-weight: 300; 9 | } 10 | .error { 11 | text-align: center; 12 | padding: 10px; 13 | border-radius: 3px; 14 | background-color: #d33133; 15 | color: white; 16 | } 17 | } 18 | 19 | .form-wrapper { 20 | background-color: $color4; 21 | padding: 100px 0; 22 | .form-container { 23 | max-width: 700px; 24 | padding: 0 30px; 25 | margin: auto; 26 | } 27 | label { 28 | display: block; 29 | margin: 30px 0; 30 | font-size: 16px; 31 | font-weight: 300; 32 | } 33 | input { 34 | margin-left: 40px; 35 | background-color: transparent; 36 | border: 0; 37 | border-bottom: 1px solid white; 38 | color: white; 39 | font-size: 16px; 40 | font-weight: 300; 41 | outline: none; 42 | padding: 5px; 43 | } 44 | input[type="text"] { 45 | width: 100%; 46 | margin: 0; 47 | padding: 10px 0; 48 | &::placeholder { 49 | color: rgba(white, .5); 50 | } 51 | } 52 | textarea { 53 | display: block; 54 | margin: 10px 0; 55 | width: 100%; 56 | min-height: 200px; 57 | resize: vertical; 58 | padding: 10px 0; 59 | line-height: 1.4em; 60 | background-color: transparent; 61 | border: 0; 62 | border-bottom: 1px solid white; 63 | color: white; 64 | font-size: 16px; 65 | font-weight: 300; 66 | outline: none; 67 | &::placeholder { 68 | color: rgba(white, .5); 69 | } 70 | } 71 | input[type="email"] { 72 | width: 250px; 73 | } 74 | .submit-wrapper { 75 | text-align: right; 76 | padding: 50px 0 0; 77 | button { 78 | margin: 10px 0; 79 | width: 200px; 80 | height: 50px; 81 | border: 0; 82 | outline: 0; 83 | background: #041824; 84 | box-shadow: 0 0 100px rgba(0, 0, 0, 0.38); 85 | font-size: 14px; 86 | letter-spacing: 0.02em; 87 | color: #FFFFFF; 88 | cursor: pointer; 89 | transition: all .2s; 90 | &:hover { 91 | background: lighten(#041824, 2%); 92 | box-shadow: 0 0 100px rgba(0, 0, 0, 0.5); 93 | } 94 | &:disabled { 95 | background: #0e1c24 !important; 96 | color: gray; 97 | } 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/components/TokenAbout/Eth_cand.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/components/TokenAbout/Eth_cand.gif -------------------------------------------------------------------------------- /src/components/TokenAbout/Eth_draw.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/components/TokenAbout/Eth_draw.gif -------------------------------------------------------------------------------- /src/components/TokenAbout/Eth_pool.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/components/TokenAbout/Eth_pool.gif -------------------------------------------------------------------------------- /src/components/TokenAbout/Eth_vote.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/components/TokenAbout/Eth_vote.gif -------------------------------------------------------------------------------- /src/components/TokenAbout/Eth_winners.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/components/TokenAbout/Eth_winners.gif -------------------------------------------------------------------------------- /src/components/TokenListings/currentLeaderboard.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | '0xA4e8C3Ec456107eA67d3075bF9e3DF3A75823DB0', 3 | '0xEBBdf302c940c6bfd49C6b165f457fdb324649bc', 4 | '0x6fB3e0A217407EFFf7Ca062D46c26E5d60a14d69', 5 | '0xC39E626A04C5971D770e319760D7926502975e47', 6 | '0x3F17Dd476faF0a4855572F0B6ed5115D9bBA22AD', 7 | '0x1D287CC25dAD7cCaF76a26bc660c5F7C8E2a05BD', 8 | '0x8B40761142B9aa6dc8964e61D0585995425C3D94', 9 | '0x846C66cf71C43f80403B51fE3906B3599D63336f', 10 | '0xf4c90e18727C5C76499eA6369C856A6d61D3E92E', 11 | '0x9e88613418cF03dCa54D6a2cf6Ad934A78C7A17A', 12 | '0x8069080a922834460C3A092FB2c1510224dc066b', 13 | '0x446C9033E7516D820cc9a2ce2d0B7328b579406F', 14 | ]; 15 | -------------------------------------------------------------------------------- /src/components/TokenPool/TokenPool.scss: -------------------------------------------------------------------------------- 1 | @import '../../common/variables'; 2 | 3 | .pool { 4 | margin: auto; 5 | padding: 10px 0; 6 | min-height: calc(100vh - 177px); 7 | 8 | h1 { 9 | font-size: 70px; 10 | margin: 100px 0 20px; 11 | font-weight: 300; 12 | } 13 | h5 { 14 | font-size: 25px; 15 | margin: 0; 16 | font-weight: 400; 17 | } 18 | .active-section { 19 | background-color: white; 20 | color: $color1; 21 | padding: 10px 0; 22 | h5 { 23 | margin: 1em 0; 24 | } 25 | } 26 | .pool-grid { 27 | padding: 0; 28 | display: flex; 29 | flex-wrap: wrap; 30 | @media screen and (max-width: 1000px) { 31 | justify-content: center; 32 | } 33 | .token-wrapper { 34 | text-align: center; 35 | min-width: 200px; 36 | max-width: 220px; 37 | position: relative; 38 | height: 230px; 39 | .title { 40 | position: relative; 41 | z-index: 1; 42 | font-size: 15px; 43 | font-weight: 500; 44 | margin: 15px 0 20px; 45 | div { 46 | font-size: 12px; 47 | font-weight: normal; 48 | } 49 | .listed { 50 | display: inline-block; 51 | margin-left: 4px; 52 | margin-bottom: -2px; 53 | height: 16px; 54 | width: 16px; 55 | background-image: url('./check-circle.svg'); 56 | background-size: cover; 57 | } 58 | } 59 | .token-logo-wrapper { 60 | margin: 0 auto; 61 | img { 62 | width: 100px; 63 | height: auto; 64 | max-height: 100px; 65 | } 66 | } 67 | .meta { 68 | opacity: 0; 69 | position: absolute; 70 | top: 0; 71 | bottom: 0; 72 | left: 0; 73 | right: 0; 74 | background-color: rgba(#E5E5E5, .9); 75 | transition: opacity .2s; 76 | padding: 50px 10px 25px; 77 | display: flex; 78 | align-items: center; 79 | .description { 80 | font-size: 12px; 81 | font-weight: 300; 82 | width: 100%; 83 | max-height: 150px; 84 | overflow: auto; 85 | } 86 | a { 87 | position: absolute; 88 | bottom: 10px; 89 | left: 0; 90 | right: 0; 91 | font-size: 12px; 92 | color: inherit; 93 | text-decoration: none; 94 | } 95 | } 96 | &:hover { 97 | .meta { 98 | opacity: 1; 99 | } 100 | } 101 | } 102 | } 103 | .conditions-wrapper { 104 | padding: 30px 0px; 105 | line-height: 1.5em; 106 | font-weight: 300; 107 | @media screen and (max-width: 1100px) { 108 | padding: 30px; 109 | } 110 | h3 { 111 | margin: 1em 0; 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/components/TokenPool/check-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/components/Traderboard/arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/components/TradingReward/TradingReward.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import eth from '../../services/ethereumService'; 3 | 4 | import './TradingReward.scss'; 5 | 6 | class TradingReward extends Component { 7 | constructor() { 8 | super(); 9 | 10 | this.state = { 11 | input: 1000, 12 | calculated: 0, 13 | }; 14 | 15 | this.calculate = this.calculate.bind(this); 16 | } 17 | 18 | async componentWillMount() { 19 | const calculated = await eth.calculateNecReward(1000); 20 | const rounded = Math.floor(calculated * 1000) / 1000; 21 | this.setState({ 22 | calculated: rounded, 23 | }); 24 | } 25 | 26 | async calculate(e) { 27 | if (e.target.value.length > 12) return; 28 | let input = parseInt(e.target.value, 10); 29 | if (isNaN(input)) input = 0; 30 | const calculated = await eth.calculateNecReward(input); 31 | const rounded = Math.floor(calculated * 1000) / 1000; 32 | this.setState({ 33 | input, 34 | calculated: rounded, 35 | }); 36 | } 37 | 38 | render() { 39 | return ( 40 |
41 |

Earn by trading on Ethfinex

42 |
43 |
44 |

45 | You can earn new NEC tokens in proportion to your maker volume (matched limit orders). 46 | Tokens become harder to earn each month. Estimate how many you would earn 47 | based on your trading volume. 48 |

49 |
50 |

51 | 65 |

66 |
67 | ); 68 | } 69 | } 70 | 71 | export default TradingReward; 72 | -------------------------------------------------------------------------------- /src/components/TradingReward/TradingReward.scss: -------------------------------------------------------------------------------- 1 | @import "../../common/variables"; 2 | 3 | .trading-reward-wrapper { 4 | padding: 100px 0; 5 | 6 | h2 { 7 | font-size: 48px; 8 | margin: 0; 9 | @media screen and (max-width: 1365px) { 10 | font-size: 40px; 11 | } 12 | @media screen and (max-width: 767px) { 13 | font-size: 32px; 14 | } 15 | } 16 | 17 | .calculator-desc { 18 | display: flex; 19 | @media screen and (min-width: 600px) { 20 | margin-top: 40px; 21 | padding-left: 50px; 22 | } 23 | 24 | 25 | .line-indent { 26 | height: 1px; 27 | background: #fff; 28 | width: 36px; 29 | position: relative; 30 | top: 10px; 31 | @media screen and (max-width: 767px) { 32 | display: none; 33 | } 34 | } 35 | 36 | p { 37 | font-weight: normal; 38 | line-height: 23px; 39 | font-size: 14px; 40 | letter-spacing: 0.02em; 41 | width: 350px; 42 | @media screen and (max-width: 767px) { 43 | line-height: 19px; 44 | font-size: 12px; 45 | } 46 | @media screen and (min-width: 600px) { 47 | margin: 0 50px 25px 50px; 48 | } 49 | } 50 | } 51 | h3 { 52 | font-weight: bold; 53 | font-size: 30px; 54 | color: $color4; 55 | width: 610px; 56 | line-height: 20px; 57 | margin: 100px 0; 58 | float: right; 59 | @media screen and (max-width: 767px) { 60 | float: none; 61 | width: auto; 62 | line-height: 45px; 63 | input { 64 | margin: 0 !important; 65 | } 66 | div { 67 | text-align: left; 68 | } 69 | } 70 | input { 71 | margin: 10px 10px 10px 0; 72 | padding: 0 3px; 73 | width: 80px; 74 | background: transparent; 75 | border: 0; 76 | font-weight: bold; 77 | font-size: 30px; 78 | color: white; 79 | text-align: center; 80 | outline: none; 81 | border-bottom: 2px solid transparent; 82 | &:focus { 83 | border-color: $color4; 84 | } 85 | } 86 | b { 87 | color: white; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/components/Voting/Current/Current.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import { connect } from 'react-redux'; 4 | import PropTypes from 'prop-types'; 5 | import { getActiveProposals } from '../../../actions/proposalActions'; 6 | import './Current.scss'; 7 | 8 | class Current extends Component { 9 | componentDidMount() { 10 | this.props.getActiveProposals(); 11 | } 12 | 13 | render() { 14 | return ( 15 |
16 | { 17 | this.props.proposals.map(proposal => ( 18 |
19 |

{proposal.startTime.toLocaleString()}

20 |

21 | {proposal.title} 22 |

23 |

{proposal.description}

24 | VOTE 25 |
26 | )) 27 | } 28 | { 29 | this.props.proposals.length === 0 && 30 |

No active proposals

31 | } 32 |
33 | ); 34 | } 35 | } 36 | 37 | Current.propTypes = { 38 | getActiveProposals: PropTypes.func.isRequired, 39 | proposals: PropTypes.array.isRequired, 40 | }; 41 | 42 | const mapStateToProps = state => ({ 43 | proposals: state.proposal.activeProposals, 44 | }); 45 | 46 | export default connect(mapStateToProps, { 47 | getActiveProposals, 48 | })(Current); 49 | -------------------------------------------------------------------------------- /src/components/Voting/Current/Current.scss: -------------------------------------------------------------------------------- 1 | @import '../../../common/variables'; 2 | 3 | .current-proposals { 4 | max-width: 1200px; 5 | margin: auto; 6 | padding: 30px; 7 | padding-left: 0; 8 | display: flex; 9 | flex-wrap: wrap; 10 | .proposal-wrapper { 11 | margin: 30px 0; 12 | width: 50%; 13 | p { 14 | margin: 10px 0; 15 | line-height: 1.5em; 16 | font-size: 14px; 17 | letter-spacing: 0.02em; 18 | } 19 | .started { 20 | color: $color4; 21 | } 22 | .title { 23 | font-weight: bold; 24 | line-height: 32px; 25 | font-size: 24px; 26 | letter-spacing: 0.02em; 27 | } 28 | .description { 29 | white-space: pre-wrap; 30 | max-height: 105px; 31 | overflow: hidden; 32 | position: relative; 33 | &:after { 34 | content: ''; 35 | display: block; 36 | background-color: $color2; 37 | background: linear-gradient(to bottom, rgba($color2, 0), rgba($color2, 1)); 38 | width: 100%; 39 | height: 40px; 40 | position: absolute; 41 | bottom: 0; 42 | left: 0; 43 | pointer-events: none; 44 | } 45 | } 46 | a { 47 | color: white; 48 | text-decoration: none; 49 | font-weight: bold; 50 | font-size: 24px; 51 | letter-spacing: 0.02em; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/components/Voting/Past/Past.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { connect } from 'react-redux'; 4 | import { Link } from 'react-router-dom'; 5 | import { getProposals } from '../../../actions/proposalActions'; 6 | import './Past.scss'; 7 | 8 | class Past extends Component { 9 | componentDidMount() { 10 | this.props.getProposals(); 11 | } 12 | 13 | render() { 14 | return ( 15 |
16 | { 17 | this.props.proposals.filter(p => !p._active).map(proposal => ( 18 |
19 |

{proposal.startTime.toLocaleString()}

20 |

21 | {proposal.title} 22 |

23 |

{proposal.description}

24 | Details 25 |
26 | )) 27 | } 28 | { 29 | this.props.proposals.filter(p => !p._active).length === 0 && 30 |

No past proposals

31 | } 32 |
33 | ); 34 | } 35 | } 36 | 37 | Past.propTypes = { 38 | getProposals: PropTypes.func.isRequired, 39 | proposals: PropTypes.array.isRequired, 40 | }; 41 | 42 | const mapStateToProps = state => ({ 43 | proposals: state.proposal.proposals, 44 | }); 45 | 46 | export default connect(mapStateToProps, { 47 | getProposals, 48 | })(Past); 49 | -------------------------------------------------------------------------------- /src/components/Voting/Past/Past.scss: -------------------------------------------------------------------------------- 1 | @import '../../../common/variables'; 2 | 3 | .past-proposals { 4 | max-width: 1200px; 5 | margin: auto; 6 | padding: 30px; 7 | padding-left: 0; 8 | display: flex; 9 | flex-wrap: wrap; 10 | .proposal-wrapper { 11 | margin: 30px 0; 12 | width: 50%; 13 | p { 14 | margin: 10px 0; 15 | line-height: 1.5em; 16 | font-size: 14px; 17 | letter-spacing: 0.02em; 18 | } 19 | .started { 20 | color: $color4; 21 | } 22 | .title { 23 | font-weight: bold; 24 | line-height: 38px; 25 | font-size: 24px; 26 | letter-spacing: 0.02em; 27 | } 28 | .description { 29 | white-space: pre-wrap; 30 | max-height: 105px; 31 | overflow: hidden; 32 | position: relative; 33 | &:after { 34 | content: ''; 35 | display: block; 36 | background: linear-gradient(to bottom, rgba($color2, 0), rgba($color2, 1)); 37 | width: 100%; 38 | height: 40px; 39 | position: absolute; 40 | bottom: 0; 41 | left: 0; 42 | pointer-events: none; 43 | } 44 | } 45 | a { 46 | color: white; 47 | text-decoration: none; 48 | font-weight: bold; 49 | line-height: 40px; 50 | font-size: 24px; 51 | letter-spacing: 0.02em; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/components/Voting/Voting.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | import Current from './Current/Current'; 5 | import Past from './Past/Past'; 6 | 7 | import './Voting.scss'; 8 | 9 | class Voting extends Component { 10 | constructor() { 11 | super(); 12 | 13 | this.state = { 14 | shown: 'current', 15 | }; 16 | 17 | this.switch = this.switch.bind(this); 18 | } 19 | 20 | switch(slug) { 21 | this.setState({ 22 | shown: slug, 23 | }); 24 | } 25 | 26 | render() { 27 | return ( 28 |
29 |
voting
30 | 31 |
32 |

Voting

33 | 34 |
35 |
36 |
37 |
38 | As a holder of Nectar tokens you can submit new ideas as proposals, as well as 39 | vote on those submitted by others, to help determine the future of Ethfinex 40 |
41 |
42 |
43 | 44 | 64 |
65 |
66 | { 67 | this.state.shown === 'current' && 68 | 69 | } 70 | { 71 | this.state.shown === 'past' && 72 | 73 | } 74 | { 75 | this.state.shown === 'submit' && 76 |
77 |

78 | Every individual who holds NEC is able to contribute their ideas for Ethfinex to 79 | be voted on by the rest of the token holders. If the proposal you create is 80 | accepted it will initialise a vote, giving everyone a new voting token to match 81 | their NEC balances. 82 |

83 | Go to submit page 84 |
85 | } 86 |
87 |
88 |
89 | ); 90 | } 91 | } 92 | 93 | export default Voting; 94 | -------------------------------------------------------------------------------- /src/components/Voting/Voting.scss: -------------------------------------------------------------------------------- 1 | @import "../../common/variables"; 2 | 3 | .voting-wrapper { 4 | position: relative; 5 | 6 | .voting-bg-text { 7 | top: 70px; 8 | right: 500px; 9 | } 10 | 11 | .voting-content-wrapper { 12 | position: relative; 13 | z-index: 2; 14 | 15 | h2 { 16 | font-size: 48px; 17 | margin: 0; 18 | @media screen and (max-width: 1365px) { 19 | font-size: 40px; 20 | } 21 | @media screen and (max-width: 767px) { 22 | font-size: 32px; 23 | } 24 | } 25 | 26 | .meta { 27 | @media screen and (min-width: 600px) { 28 | margin-left: 50px; 29 | } 30 | 31 | .voting-desc { 32 | margin: 32px 0 35px 0; 33 | 34 | h3 { 35 | margin: 0 0 32px 0; 36 | font-size: 34px; 37 | } 38 | 39 | .desc-sections { 40 | display: flex; 41 | justify-content: space-between; 42 | align-items: center; 43 | 44 | .desc-section { 45 | font-size: 14px; 46 | line-height: 23px; 47 | max-width: 380px; 48 | @media screen and (max-width: 767px) { 49 | line-height: 19px; 50 | font-size: 12px; 51 | } 52 | } 53 | } 54 | } 55 | 56 | p { 57 | font-weight: normal; 58 | margin: 25px 50px; 59 | line-height: 23px; 60 | font-size: 14px; 61 | letter-spacing: 0.02em; 62 | width: 350px; 63 | } 64 | .nav { 65 | max-width: 900px; 66 | @media screen and (min-width: 768px) { 67 | border-bottom: 1px solid $color4; 68 | } 69 | a { 70 | display: inline-block; 71 | line-height: 60px; 72 | font-size: 25px; 73 | letter-spacing: 0.02em; 74 | color: $color4; 75 | cursor: pointer; 76 | transition: .2s color; 77 | border-bottom: 6px solid transparent; 78 | margin: 0 40px -3px 0; 79 | @media screen and (max-width: 767px) { 80 | display: block; 81 | } 82 | &:hover { 83 | color: white; 84 | } 85 | &.active { 86 | font-weight: bold; 87 | color: white; 88 | border-color: white; 89 | &:before { 90 | background-color: white; 91 | } 92 | } 93 | } 94 | } 95 | } 96 | .content { 97 | @media screen and (min-width: 600px) { 98 | margin-left: 100px; 99 | } 100 | //max-height: calc(100vh - 200px); 101 | //overflow: auto; 102 | .submit-wrapper { 103 | padding: 30px 0; 104 | h3 { 105 | font-size: 24px; 106 | letter-spacing: 0.02em; 107 | } 108 | p { 109 | line-height: 1.5em; 110 | letter-spacing: 0.02em; 111 | } 112 | a { 113 | font-weight: bold; 114 | color: white; 115 | font-size: 48px; 116 | text-decoration: none; 117 | margin: 20px 0; 118 | display: block; 119 | } 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/components/WhitePaper/NEC 2.0 WP V1.4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/components/WhitePaper/NEC 2.0 WP V1.4.pdf -------------------------------------------------------------------------------- /src/components/WhitePaper/WhitePaper.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import pdf from './NEC 2.0 WP V1.4.pdf'; 3 | import './WhitePaper.scss'; 4 | 5 | export default ({ location }) => ( 6 |
7 |
8 |
9 | Whitepaper 10 |
11 |
12 | This whitepaper was last updated in September 2019. The Nectar ecosystem and NEC's tokenomics 13 | are highly dynamic and subject to adjustments and so the ideas and proposals made 14 | in this whitepaper may no longer accurately represent NEC. 15 |
16 |
17 | 18 |
19 |
20 |
21 | ); 22 | -------------------------------------------------------------------------------- /src/components/WhitePaper/WhitePaper.scss: -------------------------------------------------------------------------------- 1 | .whitepaper { 2 | padding: 52px 0 0 0; 3 | 4 | .whitepaper__document { 5 | display: flex; 6 | justify-content: center; 7 | } 8 | 9 | @media all and (max-width: 868px) { 10 | embed { 11 | width: auto !important; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/constants/config.dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": 42, 3 | "providerUrl": "https://kovan.infura.io/v3/d8c4c4f651ed4d3d9b3dcde50a37f2e8", 4 | "backendUrl": "http://139.59.146.81:3000", 5 | "defaultGasPrice": 20, 6 | "hiddenProposals": [1, 2, 3, 4], 7 | "chartDuration": 7, 8 | "tokenListingManagerAdvanced": "0xf9795c887299414a8b1da2cad325a8a9992335dc", 9 | "proposalContract": "0xc6619f0bb2d70088236ca23554ed4f722242cb91", 10 | "simpleVoteContract": "0x0000000000000000000000000000000000000000", 11 | "necEngineContract": "0x2BDBeeB631c8Bc4b18208486667aE663F81f6014", 12 | "necTokenContract": "0x68CEE0BBFe45E5aC0d6C34F19e57204094d4658a", 13 | "necTokenControllerContract": "0xc330aa14183a1962a187135d085e88b8b6e1d680", 14 | "klerosT2CR": "0x25dd2659a1430cdbd678615c7409164ae486c146", 15 | "klerosBadge": "0xd58bdd286e8155b6223e2a62932ae3e0a9a75759" 16 | } 17 | -------------------------------------------------------------------------------- /src/constants/config.dist.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": 1, 3 | "providerUrl": "https://mainnet.infura.io/v3/d8c4c4f651ed4d3d9b3dcde50a37f2e8", 4 | "backendUrl": "http://139.59.146.81:3000", 5 | "defaultGasPrice": 20, 6 | "hiddenProposals": [1, 2, 3, 4], 7 | "chartDuration": 30, 8 | "tokenListingManager": "0x29c317dCcC79ef77eF04BB7e9A852926050Bc92D", 9 | "proposalContract": "0xb24ed9d62d4c660faf56a4cddada06c88b2d5ddb", 10 | "simpleVoteContract": "0x7511576ab7adbcb466366C8cd3529FE2E4833c6E", 11 | "necEngineContract": "0x2bD9BaEDf2C6fc4b86C725857Ae156CB43fCcba6", 12 | "necTokenContract": "0xcc80c051057b774cd75067dc48f8987c4eb97a5e", 13 | "necTokenControllerContract": "0x0e55c54249f25f70d519b7fb1c20e3331e7ba76d", 14 | "klerosT2CR": "0xebcf3bca271b26ae4b162ba560e243055af0e679", 15 | "klerosBadge": "0x916deab80dfbc7030277047cd18b233b3ce5b4ab" 16 | } 17 | -------------------------------------------------------------------------------- /src/constants/images/Ethfinex_voEDIT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/constants/images/Ethfinex_voEDIT.png -------------------------------------------------------------------------------- /src/constants/images/delegate-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/constants/images/delegators-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/constants/images/dolphin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/constants/images/down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/constants/images/landing-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/constants/images/landing-bg.png -------------------------------------------------------------------------------- /src/constants/images/landingBackgrounds/buy-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/constants/images/landingBackgrounds/buy-bg.png -------------------------------------------------------------------------------- /src/constants/images/landingBackgrounds/buy-bg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/constants/images/landingBackgrounds/buy-bg@2x.png -------------------------------------------------------------------------------- /src/constants/images/landingBackgrounds/dao-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/constants/images/landingBackgrounds/dao-bg.png -------------------------------------------------------------------------------- /src/constants/images/landingBackgrounds/dao-bg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/constants/images/landingBackgrounds/dao-bg@2x.png -------------------------------------------------------------------------------- /src/constants/images/landingBackgrounds/exchanges-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/constants/images/landingBackgrounds/exchanges-bg.png -------------------------------------------------------------------------------- /src/constants/images/landingBackgrounds/exchanges-bg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/constants/images/landingBackgrounds/exchanges-bg@2x.png -------------------------------------------------------------------------------- /src/constants/images/landingBackgrounds/fee-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/constants/images/landingBackgrounds/fee-bg.png -------------------------------------------------------------------------------- /src/constants/images/landingBackgrounds/fee-bg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/constants/images/landingBackgrounds/fee-bg@2x.png -------------------------------------------------------------------------------- /src/constants/images/landingBackgrounds/nec-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/constants/images/landingBackgrounds/nec-bg.png -------------------------------------------------------------------------------- /src/constants/images/landingBackgrounds/nec-bg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/constants/images/landingBackgrounds/nec-bg@2x.png -------------------------------------------------------------------------------- /src/constants/images/landingIcons/DeversiFi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/constants/images/landingIcons/DeversiFi.png -------------------------------------------------------------------------------- /src/constants/images/landingIcons/arrow-bottom.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/constants/images/landingIcons/badge.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/constants/images/landingIcons/balancer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/constants/images/landingIcons/balancer.png -------------------------------------------------------------------------------- /src/constants/images/landingIcons/bitfinex.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/constants/images/landingIcons/buy-active.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/constants/images/landingIcons/buy-mono.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/constants/images/landingIcons/buy.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/constants/images/landingIcons/dao-active.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/constants/images/landingIcons/dao.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/constants/images/landingIcons/deversifi-logo-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/constants/images/landingIcons/deversifi-logo-dark.png -------------------------------------------------------------------------------- /src/constants/images/landingIcons/dexag.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/constants/images/landingIcons/discord.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default () => ( 4 | 5 | 11 | 12 | 20 | 21 | 22 | 23 | 24 | ) 25 | -------------------------------------------------------------------------------- /src/constants/images/landingIcons/exchanges-active.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/constants/images/landingIcons/exchanges.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/constants/images/landingIcons/fee-active.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/constants/images/landingIcons/fee.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/constants/images/landingIcons/icon-arrows.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/constants/images/landingIcons/neconomics-active.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/constants/images/landingIcons/neconomics.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/constants/images/landingIcons/twitter.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default () => ( 4 | 5 | 9 | 10 | 18 | 19 | 20 | 21 | 22 | 23 | ) 24 | -------------------------------------------------------------------------------- /src/constants/images/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/constants/images/minnow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/constants/images/nectar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/constants/images/nectar.png -------------------------------------------------------------------------------- /src/constants/images/new-logo-v.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/constants/images/new-logo-wh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/constants/images/shark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/constants/images/up.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/constants/images/wave1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/constants/images/wave1.png -------------------------------------------------------------------------------- /src/constants/images/wave2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/constants/images/wave2.png -------------------------------------------------------------------------------- /src/constants/images/whale.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/constants/images/x.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/constants/videos/Ethfinex_voEDIT.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nec-community/community-gateway/3f49903bd944cf603c1a168bfbc908e1b33a9461/src/constants/videos/Ethfinex_voEDIT.mp4 -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Nectar Community 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /src/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import { Provider } from 'react-redux'; 4 | import ReactGA from 'react-ga'; 5 | import { BrowserRouter } from 'react-router-dom'; 6 | import App from './components/App/App'; 7 | import store from './store'; 8 | import "react-loader-spinner/dist/loader/css/react-spinner-loader.css"; 9 | 10 | ReactGA.initialize('UA-156671651-3'); 11 | 12 | render( 13 | 14 | 15 | 16 | 17 | , 18 | document.getElementById('root') 19 | ); 20 | -------------------------------------------------------------------------------- /src/reducers/accountReducer.js: -------------------------------------------------------------------------------- 1 | import { 2 | GET_ACCOUNT_SUCCESS, 3 | GET_ACCOUNT_ERROR, 4 | TOKEN_BALANCE, 5 | VOTING_TOKEN_BALANCE, 6 | VOTES_SPENT_BALANCE, 7 | UPDATE_ETHFINEX_DATA, 8 | OPEN_LOGIN, 9 | CLOSE_LOGIN, 10 | OPEN_HELP, 11 | CLOSE_HELP, 12 | } from '../actions/actionTypes'; 13 | 14 | const INITIAL_STATE = { 15 | account: '', 16 | accountType: '', 17 | accountBalance: '', 18 | accountError: '', 19 | isAdmin: false, 20 | tokenBalance: '0', 21 | votingTokenBalance: '0', 22 | tokenPayout: '0', 23 | ethfinexData: { 24 | ethPrice: 0, 25 | necPrice: 0, 26 | necConversionRate: 0, 27 | totalFee: '0', 28 | totalTokens: '0', 29 | burningEnabled: false, 30 | }, 31 | loginOpen: false, 32 | helpOpen: false, 33 | }; 34 | 35 | export default (state = INITIAL_STATE, action) => { 36 | switch (action.type) { 37 | case GET_ACCOUNT_SUCCESS: 38 | return { 39 | ...state, 40 | account: action.account, 41 | accountType: action.accountType, 42 | accountBalance: action.balance, 43 | tokenBalance: action.necBalance, 44 | votingTokenBalance: action.votingTokenBalance, 45 | votesSpentBalance: action.votesSpentBalance, 46 | accountError: '', 47 | isAdmin: action.isAdmin, 48 | loginOpen: false, 49 | }; 50 | case GET_ACCOUNT_ERROR: 51 | return { 52 | ...state, 53 | account: '', 54 | accountType: '', 55 | accountBalance: '', 56 | tokenBalance: '', 57 | isAdmin: false, 58 | accountError: action.error, 59 | }; 60 | case TOKEN_BALANCE: 61 | return { 62 | ...state, 63 | tokenBalance: action.balance, 64 | tokenPayout: action.payout, 65 | }; 66 | case VOTING_TOKEN_BALANCE: 67 | return { 68 | ...state, 69 | votingTokenBalance: action.balance, 70 | }; 71 | case VOTES_SPENT_BALANCE: 72 | return { 73 | ...state, 74 | votesSpentBalance: action.balance, 75 | }; 76 | case UPDATE_ETHFINEX_DATA: 77 | return { 78 | ...state, 79 | ethfinexData: action.data, 80 | }; 81 | case OPEN_LOGIN: 82 | return { 83 | ...state, 84 | loginOpen: true, 85 | }; 86 | case CLOSE_LOGIN: 87 | return { 88 | ...state, 89 | loginOpen: false, 90 | }; 91 | case OPEN_HELP: 92 | return { 93 | ...state, 94 | helpOpen: true, 95 | }; 96 | case CLOSE_HELP: 97 | return { 98 | ...state, 99 | helpOpen: false, 100 | }; 101 | default: 102 | return state; 103 | } 104 | }; 105 | -------------------------------------------------------------------------------- /src/reducers/auctionReducer.js: -------------------------------------------------------------------------------- 1 | import { 2 | FETCH_CIRCULATING_NEC_DATA, 3 | FETCH_BURNED_NEC, 4 | FETCH_DEVERSIFI_NEC_ETH_DATA, 5 | FETCH_NEXT_AUCTION_ETH_DATA, 6 | FETCH_CURRENT_AUCTION_SUMMARY, 7 | FETCH_AUCTION_INTERVAL_DATA, 8 | SELL_IN_AUCTION_START, 9 | FETCH_AUCTION_TRANSACTIONS, 10 | FETCH_ETH_PRICE, 11 | FETCH_NEC_PRICE, 12 | FETCH_NEXT_AUCTION_DATE, 13 | FETCH_DEVERSIFI_NEC_USD_DATA 14 | } from '../actions/actionTypes'; 15 | 16 | const INITIAL_STATE = { 17 | circulatingNecData: [], 18 | burnedNecData: [], 19 | deversifiNecEthPriceData: [], 20 | nextAuctionEthData: [], 21 | deversifiNecUsdData: [], 22 | deversifiNecEthData: [], 23 | necPrice: '', 24 | ethPrice: '', 25 | nextPriceChange: '', 26 | nextAuctionDate: '', 27 | }; 28 | 29 | export default (state = INITIAL_STATE, action) => { 30 | switch (action.type) { 31 | case FETCH_CIRCULATING_NEC_DATA: 32 | return { 33 | ...state, 34 | circulatingNecData: action.circulatingNecData, 35 | }; 36 | case FETCH_BURNED_NEC: 37 | return { 38 | ...state, 39 | burnedNecData: action.burnedNecData 40 | }; 41 | case FETCH_DEVERSIFI_NEC_ETH_DATA: 42 | return { 43 | ...state, 44 | deversifiNecEthData: action.deversifiNecEthData, 45 | }; 46 | case FETCH_DEVERSIFI_NEC_USD_DATA: 47 | return { 48 | ...state, 49 | deversifiNecUsdData: action.deversifiNecUsdData 50 | } 51 | case FETCH_NEXT_AUCTION_ETH_DATA: 52 | return { 53 | ...state, 54 | nextAuctionEthData: action.nextAuctionEthData, 55 | }; 56 | case FETCH_NEXT_AUCTION_DATE: 57 | return { 58 | ...state, 59 | nextAuctionDate: action.nextAuctionDate 60 | }; 61 | case FETCH_CURRENT_AUCTION_SUMMARY: 62 | return { 63 | ...state, 64 | currentAuctionSummary: action.currentAuctionSummary, 65 | nextPriceChange: action.nextPriceChange, 66 | startTimeSeconds: action.startTimeSeconds, 67 | priceChangeLengthSeconds: action.priceChangeLengthSeconds, 68 | }; 69 | case FETCH_AUCTION_INTERVAL_DATA: 70 | return { 71 | ...state, 72 | auctionIntervalData: action.auctionIntervalData, 73 | }; 74 | case SELL_IN_AUCTION_START: 75 | return { 76 | ...state, 77 | sellInAuctionData: action.sellInAuctionData, 78 | }; 79 | case FETCH_AUCTION_TRANSACTIONS: 80 | return { 81 | ...state, 82 | auctionTransactions: action.auctionTransactions, 83 | }; 84 | case FETCH_NEC_PRICE: 85 | return { 86 | ...state, 87 | necPrice: action.necPrice 88 | } 89 | case FETCH_ETH_PRICE: 90 | return { 91 | ...state, 92 | ethPrice: action.ethPrice 93 | } 94 | default: 95 | return state; 96 | } 97 | }; 98 | -------------------------------------------------------------------------------- /src/reducers/competitionReducer.js: -------------------------------------------------------------------------------- 1 | import { FETCH_COMPETITIONS_BY_TAG } from '../actions/actionTypes'; 2 | 3 | const initialState = { 4 | posts: [], 5 | }; 6 | 7 | export default (state = initialState, action) => { 8 | switch (action.type) { 9 | case FETCH_COMPETITIONS_BY_TAG: 10 | return { 11 | ...state, 12 | posts: action.posts, 13 | }; 14 | default: 15 | return state; 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /src/reducers/delegateReducer.js: -------------------------------------------------------------------------------- 1 | import { FETCHED_DELEGATES, MY_DELEGATE } from '../actions/actionTypes'; 2 | 3 | const INITIAL_STATE = { 4 | delegates: [], 5 | myDelegate: '0x0000000000000000000000000000000000000000', 6 | userHasVoted: false, 7 | }; 8 | 9 | export default (state = INITIAL_STATE, action) => { 10 | switch (action.type) { 11 | case FETCHED_DELEGATES: 12 | return { 13 | ...state, 14 | delegates: action.delegates, 15 | }; 16 | case MY_DELEGATE: 17 | return { 18 | ...state, 19 | myDelegate: action.myDelegate, 20 | userHasVoted: action.userHasVoted, 21 | }; 22 | default: 23 | return state; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { routerReducer } from 'react-router-redux'; 2 | import { combineReducers } from 'redux'; 3 | import account from './accountReducer'; 4 | import notification from './notificationReducer'; 5 | import proposal from './proposalReducer'; 6 | import token from './tokenReducer'; 7 | import delegate from './delegateReducer'; 8 | import traders from './traderReducer'; 9 | import competitions from './competitionReducer'; 10 | import auction from './auctionReducer'; 11 | 12 | export default combineReducers({ 13 | routing: routerReducer, 14 | account, 15 | notification, 16 | proposal, 17 | token, 18 | delegate, 19 | traders, 20 | competitions, 21 | auction, 22 | }); 23 | -------------------------------------------------------------------------------- /src/reducers/notificationReducer.js: -------------------------------------------------------------------------------- 1 | import { SHOW_NOTIF, HIDE_NOTIF } from '../actions/actionTypes'; 2 | 3 | const INITIAL_STATE = { 4 | displayed: false, 5 | type: 'neutral', 6 | message: 'Initial message', 7 | }; 8 | 9 | export default (state = INITIAL_STATE, action) => { 10 | switch (action.type) { 11 | case SHOW_NOTIF: 12 | return { 13 | ...state, 14 | displayed: true, 15 | message: action.message, 16 | type: action.notifType, 17 | }; 18 | 19 | case HIDE_NOTIF: 20 | return { 21 | ...state, 22 | displayed: false, 23 | }; 24 | 25 | default: 26 | return state; 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /src/reducers/proposalReducer.js: -------------------------------------------------------------------------------- 1 | import { 2 | FETCHED_PROPOSALS, 3 | FETCHED_ACTIVE_PROPOSALS, 4 | FETCHED_NONAPPROVED_PROPOSALS, 5 | } from '../actions/actionTypes'; 6 | 7 | const INITIAL_STATE = { 8 | proposals: [], 9 | activeProposals: [], 10 | nonApprovedProposals: [], 11 | }; 12 | 13 | export default (state = INITIAL_STATE, action) => { 14 | switch (action.type) { 15 | case FETCHED_PROPOSALS: 16 | return { 17 | ...state, 18 | proposals: action.proposals, 19 | }; 20 | case FETCHED_ACTIVE_PROPOSALS: 21 | return { 22 | ...state, 23 | activeProposals: action.proposals, 24 | }; 25 | case FETCHED_NONAPPROVED_PROPOSALS: 26 | return { 27 | ...state, 28 | nonApprovedProposals: action.proposals, 29 | }; 30 | default: 31 | return state; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /src/reducers/tokenReducer.js: -------------------------------------------------------------------------------- 1 | import { FETCHED_TOKENS, FETCHED_POOL_TOKENS } from '../actions/actionTypes'; 2 | 3 | const INITIAL_STATE = { 4 | tokens: [], 5 | endingTime: new Date(), 6 | isActive: false, 7 | pool: [], 8 | }; 9 | 10 | export default (state = INITIAL_STATE, action) => { 11 | switch (action.type) { 12 | case FETCHED_TOKENS: 13 | return { 14 | ...state, 15 | tokens: action.tokens, 16 | endingTime: action.endingTime, 17 | isActive: action.isActive, 18 | evtAddress: action.evtAddress, 19 | }; 20 | case FETCHED_POOL_TOKENS: 21 | return { 22 | ...state, 23 | pool: action.poolTokens, 24 | }; 25 | default: 26 | return state; 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /src/reducers/traderReducer.js: -------------------------------------------------------------------------------- 1 | import { 2 | FETCH_TRADERS_BY_TOKEN, 3 | FETCH_TRADERS_START, 4 | FETCH_TRADERS_ERROR, 5 | } from '../actions/actionTypes'; 6 | 7 | const INITIAL_STATE = { 8 | traders: [], 9 | dates: [], 10 | token: '', 11 | volume: [], 12 | isFetching: false, 13 | error: {}, 14 | }; 15 | 16 | export default (state = INITIAL_STATE, action) => { 17 | switch (action.type) { 18 | case FETCH_TRADERS_START: 19 | return { 20 | ...state, 21 | isFetching: true, 22 | }; 23 | case FETCH_TRADERS_BY_TOKEN: 24 | return { 25 | ...state, 26 | traders: action.traders, 27 | dates: action.dates, 28 | token: action.token, 29 | volume: [], 30 | isFetching: false, 31 | }; 32 | case FETCH_TRADERS_ERROR: 33 | return { 34 | ...state, 35 | isFetching: false, 36 | error: action.err, 37 | }; 38 | default: 39 | return state; 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /src/routes.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Route, Switch } from 'react-router-dom'; 3 | import Landing from './components/Landing/Landing'; 4 | import DAO from './components/DAO/DAO'; 5 | 6 | const Routes = () => ( 7 | 8 | 9 | 10 | 11 | 12 | ); 13 | 14 | export default Routes; 15 | -------------------------------------------------------------------------------- /src/services/grenacheService.js: -------------------------------------------------------------------------------- 1 | import config from '../constants/config.json'; 2 | 3 | const serviceUrl = config.backendUrl; 4 | 5 | const get = async hash => { 6 | const res = await fetch(`${serviceUrl}/get`, { 7 | method: 'post', 8 | headers: { 9 | 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8', 10 | }, 11 | body: `hash=${encodeURIComponent(hash)}`, 12 | }); 13 | return res.text(); 14 | }; 15 | 16 | const put = async (data, email) => { 17 | const res = await fetch(`${serviceUrl}/put`, { 18 | method: 'post', 19 | headers: { 20 | 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8', 21 | }, 22 | body: `data=${encodeURIComponent(data)}&email=${encodeURIComponent(email)}`, 23 | }); 24 | return res.text(); 25 | }; 26 | 27 | export default { 28 | get, 29 | put, 30 | }; 31 | -------------------------------------------------------------------------------- /src/services/klerosService.js: -------------------------------------------------------------------------------- 1 | import config from '../constants/config.json'; 2 | import abis from '../constants/abis.json'; 3 | import tokenData from '../actions/tokenData'; 4 | import listedTokens from './listedTokens'; 5 | 6 | const filter = [ 7 | false, // Do not include tokens which are not on the TCR. 8 | true, // Include registered tokens. 9 | false, // Do not include tokens with pending registration requests. 10 | true, // Include tokens with pending clearing requests. 11 | false, // Do not include tokens with challenged registration requests. 12 | true, // Include tokens with challenged clearing requests. 13 | false, // Include token if caller is the author of a pending request. 14 | false, // Include token if caller is the challenger of a pending request. 15 | ]; 16 | 17 | const zeroAddress = '0x0000000000000000000000000000000000000000'; 18 | const zeroSubmissionID = '0x0000000000000000000000000000000000000000000000000000000000000000'; 19 | 20 | export const getTokenInfo = async address => { 21 | try { 22 | const t2crContract = new window._web3.eth.Contract(abis.klerosT2CR, config.klerosT2CR); 23 | 24 | const submissionID = await t2crContract.methods 25 | .queryTokens( 26 | zeroSubmissionID, // A token ID from which to start/end the query from. Set to zero means unused. 27 | 100, // Number of items to return at once. 28 | filter, 29 | true, // Return oldest first. 30 | address // The token address for which to return the submissions. 31 | ) 32 | .call() 33 | .then(res => res.values.find(ID => ID !== zeroSubmissionID)); 34 | 35 | const data = await t2crContract.methods.getTokenInfo(submissionID).call(); 36 | const extraData = tokenData[address.toLowerCase()]; 37 | 38 | return { 39 | token: data.name, 40 | shortName: data.name, 41 | symbol: data.ticker, 42 | address: data.addr, 43 | logo: `https://ipfs.kleros.io${data.symbolMultihash}`, 44 | description: extraData && extraData.description, 45 | website: extraData && extraData.website, 46 | listed: listedTokens.indexOf(address) !== -1, 47 | }; 48 | } catch (e) { 49 | console.error(e); 50 | return {}; 51 | } 52 | }; 53 | 54 | export const getApprovedTokens = async () => { 55 | const badgeContract = new window._web3.eth.Contract(abis.klerosBadge, config.klerosBadge); 56 | 57 | // Fetch addresses of tokens that have the badge. 58 | // Since the contract returns fixed sized arrays, we must filter out unused items. 59 | const addressesWithBadge = (await badgeContract.methods 60 | .queryAddresses( 61 | zeroAddress, // A token address to start/end the query from. Set to zero means unused. 62 | 100, // Number of items to return at once. 63 | filter, 64 | true // Return oldest first. 65 | ) 66 | .call()).values 67 | .map(address => address.toLowerCase()) 68 | .filter(address => address !== zeroAddress); 69 | // .filter(address => listedTokens.indexOf(address) === -1); 70 | 71 | // With the token IDs, get the information and add it to the object. 72 | return Promise.all(addressesWithBadge.map(address => getTokenInfo(address))); 73 | }; 74 | -------------------------------------------------------------------------------- /src/services/listedTokens.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | '0x6810e776880c02933d47db1b9fc05908e5386b96', // Gnosis 3 | '0x24dcc881e7dd730546834452f21872d5cb4b5293', // Scroll 4 | '0x543ff227f64aa17ea132bf9886cab5db55dcaddf', // DAOstack 5 | ]; 6 | -------------------------------------------------------------------------------- /src/services/scrollAnimation.js: -------------------------------------------------------------------------------- 1 | const _scrollTo = (to, duration) => { 2 | if (duration <= 0) return; 3 | const currentPos = 4 | window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; 5 | const difference = to - currentPos; 6 | const perTick = (difference / duration) * 10; 7 | setTimeout(function() { 8 | window.scrollTo(0, currentPos + perTick); 9 | if (currentPos === to) return; 10 | _scrollTo(to, duration - 10); 11 | }, 10); 12 | }; 13 | 14 | export const scrollToSection = id => { 15 | const el = document.getElementById(id.substr(0, 1) === '#' ? id.substr(1) : id); 16 | if (!el) return console.error('No element found to scroll to:', id); 17 | const position = el.offsetTop; 18 | const duration = 150; 19 | _scrollTo(position, duration); 20 | }; 21 | 22 | export const scrollToTop = () => { 23 | window.scrollTo(0, 0); 24 | }; 25 | -------------------------------------------------------------------------------- /src/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux'; 2 | import thunk from 'redux-thunk'; 3 | import reducers from './reducers/index'; 4 | 5 | const reduxDevToolsEnchancer = 6 | window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(); 7 | const createStoreWithMiddleware = applyMiddleware(thunk)(createStore); 8 | const store = createStoreWithMiddleware(reducers, reduxDevToolsEnchancer); 9 | 10 | if (module.hot) { 11 | module.hot.accept('./reducers/', () => { 12 | // eslint-disable-line global-require 13 | const nextRootReducer = require('./reducers/index').default; 14 | store.replaceReducer(nextRootReducer); 15 | }); 16 | } 17 | 18 | export default store; 19 | -------------------------------------------------------------------------------- /static.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "dist/" 3 | } --------------------------------------------------------------------------------