Initial design & interactions

Began porting some function and state names to more generic descriptors
like inputToken & outputToken thats align more closely with the
proposed UX model in this design. Needs discussion.
This commit is contained in:
Callil Capuozzo 2018-01-18 14:11:51 -05:00
parent 1086f4c9bb
commit a9b8740734
37 changed files with 10393 additions and 12511 deletions

21
.gitignore vendored Normal file

@ -0,0 +1,21 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

2436
README.md

File diff suppressed because it is too large Load Diff

11487
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,25 +1,20 @@
{
"name": "uniswap",
"homepage": ".",
"version": "0.1.0",
"private": true,
"dependencies": {
"metamask-logo": "^2.1.3",
"ramda": "^0.25.0",
"react": "^16.1.1",
"react-dom": "^16.1.1",
"react-scripts": "1.0.14",
"react-web3": "^0.4.3",
"web3": "^1.0.0-beta.26"
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-helmet": "^5.2.0",
"react-scripts": "1.1.0",
"react-select": "^1.2.1",
"react-web3": "^0.4.4",
"web3": "1.0.0-beta.18"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"deploy": "npm run build&&gh-pages -d build"
},
"devDependencies": {
"gh-pages": "^1.0.0"
"eject": "react-scripts eject"
}
}

@ -1 +0,0 @@
uniswap.io

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

@ -1,4 +1,4 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
@ -8,12 +8,8 @@
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="./favicon.ico">
<!-- <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"> -->
<!-- <link rel="manifest" href="%PUBLIC_URL%/manifest.json"> -->
<link rel="shortcut icon" href="%PUBLIC_URL%/🦄.ico">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.

@ -4,8 +4,8 @@
"icons": [
{
"src": "favicon.ico",
"sizes": "192x192",
"type": "image/png"
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": "./index.html",

BIN
public/🦄.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

@ -1,49 +0,0 @@
pragma solidity 0.4.18;
/// @title SafeMath
/// @dev Math operations with safety checks that throw on error
library SafeMath {
/// @dev Multiplies a times b
function mul(uint256 a, uint256 b)
internal
pure
returns (uint256)
{
uint256 c = a * b;
assert(a == 0 || c / a == b);
return c;
}
/// @dev Divides a by b
function div(uint256 a, uint256 b)
internal
pure
returns (uint256)
{
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/// @dev Subtracts a from b
function sub(uint256 a, uint256 b)
internal
pure
returns (uint256)
{
assert(b <= a);
return a - b;
}
/// @dev Adds a to b
function add(uint256 a, uint256 b)
internal
pure
returns (uint256)
{
uint256 c = a + b;
assert(c >= a);
return c;
}
}

@ -1,164 +0,0 @@
pragma solidity ^0.4.18;
contract ERC20Token {
uint256 public totalSupply;
function balanceOf(address who) public constant returns (uint256);
function transfer(address to, uint256 value) public returns (bool);
function allowance(address owner, address spender) public constant returns (uint256);
function transferFrom(address from, address to, uint256 value) public returns (bool);
function approve(address spender, uint256 value) public returns (bool);
event Approval(address indexed owner, address indexed spender, uint256 value);
event Transfer(address indexed from, address indexed to, uint256 value);
}
library SafeMath {
function mul(uint256 a, uint256 b) internal constant returns (uint256) {
uint256 c = a * b;
assert(a == 0 || c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal constant returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function sub(uint256 a, uint256 b) internal constant returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal constant returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
contract StandardToken is ERC20Token {
using SafeMath for uint256;
mapping(address => uint256) balances;
mapping (address => mapping (address => uint256)) internal allowed;
function transfer(address _to, uint256 _value) public returns (bool) {
require(_to != address(0));
require(_value <= balances[msg.sender]);
// SafeMath.sub will throw if there is not enough balance.
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
Transfer(msg.sender, _to, _value);
return true;
}
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
require(_to != address(0));
require(_value <= balances[_from]);
require(_value <= allowed[_from][msg.sender]);
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(_value);
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
Transfer(_from, _to, _value);
return true;
}
function approve(address _spender, uint256 _value) public returns (bool) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
function allowance(address _owner, address _spender) public constant returns (uint256 remaining) {
return allowed[_owner][_spender];
}
function increaseApproval (address _spender, uint _addedValue) public returns (bool success) {
allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
function decreaseApproval (address _spender, uint _subtractedValue) public returns (bool success) {
uint oldValue = allowed[msg.sender][_spender];
if (_subtractedValue > oldValue) {
allowed[msg.sender][_spender] = 0;
} else {
allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
}
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
function balanceOf(address _owner) public constant returns (uint256 balance) {
return balances[_owner];
}
}
contract Ownable {
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
address public owner;
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function Ownable() {
owner = msg.sender;
}
function transferOwnership(address newOwner) onlyOwner public {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
contract MintableToken is StandardToken, Ownable {
event Mint(address indexed to, uint256 amount);
event MintFinished();
bool public mintingFinished = false;
modifier canMint() {
require(!mintingFinished);
_;
}
/**
* @dev Function to mint tokens
* @param _to The address that will receive the minted tokens.
* @param _amount The amount of tokens to mint.
* @return A boolean that indicates if the operation was successful.
*/
function mint(address _to, uint256 _amount) onlyOwner canMint public returns (bool) {
totalSupply = totalSupply.add(_amount);
balances[_to] = balances[_to].add(_amount);
Mint(_to, _amount);
Transfer(0x0, _to, _amount);
return true;
}
/**
* @dev Function to stop minting new tokens.
* @return True if the operation was successful.
*/
function finishMinting() onlyOwner public returns (bool) {
mintingFinished = true;
MintFinished();
return true;
}
}
contract uniTestToken is MintableToken {
string public constant name = "UNI Test Token";
string public constant symbol = "UNT";
uint8 public constant decimals = 6;
}

@ -1,162 +0,0 @@
pragma solidity ^0.4.18;
/// @title SafeMath
/// @dev Math operations with safety checks that throw on error
library SafeMath {
/// @dev Multiplies a times b
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a * b;
assert(a == 0 || c / a == b);
return c;
}
/// @dev Divides a by b
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/// @dev Subtracts a from b
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
/// @dev Adds a to b
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
contract Ownable {
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
address public owner;
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function Ownable() public {
owner = msg.sender;
}
function transferOwnership(address newOwner) onlyOwner public {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
contract ERC20Token {
uint256 public totalSupply;
function balanceOf(address who) public constant returns (uint256);
function transfer(address to, uint256 value) public returns (bool);
function allowance(address owner, address spender) public constant returns (uint256);
function transferFrom(address from, address to, uint256 value) public returns (bool);
function approve(address spender, uint256 value) public returns (bool);
event Approval(address indexed owner, address indexed spender, uint256 value);
event Transfer(address indexed from, address indexed to, uint256 value);
}
contract uniswap is Ownable{
using SafeMath for uint256;
event TokenPurchase(address indexed buyer, uint256 tokensPurchased, uint256 ethSpent);
event EthPurchase(address indexed buyer, uint256 ethPurchased, uint256 tokensSpent);
uint256 public totalEthQuantity;
uint256 public totalTokenQuantity;
uint256 public invariant = 0;
address public tokenAddress;
ERC20Token token;
function uniswap(address _tokenAddress) public payable {
tokenAddress = _tokenAddress;
token = ERC20Token(tokenAddress);
totalEthQuantity = msg.value;
}
function initiateUniswap(uint256 initialTokenQuantity) public onlyOwner {
require(invariant == 0);
token.transferFrom(msg.sender, address(this), initialTokenQuantity);
totalTokenQuantity = initialTokenQuantity;
invariant = initialTokenQuantity.mul(totalEthQuantity);
require(invariant > 0);
}
function ethToTokens(uint256 minimumTokens, uint256 timeout) public payable {
require(msg.value != 0 && timeout != 0);
uint256 fee = msg.value/500;
uint256 ethInPurchase = msg.value.sub(fee);
uint256 newTotalEth = totalEthQuantity.add(ethInPurchase);
uint256 newTotalTokens = invariant/newTotalEth;
uint256 purchasedTokens = totalTokenQuantity.sub(newTotalTokens);
require(purchasedTokens >= minimumTokens);
require(now < timeout);
token.transfer(msg.sender, purchasedTokens);
totalEthQuantity = newTotalEth;
totalTokenQuantity = newTotalTokens;
TokenPurchase(msg.sender, purchasedTokens, ethInPurchase);
}
function tokenToEth(uint256 sellQuantity, uint256 minimumEth, uint256 timeout) public {
require(sellQuantity!=0 && minimumEth != 0 && timeout != 0);
token.transferFrom(msg.sender, address(this), sellQuantity);
uint256 fee = sellQuantity/500;
uint256 tokensInPurchase = sellQuantity - fee;
uint256 newTotalTokens = totalTokenQuantity.add(tokensInPurchase);
uint256 newTotalEth = invariant/newTotalTokens;
uint256 purchasedEth = totalEthQuantity.sub(newTotalEth);
require(purchasedEth >= minimumEth);
require(now < timeout);
msg.sender.transfer(purchasedEth);
totalTokenQuantity = newTotalTokens;
totalEthQuantity = newTotalEth;
EthPurchase(msg.sender, purchasedEth, tokensInPurchase);
}
function ownerTokenDeposit(uint256 tokenAmount) public onlyOwner {
require(tokenAmount !=0);
token.transferFrom(msg.sender, address(this), tokenAmount);
totalTokenQuantity = totalTokenQuantity.add(tokenAmount);
invariant = totalTokenQuantity.mul(totalEthQuantity);
}
function ownerEthDeposit() public payable onlyOwner {
require(msg.value != 0);
totalEthQuantity = totalEthQuantity.add(msg.value);
invariant = totalEthQuantity.mul(totalTokenQuantity);
}
function ownerTokenWithdraw(uint256 tokenAmount) public onlyOwner {
require(tokenAmount !=0);
token.transfer(msg.sender, tokenAmount);
totalTokenQuantity = totalTokenQuantity.sub(tokenAmount);
invariant = totalTokenQuantity.mul(totalEthQuantity);
}
function ownerEthWithdraw(uint256 ethAmount) public onlyOwner {
require(ethAmount !=0);
msg.sender.transfer(ethAmount);
totalEthQuantity = totalEthQuantity.sub(ethAmount);
invariant = totalEthQuantity.mul(totalTokenQuantity);
}
}

@ -1,200 +1,152 @@
@font-face {
font-family: 'LeveloLineBold'; /*a name to be used later*/
src: url('./fonts/LoveloLineBold.otf'); /*URL to font*/
body{
margin: 0;
padding: 0;
font-family: "Inter UI", sans-serif;
font-size: 24px;
line-height: 1.2;
}
@keyframes blinker {
50% { opacity: 0.55; }
input{
font-size: 24px;
line-height: 1.2;
}
.App {
text-align: center;
background-size: 100%;
height:100vh;
background-image: url('./images/background.jpg');
background-repeat: repeat-y;
font-family: Optima, sans-serif;
p{
margin: 0px;
}
.noICO {
position:fixed;
font-size: 14px;
top:3vh;
left: 40vw;
right: 28vw;
color: rgb(216, 169, 236);
text-shadow: 1px 1px 5px #ff69f0;
/* .app{
margin: 10rem;
margin-top: 5rem;
} */
.pa2 {
padding: 3rem;
}
.ethLogo {
position: fixed;
right: 22vh;
position: fixed;
height: 10vh;
z-index: 1;
margin-top: 1vh;
margin-right: 5vh;
opacity: 0.6;
-webkit-filter: invert(.8);
filter: invert(.8);
-moz-user-select: -moz-none;
-khtml-user-select: none;
-webkit-user-select: none;
-o-user-select: none;
user-select: none;
}.ethLogo:hover {
opacity: 1;
.title{
display: flex;
flex-direction: row;
}
.border{
border: 1px solid #f2f2f2;
}
.title {
width: 100%;
}
.logo{
/* width: 10vw; */
display: flex;
justify-content: center;
align-items: center;
}
.connection{
display: flex;
justify-content: space-between;
align-items: center;
color: #27AE60;
flex: 1;
}
.unicorn {
position: fixed;
right: 0;
clear: both;
z-index: 1;
height:25vh;
opacity: 0.9;
-moz-user-select: -moz-none;
-khtml-user-select: none;
-webkit-user-select: none;
-o-user-select: none;
user-select: none;
}.unicorn:hover {
opacity: 1;
.connection a{
text-decoration: none;
color: #27AE60;
}
.Warning {
position: fixed;
z-index: 3;
text-align: center;
left: 35vw;
color: rgb(238, 0, 0);
text-shadow: 2px 2px 10px #2daae0;
animation: blinker 1s linear infinite;
.order {
width: 100%;
display: flex;
flex-direction: row;
/* justify-content: space-between; */
}
.Account-info {
position: fixed;
z-index: 2;
text-align: left;
font-size: 14px;
top: 13vh;
left: 40vw;
color: rgb(122, 251, 255);
text-shadow: 1px 1px 5px #2daae0;
.arrow{
max-width: 100vw;
display: flex;
justify-content: center;
align-items: center;
flex: 1;
}
.value{
width: 35vw;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: auto;
text-overflow: ellipsis;
-webkit-transition-duration: 0.5s;
transition-duration: 0.5s;
}.Account-info:hover{
color: rgb(209, 151, 245);
text-shadow: 1px 1px 5px #ff69f0;
display: flex;
justify-content: space-between;
align-items: center;
}
.value input, select{
width: 100%;
height: 100%;
border: none;
}
.value input:focus{
outline: none;
}
.pinkText {
position: fixed;
font-size: 16px;
z-index: 3;
color: rgb(216, 169, 236);
text-shadow: 1px 1px 5px #ff69f0;
text-align: left;
-webkit-transition-duration: 0.5s;
transition-duration: 0.5s;
/*overflow-x: auto;*/
}.pinkText:hover{
color: rgb(122, 251, 255);
text-shadow: 1px 1px 5px #2daae0;
.dropdown{
transform: rotate(-90deg);
opacity: .4;
position: relative;
z-index: -99;
left: -5px;
}
.approveButtonContainer{
position:fixed;
z-index: 3;
right: 10vw;
top: 30vh;
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
.approveButton {
position: fixed;
font-family: Optima, sans-serif;
font-size: 18px;
font-weight: bolder;
border-radius: 8px;
padding: 4px 8px;
color: rgb(220, 160, 245);
background-color: rgba(126, 228, 255, 0.25);
border: 2px solid rgba(180, 81, 179, 0.55);
-webkit-transition-duration: 0.5s;
transition-duration: 0.5s;
}.approveButton:hover {
color: rgb(122, 251, 255);
border: 2px solid rgb(122, 251, 255);
text-shadow: 1px 1px 5px #2daae0;
cursor: pointer;
.rate-info{
display: flex;
flex-direction: row;
justify-content: space-between;
}
.exchange {
position: fixed;
font-family: Optima, sans-serif;
font-size: 18px;
color: rgb(122, 251, 255);
text-shadow: 1px 1px 5px #2daae0;
.info{
display: flex;
flex-direction: row;
justify-content: space-between;
}
.exchange-buyTokens {
margin-left: 35vw;
.links{
display: flex;
flex-direction: row;
justify-content: center;
}
.exchange-buyEth {
margin-left: 60vw;
.link{
display: flex;
flex-direction: row;
justify-content: space-between;
flex: 1;
text-decoration: none;
color: black;
}
.exchange-buyEth, .exchange-buyTokens {
position: fixed;
float: left;
bottom: 72vh;
.link:hover .underline{
text-decoration: underline;
}
.exchange-buyEthInput, .exchange-buyTokensInput{
width: 10vw;
height: 4.2vh;
text-align: center;
color: rgb(209, 151, 245);
vertical-align: middle;
border: 1px solid rgba(180, 81, 179, 0.55);
border-radius: 8px 0 0 8px;
background-color: rgba(122, 251, 255, 0.15);
font-size: 18px;
font-family: Optima, sans-serif;
-webkit-transition-duration: 0.8s;
transition-duration: 0.8s;
}.exchange-buyEthInput:hover, .exchange-buyTokensInput:hover{
border: 1px solid rgb(122, 251, 255);
.swap{
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
background-color: #f2f2f2;
color: blue;
height: 48px;
transition: height .3s ease;
}
.exchange-buyEthButton, .exchange-buyTokensButton{
position: fixed;
font-size: 1.55vw;
width: 7vw;
text-shadow: 3px 3px 10px #1c5f7c;
margin-top: 10px;
height: 4.2vh;
text-align: center;
color: rgb(220, 160, 245);
text-shadow: 1px 1px 5px #ff69f0;
background-color: rgba(126, 228, 255, 0.25);
vertical-align: middle;
margin-top: 0px;
border: 1px solid rgb(180, 81, 179);
border-radius: 0 8px 8px 0;
font-family: Optima, sans-serif;
-webkit-transition-duration: 0.8s;
transition-duration: 0.8s;
}.exchange-buyEthButton:hover, .exchange-buyTokensButton:hover{
cursor: pointer;
color: rgb(122, 251, 255);
text-shadow: 2px 2px 10px #1c5f7c;
border: 1px solid rgb(122, 251, 255);
.red{
color: red;
}
.dim .order, .rate{
opacity: .4;
}
.hidden{
padding: 0px;
height: 0px;
border: 0px solid #f2f2f2;
}

@ -1,62 +1,65 @@
import React, { Component } from 'react';
import Web3 from 'web3';
import Head from './components/Head'
import NetworkStatus from './components/NetworkStatus'
import HelperMessages from './components/HelperMessages'
import SelectToken from './components/SelectToken'
import './App.css';
import Title from './components/misc/Title';
import Instructions from './components/menus/Instructions';
import unicorn from './images/unicornNoBackground.png';
import ethLogo from './images/ethLogo.png';
import {uniswapABI, tokenABI} from './helpers/abi.js'
var localweb3;
class App extends Component {
constructor(props){
constructor (props) {
super(props)
if (typeof props.metamask !== 'undefined'){
localweb3 = new Web3(window.web3.currentProvider)
} else {
localweb3 = null
}
localweb3 = new Web3(window.web3.currentProvider);
var uniswapABI = [{"constant":false,"inputs":[{"name":"tokenAmount","type":"uint256"}],"name":"ownerTokenWithdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"tokenAmount","type":"uint256"}],"name":"ownerTokenDeposit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"ethAmount","type":"uint256"}],"name":"ownerEthWithdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"sellQuantity","type":"uint256"},{"name":"minimumEth","type":"uint256"},{"name":"timeout","type":"uint256"}],"name":"tokenToEth","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalTokenQuantity","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"minimumTokens","type":"uint256"},{"name":"timeout","type":"uint256"}],"name":"ethToTokens","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"initialTokenQuantity","type":"uint256"}],"name":"initiateUniswap","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalEthQuantity","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"invariant","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"ownerEthDeposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_tokenAddress","type":"address"}],"payable":true,"stateMutability":"payable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"buyer","type":"address"},{"indexed":false,"name":"tokensPurchased","type":"uint256"},{"indexed":false,"name":"ethSpent","type":"uint256"}],"name":"TokenPurchase","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"buyer","type":"address"},{"indexed":false,"name":"ethPurchased","type":"uint256"},{"indexed":false,"name":"tokensSpent","type":"uint256"}],"name":"EthPurchase","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}]
const uniswapAddress = '0x60e5f3cd0381c501971b6fbbddaa49cfd58a4fa1';
const uniContract = new localweb3.eth.Contract(uniswapABI, uniswapAddress);
var tokenABI = [{"constant":true,"inputs":[],"name":"mintingFinished","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"mint","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_subtractedValue","type":"uint256"}],"name":"decreaseApproval","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"finishMinting","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_addedValue","type":"uint256"}],"name":"increaseApproval","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[],"name":"MintFinished","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}]
const tokenAddress = '0xca9901076d02f89794262869aad1340bd45d8489';
const tokContract = new localweb3.eth.Contract(tokenABI, tokenAddress);
const tokenContract = new localweb3.eth.Contract(tokenABI, tokenAddress);
this.state = {uniswapAddress: '0x60e5f3cd0381c501971b6fbbddaa49cfd58a4fa1',
tokenAddress: '0xca9901076d02f89794262869aad1340bd45d8489',
uniswapContract: uniContract,
tokenContract: tokContract,
ethBalance: 0,
tokenBalance: 0,
tokenAllowance: null,
currentMaskAddress: '0x0000000000000000000000000000000000000000',
minimumTokensPurchased: null,
minimumEthPurchased: null,
invariant: 0,
marketEth: 0,
marketTokens: 0,
tokenBuyRate: 0,
ethBuyRate: 0,
tokenCost: 0, //eth price of tokens
ethCost: 0, //token price of eth
tokenFee: 0,
ethFee: 0,
networkMessage: '',
locked: false
this.state = {
uniswapAddress: '0x60e5f3cd0381c501971b6fbbddaa49cfd58a4fa1',
tokenAddress: '0xca9901076d02f89794262869aad1340bd45d8489',
uniswapContract: uniContract,
tokenContract: tokenContract,
ethBalance: 0,
tokenBalance: 0,
tokenAllowance: null,
currentMaskAddress: '0x0000000000000000000000000000000000000000',
minimumTokensPurchased: null,
minimumEthPurchased: null,
invariant: 0,
marketEth: 0,
marketTokens: 0,
tokenBuyRate: 0,
ethBuyRate: 0,
tokenCost: 0, //eth price of tokens
ethCost: 0, //token price of eth
tokenFee: 0,
ethFee: 0,
networkMessage: '',
locked: false,
connected: false,
interaction: 'disconnected',
input: 0,
output: 0,
inputToken: { value: 'ETH', label: 'ETH', clearableValue: false },
outputToken: { value: 'OMG', label: 'OMG', clearableValue: false }
}
this.onBuyEthInputChange = this.onBuyEthInputChange.bind(this);
this.onBuyTokensInputChange = this.onBuyTokensInputChange.bind(this);
this.tokenBuyRate = this.tokenBuyRate.bind(this);
this.ethBuyRate = this.ethBuyRate.bind(this);
// this.isMetaMaskLocked = this.isMetaMaskLocked.bind(this);
}
componentWillMount(){
@ -87,54 +90,18 @@ class App extends Component {
}, 1000);
}
checkNetwork() {
var self = this;
localweb3.eth.net.getNetworkType((err, netId) => {
switch (netId) {
case "main":
self.setState({networkMessage: 'MetaMask connected to Ethereum Mainet. Switch to Rinkeby and refresh!'});
break
case "morden":
self.setState({networkMessage: 'MetaMask connected to Morden testnet. Switch to Rinkeby and refresh!'});
break
case "kovan":
self.setState({networkMessage: 'MetaMask connected to Kovan testnet. Switch to Rinkeby and refresh!'});
break
case "ropsten":
self.setState({networkMessage: 'MetaMask connected to Ropstein testnet. Switch to Rinkeby and refresh!'})
break
default:
console.log('Connected to ' + netId);
}
})
}
approveAllowance(value) {
this.state.tokenContract.methods.approve(this.uniswapAddress, value).send(
{from: this.state.currentMaskAddress},
function(err, txHash) {})
}
getMetaMaskAddress() {
var self = this;
localweb3.eth.getAccounts().then(function(result, error){
var address = result[0];
if (address === undefined) {
console.log('MetaMask locked');
alert('Found MetaMask but no account. Please unlock MetaMask and refresh')
}
else {
self.setState({currentMaskAddress: address})
}
});
localweb3.eth.getAccounts(function(error, result){
if(!error)
self.setState({currentMaskAddress: result[0]})
else
self.setState({locked: true})
})
}
getEthBalance() {
var self = this;
localweb3.eth.getBalance(this.state.currentMaskAddress, function(error, balance) {
var ethValue = (balance/10**18);
var roundedValue=Math.round(ethValue*10000)/10000;
@ -144,13 +111,6 @@ class App extends Component {
getTokenBalance() {
var self = this;
// tokenContract.methods.balanceOf(this.state.currentMaskAddress).call().then(function(result, error){
// var amount = result/10**6
// console.log(result);
// self.setState({tokenBalance: amount});
// })
this.state.tokenContract.methods.balanceOf(this.state.currentMaskAddress).call(function(error, balance) {
var amount = balance/10**6;
self.setState({tokenBalance: amount});
@ -159,37 +119,116 @@ class App extends Component {
getAllowance() {
var self = this;
// this.state.tokenContract.methods.allowance(this.state.currentMaskAddress, this.uniswapAddress).call().then(function(result, error){
// var amount = result/10**6
// self.setState({tokenAllowance: amount});
// })
}
this.state.tokenContract.methods.allowance(this.state.currentMaskAddress, this.uniswapAddress).call().then(function(result, error){
var amount = result/10**6
self.setState({tokenAllowance: amount});
checkNetwork() {
var self = this;
localweb3.eth.net.getNetworkType((err, networkId) => {
console.log(networkId)
switch (networkId) {
case "main":
self.setState({networkMessage: 'Ethereum Mainet', connected: false, interaction: 'disconnected'});
break;
case "morden":
self.setState({networkMessage: 'Morden testnet', connected: false, interaction: 'disconnected'});
break;
case "ropsten":
self.setState({networkMessage: 'Ropsten testnet', connected: false, interaction: 'disconnected'});
break;
case "rinkeby":
self.setState({networkMessage: '', connected: true, interaction: 'connected'});
break;
case "kovan":
self.setState({networkMessage: 'Kovan testnet', connected: false, interaction: 'disconnected'});
break;
default:
self.setState({networkMessage: 'an Unknown network', connected: false, interaction: 'disconnected'});
}
})
}
getInvarient() {
var self = this;
this.state.uniswapContract.methods.invariant().call().then(function(result, error){
self.setState({invariant: result});
})
}
getMarketEth() {
var self = this
this.state.uniswapContract.methods.totalEthQuantity().call().then(function(result, error){
self.setState({marketEth: result});
})
}
getMarketTokens() {
var self = this
this.state.uniswapContract.methods.totalTokenQuantity().call().then(function(result, error){
self.setState({marketTokens: result});
})
}
tokenBuyRate(buyTokensInput) {
if(buyTokensInput >= this.state.marketTokens/10**6) {
this.setState(
{tokenBuyRate: 0,
tokenCost: 0,
tokenFee: 0,
tokenBuyError: 'Not enough tokens'
});
}
else{
var tokensPurchased = buyTokensInput;
var invar = this.state.invariant/10**24;
var totalTokens = this.state.marketTokens/10**6;
var totalEth = this.state.marketEth/10**18;
var newTotalEth = invar/(totalTokens-tokensPurchased);
var fee = (newTotalEth - totalEth)/500;
var ethRequired = newTotalEth - totalEth + fee;
var rate = tokensPurchased/ethRequired;
//add 1% to cost displayed, some people will get more tokens than purchased
//less TX's will fail the minTokens smart contract check
var adjustedTokenCost = ethRequired*1.01;
this.setState(
{tokenBuyRate: rate,
tokenCost: adjustedTokenCost,
tokenFee: fee
});
}
}
ethBuyRate(buyEthInput) {
if(buyEthInput >= this.state.marketEth/10**18) {
this.setState(
{ethBuyRate: 0,
ethCost: 0,
ethFee: 0,
ethBuyError: 'Not enough tokens'
});
}
else{
var ethPurchased = buyEthInput;
var invar = this.state.invariant/10**24;
var totalEth = this.state.marketEth/10**18;
var totalTokens = this.state.marketTokens/10**6;
var newTotalTokens = invar/(totalEth-ethPurchased);
var fee = (newTotalTokens - totalTokens)/500;
var tokensRequired = newTotalTokens - totalTokens + fee;
var rate = ethPurchased/(tokensRequired);
//add 1% to cost displayed, some people will get more eth than purchased
//less TX's will fail the minEth smart contract check
var adjustedEthCost = tokensRequired*1.01;
this.setState(
{ethBuyRate: rate,
ethCost: adjustedEthCost,
ethFee: fee
});
}
}
buyTokens() {
var self = this;
var minTokens = this.state.minimumTokensPurchased
@ -220,7 +259,19 @@ class App extends Component {
});
}
onBuyTokensInputChange(event) {
onSelectToken = (selected, type) => {
console.log(selected)
console.log(type)
if (type === 'input'){
this.setState({inputToken: selected});
// do something here to update invariants and values
} else if (type === 'output'){
this.setState({outputToken: selected});
// do something here to update invariants and values
}
}
onBuyTokensInputChange = (event) => {
var buyTokensInput = event.target.value;
if(buyTokensInput && buyTokensInput !== 0){
this.setState({ minimumTokensPurchased: buyTokensInput });
@ -228,117 +279,79 @@ class App extends Component {
}
}
onBuyEthInputChange(event) {
onBuyEthInputChange = (event) => {
var buyEthInput = event.target.value;
if(buyEthInput && buyEthInput !== 0){
this.setState({ minimumEthPurchased: buyEthInput });
this.setState({ minimumEthPurchased: buyEthInput, input: buyEthInput, output: this.state.tokenBuyRate.toFixed(3)*buyEthInput, interaction: 'input'});
this.ethBuyRate(buyEthInput);
} else {
this.setState({input: buyEthInput, output: this.state.tokenBuyRate.toFixed(3)*buyEthInput, interaction: 'connected'});
}
}
tokenBuyRate(buyTokensInput) {
if(buyTokensInput >= this.state.marketTokens/10**6) {
this.setState({tokenBuyRate: 0,
tokenCost: 0,
tokenFee: 0,
tokenBuyError: 'Not enough tokens'
});
}
else{
var tokensPurchased = buyTokensInput;
var invar = this.state.invariant/10**24;
var totalTokens = this.state.marketTokens/10**6;
var totalEth = this.state.marketEth/10**18;
var newTotalEth = invar/(totalTokens-tokensPurchased);
var fee = (newTotalEth - totalEth)/500;
var ethRequired = newTotalEth - totalEth + fee;
var rate = tokensPurchased/ethRequired;
//add 1% to cost displayed, some people will get more tokens than purchased
//less TX's will fail the minTokens smart contract check
var adjustedTokenCost = ethRequired*1.01;
this.setState({tokenBuyRate: rate,
tokenCost: adjustedTokenCost,
tokenFee: fee
});
}
}
ethBuyRate(buyEthInput) {
if(buyEthInput >= this.state.marketEth/10**18) {
this.setState({ethBuyRate: 0,
ethCost: 0,
ethFee: 0,
ethBuyError: 'Not enough tokens'
});
}
else{
var ethPurchased = buyEthInput;
var invar = this.state.invariant/10**24;
var totalEth = this.state.marketEth/10**18;
var totalTokens = this.state.marketTokens/10**6;
var newTotalTokens = invar/(totalEth-ethPurchased);
var fee = (newTotalTokens - totalTokens)/500;
var tokensRequired = newTotalTokens - totalTokens + fee;
var rate = ethPurchased/(tokensRequired);
//add 1% to cost displayed, some people will get more eth than purchased
//less TX's will fail the minEth smart contract check
var adjustedEthCost = tokensRequired*1.01;
this.setState({ethBuyRate: rate,
ethCost: adjustedEthCost,
ethFee: fee
});
}
}
render() {
console.log(localweb3)
return (
<div className="App">
<Title />
<div className="noICO">UNI is an ERC20 test token. This is not an ICO.</div>
<img src={unicorn} className="unicorn" alt="unicorn"/>
<img src={ethLogo} className="ethLogo" alt = "ethLogo"/>
<div className="Warning">{this.state.networkMessage}</div>
<div className="Account-info">
Account Detected:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{this.state.ethBalance} ETH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
{this.state.tokenBalance.toFixed(2)} UNI<br/>
{this.state.currentMaskAddress}
&nbsp;&nbsp;
<br/>
</div>
<Instructions />
<div className="approveButtonContainer">
<button className="approveButton" onClick={() => {this.approveAllowance(20000*10**6) }}>Approve</button><br/><br/>
{/*Tokens approved: {this.state.tokenAllowance}&nbsp;&nbsp;&nbsp;*/}
</div>
<div className="exchange">
<div className="exchange-buyTokens">
<input
className="exchange-buyTokensInput"
//value={this.state.value}
onChange={this.onBuyTokensInputChange}
/>
<input className="exchange-buyTokensButton" type="exchange-button" defaultValue="Buy UNI" readOnly="readOnly" onClick={() => {this.buyTokens() }}/>
<p className="pinkText">
&nbsp;&nbsp;Rate :&nbsp;&nbsp;{this.state.tokenBuyRate.toFixed(3)} UNI/ETH<br/>
&nbsp;&nbsp;Cost :&nbsp;&nbsp;{this.state.tokenCost.toFixed(5)} ETH<br/>
&nbsp;&nbsp;Fee :&nbsp;&nbsp;&nbsp;&nbsp;{this.state.tokenFee.toFixed(5)} ETH<br/>
</p>
<div className={this.state.connected ? "App" : "App dim"}>
<Head />
<section className="title">
<div className="logo border pa2">
<span role="img" aria-label="Unicorn">🦄</span>
</div>
<div className="exchange-buyEth">
<input
className="exchange-buyEthInput"
//value={this.state.value}
onChange={this.onBuyEthInputChange}
/>
<input className="exchange-buyEthButton" type="exchange-button" defaultValue="Buy ETH" readOnly="readOnly" onClick={() => {this.buyEth() }}/>
<p className="pinkText">
&nbsp;&nbsp;Rate :&nbsp;&nbsp;{this.state.ethBuyRate.toFixed(4)} ETH/UNI<br/>
&nbsp;&nbsp;Cost :&nbsp;&nbsp;{this.state.ethCost.toFixed(5)} UNI<br/>
&nbsp;&nbsp;Fee :&nbsp;&nbsp;&nbsp;&nbsp;{this.state.ethFee.toFixed(5)} UNI
</p>
<NetworkStatus network={this.state.networkMessage} connected={this.state.connected} address={this.state.currentMaskAddress}/>
</section>
<HelperMessages interaction={this.state.interaction} inputToken={this.state.inputToken} outputToken={this.state.outputToken}/>
<section className="order">
<div className="value border pa2">
<input type="number" value={this.state.input} placeholder="0" onChange={this.onBuyEthInputChange} onChange={this.onBuyEthInputChange} />
<SelectToken token={this.state.inputToken} onSelectToken={this.onSelectToken} type="input" />
<p className="dropdown">{'<'}</p>
</div>
</div>
<div className="arrow border pa2">
<p></p>
</div>
<div className="value border pa2">
<input type="number" value={this.state.output} placeholder="0" onChange={this.onBuyTokensInputChange}/>
<SelectToken token={this.state.outputToken} onSelectToken={this.onSelectToken} type="output"/>
<p className="dropdown">{'<'}</p>
</div>
</section>
<section className="rate border pa2">
<span className="rate-info">
<p>Rate</p>
<p>{this.state.tokenBuyRate.toFixed(3)} UNI/ETH</p>
</span>
<span className="rate-info">
<p>Cost</p>
<p>{this.state.tokenCost.toFixed(5)} ETH</p>
</span>
<span className="rate-info">
<p>Fee</p>
<p>{this.state.tokenFee.toFixed(5)} ETH</p>
</span>
</section>
{this.state.interaction === 'input' ?
<section className="swap border pa2">
<a href="">{"I want to swap " + this.state.input + " " + this.state.inputToken.value + " for " + this.state.output + " " + this.state.outputToken.value}</a>
</section>
: <section className="swap hidden border pa2"></section>}
<section className="links">
<a href="" className="link border pa2">
<p className="underline">Provide Liquidity to collect fees</p>
<p>+</p>
</a>
<a href="" className="link border pa2">
<p className="underline">Add a new token</p>
<p>+</p>
</a>
</section>
<section className="links">
<a href="" className="link border pa2">
<p className="underline">About</p>
<p></p>
</a>
</section>
</div>
);
}

9
src/App.test.js Normal file

@ -0,0 +1,9 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});

16
src/components/Head.js Normal file

@ -0,0 +1,16 @@
import React from 'react';
import {Helmet} from "react-helmet";
import unicorn from '../images/🦄.png'
function Head(props) {
return (
<Helmet>
<meta charSet="utf-8" />
<link rel="icon" href={unicorn} sizes="32x32" type="image/png" />
<title>Uniswap - ERC20 Market Maker</title>
</Helmet>
);
}
export default Head;

@ -0,0 +1,24 @@
import React from 'react';
function HelperMessages(props) {
let message = ''
switch (props.interaction) {
case 'connected':
message = "Nice! You're connected. Enter a value below to get started."
break;
case 'input':
message = "You're swapping " + props.inputToken.value + " for " + props.outputToken.value + ". Want to know more about how the prices are determined?"
// message = "Oops, looks like this address doesn't have a enough " + props.inputToken.value + " to make this trade. Add more funds to make this swap."
break;
default:
message = "Hi there! This site helps you swap ERC20 tokens. Looks like you aren't connected. Need help?"
}
return (
<section className="info border pa2">
<p>{message}</p>
<p></p>
</section>
)
}
export default HelperMessages;

@ -0,0 +1,22 @@
import React from 'react';
function NetworkStatus(props) {
let isConnected = props.connected
if (isConnected){
return (
<div className="connection border pa2">
<a href={'https://etherscan.io/search?q=' + props.address}>{props.address}</a>
<p></p>
</div>
)
} else {
return (
<div className="connection red border pa2">
<p>{'MetaMask connected to ' + props.network + ' Switch to Rinkeby and refresh!'}</p>
<p></p>
</div>
)
}
}
export default NetworkStatus;

@ -0,0 +1,235 @@
/**
* React Select
* ============
* Created by Jed Watson and Joss Mackison for KeystoneJS, http://www.keystonejs.com/
* https://twitter.com/jedwatson https://twitter.com/jossmackison https://twitter.com/keystonejs
* MIT License: https://github.com/JedWatson/react-select
*/
.Select-input {
overflow: hidden;
max-width: calc(100% - 20px);
}
.Select {
position: relative;
/* width: 100%; */
}
.Select-control{
width: 5rem;
}
.Select-clear-zone{
display: none;
}
.Select-value{
position: absolute;
width: 5rem;
margin-top: 1px;
}
.Select-input{
width: 5rem;
}
.Select-menu-outer {
background-color: #fff;
border: 1px solid #ccc;
border-top-color: #e6e6e6;
box-sizing: border-box;
margin-top: -1px;
max-height: 200px;
position: absolute;
right: 0;
top: 100%;
width: 100%;
width: 200px;
z-index: 1;
-webkit-overflow-scrolling: touch;
}
.Select-menu {
max-height: 198px;
overflow-y: auto;
}
.Select-option {
box-sizing: border-box;
background-color: #fff;
color: #666666;
cursor: pointer;
display: block;
padding: 8px 10px;
}
.Select .Select-aria-only {
position: absolute;
display: inline-block;
height: 1px;
width: 1px;
clip: rect(0, 0, 0, 0);
overflow: hidden;
float: left;
}
.Select-option {
box-sizing: border-box;
background-color: #fff;
color: #666666;
cursor: pointer;
display: block;
padding: 8px 10px;
}
.Select-option.is-selected {
background-color: #f5faff;
background-color: rgba(0, 126, 255, 0.04);
color: #333;
}
.Select-option.is-focused {
background-color: #ebf5ff;
background-color: rgba(0, 126, 255, 0.08);
color: #333;
}
.Select-option.is-disabled {
color: #cccccc;
cursor: default;
}
.Select-noresults {
box-sizing: border-box;
color: #999999;
cursor: default;
display: block;
padding: 8px 10px;
}
.Select--multi .Select-input {
vertical-align: middle;
margin-left: 10px;
padding: 0;
}
.Select--multi.Select--rtl .Select-input {
margin-left: 0;
margin-right: 10px;
}
.Select--multi.has-value .Select-input {
margin-left: 5px;
}
.Select--multi .Select-value {
background-color: #ebf5ff;
background-color: rgba(0, 126, 255, 0.08);
/* border: 1px solid #c2e0ff;
border: 1px solid rgba(0, 126, 255, 0.24); */
color: #007eff;
display: inline-block;
font-size: 0.9em;
line-height: 1.4;
margin-left: 5px;
margin-top: 5px;
vertical-align: top;
}
.Select--multi .Select-value-icon,
.Select--multi .Select-value-label {
display: inline-block;
vertical-align: middle;
}
.Select--multi .Select-value-label {
border-bottom-right-radius: 2px;
border-top-right-radius: 2px;
cursor: default;
padding: 2px 5px;
}
.Select--multi a.Select-value-label {
color: #007eff;
cursor: pointer;
text-decoration: none;
}
.Select--multi a.Select-value-label:hover {
text-decoration: underline;
}
.Select--multi .Select-value-icon {
cursor: pointer;
border-bottom-left-radius: 2px;
border-top-left-radius: 2px;
border-right: 1px solid #c2e0ff;
border-right: 1px solid rgba(0, 126, 255, 0.24);
padding: 1px 5px 3px;
}
.Select--multi .Select-value-icon:hover,
.Select--multi .Select-value-icon:focus {
background-color: #d8eafd;
background-color: rgba(0, 113, 230, 0.08);
color: #0071e6;
}
.Select--multi .Select-value-icon:active {
background-color: #c2e0ff;
background-color: rgba(0, 126, 255, 0.24);
}
.Select--multi.Select--rtl .Select-value {
margin-left: 0;
margin-right: 5px;
}
.Select--multi.Select--rtl .Select-value-icon {
border-right: none;
border-left: 1px solid #c2e0ff;
border-left: 1px solid rgba(0, 126, 255, 0.24);
}
.Select--multi.is-disabled .Select-value {
background-color: #fcfcfc;
border: 1px solid #e3e3e3;
color: #333;
}
.Select--multi.is-disabled .Select-value-icon {
cursor: not-allowed;
border-right: 1px solid #e3e3e3;
}
.Select--multi.is-disabled .Select-value-icon:hover,
.Select--multi.is-disabled .Select-value-icon:focus,
.Select--multi.is-disabled .Select-value-icon:active {
background-color: #fcfcfc;
}
.Select input::-webkit-contacts-auto-fill-button,
.Select input::-webkit-credentials-auto-fill-button {
display: none !important;
}
.Select input::-ms-clear {
display: none !important;
}
.Select input::-ms-reveal {
display: none !important;
}
.Select,
.Select div,
.Select input,
.Select span {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.Select input:focus{
/* outline: none; */
}
.Select.is-searchable.is-open > .Select-control {
cursor: text;
}
.Select.is-searchable.is-focused:not(.is-open) > .Select-control {
cursor: text;
}
.Select.Select--rtl {
direction: rtl;
text-align: right;
}
.Select.is-open .Select-input {
padding: 0 !important;
}
.Select.is-open .Select-input,
.Select.is-open .Select-input input {
width: 100% !important;
}
.Select.is-open .Select-input input {
text-indent: 1px;
}

@ -0,0 +1,38 @@
import React, { Component } from 'react'
import Select from 'react-select'
import './SelectToken.css'
class SelectToken extends Component {
constructor (props) {
super(props)
this.state = {
selectedOption: this.props.token,
}
}
handleChange = (selectedOption) => {
this.setState({ selectedOption })
this.props.onSelectToken(selectedOption, this.props.type)
console.log(`Selected: ${selectedOption.label}`)
}
render () {
const { selectedOption } = this.state
const value = selectedOption && selectedOption.value
return (
<Select
name="form-field-name"
value={value}
onChange={this.handleChange}
className="select"
options={[
{ value: 'OMG', label: 'OMG', clearableValue: false },
{ value: 'ETH', label: 'ETH', clearableValue: false },
{ value: 'BNT', label: 'BNT', clearableValue: false },
{ value: 'FOM', label: 'FOAM', clearableValue: false },
]}
/>
)
}
}
export default SelectToken;

@ -1,57 +0,0 @@
.instructions {
position: fixed;
background-color: rgba(0,0,0,0.4);
bottom: 1vh;
border-radius: 15px;
border: 1px solid;
border-color: rgb(122, 251, 255);
color: rgb(122, 251, 255);
text-shadow: 2px 2px 10px #2daae0;
margin-left: 35vw;
margin-right: 1vw;
max-height: 55vh;
-webkit-transition-duration: 0.5s;
transition-duration: 0.5s;
overflow-y: scroll;
} .instructions:hover{
color: rgb(216, 169, 236);
text-shadow: 1px 1px 5px #ff69f0;
border-color: rgb(180, 81, 179);
background-color: rgba(0,0,0,0.55);
}
.instructions-title {
text-align: left;
padding-left: 40px;
padding-top: 10px;
font-size: 22px;
}
.instructions-text {
font-size: 15px;
padding-top: 10px;
text-align: left;
padding-left: 25px;
padding-right: 15px;
}
.instructions a:link, .instructions a:visited, .instructions-highlight {
color: rgb(122, 251, 255);
text-shadow: 2px 2px 10px #2daae0;
}
.instructions a:hover, .instructions-highlight:hover {
text-shadow: none;
}
.instructions-approve {
font-size: 12px;
color: rgb(122, 251, 255);
border: 1px solid;
border-color: rgb(122, 251, 255);
border-radius: 4px;
padding: 1px 2px;
text-shadow: none;
}.instructions-approve:hover {
cursor: default;
}

@ -1,32 +0,0 @@
import React, { Component } from 'react';
import './Instructions.css';
class Instructions extends Component{
render() {
return (
<div className="instructions">
<div className="instructions-title">Instructions and Info</div>
<div className="instructions-text">
1) Add UNI test token address to MetaMask (first time only)<br/>
&nbsp;&nbsp;&nbsp;&nbsp;Token Address: <span className="instructions-highlight">0xca9901076d02f89794262869aad1340bd45d8489</span><br/><br/>
2) Check that MetaMask is connected to the Rinkeby Testnet<br/><br/>
3) You can now buy UNI test tokens with ETH! Visit the&nbsp;
<a href= "https://faucet.rinkeby.io/">Rinkeby faucet</a> to aquire testnet ETH <br/><br/>
4) To buy ETH with UNI you must approve the Uniswap smart contract to transfer UNI tokens on your behalf. Click the&nbsp;
<span className="instructions-approve"><b>Approve</b></span> button now! (first time only)<br/><br/>
5) Rate is variable based on token availiblity, enter number of tokens to see rate and cost.<br/><br/>
6) This is a proof-of-concept for a decentralized Market Maker exchange. Stay tuned for ENS support, token-to-token pairs, the ability to become a liquidity provider and collect fees,
and a Mainet launch! :) <br/> <br/>
7) This demo was hastily programmed by a single developer <i>(Hi, my name is Hayden!)</i>. Please reach out to me with any questions, comments, complaints, or bug reports.<br/><br/>
&nbsp;&nbsp;Email: <span className="instructions-highlight">hayden@uniswap.io</span>&nbsp;&nbsp;
GitHub: <a href= "https://github.com/haydenadams/uniswap">https://github.com/haydenadams/uniswap<br/></a>
&nbsp;&nbsp;ETH Address: <span className="instructions-highlight">0x4779721CaC18A46DbCF148f2Dd7A8E6cc1F90078</span><br/><br/>
</div>
</div>
);
}
}
export default Instructions;

@ -1,11 +0,0 @@
.install-metamask {
width: 100vw;
top: 70vh;
position: fixed;
font-family: Optima, sans-serif;
font-size: 26px;
text-align: center;
}
.install-metamask a {
}

@ -1,40 +0,0 @@
import React, { Component } from 'react';
import './Splashscreen.css';
var ModelViewer = require('metamask-logo')
// To render with fixed dimensions:
var viewer = ModelViewer({
// Dictates whether width & height are px or multiplied
// pxNotRatio: true,
// width: 500,
// height: 400,
pxNotRatio: false,
width: 1,
height: 0.7,
// To make the face follow the mouse.
followMouse: true,
// head should slowly drift (overrides lookAt)
// slowDrift: true,
})
var metamaskLink = 'https://metamask.io';
class Splashscreen extends Component{
render() {
return (
<div className="install-metamask">
Uniswap requires MetaMask to connect to the Ethereum blockchain.<br/><br/>
<a href="https://metamask.io"> {metamaskLink} </a>
</div>
);
}
}
export default Splashscreen;

@ -1,74 +0,0 @@
@font-face {
font-family: 'LeveloLineBold'; /*a name to be used later*/
src: url('../../fonts/LoveloLineBold.otf'); /*URL to font*/
}
.title {
font-family: 'LeveloLineBold';
/*font-family: Optima, sans-serif;*/
position: fixed;
font-size: 10vh;
margin-top: 2vh;
margin-left: 2vh;
float: left;
text-shadow: 3px 3px 10px #f29ae1;
color: rgb(220, 173, 247);
font-weight: bold;
-webkit-text-stroke-width: 1px;
-webkit-text-stroke-color: rgb(180, 81, 179);
cursor: default;
}
/* entire container, keeps perspective */
.titled-container {
position: fixed;
perspective: 800px;
/*border: 2px solid rgb(207, 9, 158);*/
}
/* flip the pane when hovered */
.titled-container:hover > .title{
transform: rotateX(180deg);
color: rgb(122, 251, 255);
text-shadow: 3px 3px 10px #2daae0;
font-weight: bold;
-webkit-text-stroke-width: 2px;
-webkit-text-stroke-color: rgb(22, 177, 210);
}
.titled-container, .front, .back {
width: 26vw;
height: 11vh;
}
.front, .back {
width: 0.1vh;
height: 0.001vh;
}
/* flip speed goes here */
.title {
transition: 2.5s;
transform-style: preserve-3d;
position: relative;
}
/* hide back of pane during swap */
.front, .back {
backface-visibility: hidden;
position: absolute;
top: 0;
left: 0;
}
/* front pane, placed above back */
.front {
z-index: 2;
/* for firefox 31 */
transform: rotateX(0deg);
}
/* back, initially hidden pane */
.back {
transform: rotateX(180deg);
}

@ -1,19 +0,0 @@
import React, { Component } from 'react';
import './Title.css';
class Title extends Component{
render() {
return (
<div className="titled-container">
<div className="title">
<div className="front">UNISWAP</div>
<div className="back">UNISWAP</div>
</div>
</div>
);
}
}
export default Title;

Binary file not shown.

4
src/helpers/abi.js Normal file

@ -0,0 +1,4 @@
module.exports.uniswapABI = [{"constant":false,"inputs":[{"name":"tokenAmount","type":"uint256"}],"name":"ownerTokenWithdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"tokenAmount","type":"uint256"}],"name":"ownerTokenDeposit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"ethAmount","type":"uint256"}],"name":"ownerEthWithdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"sellQuantity","type":"uint256"},{"name":"minimumEth","type":"uint256"},{"name":"timeout","type":"uint256"}],"name":"tokenToEth","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalTokenQuantity","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"minimumTokens","type":"uint256"},{"name":"timeout","type":"uint256"}],"name":"ethToTokens","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"initialTokenQuantity","type":"uint256"}],"name":"initiateUniswap","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalEthQuantity","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"invariant","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"ownerEthDeposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_tokenAddress","type":"address"}],"payable":true,"stateMutability":"payable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"buyer","type":"address"},{"indexed":false,"name":"tokensPurchased","type":"uint256"},{"indexed":false,"name":"ethSpent","type":"uint256"}],"name":"TokenPurchase","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"buyer","type":"address"},{"indexed":false,"name":"ethPurchased","type":"uint256"},{"indexed":false,"name":"tokensSpent","type":"uint256"}],"name":"EthPurchase","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}]
module.exports.tokenABI = [{"constant":true,"inputs":[],"name":"mintingFinished","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"mint","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_subtractedValue","type":"uint256"}],"name":"decreaseApproval","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"finishMinting","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_addedValue","type":"uint256"}],"name":"increaseApproval","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[],"name":"MintFinished","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 941 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

BIN
src/images/🦄.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

@ -2,5 +2,4 @@ body {
margin: 0;
padding: 0;
font-family: sans-serif;
overflow: hidden;
}

@ -2,20 +2,7 @@ import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import Splashscreen from './components/misc/Splashscreen'
import registerServiceWorker from './registerServiceWorker';
function Detect(props) {
const metamask = props.metamask;
if(typeof metamask === 'undefined') {
return <Splashscreen />
}
else {
return <App />
}
}
ReactDOM.render(
<Detect metamask={window.web3} />, document.getElementById('root')
);
ReactDOM.render(<App metamask={window.web3} />, document.getElementById('root'));
registerServiceWorker();

@ -32,12 +32,21 @@ export default function register() {
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (!isLocalhost) {
// Is not local host. Just register service worker
registerValidSW(swUrl);
} else {
if (isLocalhost) {
// This is running on localhost. Lets check if a service worker still exists or not.
checkValidServiceWorker(swUrl);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://goo.gl/SC7cgQ'
);
});
} else {
// Is not local host. Just register service worker
registerValidSW(swUrl);
}
});
}

7240
yarn.lock Normal file

File diff suppressed because it is too large Load Diff