Commit f570c468 authored by Callil Capuozzo's avatar Callil Capuozzo

Add UX features

Detect all metamask states
UX for approve transactions
UX for getting more ETH from faucet
UX for submitted transactions
Toggle about
parent d00d4c0b
......@@ -8,6 +8,7 @@
"react-dom": "^16.2.0",
"react-helmet": "^5.2.0",
"react-scripts": "1.1.0",
"react-scroll-to-component": "^1.0.2",
"react-select": "^1.2.1",
"web3": "1.0.0-beta.22"
},
......@@ -16,7 +17,7 @@
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"deploy" : "npm run build&&gh-pages -d build"
"deploy": "npm run build&&gh-pages -d build"
},
"devDependencies": {
"gh-pages": "^1.1.0"
......
......@@ -2,26 +2,38 @@ body{
margin: 0;
padding: 0;
font-family: "Inter UI", sans-serif;
font-size: 24px;
line-height: 1.2;
font-size: 20px;
line-height: 1.6;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.dim input{
pointer-events:none;
}
input{
font-size: 24px;
line-height: 1.2;
font-size: 20px;
line-height: 1.6;
}
p{
margin: 0px;
/* margin: .25rem; */
}
/* .app{
margin: 10rem;
margin-top: 5rem;
} */
a{
color: #2F80ED;
text-decoration: none;
}
a:hover{
text-decoration: underline;
}
.pa2 {
padding: 3rem;
padding: 2.5rem;
padding-top: 1.5rem;
padding-bottom: 1.5rem;
}
.title{
......@@ -29,7 +41,7 @@ p{
flex-direction: row;
}
.border{
border: 1px solid #f2f2f2;
border: 1px solid #f7f7f7;
}
.title {
width: 100%;
......@@ -44,13 +56,13 @@ p{
display: flex;
justify-content: space-between;
align-items: center;
color: #27AE60;
/* color: #27AE60; */
flex: 1;
}
.connection a{
text-decoration: none;
color: #27AE60;
/* text-decoration: none; */
/* color: #27AE60; */
}
.order {
......@@ -99,6 +111,12 @@ input[type=number]::-webkit-outer-spin-button {
display: flex;
flex-direction: row;
justify-content: space-between;
opacity: .6;
}
.rate p{
margin: 0px;
padding: 0px;
}
.info{
......@@ -126,23 +144,80 @@ input[type=number]::-webkit-outer-spin-button {
text-decoration: underline;
}
.about{
display: flex;
flex-direction: row;
justify-content: space-between;
flex: 1;
text-decoration: none;
color: black;
}
.about:hover .underline{
text-decoration: underline;
}
.swap{
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
background-color: #f2f2f2;
color: blue;
height: 48px;
height: 64px;
transition: height .3s ease;
}
.expand{
display: flex;
flex-direction: column;
/* justify-content: center; */
background-color: #f2f2f2;
height: auto;
transition: height .3s ease;
}
.transaction p{
margin: 0px;
}
.transaction ol{
padding: 0px;
padding-left: 20px;
}
.grey-bg{
background-color: #f7f7f7;
}
.red{
color: red;
}
.dim .order, .rate{
.green{
color: #27AE60;
}
.green a{
color: #27AE60;
}
.yellow{
color: #F2C94C;
}
.dim .order , .liq, .ex{
opacity: .4;
pointer-events:none;
}
.dim .rate{
opacity: .4;
pointer-events:none;
}
.dim .links{
opacity: .4;
pointer-events:none;
}
.hidden{
......@@ -150,3 +225,7 @@ input[type=number]::-webkit-outer-spin-button {
height: 0px;
border: 0px solid #f2f2f2;
}
.f-a{
cursor: pointer;
}
import React, { Component } from 'react';
import Web3 from 'web3';
import scrollToComponent from 'react-scroll-to-component';
import Head from './components/Head'
import NetworkStatus from './components/NetworkStatus'
import HelperMessages from './components/HelperMessages'
import ConnectionHelper from './components/ConnectionHelper'
import Transactions from './components/Transactions'
import SelectToken from './components/SelectToken'
import './App.css';
......@@ -19,36 +21,21 @@ class App extends Component {
if (typeof props.metamask !== 'undefined'){
localweb3 = new Web3(window.web3.currentProvider)
} else {
localweb3 = null
localweb3 = 'undefined'
}
const uniExchangeAddress = '0xcDc30C3b02c5776495298198377D2Fc0fd6B1F1C';
const uniExchangeContract = new localweb3.eth.Contract(exchangeABI, uniExchangeAddress);
const swapExchangeAddress = '0x4632a7Cd732c625dcc48d75E289c209422e1D2B7';
const swapExchangeContract = new localweb3.eth.Contract(exchangeABI, swapExchangeAddress);
const uniTokenAddress = '0x350E5DD084ecF271e8d3531D4324443952F47756';
const uniTokenContract = new localweb3.eth.Contract(tokenABI, uniTokenAddress);
const swapTokenAddress = '0x8B2A87F8243f23C33fb97E23a21Ae8EDB3b71AcA';
const swapTokenContract = new localweb3.eth.Contract(tokenABI, swapTokenAddress);
const factoryAddress = '0xD6D22d102A4237F3D35361BC022a78789E6174Aa';
const factoryContract = new localweb3.eth.Contract(factoryABI, factoryAddress);
this.state = {
uniExchangeAddress: '0xcDc30C3b02c5776495298198377D2Fc0fd6B1F1C',
swapExchangeAddress: '0x4632a7Cd732c625dcc48d75E289c209422e1D2B7',
uniTokenAddress: '0x350E5DD084ecF271e8d3531D4324443952F47756',
swapTokenAddress: '0x8B2A87F8243f23C33fb97E23a21Ae8EDB3b71AcA',
currentMaskAddress: '0x0000000000000000000000000000000000000000',
factoryAddress: '0xD6D22d102A4237F3D35361BC022a78789E6174Aa',
uniExchange: uniExchangeContract,
uniToken: uniTokenContract,
swapExchange: swapExchangeContract,
swapToken: swapTokenContract,
factory: factoryContract,
uniExchangeAddress: '',
swapExchangeAddress: '',
uniTokenAddress: '',
swapTokenAddress: '',
currentMaskAddress: '',
factoryAddress: '',
uniExchange: {},
uniToken: {},
swapExchange: {},
swapToken: {},
factory: {},
blockTimestamp: 0,
inputBalance: 0,
outputBalance: 0,
......@@ -63,12 +50,17 @@ class App extends Component {
fee: 0,
cost: 0,
networkMessage: '',
locked: false,
locked: true,
connected: false,
approved: true,
firstRun: true,
about: false,
interaction: 'disconnected',
exchangeType: 'ETH to Token',
input: 0,
output: 0,
transactionStatus: 'waiting',
transactions: [],
inputToken: { value: 'ETH',
label: 'ETH',
clearableValue: false
......@@ -81,16 +73,61 @@ class App extends Component {
}
componentWillMount(){
if(localweb3 === 'undefined'){
this.setState({connected: false})
} else {
this.getContracts();
this.getUserAddress();
this.checkNetwork();
this.getBlock();
}
}
componentDidMount(){
if(localweb3 === 'undefined'){
this.setState({connected: false})
} else if(this.state.connected === true) {
setInterval(this.getBlock, 10000);
setInterval(this.getMarketInfo, 15000);
setInterval(this.getAccountInfo, 15000);
setInterval(this.getUserAddress, 10000);
} else {
setInterval(this.getUserAddress, 500);
}
}
componentWillReceiveProps(nextProps) {
console.log(nextProps)
}
getContracts() {
const uniExchangeAddress = '0xcDc30C3b02c5776495298198377D2Fc0fd6B1F1C';
const uniExchangeContract = new localweb3.eth.Contract(exchangeABI, uniExchangeAddress);
const swapExchangeAddress = '0x4632a7Cd732c625dcc48d75E289c209422e1D2B7';
const swapExchangeContract = new localweb3.eth.Contract(exchangeABI, swapExchangeAddress);
const uniTokenAddress = '0x350E5DD084ecF271e8d3531D4324443952F47756';
const uniTokenContract = new localweb3.eth.Contract(tokenABI, uniTokenAddress);
const swapTokenAddress = '0x8B2A87F8243f23C33fb97E23a21Ae8EDB3b71AcA';
const swapTokenContract = new localweb3.eth.Contract(tokenABI, swapTokenAddress);
const factoryAddress = '0xD6D22d102A4237F3D35361BC022a78789E6174Aa';
const factoryContract = new localweb3.eth.Contract(factoryABI, factoryAddress);
this.setState({
uniExchangeAddress: uniExchangeAddress,
swapExchangeAddress: swapExchangeAddress,
uniTokenAddress: uniTokenAddress,
swapTokenAddress: swapTokenAddress,
factoryAddress: factoryAddress,
uniExchange: uniExchangeContract,
uniToken: uniTokenContract,
swapExchange: swapExchangeContract,
swapToken: swapTokenContract,
factory: factoryContract,
})
}
checkNetwork() {
......@@ -107,7 +144,7 @@ class App extends Component {
this.setState({networkMessage: 'Ropsten testnet', connected: false, interaction: 'disconnected'});
break;
case "rinkeby":
this.setState({networkMessage: '', connected: true, interaction: 'connected'});
this.setState({networkMessage: 'Rinkeby testnet', connected: true, interaction: 'connected'});
break;
case "kovan":
this.setState({networkMessage: 'Kovan testnet', connected: false, interaction: 'disconnected'});
......@@ -126,10 +163,10 @@ class App extends Component {
getUserAddress = () => {
localweb3.eth.getAccounts((error, result) => {
if(!error)
this.setState({currentMaskAddress: result[0]})
if(result.length > 0)
this.setState({currentMaskAddress: result[0], locked: false, connected: true})
else
this.setState({locked: true})
this.setState({locked: true, connected: false, interaction: 'locked'})
})
}
......@@ -284,6 +321,10 @@ class App extends Component {
token.methods.allowance(this.state.currentMaskAddress, exchangeAddress).call().then((result, error) => {
console.log(this.state.inputToken.value + ' allowance: ' + result);
if(result === '0'){
this.setState({approved: false})
console.log(this.state.approved)
}
})
}
}
......@@ -297,7 +338,10 @@ class App extends Component {
token.methods.approve(exchangeAddress, amount).send({from: this.state.currentMaskAddress})
.on('transactionHash', console.log('Transaction Hash created'))
.on('receipt', (receipt) => {console.log(receipt)}) //Transaction Submitted to blockchain
.on('receipt', (receipt) => {
console.log(receipt)
this.setState({approved: true})
}) //Transaction Submitted to blockchain
.on('confirmation', (confirmationNumber, receipt) => {console.log("Block Confirmations: " + confirmationNumber)}) //Transaction Mined
.on('error', console.error);
}
......@@ -310,7 +354,7 @@ class App extends Component {
}
onSelectToken = (selected, type) => {
this.setState({input: 0, output:0, rate:0, fee: 0, interaction: 'connected'})
this.setState({input: 0, output:0, rate:0, fee: 0, interaction: 'connected', firstRun: true})
var marketType = '';
if (type === 'input') {
this.setState({inputToken: selected});
......@@ -348,6 +392,7 @@ class App extends Component {
this.setState({input: inputValue, output: 0, interaction: 'error1'});
} else if(inputValue && inputValue !== 0 && inputValue !== '0'){
this.setState({input: inputValue, interaction: 'input'});
console.log('input something')
this.getExchangeRate(inputValue);
} else {
this.setState({input: inputValue, output: 0, interaction: 'connected'});
......@@ -451,8 +496,15 @@ class App extends Component {
console.log(minTokensInt, weiSold, timeout);
exchange.methods.ethToTokenSwap(minTokensInt, timeout).send({from: this.state.currentMaskAddress, value: weiSold})
.on('transactionHash', console.log('Transaction Hash created'))
.on('receipt', (receipt) => {console.log(receipt)}) //Transaction Submitted to blockchain
.on('transactionHash', (result) => {
console.log('Transaction Hash created')
let transactions = this.state.transactions
transactions.push(result)
this.setState({transactions: transactions, input: '', output: '', interaction: 'submitted'})
})
.on('receipt', (receipt) => {
console.log(receipt)
}) //Transaction Submitted to blockchain
.on('confirmation', (confirmationNumber, receipt) => {console.log("Block Confirmations: " + confirmationNumber)}) //Transaction Mined
.on('error', console.error);
}
......@@ -466,7 +518,12 @@ class App extends Component {
var timeout = this.state.blockTimestamp + 300; //current block time + 5mins
exchange.methods.tokenToEthSwap(tokensSoldInt, minEthInt, timeout).send({from: this.state.currentMaskAddress})
.on('transactionHash', console.log('Transaction Hash created'))
.on('transactionHash', (result) => {
console.log('Transaction Hash created')
let transactions = this.state.transactions
transactions.push(result)
this.setState({transactions: transactions, input: '', output: '', interaction: 'submitted'})
})
.on('receipt', (receipt) => {console.log(receipt)}) //Transaction Submitted to blockchain
.on('confirmation', (confirmationNumber, receipt) => {console.log("Block Confirmations: " + confirmationNumber)}) //Transaction Mined
.on('error', console.error);
......@@ -482,24 +539,66 @@ class App extends Component {
var timeout = this.state.blockTimestamp + 300; //current block time + 5mins
exchange.methods.tokenToTokenSwap(tokenOutAddress, tokensSoldInt, minTokensInt, timeout).send({from: this.state.currentMaskAddress})
.on('transactionHash', console.log('Transaction Hash created'))
.on('transactionHash', (result) => {
console.log('Transaction Hash created')
let transactions = this.state.transactions
transactions.push(result)
this.setState({transactions: transactions, input: '', output: '', interaction: 'submitted'})
})
.on('receipt', (receipt) => {console.log(receipt)}) //Transaction Submitted to blockchain
.on('confirmation', (confirmationNumber, receipt) => {console.log("Block Confirmations: " + confirmationNumber)}) //Transaction Mined
.on('error', console.error);
}
onCloseHelper = () => {
this.setState({firstRun: !this.state.firstRun})
}
toggleAbout = () => {
this.setState({about: !this.state.about})
setTimeout(this.scrollToAbout, 300)
}
scrollToAbout = () => {
scrollToComponent(this.About, { offset: 0, align: 'top', duration: 500})
}
render() {
//console.log(localweb3)
return (
<div className={this.state.connected ? "App" : "App dim"}>
<div className={this.state.connected && !this.state.locked ? "App" : "App dim"}>
<Head />
<section className="title">
<div className="logo border pa2">
<span role="img" aria-label="Unicorn">🦄</span>
</div>
<NetworkStatus network={this.state.networkMessage} connected={this.state.connected} address={this.state.currentMaskAddress}/>
<NetworkStatus
network={this.state.networkMessage}
connected={this.state.connected}
metamask={this.props.metamask}
address={this.state.currentMaskAddress}
locked={this.state.locked}
balance={this.state.inputBalance}/>
</section>
<HelperMessages interaction={this.state.interaction} inputToken={this.state.inputToken} outputToken={this.state.outputToken}/>
<ConnectionHelper
network={this.state.networkMessage}
connected={this.state.connected}
metamask={this.props.metamask}
address={this.state.currentMaskAddress}
locked={this.state.locked}
approved={this.state.approved}
tokenAdded={this.state.tokenAdded}
approveAllowance={this.approveAllowance}
interaction={this.state.interaction}
exchangeType={this.state.exchangeType}
firstRun={this.state.firstRun}
onCloseHelper={this.onCloseHelper}
input={this.state.input}
balance={this.state.inputBalance}
toggleAbout={this.toggleAbout}
inputToken={this.state.inputToken}
outputToken={this.state.outputToken}
about={this.state.about}
/>
<section className="order">
<div className="value border pa2">
<input type="number" value={this.state.input} placeholder="0" onChange={this.onInputChange} />
......@@ -507,7 +606,7 @@ class App extends Component {
<p className="dropdown">{'<'}</p>
</div>
<div className="arrow border pa2">
<p onClick={() => {this.approveAllowance()}}></p>
<p></p>
</div>
<div className="value border pa2">
<input type="number" value={this.state.output/10**18} placeholder="0"/>
......@@ -525,32 +624,55 @@ class App extends Component {
<p>{this.state.fee/10**18} {this.state.inputToken.value}</p>
</span>
</section>
{this.state.interaction === 'input' ?
<section className="swap border pa2">
<section className="swap grey-bg border pa2">
<a href="#" role="button" onClick={() => {this.purchaseTokens()}}>
{"I want to swap " + this.state.input + " " + this.state.inputToken.value + " for " + this.state.output/10**18 + " " + this.state.outputToken.value}
<b>{"I want to swap " + this.state.input + " " + this.state.inputToken.value + " for " + this.state.output/10**18 + " " + this.state.outputToken.value}</b>
</a>
<button> Approve </button>
{/* <button> Approve </button> */}
</section>
: <section className="swap hidden border pa2"></section>}
: <section className="swap grey-bg hidden border pa2"></section>}
{this.state.transactions.length > 0 ?
<section className="transaction border grey-bg pa2">
<p className="underline">Transactions submitted</p>
<Transactions transactions={this.state.transactions}/>
</section>
: <section className="hidden border pa2"></section>}
<section className="About" ref={(section) => { this.About = section; }}>
<a onClick={() => {this.toggleAbout()}} className="link border pa2 f-a">
<p className="underline">About Uniswap.</p>
<p></p>
</a>
</section>
{this.state.about ?
<section className="expand grey-bg border pa2">
<p>Uniswap is a trustless, decentralized exchange for Ether and ERC20 tokens</p>
<p>Uniswap exchange uses a "Market Maker" mechanism, where liquidity providers store a reserve of ETH and ERC20 tokens within an Ethereum smart contract. An exchange rate is set between the tokens and ETH based on the relative availibility of each token. Arbitrage ensures that the rate will be the same as on other exchanges. Buyers who send Token 1 to the smart contract will receive back Token 2 at the current rate. A small fee is paid from the buyer to the liquidity providers to incentive participation.</p>
<p>Until then, here is some more info on Market Makers:</p>
<a href="https://www.reddit.com/r/ethereum/comments/55m04x/lets_run_onchain_decentralized_exchanges_the_way/">https://www.reddit.com/r/ethereum/comments/55m04x/lets_run_onchain_decentralized_exchanges_the_way/</a>
<a href="http://vitalik.ca/general/2017/06/22/marketmakers.html">http://vitalik.ca/general/2017/06/22/marketmakers.html</a>
<p>Please reach out if you would like to get involved or support the project.</p>
<p>Email: <a href="mailto:hayden@uniswap.io">hayden@uniswap.io</a> ETH Address: 0x4779721CaC18A46DbCF148f2Dd7A8E6cc1F90078</p>
</section>
: <section className="expand grey-bg border pa2 hidden"> </section>
}
<section className="links">
<a href="" className="link border pa2">
<a href="" className="link border pa2 liq">
<p className="underline">Provide liquidity to collect fees</p>
<p>+</p>
</a>
<a href="" className="link border pa2">
<a href="" className="link border pa2 ex">
<p className="underline">Launch a new exchange</p>
<p>+</p>
</a>
</section>
<section className="links">
<a href="" className="link border pa2">
<p className="underline">About</p>
<p></p>
</a>
</section>
</div>
);
)
}
}
......
import React from 'react';
function ConnectionHelper(props) {
// console.log(props.input > props.balance/10**18)
if (!props.metamask) {
return (
<div className="grey-bg connection border pa2">
<p>Welcome! Uniswap is a decentralized exhange platform for ERC20 Tokens. <a onClick={() => {props.toggleAbout()}} className="f-a">How it works</a>.<br />
<b>To get started, please install <a href="">Metamask</a>.</b></p>
{/* <p></p> */}
</div>
)
} if (props.locked) {
return (
<div className="grey-bg connection border pa2">
<p>Welcome! Uniswap is a decentralized exhange platform for ERC20 Tokens. <a onClick={() => {props.toggleAbout()}} className="f-a" >How it works</a>.<br />
Looks like you aren't connected. <b>Please unlock Metamask to continue.</b></p>
{/* <p><a href="">How it works</a></p> */}
</div>
)
} else if (!props.approved && props.exchangeType === "Token to Token") {
return (
<div className="grey-bg connection border pa2">
<p>Our smart contract has to be approved by your address to be able to swap tokens for tokens.<br /> We set the transfer limit to 250 (<a href="">Why?</a>).</p>
<a className="f-a" onClick={() => props.approveAllowance()}>Approve ⭞</a>
</div>
)
} else if (!props.approved && props.exchangeType === "Token to ETH") {
return (
<div className="grey-bg connection border pa2">
<p>Our smart contract has to be approved by your address to be able to swap tokens for ETH.<br /> We set the transfer limit to 250 (<a href="">Why?</a>).</p>
<a className="f-a" onClick={() => props.approveAllowance()}>Approve ⭞</a>
</div>
)
} else if (props.interaction === "error1") {
return (
<div className="grey-bg connection border pa2">
<p>You can't swap a token for itself! 😂</p>
</div>
)
}
else if (props.interaction === "submitted") {
return (
<div className="grey-bg connection border pa2">
<p>{"Transaction submitted! Click on the transaction hash below to check its status?"}</p>
</div>
)
} else if (props.input > props.balance/10**18) {
return (
<div className="grey-bg red connection border pa2">
<p>This account doesn't have enough balance to make this transaction! Get more ETH with the <a href="https://faucet.rinkeby.io/">Rinkeby Faucet.</a></p>
</div>
)
} else if (props.firstRun) {
return (
<div className="grey-bg connection border pa2">
<p>Welcome! Uniswap is a decentralized exhange platform for ERC20 Tokens. <a onClick={() => {props.toggleAbout()}} className="f-a">How it works</a>.<br />
Enter an input value below to get started.</p>
{/* <a className="f-a" onClick={() => props.onCloseHelper()}>×</a> */}
<p>↓</p>
</div>
)
} else {
return (
null
)
}
}
export default ConnectionHelper;
......@@ -4,22 +4,25 @@ function HelperMessages(props) {
let message = ''
switch (props.interaction) {
case 'connected':
message = "Nice! You're connected. Enter an input value below to get started."
message = <p>{"Nice! You're connected. Enter an input value below to get started."}</p>
break;
case 'locked':
message = <p>{"Your metamask is locked! Please unlock to continue."}</p>
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 = <p>{"You're swapping " + props.inputToken.value + " for " + props.outputToken.value + ". Want to know more about how the prices are determined?"}</p>
// 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;
case 'error1':
message = "You can't swap a token for itself! 😂"
message = <p>{"You can't swap a token for itself! 😂"}</p>
// 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?"
message = <p>{"Hi there! This site helps you swap ERC20 tokens. Looks like you aren't connected. Need help?"}</p>
}
return (
<section className="info border pa2">
<p>{message}</p>
{message}
<p></p>
</section>
)
......
......@@ -2,16 +2,33 @@ import React from 'react';
function NetworkStatus(props) {
let isConnected = props.connected
let metamask = props.metamask
let locked = props.locked
if (isConnected){
return (
<div className="connection border pa2">
<div className="connection border pa2 green">
<a href={'https://rinkeby.etherscan.io/search?q=' + props.address} target="_blank">{props.address}</a>
<p></p>
</div>
)
} else {
} else if (!metamask) {
return (
<div className="connection red border pa2">
<p>{"Waiting for connection to the blockchain..."}</p>
<p></p>
</div>
)
} else if (locked && !isConnected) {
return (
<div className="connection yellow border pa2">
<p>{"Waiting for Metamask to unlock..."}</p>
<p></p>
</div>
)
} else {
return (
<div className="connection yellow border pa2">
<p>{'MetaMask connected to ' + props.network + ' Switch to Rinkeby and refresh!'}</p>
<p></p>
</div>
......
......@@ -25,11 +25,12 @@
.Select-value{
position: absolute;
width: 5rem;
margin-top: 1px;
margin-top: 5px;
}
.Select-input{
width: 5rem;
margin-top: 5px;
}
.Select-menu-outer {
......
import React from 'react';
function Transactions(props) {
return (
<div className="connection">
<ol>
{props.transactions.map((t) =>
<li key={t.toString()}><p><a href={'https://rinkeby.etherscan.io/search?q=' + t}>{t}</a></p></li>
)}
</ol>
</div>
)
}
export default Transactions;
......@@ -1511,6 +1511,37 @@ commondir@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
component-clone@0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/component-clone/-/component-clone-0.2.2.tgz#c7f5979822880fad8cfb0962ba29186d061ee04f"
dependencies:
component-type "*"
component-emitter@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.0.tgz#ccd113a86388d06482d03de3fc7df98526ba8efe"
component-raf@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/component-raf/-/component-raf-1.2.0.tgz#b2bc72d43f1b014fde7a4b3c447c764bc73ccbaa"
component-tween@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/component-tween/-/component-tween-1.2.0.tgz#cc39ce5dbab05b52825f41d1947638a0b01b2b8a"
dependencies:
component-clone "0.2.2"
component-emitter "1.2.0"
component-type "1.1.0"
ease-component "1.0.0"
component-type@*:
version "1.2.1"
resolved "https://registry.yarnpkg.com/component-type/-/component-type-1.2.1.tgz#8a47901700238e4fc32269771230226f24b415a9"
component-type@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/component-type/-/component-type-1.1.0.tgz#95b666aad53e5c8d1f2be135c45b5d499197c0c5"
compressible@~2.0.11:
version "2.0.12"
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.12.tgz#c59a5c99db76767e9876500e271ef63b3493bd66"
......@@ -2131,6 +2162,10 @@ duplexer@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
ease-component@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/ease-component/-/ease-component-1.0.0.tgz#b375726db0b5b04595b77440396fec7daa5d77c9"
ecc-jsbn@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505"
......@@ -5668,6 +5703,12 @@ react-scripts@1.1.0:
optionalDependencies:
fsevents "1.1.2"
react-scroll-to-component@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/react-scroll-to-component/-/react-scroll-to-component-1.0.2.tgz#f260dc936c62a53e772786d7832fe0884e195354"
dependencies:
scroll-to "0.0.2"
react-select@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/react-select/-/react-select-1.2.1.tgz#a2fe58a569eb14dcaa6543816260b97e538120d1"
......@@ -6061,6 +6102,13 @@ schema-utils@^0.3.0:
dependencies:
ajv "^5.0.0"
scroll-to@0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/scroll-to/-/scroll-to-0.0.2.tgz#936d398a9133660a2492145c2c0081dfcb0728f3"
dependencies:
component-raf "1.2.0"
component-tween "1.2.0"
scrypt.js@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/scrypt.js/-/scrypt.js-0.2.0.tgz#af8d1465b71e9990110bedfc593b9479e03a8ada"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment