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.
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*
|
11487
package-lock.json
generated
21
package.json
@ -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
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);
|
||||
}
|
||||
}
|
286
src/App.css
@ -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;
|
||||
}
|
||||
|
409
src/App.js
@ -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: {this.state.ethBalance} ETH
|
||||
{this.state.tokenBalance.toFixed(2)} UNI<br/>
|
||||
{this.state.currentMaskAddress}
|
||||
|
||||
<br/>
|
||||
</div>
|
||||
<Instructions />
|
||||
<div className="approveButtonContainer">
|
||||
<button className="approveButton" onClick={() => {this.approveAllowance(20000*10**6) }}>Approve</button><br/><br/>
|
||||
{/*Tokens approved: {this.state.tokenAllowance} */}
|
||||
</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">
|
||||
Rate : {this.state.tokenBuyRate.toFixed(3)} UNI/ETH<br/>
|
||||
Cost : {this.state.tokenCost.toFixed(5)} ETH<br/>
|
||||
Fee : {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">
|
||||
Rate : {this.state.ethBuyRate.toFixed(4)} ETH/UNI<br/>
|
||||
Cost : {this.state.ethCost.toFixed(5)} UNI<br/>
|
||||
Fee : {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;
|
24
src/components/HelperMessages.js
Normal file
@ -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;
|
22
src/components/NetworkStatus.js
Normal file
@ -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;
|
235
src/components/SelectToken.css
Normal file
@ -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;
|
||||
}
|
38
src/components/SelectToken.js
Normal file
@ -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/>
|
||||
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
|
||||
<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
|
||||
<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/>
|
||||
Email: <span className="instructions-highlight">hayden@uniswap.io</span>
|
||||
GitHub: <a href= "https://github.com/haydenadams/uniswap">https://github.com/haydenadams/uniswap<br/></a>
|
||||
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;
|
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"}]
|
Before Width: | Height: | Size: 941 KiB |
Before Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 1.7 MiB |
Before Width: | Height: | Size: 57 KiB |
BIN
src/images/🦄.png
Normal file
After Width: | Height: | Size: 14 KiB |
@ -2,5 +2,4 @@ body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: sans-serif;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
15
src/index.js
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|