├── src
├── contracts
│ ├── Exchange.blocknumber.js
│ ├── Exchange.address.js
│ ├── Bridge.abi.js
│ └── StableCoin.abi.js
├── planeta
│ ├── contracts
│ │ ├── CO2.address.js
│ │ └── Air.json
│ ├── PlantReceipt.js
│ ├── cooldown.js
│ ├── HandshakeButtons.js
│ ├── FinalizeHandshake.js
│ ├── MoreButtons.js
│ ├── GlobalCO2.js
│ ├── StartHandshake.js
│ ├── plasma-utils.js
│ ├── PlantTrees.js
│ ├── TransferPassport.js
│ └── transactionHandler.js
├── assets
│ ├── dai.png
│ ├── bity.png
│ ├── pdai.png
│ ├── pingu.gif
│ ├── qrcode.png
│ ├── rekt.gif
│ ├── wyre.jpg
│ ├── wyre.png
│ ├── xdai.jpg
│ ├── ethereum.png
│ ├── ka-ching.mp3
│ ├── new-tag.png
│ ├── papyrus.png
│ ├── sendwyre.png
│ ├── surprise.gif
│ ├── handshake.gif
│ ├── pollution.mp3
│ ├── burnerloader.gif
│ ├── burnerwallet.png
│ ├── customRPCHint.png
│ ├── planeta-logo.gif
│ ├── sad-trombone.mp3
│ ├── unreal-tournament-humiliation-sound.mp3
│ ├── unreal-tournament-monster-kill-sound.mp3
│ └── flame.svg
├── components
│ ├── Ruler.js
│ ├── InputInfo.js
│ ├── Footer.js
│ ├── StyledCard.js
│ ├── Bottom.js
│ ├── Receipt.js
│ ├── Share.js
│ ├── MainCard.js
│ ├── ErrorReceipt.js
│ ├── BurnWallet.js
│ ├── NavCard.js
│ ├── GoellarsBalance.js
│ ├── Buttons.js
│ ├── SimpleBalance.js
│ ├── ShareLink.js
│ ├── Balance.js
│ ├── Loader.js
│ ├── PassportReceipt.js
│ ├── Passports
│ │ ├── PassportView.js
│ │ ├── styles.js
│ │ └── index.js
│ ├── Receive.js
│ ├── Header.js
│ ├── BityHistory.js
│ ├── TxReceipt.js
│ ├── RequestFunds.js
│ └── RecentTransactions.js
├── index.js
├── i18n
│ ├── locales
│ │ ├── index.js
│ │ ├── zh_TW.json
│ │ ├── ja.json
│ │ ├── he.json
│ │ ├── es.json
│ │ ├── ro.json
│ │ ├── pt.json
│ │ ├── ca.json
│ │ ├── fr.json
│ │ ├── ru.json
│ │ ├── en.json
│ │ └── de.json
│ └── index.js
├── services
│ ├── localStorage.js
│ ├── ethgasstation.js
│ ├── bity.js
│ ├── plasma.js
│ └── incogDetect.js
└── theme.js
├── .editorconfig
├── twinkling.png
├── public
├── favicon.ico
├── whiteburn.png
├── icons
│ ├── icon-72x72.png
│ ├── icon-96x96.png
│ ├── icon-144x144.png
│ ├── icon-152x152.png
│ └── icon-192x192.png
├── fonts
│ └── arial_rounded_mt_bold.ttf
├── manifest.json
├── images
│ └── flame.svg
└── index.html
├── scripts
├── ci_deploy.sh
├── test.js
├── start.js
├── consolidate.js
└── build.js
├── config
├── jest
│ ├── cssTransform.js
│ └── fileTransform.js
├── paths.js
├── env.js
└── webpackDevServer.config.js
├── DEPLOY.MD
├── .travis.yml
├── .gitignore
├── LICENSE
├── README.md
└── package.json
/src/contracts/Exchange.blocknumber.js:
--------------------------------------------------------------------------------
1 | module.exports = "6627956"
2 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.js]
2 | indent_style = space
3 | indent_size = 2
4 |
--------------------------------------------------------------------------------
/twinkling.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/twinkling.png
--------------------------------------------------------------------------------
/src/contracts/Exchange.address.js:
--------------------------------------------------------------------------------
1 | module.exports = "0x2C4Bd064b998838076fa341A83d007FC2FA50957"
2 |
--------------------------------------------------------------------------------
/src/planeta/contracts/CO2.address.js:
--------------------------------------------------------------------------------
1 | module.exports = "0xF64fFBC4A69631D327590f4151B79816a193a8c6";
2 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/src/assets/dai.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/dai.png
--------------------------------------------------------------------------------
/public/whiteburn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/public/whiteburn.png
--------------------------------------------------------------------------------
/src/assets/bity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/bity.png
--------------------------------------------------------------------------------
/src/assets/pdai.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/pdai.png
--------------------------------------------------------------------------------
/src/assets/pingu.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/pingu.gif
--------------------------------------------------------------------------------
/src/assets/qrcode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/qrcode.png
--------------------------------------------------------------------------------
/src/assets/rekt.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/rekt.gif
--------------------------------------------------------------------------------
/src/assets/wyre.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/wyre.jpg
--------------------------------------------------------------------------------
/src/assets/wyre.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/wyre.png
--------------------------------------------------------------------------------
/src/assets/xdai.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/xdai.jpg
--------------------------------------------------------------------------------
/src/assets/ethereum.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/ethereum.png
--------------------------------------------------------------------------------
/src/assets/ka-ching.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/ka-ching.mp3
--------------------------------------------------------------------------------
/src/assets/new-tag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/new-tag.png
--------------------------------------------------------------------------------
/src/assets/papyrus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/papyrus.png
--------------------------------------------------------------------------------
/src/assets/sendwyre.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/sendwyre.png
--------------------------------------------------------------------------------
/src/assets/surprise.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/surprise.gif
--------------------------------------------------------------------------------
/src/assets/handshake.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/handshake.gif
--------------------------------------------------------------------------------
/src/assets/pollution.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/pollution.mp3
--------------------------------------------------------------------------------
/public/icons/icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/public/icons/icon-72x72.png
--------------------------------------------------------------------------------
/public/icons/icon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/public/icons/icon-96x96.png
--------------------------------------------------------------------------------
/src/assets/burnerloader.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/burnerloader.gif
--------------------------------------------------------------------------------
/src/assets/burnerwallet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/burnerwallet.png
--------------------------------------------------------------------------------
/src/assets/customRPCHint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/customRPCHint.png
--------------------------------------------------------------------------------
/src/assets/planeta-logo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/planeta-logo.gif
--------------------------------------------------------------------------------
/src/assets/sad-trombone.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/sad-trombone.mp3
--------------------------------------------------------------------------------
/public/icons/icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/public/icons/icon-144x144.png
--------------------------------------------------------------------------------
/public/icons/icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/public/icons/icon-152x152.png
--------------------------------------------------------------------------------
/public/icons/icon-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/public/icons/icon-192x192.png
--------------------------------------------------------------------------------
/public/fonts/arial_rounded_mt_bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/public/fonts/arial_rounded_mt_bold.ttf
--------------------------------------------------------------------------------
/src/assets/unreal-tournament-humiliation-sound.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/unreal-tournament-humiliation-sound.mp3
--------------------------------------------------------------------------------
/src/assets/unreal-tournament-monster-kill-sound.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/social-dist0rtion-protocol/planet-a/HEAD/src/assets/unreal-tournament-monster-kill-sound.mp3
--------------------------------------------------------------------------------
/src/components/Ruler.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Ruler = () => {
4 | return (
5 |
6 | )
7 | };
8 |
9 | export default Ruler;
10 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.scss';
4 | import App from './App';
5 | import 'bootstrap/dist/css/bootstrap.min.css';
6 |
7 |
8 | ReactDOM.render(, document.getElementById('root'));
9 |
--------------------------------------------------------------------------------
/src/components/InputInfo.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 |
3 | // TODO: Open an issue on Consensys/rimble-ui about this feature.
4 | export default styled.span`
5 | padding: 6px 0 0 0;
6 | color: ${props => props.color};
7 | font-size: 0.7em;
8 | `;
9 |
--------------------------------------------------------------------------------
/src/components/Footer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default ({alert, changeAlert}) => {
4 | return (
5 | changeAlert(null)}>
6 |
7 | {alert.message}
8 |
9 |
10 | )
11 | };
12 |
--------------------------------------------------------------------------------
/scripts/ci_deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | echo "For now, we deploy manually to planeta.leap.rocks. Ask a SDP member to deploy!"
4 | #pip install --user awscli
5 | #npm run build
6 | #aws s3 sync ./build s3://$S3_BUCKET/ --acl public-read
7 | #aws configure set preview.cloudfront true
8 | #aws cloudfront create-invalidation --distribution-id $CLOUDFRONT_DISTRIBUTION --paths "/*"
9 |
--------------------------------------------------------------------------------
/src/components/StyledCard.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 | import {Card} from 'rimble-ui';
3 |
4 | const StyledCard = styled(Card).attrs(()=>({
5 | my: 0,
6 | p: 3
7 | }))`
8 | background-color: #FAFAFA;
9 | border-radius: 4px;
10 | @media screen and (max-width: 480px){
11 | border-radius: 0;
12 | }
13 | `;
14 |
15 | export default StyledCard;
16 |
--------------------------------------------------------------------------------
/config/jest/cssTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // This is a custom Jest transformer turning style imports into empty objects.
4 | // http://facebook.github.io/jest/docs/en/webpack.html
5 |
6 | module.exports = {
7 | process() {
8 | return 'module.exports = {};';
9 | },
10 | getCacheKey() {
11 | // The output is always the same.
12 | return 'cssTransform';
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/src/i18n/locales/index.js:
--------------------------------------------------------------------------------
1 | import en from "./en.json";
2 | import fr from "./fr.json";
3 | import es from "./es.json";
4 | import ca from "./ca.json";
5 | import de from "./de.json";
6 | import ro from "./ro.json";
7 | import he from "./he.json";
8 | import ru from "./ru.json";
9 | import pt from "./pt.json";
10 | import ja from "./ja.json";
11 |
12 |
13 | export { en, fr, es, ca, de, ro, he, ru, pt, ja, };
14 |
--------------------------------------------------------------------------------
/src/contracts/Bridge.abi.js:
--------------------------------------------------------------------------------
1 | module.exports = [{
2 | "constant": false,
3 | "inputs": [
4 | {
5 | "name": "_owner",
6 | "type": "address"
7 | },
8 | {
9 | "name": "_amountOrTokenId",
10 | "type": "uint256"
11 | },
12 | {
13 | "name": "_color",
14 | "type": "uint16"
15 | }
16 | ],
17 | "name": "deposit",
18 | "outputs": [],
19 | "payable": false,
20 | "stateMutability": "nonpayable",
21 | "type": "function"
22 | }]
--------------------------------------------------------------------------------
/DEPLOY.MD:
--------------------------------------------------------------------------------
1 | # Deployment
2 |
3 | We're automatically deploying on every update to master using Travis-CI. Check
4 | the .travis.yml file for details.
5 |
6 | Manual deployment is still possible. For manual deployment, follow the
7 | instructions below:
8 |
9 | 1. Ask Kosta, Alberto or Tim for AWS S3 credentials.
10 | 1. Go to `~/.aws/credentials` and create a [named profile](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) called "leap" with the credentials you received.
11 | 1. `npm run deploy` will re-build, deploy to S3 and invalidate the Cloudfront Distribution.
12 |
--------------------------------------------------------------------------------
/src/planeta/PlantReceipt.js:
--------------------------------------------------------------------------------
1 | // @format
2 | import React, { Component } from "react";
3 | import { Flex, Box } from "rimble-ui";
4 | import styled, { keyframes } from "styled-components";
5 | import { Blockie } from "dapparatus";
6 |
7 | const blockieSize = 10;
8 |
9 | export default class PlantReceipt extends Component {
10 | constructor(props) {
11 | super(props);
12 | }
13 |
14 | render() {
15 | const {
16 | receipt: { txHash, amount }
17 | } = this.props;
18 |
19 | return (
20 |
21 | You locked {amount} Gigatonnes of CO₂
22 |
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/services/localStorage.js:
--------------------------------------------------------------------------------
1 | const getFieldName = (name, account) => (account ? `${account}${name}` : name);
2 |
3 | export const getStoredValue = (name, account) =>
4 | localStorage.getItem(getFieldName(name, account));
5 |
6 | export const storeValues = (params, account) => {
7 | const keys = Object.keys(params);
8 | const values = Object.values(params);
9 | keys.forEach((key, index) => {
10 | const value = values[index];
11 | const name = getFieldName(key, account);
12 | localStorage.setItem(name, value);
13 | });
14 | };
15 |
16 | export const eraseStoredValue = (name, account) =>
17 | localStorage.removeItem(getFieldName(name, account));
18 |
--------------------------------------------------------------------------------
/src/components/Bottom.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import i18n from '../i18n';
3 | import { Icon } from 'rimble-ui'
4 | import { ActionButton } from '../components/Buttons'
5 |
6 | export default class Receive extends React.Component {
7 | render() {
8 | let {icon,text,action} = this.props
9 |
10 | if(!icon) icon = "times"
11 | if(!text) text = i18n.t('done')
12 |
13 | //icon = "fas fa-"+icon
14 |
15 | return (
16 |
17 |
{action()}}>
18 |
19 | {text}
20 |
21 |
22 | )
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | node_js:
4 | - '8.11'
5 |
6 | before_install:
7 | - pip install --user awscli
8 | - export PATH=$PATH:$HOME/.local/bin
9 |
10 | install: npm i
11 |
12 | script:
13 | - unset CI
14 | - npm run build
15 |
16 | cache: npm
17 |
18 | deploy:
19 | - provider: s3
20 | cache_control: "max-age=31536000"
21 | access_key_id: $AWS_ACCESS_KEY_ID
22 | secret_access_key: $AWS_SECRET_ACCESS_KEY
23 | bucket: $PROD_S3_BUCKET
24 | acl: public_read
25 | local_dir: build
26 | skip_cleanup: true
27 | region: 'eu-west-1'
28 | on:
29 | branch: master
30 |
31 | after_deploy:
32 | - aws configure set preview.cloudfront true
33 | - aws cloudfront create-invalidation --distribution-id $PROD_CLOUDFRONT_DISTRIBUTION --paths "/*"
34 |
--------------------------------------------------------------------------------
/src/i18n/index.js:
--------------------------------------------------------------------------------
1 | import i18next from "i18next";
2 | import LanguageDetector from "i18next-browser-languagedetector";
3 | import { fr, en, es, ca, de, ro, he, ru, pt, ja } from "./locales";
4 |
5 | const i18n = i18next;
6 | const options = {
7 | interpolation: {
8 | escapeValue: false // not needed for react!!
9 | },
10 |
11 | debug: true,
12 |
13 | resources: {
14 | en: {
15 | common: en.en
16 | }
17 | },
18 |
19 | fallbackLng: "en",
20 |
21 | ns: ["common"],
22 |
23 | defaultNS: "common",
24 |
25 | react: {
26 | wait: false,
27 | bindI18n: "languageChanged loaded",
28 | bindStore: "added removed",
29 | nsMode: "default"
30 | }
31 | };
32 |
33 | i18next.use(LanguageDetector).init(options);
34 | i18next.changeLanguage(navigator.language, (err, t) => {
35 | if (err) return console.log("Something went wrong during loading");
36 | });
37 |
38 | export default i18n;
39 |
--------------------------------------------------------------------------------
/src/components/Receipt.js:
--------------------------------------------------------------------------------
1 | // @format
2 | import React, { Component } from "react";
3 | import TxReceipt from "./TxReceipt";
4 | import TradeReceipt from "./TradeReceipt";
5 | import PlantReceipt from "../planeta/PlantReceipt";
6 | import PassportReceipt from "./PassportReceipt";
7 | import ErrorReceipt from "./ErrorReceipt";
8 |
9 | export default class Receipt extends Component {
10 | render() {
11 | const {
12 | receipt: { type }
13 | } = this.props;
14 |
15 | if (type === "trade") {
16 | return ;
17 | } else if (type === "error") {
18 | return ;
19 | } else if (type === "plant") {
20 | return ;
21 | } else if (type === "passport_transfer") {
22 | return ;
23 | } else {
24 | return ;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/config/jest/fileTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 |
5 | // This is a custom Jest transformer turning file imports into filenames.
6 | // http://facebook.github.io/jest/docs/en/webpack.html
7 |
8 | module.exports = {
9 | process(src, filename) {
10 | const assetFilename = JSON.stringify(path.basename(filename));
11 |
12 | if (filename.match(/\.svg$/)) {
13 | return `const React = require('react');
14 | module.exports = {
15 | __esModule: true,
16 | default: ${assetFilename},
17 | ReactComponent: React.forwardRef((props, ref) => ({
18 | $$typeof: Symbol.for('react.element'),
19 | type: 'svg',
20 | ref: ref,
21 | key: null,
22 | props: Object.assign({}, props, {
23 | children: ${assetFilename}
24 | })
25 | })),
26 | };`;
27 | }
28 |
29 | return `module.exports = ${assetFilename};`;
30 | },
31 | };
32 |
--------------------------------------------------------------------------------
/src/components/Share.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { CopyToClipboard } from "react-copy-to-clipboard";
3 | import i18n from '../i18n';
4 | import {
5 | Flex,
6 | Box,
7 | Input,
8 | QR as QRCode
9 | } from 'rimble-ui'
10 |
11 | export default class Receive extends React.Component {
12 |
13 | render() {
14 | let {
15 | changeAlert,
16 | url
17 | } = this.props
18 |
19 | return (
20 |
21 | {
22 | changeAlert({type: 'success', message: i18n.t('share.copied')})
23 | }}>
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | )
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/components/MainCard.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Flex, Icon, Box } from "rimble-ui";
3 | import i18next from "i18next";
4 | import { BorderButton } from "./Buttons";
5 |
6 | export default ({
7 | changeView,
8 | }) => {
9 | let sendButtons = (
10 |
11 |
12 |
13 | changeView("receive")}>
14 |
15 |
16 | {i18next.t("main_card.receive")}
17 |
18 |
19 |
20 |
21 | changeView("send_to_address")}>
22 |
23 |
24 | {i18next.t("main_card.send")}
25 |
26 |
27 |
28 |
29 |
30 | );
31 |
32 |
33 | return sendButtons
34 | };
35 |
--------------------------------------------------------------------------------
/src/services/ethgasstation.js:
--------------------------------------------------------------------------------
1 | // @format
2 | import getConfig from "../config";
3 |
4 | const CONFIG = getConfig();
5 | const API = "https://ethgasstation.info/json/ethgasAPI.json";
6 |
7 | function get() {
8 | return fetch(API, {
9 | mode: "cors",
10 | method: "get"
11 | }).then(r => r.json());
12 | }
13 |
14 | // NOTE: Both price() and time() currently default to average. In the future,
15 | // these function could be adjusted to feature "fast" and "safe" too.
16 | // NOTE2: Both functions can throw and should be try-catch'ed.
17 | export async function price() {
18 | const { average } = await get();
19 | if (average > 0 && average < 200) {
20 | const avg = average + average * CONFIG.ROOTCHAIN.GAS.BOOST_BY;
21 | return Math.round(avg * 100) / 1000;
22 | }
23 | return Promise.reject("Average out of range (0–200)");
24 | }
25 |
26 | export async function time() {
27 | // NOTE: avgWait is returned from ethgasstation in minutes (double)
28 | const { avgWait } = await get();
29 | // We convert minutes to milliseconds
30 | return avgWait * 60 * 1000;
31 | }
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.env*
2 | *.pem
3 | .env
4 | clevis.json
5 | relayhttpprovider.env
6 | cloudfront.id.1
7 | cloudfront.id.2
8 | deploy.network
9 | aws.json
10 | backend/redisdata
11 | accounts.json
12 | **.DS_*
13 | *.log
14 | *.ens
15 | */*/*.abi
16 | */*/*.bytecode
17 | */*/*.address
18 | */*/*.run.xml
19 | */*/*.log.*
20 | */*/*.blockNumber
21 | */*.abi
22 | */*.bytecode
23 | */*.bib
24 | */*.address
25 | */*.run.xml
26 | */*.log.*
27 | */*.blockNumber
28 | */.clevis
29 | */*/.clevis
30 | */node_modules
31 | /node_modules
32 | public/reload.txt
33 |
34 | **/*.blockNumber
35 | **/*.head.address
36 | **/*.previous.address
37 | **/*.compiled
38 | **/*.bytecode
39 | **/*.abi
40 | **/*.address
41 | deploy.log
42 | public/reload.txt
43 | geth.log
44 | react.log
45 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
46 |
47 | # dependencies
48 | /node_modules
49 |
50 | # testing
51 | /coverage
52 |
53 | # production
54 | /build
55 |
56 | # misc
57 | .DS_Store
58 | .env.local
59 | .env.development.local
60 | .env.test.local
61 | .env.production.local
62 |
63 | npm-debug.log*
64 | yarn-debug.log*
65 | yarn-error.log*
66 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Austin Griffith
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Planet A",
3 | "name": "Planet A - A Tragedy of the CO2mmons",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "type": "image/x-icon",
8 | "sizes": "64x64 32x32 24x24 16x16"
9 | },
10 | {
11 | "src": "/icons/icon-72x72.png",
12 | "type": "image/png",
13 | "sizes": "72x72"
14 | },
15 | {
16 | "src": "/icons/icon-96x96.png",
17 | "type": "image/png",
18 | "sizes": "96x96"
19 | },
20 | {
21 | "src": "/icons/icon-128x128.png",
22 | "type": "image/png",
23 | "sizes": "128x128"
24 | },
25 | {
26 | "src": "/icons/icon-144x144.png",
27 | "type": "image/png",
28 | "sizes": "144x144"
29 | },
30 | {
31 | "src": "/icons/icon-152x152.png",
32 | "type": "image/png",
33 | "sizes": "152x152"
34 | },
35 | {
36 | "src": "/icons/icon-192x192.png",
37 | "type": "image/png",
38 | "sizes": "192x192"
39 | },
40 | ],
41 | "start_url": "./index.html",
42 | "orientation": "portrait",
43 | "theme_color": "#0F1C24",
44 | "background_color": "#0F1C24"
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/ErrorReceipt.js:
--------------------------------------------------------------------------------
1 | // @format
2 | import React, { Component } from "react";
3 | import { Flex, Box } from "rimble-ui";
4 |
5 | export default class ErrorReceipt extends Component {
6 | render() {
7 | const {
8 | receipt: { message }
9 | } = this.props;
10 |
11 | return (
12 |
13 |
20 | ❌ Err0r! 😭
21 |
22 |
23 |
24 | {/* Yeah this is not rimble-ui but I really had no time... */}
25 |
34 | {message}
35 |
36 |
37 |
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/planeta/cooldown.js:
--------------------------------------------------------------------------------
1 | import { getStoredValue, storeValues } from "../services/localStorage";
2 | import getConfig from "../config";
3 |
4 | // COOLDOWN is in seconds, here we convert it to millisecods
5 | // so we don't need to do extra conversions.
6 | const COOLDOWN = getConfig().PLANET_A.COOLDOWN * 1000;
7 |
8 | const getCooldownFor = passport => JSON.parse(getStoredValue("cooldown", passport) || "{}");
9 |
10 | export function purge(myPassport) {
11 | const now = Date.now();
12 | const cooldown = getCooldownFor(myPassport);
13 | for (let passport in cooldown) {
14 | if (now - cooldown[passport] > COOLDOWN) {
15 | delete cooldown[passport]
16 | }
17 | }
18 | return cooldown;
19 | }
20 |
21 | export function addHandshake(myPassport, theirPassport) {
22 | const now = Date.now();
23 | const cooldown = purge(myPassport);
24 | cooldown[theirPassport] = now;
25 | storeValues({cooldown: JSON.stringify(cooldown)}, myPassport);
26 | }
27 |
28 | export function timeLeft(myPassport, theirPassport) {
29 | const now = Date.now();
30 | const timestamp = getCooldownFor(myPassport)[theirPassport] || 0;
31 | return Math.max(COOLDOWN - (now - timestamp), 0);
32 | }
33 |
--------------------------------------------------------------------------------
/src/components/BurnWallet.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Ruler from "./Ruler";
3 | import i18n from '../i18n';
4 |
5 |
6 | export default ({ mainStyle, burnWallet, goBack }) => {
7 | return (
8 |
9 |
10 | {i18n.t('burn_wallet.burn_private_key_question')}
11 |
12 |
13 | {i18n.t('burn_wallet.disclaimer')}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/src/components/NavCard.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Text,
4 | Link,
5 | Icon,
6 | Flex
7 | } from "rimble-ui";
8 |
9 | export default ({title, titleLink, goBack, darkMode}) => {
10 |
11 | let titleDisplay = ""
12 |
13 | if (titleLink){
14 | titleDisplay = (
15 |
16 | {title}
17 |
18 | )
19 | } else {
20 | titleDisplay = (
21 |
22 | {title}
23 |
24 | )
25 | }
26 |
27 | return (
28 |
38 | {titleDisplay}
39 | {console.log("CLICKED");goBack()}}
48 | >
49 |
50 |
51 |
52 | )
53 | };
54 |
--------------------------------------------------------------------------------
/src/components/GoellarsBalance.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Flex, Text } from "rimble-ui";
3 | import styled from "styled-components";
4 | import { getStoredValue } from "../services/localStorage";
5 |
6 | const Container = styled(Flex).attrs(() => ({
7 | flexDirection: "column",
8 | alignItems: "center",
9 | pb:3,
10 | mb:3,
11 | borderBottom: "1px solid #DFDFDF",
12 | width:"100%",
13 | justifyContent:"space-around"
14 | }))``;
15 |
16 | const Label = styled(Text).attrs(() => ({
17 | fontSize: 2,
18 | color: "silver"
19 | }))``;
20 |
21 | const Value = styled(Text).attrs(() => ({
22 | fontSize: 5,
23 | fontWeight: 4,
24 | color: "black"
25 | }))``;
26 |
27 |
28 | const GoellarsBalance = props => {
29 | const { balance } = props;
30 | const locale = getStoredValue("i18nextLng") || "en.en";
31 | const formatter = new Intl.NumberFormat(locale, {
32 | style: "currency",
33 | currency: "PYG", // We use Paraguayan guaraní to display ₲ next to a value
34 | currencyDisplay: "symbol",
35 | maximumFractionDigits: 2
36 | });
37 | const noBalance = isNaN(balance);
38 | const balanceValue = noBalance
39 | ? "--"
40 | : formatter.format(balance).replace('PYG', '₲');
41 | return (
42 |
43 |
44 | {balanceValue}
45 |
46 | );
47 | };
48 |
49 | export default GoellarsBalance;
50 |
--------------------------------------------------------------------------------
/src/components/Buttons.js:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { Button, OutlineButton } from "rimble-ui";
3 |
4 | export const PrimaryButton = styled(Button)`
5 | cursor: pointer;
6 | color: var(--primary-btn-text-color);
7 | background-color: var(--primary-btn-bg-color) !important;
8 | border-bottom: 3px solid var(--primary-btn-border-color);
9 | &:hover {
10 | box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.15);
11 | border: 1px solid transparent;
12 | }
13 | &:focus {
14 | outline: 0;
15 | }
16 | @media screen and (max-width: 480px){
17 | padding-left: 0;
18 | padding-right: 0;
19 | }
20 | `;
21 |
22 | export const ActionButton = styled(Button)`
23 | cursor: pointer;
24 | background-color: #41545f !important;
25 | border: 1px solid #182933;
26 | color: #cad7de;
27 | & span {
28 | display: flex;
29 | align-items: center;
30 | }
31 | `;
32 |
33 | export const BorderButton = styled(OutlineButton)`
34 | cursor: pointer;
35 | color: var(--secondary-btn-text-color);
36 | border-bottom: 3px solid var(--secondary-btn-border-color);
37 | background-color: var(--secondary-btn-bg-color) !important;
38 | &:hover {
39 | color: var(--secondary-btn-text-color);
40 | border: 1px solid transparent;
41 | }
42 | &:focus {
43 | outline: 0;
44 | }
45 | @media screen and (max-width: 480px){
46 | padding-left: 0;
47 | padding-right: 0;
48 | }
49 | `;
50 |
--------------------------------------------------------------------------------
/src/planeta/HandshakeButtons.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Text, Heading } from "rimble-ui";
3 | import { BorderButton } from "../components/Buttons";
4 | import { choice } from "./utils";
5 |
6 | const COLLABORATE_EMOJIS = ["🥰", "🌹", "🍻", "🥂"];
7 | const DEFECT_EMOJIS = ["😈", "☠️", "🗡️", "💣"];
8 |
9 | export default function HandshakeButtons({ handleStrategy }) {
10 | const collaborateEmoji = choice(COLLABORATE_EMOJIS);
11 | const defectEmoji = choice(DEFECT_EMOJIS);
12 |
13 | return (
14 | <>
15 | Play fair ☺️
16 |
17 | Earn Göllars by being fair to the environment and to
18 | your handshake partner. Hopefully, they are fair to you as well 🤞
19 |
20 | handleStrategy("collaborate")}
24 | >
25 | {collaborateEmoji} I want to be fair! {collaborateEmoji}
26 |
27 |
28 | Play greedy 🤑
29 |
30 | Wanna make extra money? Deceive your handshake partner{" "}
31 | Play smart to earn more Göllars. But watch out, you
32 | might get the same treatment 🤔
33 |
34 | handleStrategy("defect")}>
35 | {defectEmoji} I want to be greedy! {defectEmoji}
36 |
37 | >
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/src/components/SimpleBalance.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 | import { Text } from "rimble-ui";
4 |
5 | // Here are all the classes that can be used to style the balance.
6 | // Some of them are not in use now, but are listed for clarity.
7 |
8 | const StyledBalance = styled.div`
9 | text-align: center;
10 | padding: 20px 0;
11 |
12 | color: var(--primary);
13 |
14 | .otherAssets {
15 | color: var(--dark-text);
16 | }
17 |
18 | span {
19 | font-size: 200%;
20 | }
21 |
22 | .integer {
23 | font-size: 400%;
24 | }
25 |
26 | .group {
27 | }
28 |
29 | .decimal {
30 | }
31 |
32 | .fraction {
33 | }
34 |
35 | .literal {
36 | }
37 |
38 | .currency {
39 | font-size: 100%;
40 | }
41 | `;
42 |
43 | export default ({ mainAmount, otherAmounts, currencyDisplay }) => {
44 | if (isNaN(mainAmount) || typeof mainAmount === "undefined") {
45 | mainAmount = 0.0;
46 | }
47 |
48 | const otherAssetsTotal = Object.values(otherAmounts).reduce(
49 | (acc, curr) => acc + parseInt(curr, 10),
50 | 0
51 | );
52 | const parts = currencyDisplay(mainAmount, true);
53 |
54 | return (
55 | <>
56 |
57 | {parts.map(({ type, value }) => (
58 | {value}
59 | ))}
60 | {otherAssetsTotal > 0 && (
61 |
62 | +{currencyDisplay(otherAssetsTotal)} in other assets
63 |
64 | )}
65 |
66 |
67 | >
68 | );
69 | };
70 |
--------------------------------------------------------------------------------
/src/components/ShareLink.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Ruler from "./Ruler";
3 | import {CopyToClipboard} from 'react-copy-to-clipboard';
4 | import QRCode from 'qrcode.react';
5 |
6 | export default class ShareLink extends React.Component {
7 |
8 | constructor(props) {
9 | super(props);
10 | this.state = {
11 | copied: false
12 | }
13 | }
14 | render() {
15 |
16 | let port = window.location.port
17 | if(port && port!=="80"){
18 | port=":"+port
19 | }else{
20 | port=""
21 | }
22 |
23 | let url = window.location.protocol + "//" + window.location.hostname+port;
24 | let qrValue = url + "/" + this.props.sendLink + ";" + this.props.sendKey;
25 | let qrSize = Math.min(document.documentElement.clientWidth,512)-90
26 |
27 | return (
28 |
29 |
{
30 | this.props.changeAlert({type: 'success', message: 'Link copied to clipboard'})
31 | }}>
32 |
33 |
34 |
35 |
36 |
37 |
45 |
46 |
47 |
48 |
49 | )
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/services/bity.js:
--------------------------------------------------------------------------------
1 | // @format
2 |
3 | const API = "https://exchange.api.bity.com/v2/";
4 |
5 | export const placeOrder = (name, IBAN, amount, address) => {
6 | return fetch(API + "orders", {
7 | method: "POST",
8 | credentials: "include",
9 | body: JSON.stringify({
10 | input: {
11 | amount,
12 | currency: "ETH",
13 | type: "crypto_address",
14 | crypto_address: address
15 | },
16 | output: {
17 | currency: "EUR",
18 | type: "bank_account",
19 | iban: IBAN,
20 | owner: {
21 | name
22 | }
23 | }
24 | }),
25 | headers: {
26 | "Content-Type": "application/json",
27 | Accept: "application/json"
28 | }
29 | });
30 | };
31 |
32 | export const getOrder = id => {
33 | return fetch(`${API}orders/${id}`, {
34 | method: "GET",
35 | credentials: "include",
36 | headers: {
37 | "Content-Type": "application/json",
38 | Accept: "application/json"
39 | }
40 | }).then(res => res.json());
41 | };
42 |
43 | export const getOrders = () => {
44 | return fetch(`${API}orders`, {
45 | method: "GET",
46 | credentials: "include",
47 | headers: {
48 | "Content-Type": "application/json",
49 | Accept: "application/json"
50 | }
51 | }).then(res => res.json());
52 | };
53 |
54 | export const getEstimate = amount => {
55 | return fetch("https://exchange.api.bity.com/v2/orders/estimate", {
56 | method: "POST",
57 | credentials: "include",
58 | body: JSON.stringify({
59 | input: {
60 | currency: "ETH",
61 | amount: amount
62 | },
63 | output: {
64 | currency: "EUR"
65 | }
66 | }),
67 | headers: {
68 | "Content-Type": "application/json"
69 | }
70 | }).then(response => response.json());
71 | };
72 |
--------------------------------------------------------------------------------
/scripts/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Do this as the first thing so that any code reading it knows the right env.
4 | process.env.BABEL_ENV = 'test';
5 | process.env.NODE_ENV = 'test';
6 | process.env.PUBLIC_URL = '';
7 |
8 | // Makes the script crash on unhandled rejections instead of silently
9 | // ignoring them. In the future, promise rejections that are not handled will
10 | // terminate the Node.js process with a non-zero exit code.
11 | process.on('unhandledRejection', err => {
12 | throw err;
13 | });
14 |
15 | // Ensure environment variables are read.
16 | require('../config/env');
17 |
18 |
19 | const jest = require('jest');
20 | const execSync = require('child_process').execSync;
21 | let argv = process.argv.slice(2);
22 |
23 | function isInGitRepository() {
24 | try {
25 | execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
26 | return true;
27 | } catch (e) {
28 | return false;
29 | }
30 | }
31 |
32 | function isInMercurialRepository() {
33 | try {
34 | execSync('hg --cwd . root', { stdio: 'ignore' });
35 | return true;
36 | } catch (e) {
37 | return false;
38 | }
39 | }
40 |
41 | // Watch unless on CI, in coverage mode, explicitly adding `--no-watch`,
42 | // or explicitly running all tests
43 | if (
44 | !process.env.CI &&
45 | argv.indexOf('--coverage') === -1 &&
46 | argv.indexOf('--no-watch') === -1 &&
47 | argv.indexOf('--watchAll') === -1
48 | ) {
49 | // https://github.com/facebook/create-react-app/issues/5210
50 | const hasSourceControl = isInGitRepository() || isInMercurialRepository();
51 | argv.push(hasSourceControl ? '--watch' : '--watchAll');
52 | }
53 |
54 | // Jest doesn't have this option so we'll remove it
55 | if (argv.indexOf('--no-watch') !== -1) {
56 | argv = argv.filter(arg => arg !== '--no-watch');
57 | }
58 |
59 |
60 | jest.run(argv);
61 |
--------------------------------------------------------------------------------
/src/planeta/FinalizeHandshake.js:
--------------------------------------------------------------------------------
1 | //@format
2 | import React from "react";
3 | import { finalizeHandshake } from "./utils";
4 | import HandshakeButtons from "./HandshakeButtons";
5 | import { getStoredValue } from "../services/localStorage";
6 |
7 | export default class FinalizeHandshake extends React.Component {
8 | constructor(props) {
9 | super(props);
10 | this.handleStrategy = this.handleStrategy.bind(this);
11 | }
12 |
13 | async handleStrategy(strategy) {
14 | if (getStoredValue("expertMode") === "true") {
15 | strategy = strategy === "collaborate" ? "defect" : "collaborate";
16 | }
17 | const {
18 | plasma,
19 | defaultPassport,
20 | scannerState,
21 | metaAccount,
22 | setReceipt,
23 | changeView
24 | } = this.props;
25 | changeView("loader");
26 |
27 | let finalReceipt;
28 | try {
29 | finalReceipt = await finalizeHandshake(
30 | plasma,
31 | defaultPassport.unspent,
32 | scannerState.receipt,
33 | metaAccount.privateKey,
34 | strategy
35 | );
36 | } catch (err) {
37 | // note: cooldown raises an error too, check planeta/utils.js
38 | setReceipt({ type: "error", message: err.toString() });
39 | changeView("receipt");
40 | return;
41 | }
42 | }
43 |
44 | componentDidMount() {
45 | const { goBack, changeAlert, defaultPassport: passport } = this.props;
46 |
47 | if (!passport) {
48 | // Sorry.
49 | goBack();
50 | setTimeout(
51 | () => changeAlert({ type: "warning", message: "Select a passport" }),
52 | 100
53 | );
54 | }
55 | }
56 |
57 | render() {
58 | const { defaultPassport: passport } = this.props;
59 |
60 | if (!passport) {
61 | return null;
62 | }
63 |
64 | return ;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/planeta/MoreButtons.js:
--------------------------------------------------------------------------------
1 | // @format
2 | import React from "react";
3 | import { Flex, Icon, Box } from "rimble-ui";
4 | import { PrimaryButton, BorderButton } from "../components/Buttons";
5 |
6 | export default ({ changeView, passport, changeAlert }) => {
7 | const passportAlert = () =>
8 | changeAlert({ type: "warning", message: "Select a passport" });
9 | return (
10 |
17 |
18 | {
22 | if (passport) {
23 | changeView("planet_a_handshake");
24 | } else {
25 | passportAlert();
26 | }
27 | }}
28 | >
29 |
30 |
31 | Handshake
32 |
33 |
34 | {
38 | if (passport) {
39 | changeView("planet_a_plant_trees");
40 | } else {
41 | passportAlert();
42 | }
43 | }}
44 | >
45 |
46 |
47 | Plant Trees
48 |
49 |
50 |
51 |
52 | {
56 | if (passport) {
57 | changeView("planet_a_transfer_passport");
58 | } else {
59 | passportAlert();
60 | }
61 | }}
62 | >
63 |
64 |
65 | Transfer Passport
66 |
67 |
68 |
69 |
70 | );
71 | };
72 |
--------------------------------------------------------------------------------
/src/components/Balance.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Flex, Text, Image } from "rimble-ui";
3 | import styled from 'styled-components'
4 |
5 | const Fiat = styled(Text).attrs(()=>({
6 | fontSize: 4,
7 | fontWeight: 4
8 | }))``;
9 |
10 | const Token = styled(Text).attrs(()=>({
11 | fontSize: 1,
12 | }))`
13 | color: var(--secondary-btn-text-color)
14 | `;
15 |
16 | const Amount = styled(Flex)`
17 | flex-direction: column;
18 | align-items: flex-end;
19 | `;
20 |
21 | const tokenDisplay = (amount, symbol = "", maximumFractionDigits = 2) => {
22 | const locale = localStorage.getItem('i18nextLng')
23 | const formatter = new Intl.NumberFormat(locale, {
24 | minimumFractionDigits: 2,
25 | maximumFractionDigits: maximumFractionDigits,
26 | });
27 | return `${formatter.format(amount)} ${symbol}`
28 | };
29 |
30 | const valuableTokens = ["ETH"]
31 |
32 | export default ({icon, text, amount, tokenAmount, currencyDisplay, symbol}) => {
33 | let opacity;
34 | let fiatValue;
35 | let tokenValue;
36 | const floatNumbers = valuableTokens.includes(text) ? 5 : 2
37 |
38 | if(isNaN(amount)){
39 | opacity = 0.25;
40 |
41 | /* NOTE: Sometimes the exchangeRate to fiat wasn't loaded yet and hence
42 | * amount can become NaN. In this case, we simply pass 0 to currencyDisplay
43 | */
44 |
45 | fiatValue = currencyDisplay(0);
46 | tokenValue = tokenDisplay(0);
47 | }else{
48 | opacity = 1;
49 | fiatValue = currencyDisplay(amount);
50 | tokenValue = tokenDisplay(tokenAmount || amount, symbol || text, floatNumbers);
51 | }
52 |
53 | return (
54 |
55 |
56 |
57 |
58 | {text}
59 |
60 |
61 |
62 |
63 | {fiatValue}
64 | {tokenValue}
65 |
66 |
67 |
68 | )
69 | };
70 |
--------------------------------------------------------------------------------
/src/components/Loader.js:
--------------------------------------------------------------------------------
1 | // @format
2 | import React, { Component } from "react";
3 | import { time } from "../services/ethgasstation";
4 | import getConfig from "../config";
5 |
6 | let interval;
7 | const CONFIG = getConfig();
8 | const RATE = {
9 | PERCENT: 3,
10 | EACH: 250
11 | };
12 |
13 | class Loader extends Component {
14 | constructor(props) {
15 | super(props);
16 |
17 | this.state = {
18 | progress: 0,
19 | rate: {
20 | percent: RATE.PERCENT,
21 | each: RATE.EACH
22 | }
23 | };
24 |
25 | this.progress = this.progress.bind(this);
26 | }
27 |
28 | async componentDidMount() {
29 | const { network } = this.props;
30 |
31 | let t;
32 | if (network === "ROOTCHAIN") {
33 | t = await time();
34 | } else if (network === "SIDECHAIN") {
35 | t = CONFIG.SIDECHAIN.TIME_ESTIMATES.TX; //ms
36 | } else {
37 | // 8ms was Loader's default value.
38 | t = 8333; // ms
39 | }
40 |
41 | const rate = {
42 | // NOTE: We "round" the result of "each"'s calculation
43 | each: parseInt(t / 100 / RATE.PERCENT, 10),
44 | percent: RATE.PERCENT
45 | };
46 | this.setState({ rate }, () => {
47 | interval = setInterval(this.progress, rate.each);
48 | });
49 | }
50 |
51 | componentWillUnmount() {
52 | clearInterval(interval);
53 | }
54 |
55 | progress() {
56 | const { rate, progress } = this.state;
57 |
58 | let newProgress = progress + rate.percent;
59 | if (newProgress > 100) {
60 | newProgress = 100;
61 | clearInterval(interval);
62 | }
63 | this.setState({ progress: newProgress });
64 | }
65 |
66 | render() {
67 | const { progress } = this.state;
68 | const { loaderImage } = this.props;
69 |
70 | return (
71 |
72 |

77 |
83 |
84 | );
85 | }
86 | }
87 | export default Loader;
88 |
--------------------------------------------------------------------------------
/src/components/PassportReceipt.js:
--------------------------------------------------------------------------------
1 | // @format
2 | import React, { Component } from "react";
3 | import { Flex, Box } from "rimble-ui";
4 | import styled, { keyframes } from "styled-components";
5 | import { Blockie } from "dapparatus";
6 | import newtag from "../assets/new-tag.png";
7 | import {
8 | Container,
9 | PassportCover,
10 | PassportLabel,
11 | PassportCountry,
12 | PassportName
13 | } from "./Passports/styles";
14 |
15 | const blockieSize = 10;
16 |
17 | export default class PassportReceipt extends Component {
18 | constructor(props) {
19 | super(props);
20 | }
21 |
22 | render() {
23 | const {
24 | receipt: { from, to, passport }
25 | } = this.props;
26 | const name = passport.id.slice(0, 5);
27 | const { shortName } = passport.country;
28 |
29 | return (
30 |
31 |
32 | 😭L0L you just gave your passp0rt away!!1😭
33 |
34 |
35 |
36 |
37 | {/* EXXXTREME 90ies CSS skills incoming */}
38 |
39 | You
40 |
41 |
42 |
43 |
44 |
45 | {shortName}
46 | Passport
47 |
48 | {name}
49 |
50 |
51 |
52 |
53 |
54 | {/* EXXXTREME 90ies CSS skills incoming */}
55 |
56 | The person that will commit crimes against humanity using your
57 | identity 😱
58 |
59 |
60 |
61 | );
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/planeta/GlobalCO2.js:
--------------------------------------------------------------------------------
1 | // @format
2 | import React, { Component } from "react";
3 | import styled from "styled-components";
4 | import { Heading, Flex, Text, Box } from "rimble-ui";
5 |
6 | const Aligner = styled(Flex).attrs(() => ({
7 | alignItems: "center",
8 | flexDirection: "row",
9 | mt: 2
10 | }))`
11 | color: #efcc11;
12 | text-align: center;
13 | font-family: "Saira", sans-serif;
14 | p {
15 | color: #a2852e;
16 | text-align: center;
17 | width: 100%;
18 | }
19 | h1 {
20 | flex: 1 1 0;
21 | text-align: right;
22 | margin: 0;
23 | margin-right: 16px;
24 | }
25 | `;
26 |
27 | const Label = styled(Text).attrs(() => ({
28 | px: 3,
29 | fontWeight: "bold"
30 | }))`
31 | flex: 1.5 1.5 0;
32 | text-align: left;
33 | color: #cec6ff;
34 | margin: 0;
35 | `;
36 |
37 | const CO2Display = styled.h1`
38 | color: #efcc11;
39 | font-weight: bold;
40 | `;
41 |
42 | const NextDisplay = styled.h1`
43 | color: red;
44 | font-weight: bold;
45 | font-size: 24px;
46 | `;
47 |
48 | export default class GlobalCO2 extends Component {
49 | render() {
50 | const { value } = this.props;
51 | let toNext = 3420 - value;
52 | let message;
53 | if (toNext <= 0) {
54 | toNext = 4170 - value;
55 | message = "CO₂ left to meltdown";
56 | } else {
57 | message = "CO₂ left to tipping point";
58 | }
59 |
60 | if (value) {
61 | return (
62 |
67 |
68 |
69 | {Math.round(value)}
70 |
71 |
72 |
73 | {Math.round(toNext)}
74 |
75 |
76 |
77 | (view Dashboard)
78 |
79 |
80 | values in Gigatons{" "}
81 |
82 |
83 |
84 | );
85 | } else {
86 | return null;
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/public/images/flame.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/flame.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/components/Passports/PassportView.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Card, { Flex, Text } from "rimble-ui";
3 | import { IconClose } from "./styles";
4 | import PlanetAMoreButtons from "../../planeta/MoreButtons";
5 |
6 | const Param = ({ label, value, color }) => {
7 | const locale = localStorage.getItem('i18nextLng')
8 | const formatter = new Intl.NumberFormat(locale, {
9 | minimumFractionDigits: 0,
10 | maximumFractionDigits: 2,
11 | });
12 | return (
13 |
14 |
15 | {label}
16 |
17 |
18 | {formatter.format(value)}
19 |
20 |
21 | );
22 | };
23 |
24 | const Citizen = props => {
25 | const { name, country } = props;
26 | return (
27 |
28 | {`Citizen of the ${country}`}
29 |
30 | {name.length > 0 ? name : "Mr.Mysterious"}
31 |
32 |
33 | );
34 | };
35 |
36 | const PassportView = props => {
37 | const { passport, close } = props;
38 | const { changeView, changeAlert } = props;
39 | const { data } = passport;
40 | const country = passport.country.fullName;
41 | if (!passport) {
42 | return (
43 |
44 |
45 | Border Control is checking your passport...
46 |
47 |
48 | );
49 | }
50 |
51 | const { emitted, locked, name } = data;
52 | return (
53 |
54 |
55 |
56 |
62 |
63 |
64 |
65 |
70 |
71 | );
72 | };
73 |
74 | export default PassportView;
75 |
--------------------------------------------------------------------------------
/src/components/Receive.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable jsx-a11y/anchor-is-valid */
2 |
3 | import React from 'react';
4 | import {CopyToClipboard} from "react-copy-to-clipboard";
5 | import RecentTransactions from './RecentTransactions';
6 | import i18n from '../i18n';
7 | import getConfig from "../config";
8 | import {
9 | Flex,
10 | Box,
11 | PublicAddress,
12 | QR as QRCode
13 | } from 'rimble-ui'
14 |
15 | const CONFIG = getConfig();
16 |
17 | export default class Receive extends React.Component {
18 |
19 | render() {
20 | let {
21 | view,
22 | buttonStyle,
23 | address,
24 | changeAlert,
25 | changeView,
26 | currencyDisplay,
27 | } = this.props
28 |
29 | return (
30 |
31 |
32 |
{
33 | changeAlert({type: 'success', message: i18n.t('receive.address_copied')})
34 | }}>
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
49 |
50 |
61 |
62 |
69 |
70 | )
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/components/Header.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Blockie } from "dapparatus";
3 | import burnerloader from '../assets/burnerloader.gif';
4 | import { Icon } from "rimble-ui";
5 | export default ({openScanner, network, total, ens, address, changeView, view}) => {
6 |
7 |
8 | let sendButtonOpacity = 1.0
9 | if(view==="receive" || view==="send_badge"){
10 | sendButtonOpacity = 0
11 | }
12 |
13 |
14 |
15 | let name = ens
16 | if(!name){
17 | name = address.substring(2,8)
18 | }
19 |
20 | let moneyDisplay
21 | let blockieDisplay
22 | if(typeof total == "undefined" || Number.isNaN(total)){
23 | moneyDisplay = (
24 |
25 | Connecting..
26 |
27 | )
28 | blockieDisplay = (
29 |
30 | )
31 | }else{
32 | /*moneyDisplay = (
33 |
34 | {currencyDisplay(total)}
35 |
36 | )*/
37 | moneyDisplay = (
38 |
39 | {network}
40 |
41 | )
42 | blockieDisplay = (
43 |
46 |
47 | )
48 | }
49 |
50 | let scanButtonStyle = {
51 | opacity:sendButtonOpacity,
52 | position:"fixed",
53 | zIndex:2,
54 | }
55 |
56 | if(view==="send_to_address"){
57 | scanButtonStyle.position = "absolute"
58 | scanButtonStyle.right = -3
59 | scanButtonStyle.top = 217
60 | delete scanButtonStyle.bottom
61 | }
62 |
63 | let bottomRight = (
64 |
65 |
72 |
73 | )
74 |
75 | const mainOrExchange = view === "main" || view === "exchange";
76 | const opacity =mainOrExchange ? 1 : 0.5;
77 | const blockieLink = mainOrExchange ? 'receive' : 'main';
78 | let topLeft = (
79 | changeView(blockieLink)}>
80 | {blockieDisplay}
81 |
{name}
82 |
);
83 |
84 |
85 | let topRight = (
86 |
87 | {moneyDisplay}
88 |
89 | )
90 |
91 |
92 | return (
93 |
94 | {topLeft}
95 | {topRight}
96 | {view === "main" ? bottomRight : null}
97 |
98 | )
99 | };
100 |
--------------------------------------------------------------------------------
/src/planeta/StartHandshake.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { CopyToClipboard } from "react-copy-to-clipboard";
3 | import i18n from "../i18n";
4 | import { Flex, Box, Heading, QR as QRCode } from "rimble-ui";
5 | import { startHandshake } from "./utils";
6 | import HandshakeButtons from "./HandshakeButtons";
7 | import { getStoredValue } from "../services/localStorage";
8 |
9 | function renderReceipt(receipt, changeAlert) {
10 | const url = "/planeta/handshake/" + receipt;
11 | return (
12 | <>
13 |
14 | Ask another person to scan this QRCode to handshake with you!
15 |
16 |
17 | {
20 | changeAlert({
21 | type: "success",
22 | message: i18n.t("receive.address_copied")
23 | });
24 | }}
25 | >
26 |
27 |
35 |
36 |
37 |
38 |
39 | >
40 | );
41 | }
42 |
43 | export default class Handshake extends React.Component {
44 | constructor(props) {
45 | super(props);
46 | this.state = {};
47 | this.handleStrategy = this.handleStrategy.bind(this);
48 | }
49 |
50 | async handleStrategy(strategy) {
51 | if (getStoredValue("expertMode") === "true") {
52 | strategy = strategy === "collaborate" ? "defect" : "collaborate";
53 | }
54 | const { metaAccount, web3, defaultPassport: passport } = this.props;
55 | const receipt = await startHandshake(
56 | web3,
57 | passport.unspent,
58 | metaAccount.privateKey,
59 | strategy
60 | );
61 | this.setState({ receipt });
62 | }
63 |
64 | render() {
65 | const { changeAlert, goBack } = this.props;
66 | const { receipt } = this.state;
67 |
68 | return (
69 |
70 | {receipt ? (
71 | renderReceipt(receipt, changeAlert)
72 | ) : (
73 |
74 | )}
75 |
88 |
89 | );
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/components/BityHistory.js:
--------------------------------------------------------------------------------
1 | // @format
2 |
3 | import React, { Component } from "react";
4 | import i18n from "../i18n";
5 | import { Flex, Box, Image, Icon } from "rimble-ui";
6 | import { getOrder } from "../services/bity";
7 | import Blockies from "react-blockies";
8 | import bityLogo from "../assets/bity.png";
9 | import Ruler from "./Ruler";
10 | import Loader from "./Loader";
11 | import burnerlogo from "../assets/burnerwallet.png";
12 |
13 | export default class BityHistory extends Component {
14 | constructor(props) {
15 | super(props);
16 | this.state = {};
17 | }
18 |
19 | async componentDidMount() {
20 | const { orderId } = this.props;
21 | const order = await getOrder(orderId);
22 | this.setState({ order });
23 | }
24 |
25 | render() {
26 | const { address, orderId } = this.props;
27 | const { order } = this.state;
28 |
29 | if (order) {
30 | return (
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | {order.output.amount}
41 | €*
42 |
43 |
44 |
45 |
46 |
47 |
54 |
55 |
56 |
57 |
{i18n.t("offramp.history.status_title")}
58 |
59 | {order.timestamp_created && (
60 | - ✓ {i18n.t("offramp.history.status.created")+orderId}
61 | )}
62 | {order.timestamp_payment_received && (
63 | - ✓ {i18n.t("offramp.history.status.received")}
64 | )}
65 | {order.timestamp_executed && (
66 | - ✓ {i18n.t("offramp.history.status.executed")}
67 | )}
68 |
69 |
70 |
71 | *{i18n.t("offramp.history.disclaimer")}
72 |
77 | {i18n.t("offramp.history.click")}
78 |
79 | .
80 |
81 |
82 | );
83 | } else {
84 | return ;
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/components/Passports/styles.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Flex, Text, Icon } from "rimble-ui";
3 | import styled, { keyframes, css } from "styled-components";
4 | import passportPattern from '../../assets/papyrus.png';
5 |
6 | export const Container = styled(Flex).attrs(() => ({
7 | flexWrap: "wrap",
8 | alignItems: "center",
9 | justifyContent: "center",
10 | mb: 3,
11 | pt: 3,
12 | pb: 4,
13 | borderBottom: "1px solid #DFDFDF",
14 | width: "100%"
15 | }))`
16 | transition: transform 0.5s ease-in-out;
17 | cursor: pointer;
18 | `;
19 |
20 | const jump = keyframes`
21 | 5% {
22 | transform: scale3d(1.05, 1.1, 1);
23 | }
24 | 8% {
25 | transform: scale3d(1.05, 1.05, 1);
26 | }
27 | 20% {
28 | transform: scale3d(0.95, 0.95, 1);
29 | }
30 | 0%, 30%, 100% {
31 | transform: scale3d(1, 1, 1);
32 | }
33 | `;
34 |
35 | export const PassportCover = styled(Flex).attrs(({ shortName }) => ({
36 | flexDirection: "column",
37 | bg: `passport${shortName}`,
38 | m:2,
39 | px: 3,
40 | py: 2,
41 | height: 4,
42 | width: '105px',
43 | justifyContent: "space-between"
44 | }))`
45 | border-radius: 4px 8px 8px 4px;
46 | box-shadow: inset -1px 0 rgba(255, 255, 255, 0.2),
47 | 0 1px 3px rgba(0, 0, 0, 0.2);
48 | position: relative;
49 | border: 2px solid;
50 | border-color: ${({theme, shortName}) => theme.colors[`passportBorder${shortName}`] || '#333'};
51 |
52 | @supports (background-blend-mode: color-burn) {
53 | background-repeat: no-repeat;
54 | background-blend-mode: color-burn;
55 | /* Background made by Olivia Harmon,
56 | * https://www.toptal.com/designers/subtlepatterns/papyrus-pattern/
57 | */
58 | background-image: url(${passportPattern});
59 | }
60 |
61 | animation: ${({ single }) => single ? css`${jump} 2.5s infinite linear` : 'none'};
62 | `;
63 |
64 | export const PassportLabel = styled(Text).attrs(() => ({
65 | color: "white",
66 | fontSize: 0,
67 | textAlign: "center"
68 | }))`
69 | text-transform: uppercase;
70 | `;
71 |
72 | export const PassportCountry = styled(Text).attrs(() => ({
73 | color: "white",
74 | fontSize: 4,
75 | fontWeight: 4,
76 | textAlign: "center"
77 | }))``;
78 |
79 | export const PassportName = styled(Text).attrs(() => ({
80 | color: "white",
81 | fontSize: 2,
82 | fontWeight: 2,
83 | textAlign: "center",
84 | opacity: 0.5
85 | }))``;
86 |
87 | const IconContainer = styled(Flex).attrs(() => ({
88 | bg: "#eee",
89 | p: 1
90 | }))`
91 | position: absolute;
92 | cursor: pointer;
93 | top: 10px;
94 | right: 10px;
95 | border-radius: 30px;
96 | `;
97 |
98 | export const IconClose = ({ onClick }) => {
99 | return (
100 |
101 |
102 |
103 | );
104 | };
105 |
106 | export const Loading = styled(Text).attrs(()=>({
107 | fontSize: 2,
108 | color: "silver"
109 | }))``;
110 |
--------------------------------------------------------------------------------
/config/paths.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const fs = require('fs');
5 | const url = require('url');
6 |
7 | // Make sure any symlinks in the project folder are resolved:
8 | // https://github.com/facebook/create-react-app/issues/637
9 | const appDirectory = fs.realpathSync(process.cwd());
10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
11 |
12 | const envPublicUrl = process.env.PUBLIC_URL;
13 |
14 | function ensureSlash(inputPath, needsSlash) {
15 | const hasSlash = inputPath.endsWith('/');
16 | if (hasSlash && !needsSlash) {
17 | return inputPath.substr(0, inputPath.length - 1);
18 | } else if (!hasSlash && needsSlash) {
19 | return `${inputPath}/`;
20 | } else {
21 | return inputPath;
22 | }
23 | }
24 |
25 | const getPublicUrl = appPackageJson =>
26 | envPublicUrl || require(appPackageJson).homepage;
27 |
28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer
29 | // "public path" at which the app is served.
30 | // Webpack needs to know it to put the right
115 |
116 |