chainInfo from server, routes, wallet rework
This commit is contained in:
@@ -11,7 +11,6 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="app" class="app"></div>
|
<div id="app" class="app"></div>
|
||||||
<script src="/version.js"></script>
|
<script src="/version.js"></script>
|
||||||
<script src="/generated.js"></script>
|
|
||||||
<script type="module" src="/src/main.js"></script>
|
<script type="module" src="/src/main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
known_chains = [
|
|
||||||
{
|
|
||||||
name: 'Arbitrum One',
|
|
||||||
id: 42161,
|
|
||||||
icon: null,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
_known_tokens = {
|
|
||||||
42161: [ // Arbitrum
|
|
||||||
{
|
|
||||||
name: 'Wrapped Ether',
|
|
||||||
symbol: 'WETH',
|
|
||||||
decimals: 18,
|
|
||||||
icon: null,
|
|
||||||
address: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Tether USD',
|
|
||||||
symbol: 'USDT',
|
|
||||||
decimals: 6,
|
|
||||||
icon: null,
|
|
||||||
address: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'USD Coin',
|
|
||||||
symbol: 'USDC',
|
|
||||||
decimals: 6,
|
|
||||||
icon: null,
|
|
||||||
address: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Wrapped Bitcoin',
|
|
||||||
symbol: 'WBTC',
|
|
||||||
decimals: 8,
|
|
||||||
icon: null,
|
|
||||||
address: '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'DAI Stablecoin',
|
|
||||||
symbol: 'DAI',
|
|
||||||
decimals: 18,
|
|
||||||
icon: null,
|
|
||||||
address: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Uniswap',
|
|
||||||
symbol: 'UNI',
|
|
||||||
decimals: 18,
|
|
||||||
icon: null,
|
|
||||||
address: '0xFa7F8980b0f1E64A2062791cc3b0871572f1F7f0',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Chainlink Token',
|
|
||||||
symbol: 'LINK',
|
|
||||||
decimals: 18,
|
|
||||||
icon: null,
|
|
||||||
address: '0xf97f4df75117a78c1A5a0DBb814Af92458539FB4',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'TrueUSD',
|
|
||||||
symbol: 'TUSD',
|
|
||||||
decimals: 18,
|
|
||||||
icon: null,
|
|
||||||
address: '0x4D15a3A2286D883AF0AA1B3f21367843FAc63E07',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Lido DAO Token',
|
|
||||||
symbol: 'LDO',
|
|
||||||
decimals: 18,
|
|
||||||
icon: null,
|
|
||||||
address: '0x13Ad51ed4F1B7e9Dc168d8a00cB3f4dDD85EfA60',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Arbitrum',
|
|
||||||
symbol: 'ARB',
|
|
||||||
decimals: 18,
|
|
||||||
icon: null,
|
|
||||||
address: '0x912CE59144191C1204E64559FE8253a0e49E6548',
|
|
||||||
},
|
|
||||||
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
known_tokens = {}
|
|
||||||
|
|
||||||
for( const chainId in _known_tokens ) {
|
|
||||||
known_tokens[chainId] = {}
|
|
||||||
for( const info of _known_tokens[chainId] )
|
|
||||||
known_tokens[chainId][info.address] = info
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,34 @@
|
|||||||
|
|
||||||
|
export const factoryAbi = [
|
||||||
|
]
|
||||||
|
|
||||||
|
export const queryHelperAbi = [
|
||||||
|
'function getRoutes(address tokenA,address tokenB) view returns((uint8,uint24,address)[])',
|
||||||
|
]
|
||||||
|
|
||||||
|
export const poolAbi = [
|
||||||
|
// {
|
||||||
|
// // the current price
|
||||||
|
// uint160 sqrtPriceX96;
|
||||||
|
// // the current tick
|
||||||
|
// int24 tick;
|
||||||
|
// // the most-recently updated index of the observations array
|
||||||
|
// uint16 observationIndex;
|
||||||
|
// // the current maximum number of observations that are being stored
|
||||||
|
// uint16 observationCardinality;
|
||||||
|
// // the next maximum number of observations to store, triggered in observations.write
|
||||||
|
// uint16 observationCardinalityNext;
|
||||||
|
// // the current protocol fee as a percentage of the swap fee taken on withdrawal
|
||||||
|
// // represented as an integer denominator (1/x)%
|
||||||
|
// uint8 feeProtocol;
|
||||||
|
// // whether the pool is locked
|
||||||
|
// bool unlocked;
|
||||||
|
// }
|
||||||
|
'function slot0() view returns(uint160,int24,uint16,uint16,uint16,uint8,bool)',
|
||||||
|
'function token0() view returns(address)',
|
||||||
|
'function token1() view returns(address)',
|
||||||
|
]
|
||||||
|
|
||||||
export const erc20Abi = [
|
export const erc20Abi = [
|
||||||
'function name() view returns (string)',
|
'function name() view returns (string)',
|
||||||
'function symbol() view returns (string)',
|
'function symbol() view returns (string)',
|
||||||
@@ -38,5 +69,6 @@ export const timedOrderAbi = [
|
|||||||
export const abi = {
|
export const abi = {
|
||||||
'ERC20': erc20Abi,
|
'ERC20': erc20Abi,
|
||||||
'TimedOrder': timedOrderAbi,
|
'TimedOrder': timedOrderAbi,
|
||||||
|
'QueryHelper': queryHelperAbi,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
20
src/blockchain/contract.js
Normal file
20
src/blockchain/contract.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import {ethers} from "ethers";
|
||||||
|
import {factoryAbi, queryHelperAbi} from "@/blockchain/abi.js";
|
||||||
|
import {useStore} from "@/store/store.js";
|
||||||
|
import {provider} from "@/blockchain/wallet.js";
|
||||||
|
|
||||||
|
|
||||||
|
export async function factoryContract() {
|
||||||
|
const s = useStore()
|
||||||
|
return new ethers.Contract(s.helper, factoryAbi, provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function queryHelperContract() {
|
||||||
|
const s = useStore()
|
||||||
|
return new ethers.Contract(s.helper, queryHelperAbi, provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function poolContract(addr) {
|
||||||
|
const s = useStore()
|
||||||
|
return new ethers.Contract(addr, poolAbi, provider)
|
||||||
|
}
|
||||||
@@ -1,86 +1,36 @@
|
|||||||
import {ethers} from "ethers";
|
import {ethers} from "ethers";
|
||||||
import {useStore} from "@/store/store";
|
import {useStore} from "@/store/store";
|
||||||
|
|
||||||
|
export let provider = null
|
||||||
|
|
||||||
|
function onChainChanged(chainId) {
|
||||||
|
chainId = Number(chainId)
|
||||||
|
console.log('chain changed', chainId)
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
|
store.chainId = chainId
|
||||||
|
provider = new ethers.BrowserProvider(window.ethereum, chainId)
|
||||||
class Wallet {
|
|
||||||
|
|
||||||
connected = false
|
|
||||||
_provider = null
|
|
||||||
_signer = null
|
|
||||||
_onSignerInits = {}
|
|
||||||
_enabled = true // prevents tight loops on errors
|
|
||||||
|
|
||||||
async connect() {
|
|
||||||
this._enabled = true
|
|
||||||
if( !this.connected )
|
|
||||||
await this.signer()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async provider() {
|
function onAccountsChanged(accounts) {
|
||||||
if (this._provider === null && this._enabled) {
|
console.log('accounts changed', accounts)
|
||||||
console.log('creating provider')
|
const store = useStore()
|
||||||
const provider = new ethers.BrowserProvider(window.ethereum, store.chain.id)
|
if( accounts.length === 0 ) {
|
||||||
await provider.getNetwork() // this invokes a check on having the correct network connected
|
store.account = null
|
||||||
this._provider = provider
|
}
|
||||||
console.log('wallet connected')
|
else if (accounts[0] !== store.account) {
|
||||||
|
store.account = accounts[0]
|
||||||
}
|
}
|
||||||
return this._provider
|
|
||||||
}
|
|
||||||
|
|
||||||
async signer() {
|
|
||||||
/*
|
|
||||||
if( !store.geo.approved ) {
|
|
||||||
console.log('not approved')
|
|
||||||
this._connected(false)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
const provider = await this.provider()
|
|
||||||
if( provider === null ) {
|
|
||||||
console.log('provider null')
|
|
||||||
this._connected(false)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const signer = await provider.getSigner()
|
|
||||||
if( this._signer?.address !== signer.address ) {
|
|
||||||
console.log('new signer', signer.address)
|
|
||||||
this._signer = signer
|
|
||||||
for (const key in this._onSignerInits) {
|
|
||||||
try {
|
|
||||||
await this._onSignerInits[key](signer)
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
console.log('during onSignerInit', e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log('wallet connected')
|
|
||||||
}
|
|
||||||
this._connected(true)
|
|
||||||
return signer
|
|
||||||
}
|
|
||||||
|
|
||||||
async address() {
|
|
||||||
const signer = await this.signer()
|
|
||||||
if( signer === null )
|
|
||||||
return null
|
|
||||||
return await signer.getAddress()
|
|
||||||
}
|
|
||||||
|
|
||||||
_connected(value) {
|
|
||||||
this.connected = value
|
|
||||||
store.$patch({wallet:{connected:value}})
|
|
||||||
}
|
|
||||||
|
|
||||||
onSignerInit(id, cb) {
|
|
||||||
this._onSignerInits[id] = cb
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function watchWallet() {
|
||||||
|
const chainId = (await new ethers.BrowserProvider(window.ethereum).getNetwork()).chainId
|
||||||
|
onChainChanged(chainId)
|
||||||
|
window.ethereum.on('chainChanged', onChainChanged);
|
||||||
|
window.ethereum.on('accountsChanged', onAccountsChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const handler = {
|
const errorHandlingProxy = {
|
||||||
get(target, prop, proxy) {
|
get(target, prop, proxy) {
|
||||||
const got = Reflect.get(target, prop, proxy);
|
const got = Reflect.get(target, prop, proxy);
|
||||||
if( typeof got !== 'function' ) {
|
if( typeof got !== 'function' ) {
|
||||||
@@ -95,8 +45,11 @@ const handler = {
|
|||||||
target._connected(false)
|
target._connected(false)
|
||||||
target._enabled = false
|
target._enabled = false
|
||||||
if( x.code === 'NETWORK_ERROR' ) {
|
if( x.code === 'NETWORK_ERROR' ) {
|
||||||
store.error('Wrong Blockchain', 'Your wallet is connected to a different blockchain. Please select '+import.meta.env.VITE_CHAIN_NAME+' in your wallet.')
|
// todo available chain names
|
||||||
console.log('wallet network error', x)
|
// store.error('Wrong Blockchain', 'Your wallet is connected to a different blockchain. Please select Arbitrum in your wallet.') // todo hardcoded arb
|
||||||
|
console.log('wallet chain error', x)
|
||||||
|
// store.chainId = store.chainInfo[]
|
||||||
|
throw x
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log('wallet error')
|
console.log('wallet error')
|
||||||
@@ -109,4 +62,4 @@ const handler = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default new Proxy(new Wallet(), handler)
|
// const wallet = new Proxy(new Wallet(), errorHandlingProxy);
|
||||||
|
|||||||
13
src/components/Alerts.vue
Normal file
13
src/components/Alerts.vue
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<v-alert v-for="e in s.errors" icon="mdi-alert" color="error" :title="e.title" :text="e.text" class="mb-3" :closable="e.closeable"/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {useStore} from "@/store/store";
|
||||||
|
const s = useStore()
|
||||||
|
console.log('errors', s.errors)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@use "src/styles/vars" as *;
|
||||||
|
</style>
|
||||||
24
src/components/ConnectWallet.vue
Normal file
24
src/components/ConnectWallet.vue
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<template>
|
||||||
|
<v-btn prepend-icon="mdi-lightbulb-on" text="Connect Wallet" @click="connectWallet" :disabled="disabled"/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {useStore} from "@/store/store";
|
||||||
|
import {ref} from "vue";
|
||||||
|
import {provider} from "@/blockchain/wallet.js";
|
||||||
|
|
||||||
|
const s = useStore()
|
||||||
|
const disabled = ref(false)
|
||||||
|
|
||||||
|
async function connectWallet() {
|
||||||
|
disabled.value = true
|
||||||
|
await provider.getSigner()
|
||||||
|
disabled.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@use "src/styles/vars" as *;
|
||||||
|
|
||||||
|
</style>
|
||||||
18
src/components/NeedsQueryHelper.vue
Normal file
18
src/components/NeedsQueryHelper.vue
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<template>
|
||||||
|
<slot v-if="s.helper"/>
|
||||||
|
<v-card v-if="!s.helper" prepend-icon='mdi-reload-alert' title="Change Blockchain" text="Dexorder works only with Arbitrum. Please choose the Arbitrum blockchain in your wallet."/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {useStore} from "@/store/store";
|
||||||
|
|
||||||
|
const s = useStore()
|
||||||
|
|
||||||
|
async function connect() {
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@use "src/styles/vars" as *;
|
||||||
|
|
||||||
|
</style>
|
||||||
15
src/components/NeedsWallet.vue
Normal file
15
src/components/NeedsWallet.vue
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {useStore} from "@/store/store";
|
||||||
|
|
||||||
|
const s = useStore()
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@use "src/styles/vars" as *;
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<token-choice v-model="modelValue.tokenA" class="token-choice mb-1">
|
<token-choice v-model="tokenA" class="token-choice mb-1">
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<v-btn :text="modelValue.buy ? 'Buy' : 'Sell'" :color="modelValue.buy ? 'green' : 'red'"
|
<v-btn :text="buy ? 'Buy' : 'Sell'" :color="buy ? 'green' : 'red'"
|
||||||
variant="outlined" @click="modelValue.buy=!modelValue.buy" class="bs-button"/>
|
variant="outlined" @click="buy=!buy" class="bs-button"/>
|
||||||
</template>
|
</template>
|
||||||
</token-choice>
|
</token-choice>
|
||||||
<token-choice v-model="modelValue.tokenB" class="token-choice">
|
<token-choice v-model="tokenB" class="token-choice">
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<v-btn :text="!modelValue.buy ? 'Buy' : 'Sell'" :color="!modelValue.buy ? 'green' : 'red'"
|
<v-btn :text="!buy ? 'Buy' : 'Sell'" :color="!buy ? 'green' : 'red'"
|
||||||
variant="outlined" @click="modelValue.buy=!modelValue.buy" class="bs-button"/>
|
variant="outlined" @click="buy=!buy" class="bs-button"/>
|
||||||
</template>
|
</template>
|
||||||
</token-choice>
|
</token-choice>
|
||||||
</template>
|
</template>
|
||||||
@@ -16,15 +16,52 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {useStore} from "@/store/store";
|
import {useStore} from "@/store/store";
|
||||||
import TokenChoice from "@/components/TokenChoice.vue";
|
import TokenChoice from "@/components/TokenChoice.vue";
|
||||||
import {computed, ref} from "vue";
|
import {computed} from "vue";
|
||||||
|
|
||||||
const s = useStore()
|
const s = useStore()
|
||||||
|
|
||||||
// {
|
// { tokenA, tokenB, buy }
|
||||||
// tokenA, tokenB, buy
|
// tokens may be undefined if not selected
|
||||||
// }
|
|
||||||
|
|
||||||
const props = defineProps(['modelValue'])
|
const props = defineProps(['modelValue'])
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
console.log('PairEntry setup', props.modelValue)
|
||||||
|
|
||||||
|
if((!props.modelValue || !props.modelValue.tokenA) && s.tokens && s.tokens.length >= 1)
|
||||||
|
set('tokenA', s.tokens[0])
|
||||||
|
if((!props.modelValue || !props.modelValue.tokenB) && s.tokens && s.tokens.length >= 2)
|
||||||
|
set('tokenB', s.tokens[1])
|
||||||
|
|
||||||
|
function set(k,v) {
|
||||||
|
const newModel = {}
|
||||||
|
if(props.modelValue)
|
||||||
|
Object.assign(newModel, props.modelValue);
|
||||||
|
newModel[k] = v
|
||||||
|
console.log('set', k, v, props.modelValue, newModel)
|
||||||
|
emit('update:modelValue', newModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenComputer(attr, index) {
|
||||||
|
return {
|
||||||
|
get() {
|
||||||
|
return !props.modelValue || !props.modelValue[attr] ? null : props.modelValue[attr]
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
set(attr,value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokenA = computed(tokenComputer('tokenA', 0))
|
||||||
|
const tokenB = computed(tokenComputer('tokenB', 1))
|
||||||
|
const buy = computed({
|
||||||
|
get() {
|
||||||
|
!props.modelValue ? true : props.modelValue.buy
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
set('buy',value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
19
src/components/PhoneCard.vue
Normal file
19
src/components/PhoneCard.vue
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<template>
|
||||||
|
<v-card class="d-none d-md-block" :elevation="4">
|
||||||
|
<slot/>
|
||||||
|
</v-card>
|
||||||
|
<v-container class="d-md-none">
|
||||||
|
<slot/>
|
||||||
|
</v-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {useStore} from "@/store/store";
|
||||||
|
|
||||||
|
const s = useStore()
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@use "src/styles/vars" as *;
|
||||||
|
</style>
|
||||||
@@ -1,9 +1,28 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-card title="DCA / TWAP" subtitle="Split order across time" class="order-card" elevation="4">
|
<NeedsQueryHelper>
|
||||||
|
<PhoneCard>
|
||||||
|
<v-card-title class="big">DCA / TWAP</v-card-title>
|
||||||
|
<v-card-subtitle>Split order across time</v-card-subtitle>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
|
|
||||||
<pair-entry v-model="pair"/>
|
<pair-entry v-model="pair"/>
|
||||||
|
|
||||||
|
<v-chip v-for="r in routes" variant="text">
|
||||||
|
{{ s.chain.name }}
|
||||||
|
<v-img src="https://upload.wikimedia.org/wikipedia/commons/e/e7/Uniswap_Logo.svg" width="1.5em"/>
|
||||||
|
<span class="uniswap-pink ml-0 mr-1">v3</span>
|
||||||
|
{{pairSymbol}} {{r.fee/10000}}%
|
||||||
|
</v-chip>
|
||||||
|
|
||||||
|
<div v-if="routeFinder.pending()">
|
||||||
|
<v-progress-circular indeterminate/> Searching for {{pairSymbol}} pools...
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-alert v-if="routes.length===0 && !routeFinder.pending()" text="No pool found!" type=""/>
|
||||||
|
|
||||||
|
<div v-if="routes.length">
|
||||||
<v-text-field label='Amount' type="number" step="1" variant="outlined" aria-valuemin="0" min="0"
|
<v-text-field label='Amount' type="number" step="1" variant="outlined" aria-valuemin="0" min="0"
|
||||||
:model-value="amount" :rules="[validateRequired,validateAmount]" class="amount">
|
:model-value="amount" :rules="[validateRequired,validateAmount]">
|
||||||
<template v-slot:append-inner>
|
<template v-slot:append-inner>
|
||||||
<v-btn @click="amountIsBase=!amountIsBase" variant="outlined" class="mr-2">
|
<v-btn @click="amountIsBase=!amountIsBase" variant="outlined" class="mr-2">
|
||||||
{{ amountIsBase ? pair.tokenA.symbol : pair.tokenB.symbol }}
|
{{ amountIsBase ? pair.tokenA.symbol : pair.tokenB.symbol }}
|
||||||
@@ -12,13 +31,11 @@
|
|||||||
@click="amountIsTotal=!amountIsTotal" class="total"/>
|
@click="amountIsTotal=!amountIsTotal" class="total"/>
|
||||||
</template>
|
</template>
|
||||||
</v-text-field>
|
</v-text-field>
|
||||||
<v-text-field label="Tranches" type="number" variant="outlined" aria-valuemin="1" min="1"
|
<v-text-field label="Tranches" type="number" variant="outlined" aria-valuemin="1" min="1" max="255"
|
||||||
:model-value="tranches" :rules="[validateRequired]">
|
:model-value="tranches" :rules="[validateRequired,validateTranches]">
|
||||||
<template v-slot:prepend-inner>
|
<!-- <template v-slot:prepend-inner>-->
|
||||||
<v-btn class="split-into mr-2" variant="outlined" @click="amountIsTotal=!amountIsTotal">
|
<!-- <div>{{ amountIsTotal ? 'Split into' : 'Times' }}</div>-->
|
||||||
{{ amountIsTotal ? 'Split into' : 'Times' }}
|
<!-- </template>-->
|
||||||
</v-btn>
|
|
||||||
</template>
|
|
||||||
<template v-slot:append-inner>tranches</template>
|
<template v-slot:append-inner>tranches</template>
|
||||||
</v-text-field>
|
</v-text-field>
|
||||||
<v-text-field type="number" variant="outlined" :min="1" v-model="interval" class="interval"
|
<v-text-field type="number" variant="outlined" :min="1" v-model="interval" class="interval"
|
||||||
@@ -32,11 +49,14 @@
|
|||||||
<v-btn variant="outlined" :text="timeUnits[timeUnitIndex]" @click="toggleTimeUnits" class="time-units"/>
|
<v-btn variant="outlined" :text="timeUnits[timeUnitIndex]" @click="toggleTimeUnits" class="time-units"/>
|
||||||
</template>
|
</template>
|
||||||
</v-text-field>
|
</v-text-field>
|
||||||
<v-text-field v-model="limitPrice" :label="(limitIsMinimum?'Minimum':'Maximum')+' Price'" type="number" variant="outlined" aria-valuemin="0" min="0"
|
<v-text-field v-model="limitPrice" :label="(limitIsMinimum?'Minimum':'Maximum')+' Price'" type="number"
|
||||||
|
variant="outlined" aria-valuemin="0" min="0"
|
||||||
clearable :rules="[validateAmount, validateMin]">
|
clearable :rules="[validateAmount, validateMin]">
|
||||||
<template v-slot:append-inner>
|
<template v-slot:append-inner>
|
||||||
<v-btn variant="outlined" @click="inverted=!inverted">
|
<v-btn variant="outlined" @click="inverted=!inverted">
|
||||||
{{ inverted ? pair.tokenB.symbol + '/' + pair.tokenA.symbol : pair.tokenA.symbol + '/' + pair.tokenB.symbol }}
|
{{
|
||||||
|
inverted ? pair.tokenB.symbol + '/' + pair.tokenA.symbol : pair.tokenA.symbol + '/' + pair.tokenB.symbol
|
||||||
|
}}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
</v-text-field>
|
</v-text-field>
|
||||||
@@ -58,23 +78,47 @@
|
|||||||
</template>
|
</template>
|
||||||
</v-text-field>
|
</v-text-field>
|
||||||
-->
|
-->
|
||||||
|
</div>
|
||||||
|
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
|
||||||
<v-card-actions class="d-flex justify-space-evenly mb-4">
|
<v-card-actions class="d-flex justify-space-evenly mb-4">
|
||||||
<v-btn variant="outlined" color="red">Cancel</v-btn>
|
<v-btn variant="outlined" color="red">Cancel</v-btn>
|
||||||
<v-btn variant="flat" color="green">Place Order</v-btn>
|
<v-btn variant="flat" color="green" :disabled="!routes.length">Place Order</v-btn>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
</v-card>
|
</PhoneCard>
|
||||||
|
</NeedsQueryHelper>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {useStore} from "@/store/store";
|
import {useStore} from "@/store/store";
|
||||||
import {computed, ref} from "vue";
|
import {computed, ref} from "vue";
|
||||||
import PairEntry from "@/components/PairEntry.vue";
|
import PairEntry from "@/components/PairEntry.vue";
|
||||||
|
import PhoneCard from "@/components/PhoneCard.vue";
|
||||||
|
import {queryHelperContract} from "@/blockchain/contract.js";
|
||||||
|
import {SingletonCoroutine} from "@/misc.js";
|
||||||
|
import NeedsQueryHelper from "@/components/NeedsQueryHelper.vue";
|
||||||
|
|
||||||
const s = useStore()
|
const s = useStore()
|
||||||
const pair = ref({tokenA: s.tokens[0], tokenB: s.tokens[1], buy: true})
|
// pair is computed to allow for dynamic computation of a default value based on current chainId
|
||||||
|
const _pair = ref({buy:true})
|
||||||
|
const pair = computed({
|
||||||
|
get() {
|
||||||
|
console.log('te getpair',_pair.value)
|
||||||
|
return _pair.value
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
console.log('set pair',value)
|
||||||
|
_pair.value = value
|
||||||
|
routes.value = []
|
||||||
|
routeFinder.invoke()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const pairSelected = computed(()=>_pair.value.tokenA && _pair.value.tokenB && routes.value)
|
||||||
|
const base = computed(()=>!pairSelected?{}:_pair.value.tokenA)
|
||||||
|
const quote = computed(()=>!pairSelected?{}:_pair.value.tokenB)
|
||||||
|
const pairSymbol = computed(()=>base.value?.symbol+'/'+quote.value?.symbol)
|
||||||
|
const routes = ref([])
|
||||||
const amount = ref(1)
|
const amount = ref(1)
|
||||||
const amountIsBase = ref(false)
|
const amountIsBase = ref(false)
|
||||||
const amountIsTotal = ref(true)
|
const amountIsTotal = ref(true)
|
||||||
@@ -90,6 +134,40 @@ const timeUnitIndex = ref(1)
|
|||||||
|
|
||||||
const limitIsMinimum = computed(() => !(pair.value.buy ^ inverted.value))
|
const limitIsMinimum = computed(() => !(pair.value.buy ^ inverted.value))
|
||||||
|
|
||||||
|
async function findRoute() {
|
||||||
|
if( !pair.value || !pair.value.tokenA || !pair.value.tokenB )
|
||||||
|
return null
|
||||||
|
const helper = await queryHelperContract()
|
||||||
|
console.log('helper',helper)
|
||||||
|
let rawRoutes
|
||||||
|
try {
|
||||||
|
rawRoutes = await helper.getRoutes(pair.value.tokenA.address, pair.value.tokenB.address)
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
routes.value = []
|
||||||
|
console.log('routes exception', e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// todo expose all available pools
|
||||||
|
let result = {} // we actually only find a single pool for now
|
||||||
|
for (let [exchange, fee, pool] of rawRoutes) {
|
||||||
|
exchange = Number(exchange)
|
||||||
|
fee = Number(fee)
|
||||||
|
if (result.fee === undefined || result.fee > fee) {
|
||||||
|
switch (exchange) {
|
||||||
|
case 0: // UniswapV2
|
||||||
|
break
|
||||||
|
case 1: // UniswapV3
|
||||||
|
result = {exchange: 'UniswapV3', pool, fee,}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
routes.value = [result]
|
||||||
|
}
|
||||||
|
|
||||||
|
const routeFinder = new SingletonCoroutine(findRoute,50)
|
||||||
|
|
||||||
|
|
||||||
function toggleTimeUnits() {
|
function toggleTimeUnits() {
|
||||||
timeUnitIndex.value++
|
timeUnitIndex.value++
|
||||||
@@ -110,6 +188,18 @@ function validateRequired(v) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function validateTranches(v) {
|
||||||
|
const i = parseInt(v)
|
||||||
|
if (parseFloat(v) !== i)
|
||||||
|
return 'Whole numbers only'
|
||||||
|
if (i < 1)
|
||||||
|
return 'Must have at least one tranche'
|
||||||
|
if (i > 255)
|
||||||
|
return 'Maximum 255 tranches'
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function validateAmount(v) {
|
function validateAmount(v) {
|
||||||
if (isEmpty(v))
|
if (isEmpty(v))
|
||||||
return true
|
return true
|
||||||
@@ -139,10 +229,6 @@ function validateMin(v) {
|
|||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@use "@/styles/vars" as *;
|
@use "@/styles/vars" as *;
|
||||||
|
|
||||||
.order-card {
|
|
||||||
width: 25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.amount {
|
.amount {
|
||||||
width: 23em;
|
width: 23em;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
:label="label"
|
:label="label"
|
||||||
v-bind="modelValue" @update:modelValue="updateValue"
|
v-bind="modelValue" @update:modelValue="updateValue"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||||
>
|
>
|
||||||
<template v-slot:prepend><slot name="prepend"/></template>
|
<template v-slot:prepend><slot name="prepend"/></template>
|
||||||
<template v-slot:item="{props,item}">
|
<template v-slot:item="{props,item}">
|
||||||
@@ -76,8 +77,6 @@ function updateValue(v) {
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {ethers} from "ethers";
|
import {ethers} from "ethers";
|
||||||
import wallet from "@/blockchain/wallet.js";
|
|
||||||
import {erc20Abi} from "@/blockchain/abi.js";
|
|
||||||
import {useStore} from "@/store/store.js";
|
import {useStore} from "@/store/store.js";
|
||||||
import {socket} from "@/socket.js";
|
import {socket} from "@/socket.js";
|
||||||
|
|
||||||
@@ -85,14 +84,14 @@ const s = useStore()
|
|||||||
|
|
||||||
async function addExtraToken(addr) {
|
async function addExtraToken(addr) {
|
||||||
const prom = new Promise((resolve)=>{
|
const prom = new Promise((resolve)=>{
|
||||||
socket.emit('lookupToken', s.chain.id, addr, (info) => {
|
socket.emit('lookupToken', s.chainId, addr, (info) => {
|
||||||
if( info === null )
|
if( info === null )
|
||||||
return resolve(null)
|
return resolve(null)
|
||||||
s.$patch((state)=>{
|
s.$patch((state)=>{
|
||||||
let extras = state.extraTokens[state.chain.id]
|
let extras = state.extraTokens[state.chainId]
|
||||||
if( extras === undefined ) {
|
if( extras === undefined ) {
|
||||||
extras = {}
|
extras = {}
|
||||||
state.extraTokens[state.chain.id] = extras
|
state.extraTokens[state.chainId] = extras
|
||||||
}
|
}
|
||||||
extras[info.address] = info
|
extras[info.address] = info
|
||||||
})
|
})
|
||||||
@@ -106,5 +105,4 @@ async function addExtraToken(addr) {
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@use "src/styles/vars" as *;
|
@use "src/styles/vars" as *;
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {useStore} from "@/store/store.js";
|
import {useStore} from "@/store/store.js";
|
||||||
import wallet from "@/blockchain/wallet.js";
|
|
||||||
|
|
||||||
const s = useStore()
|
const s = useStore()
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-app-bar flat>
|
<v-app-bar flat>
|
||||||
<v-app-bar-title class="sc">
|
<v-app-bar-title class="">
|
||||||
<v-icon icon="mdi-arrow-up-bold" size="x-small" class="arrow" color="green"/>
|
<span class="logo"><v-icon icon="mdi-arrow-up-bold" size="x-small" class="arrow" color="green"/>dexorder</span>
|
||||||
<span class="logo">dexorder</span>
|
|
||||||
<v-chip text="ALPHA" size="x-small" color="red" class="mx-1"/>
|
<v-chip text="ALPHA" size="x-small" color="red" class="mx-1"/>
|
||||||
</v-app-bar-title>
|
</v-app-bar-title>
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
@@ -14,10 +13,16 @@
|
|||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@use "src/styles/vars" as v;
|
@use "src/styles/vars" as v;
|
||||||
|
|
||||||
.arrow {
|
.arrow {
|
||||||
color: v.$green;
|
color: v.$green;
|
||||||
|
vertical-align: text-top;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
|
font-family: v.$heading-font-family;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
font-size: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,10 +1,26 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-main>
|
<v-main>
|
||||||
<v-container>
|
<v-container>
|
||||||
|
<div class="maxw">
|
||||||
|
<Alerts/>
|
||||||
<router-view/>
|
<router-view/>
|
||||||
|
<v-skeleton-loader v-if="!store.chainInfo" type="card" class="order-card"/>
|
||||||
|
</div>
|
||||||
</v-container>
|
</v-container>
|
||||||
</v-main>
|
</v-main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import Alerts from "@/components/Alerts.vue";
|
||||||
|
import {VSkeletonLoader} from "vuetify/labs/VSkeletonLoader";
|
||||||
|
import {useStore} from "@/store/store.js";
|
||||||
|
|
||||||
|
const store = useStore()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@use 'src/styles/vars' as *;
|
||||||
|
.maxw {
|
||||||
|
max-width: $card-maxw;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -13,14 +13,11 @@ import { createApp } from 'vue'
|
|||||||
// Plugins
|
// Plugins
|
||||||
import { registerPlugins } from '@/plugins'
|
import { registerPlugins } from '@/plugins'
|
||||||
import '@/styles/style.scss'
|
import '@/styles/style.scss'
|
||||||
import {useStore} from "@/store/store.js";
|
import {watchWallet} from "@/blockchain/wallet.js";
|
||||||
|
import "./socket.js"
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
registerPlugins(app)
|
registerPlugins(app)
|
||||||
|
|
||||||
const s = useStore()
|
|
||||||
s.chains = known_chains
|
|
||||||
s.chain = known_chains[0]
|
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
await watchWallet()
|
||||||
|
|||||||
35
src/misc.js
Normal file
35
src/misc.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
|
||||||
|
export class SingletonCoroutine {
|
||||||
|
constructor(f, delay=10, retry=true) {
|
||||||
|
this.f = f
|
||||||
|
this.delay = delay
|
||||||
|
this.timeout = null
|
||||||
|
this.args = null
|
||||||
|
}
|
||||||
|
|
||||||
|
pending() {
|
||||||
|
return this.timeout !== null
|
||||||
|
}
|
||||||
|
|
||||||
|
invoke(/*arguments*/) {
|
||||||
|
this.args = arguments
|
||||||
|
if( this.timeout !== null )
|
||||||
|
clearTimeout(this.timeout)
|
||||||
|
this.timeout = setTimeout(this.onTimeout, this.delay, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
async onTimeout(self) {
|
||||||
|
self.timeout = null
|
||||||
|
console.log('invoking', self.f, self.args)
|
||||||
|
try {
|
||||||
|
await self.f(...self.args)
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
if( self.retry )
|
||||||
|
self.invoke(self.args)
|
||||||
|
else
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,8 +5,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// todo choose
|
// todo choose
|
||||||
const displayFonts = ['Tektur', 'Turret Road', 'Chakra Petch', 'Orbitron', 'Quantico', 'Geo']
|
const displayFonts = ['Orbitron', 'Tektur', 'Turret Road', 'Chakra Petch', 'Quantico', 'Geo']
|
||||||
const bodyFonts = ['Victor Mono', 'Nunito', 'Manrope', 'Sofia Sans']
|
const bodyFonts = ['Exo 2', 'Victor Mono', 'Nunito', 'Manrope', 'Sofia Sans', 'Dosis', 'PT Sans', 'Space Grotesk', 'Rajdhani', 'Saira Semi Condensed', 'Jura']
|
||||||
|
|
||||||
export async function loadFonts () {
|
export async function loadFonts () {
|
||||||
const webFontLoader = await import(/* webpackChunkName: "webfontloader" */'webfontloader')
|
const webFontLoader = await import(/* webpackChunkName: "webfontloader" */'webfontloader')
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {io} from "socket.io-client";
|
import {io} from "socket.io-client";
|
||||||
|
import {useStore} from "@/store/store.js";
|
||||||
|
|
||||||
export const socket = io(import.meta.env.VITE_WS_URL || undefined, { transports: ["websocket"] })
|
export const socket = io(import.meta.env.VITE_WS_URL || undefined, { transports: ["websocket"] })
|
||||||
|
|
||||||
@@ -14,3 +15,7 @@ socket.on('welcome', (data)=>{
|
|||||||
console.log('welcome',data)
|
console.log('welcome',data)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
socket.on('chainInfo', async (chainInfo)=>{
|
||||||
|
const s = useStore()
|
||||||
|
s.chainInfo = chainInfo
|
||||||
|
})
|
||||||
|
|||||||
@@ -1,33 +1,44 @@
|
|||||||
// Utilities
|
// Utilities
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
import {knownTokens} from "@/tokens.js";
|
||||||
|
|
||||||
export const useStore = defineStore('app', {
|
export const useStore = defineStore('app', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
chain: {name:'Not Connected', id:0, icon:''},
|
chainInfo: null,
|
||||||
wallet: {
|
chainId: null,
|
||||||
connected: false,
|
account: null,
|
||||||
},
|
|
||||||
errors: [],
|
|
||||||
extraTokens: {},
|
|
||||||
vault: null,
|
vault: null,
|
||||||
|
errors: [{
|
||||||
|
title: 'DANGER!',
|
||||||
|
text: 'This is early development (alpha) software, which could have severe bugs that lose all your money. Thank you for testing a SMALL amount!',
|
||||||
|
closeable: false
|
||||||
|
}],
|
||||||
|
extraTokens: {},
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
|
chain: (s)=> !s.chainInfo ? null : (s.chainInfo[s.chainId] || null),
|
||||||
tokens: (s)=>{
|
tokens: (s)=>{
|
||||||
const extras = s.extraTokens[s.chain.id]
|
console.log('tokensget',s)
|
||||||
return extras === undefined ? Object.values(known_tokens[s.chain.id])
|
let known = knownTokens[s.chainId]
|
||||||
: [...Object.values(known_tokens[s.chain.id]), ...Object.values(extras)]
|
known = known ? Object.values(known) : []
|
||||||
|
let extras = s.extraTokens[s.chainId]
|
||||||
|
extras = extras ? Object.values(extras) : []
|
||||||
|
console.log('tokens for chainId',s.chainId, known, extras)
|
||||||
|
return [...known, ...extras]
|
||||||
},
|
},
|
||||||
|
factory: (s)=>!s.chain?null:s.chain.factory,
|
||||||
|
helper: (s)=>!s.chain?null:s.chain.helper,
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
error(title, message) {
|
error(title, text, closeable=true) {
|
||||||
this.errors.push({title:title, message:message})
|
this.errors.push({title, text, closeable})
|
||||||
},
|
},
|
||||||
closeError(title, message) {
|
closeError(title, text) {
|
||||||
console.log('closing error', title, message)
|
console.log('closing error', title, text)
|
||||||
const result = []
|
const result = []
|
||||||
this.errors.forEach((i)=>{if(i.title!==title && i.message!==message) result.push(i)})
|
this.errors.forEach((i)=>{if(i.title!==title && i.text!==text) result.push(i)})
|
||||||
this.errors = result
|
this.errors = result
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -11,4 +11,6 @@
|
|||||||
//$variable: false,
|
//$variable: false,
|
||||||
$body-font-family: v.$body-font-family,
|
$body-font-family: v.$body-font-family,
|
||||||
$heading-font-family: v.$heading-font-family,
|
$heading-font-family: v.$heading-font-family,
|
||||||
|
$card-title-font-size: 24px,
|
||||||
|
$app-bar-title-font-size: 32px,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -18,9 +18,17 @@
|
|||||||
.v-list-subheader {
|
.v-list-subheader {
|
||||||
font-family: v.$heading-font-family;
|
font-family: v.$heading-font-family;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.v-field__input {
|
||||||
.sc {
|
justify-content: center;
|
||||||
font-family: v.$heading-font-family;
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.uniswap-pink {
|
||||||
|
color: v.$uniswap-pink;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ $blue: #0033CC;
|
|||||||
$white: #fffefd; // just a touch green
|
$white: #fffefd; // just a touch green
|
||||||
$black: #000102;
|
$black: #000102;
|
||||||
|
|
||||||
|
$uniswap-pink: #ff007a;
|
||||||
|
|
||||||
$primary: $blue;
|
$primary: $blue;
|
||||||
$primary-50: transparentize($primary,0.5);
|
$primary-50: transparentize($primary,0.5);
|
||||||
$primary-25: transparentize($primary,0.75);
|
$primary-25: transparentize($primary,0.75);
|
||||||
@@ -29,10 +31,19 @@ $all-colors: (
|
|||||||
green: $green,
|
green: $green,
|
||||||
yellow: $yellow,
|
yellow: $yellow,
|
||||||
red: $red,
|
red: $red,
|
||||||
|
uniswap: $uniswap-pink,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$body-font-family: 'Saira Semi Condensed', monospace, sans-serif; // fairly geometric, horizontal s's, clean sans, readable
|
||||||
|
//$body-font-family: 'Exo 2', monospace, sans-serif; // good, works with Orbitron
|
||||||
|
//$body-font-family: 'Jura', monospace, sans-serif; // interesting numbers, zero dot. l-foot but the s is too twisty with light serifs
|
||||||
|
//$body-font-family: 'Manrope', monospace, sans-serif; // readable, clean, works with Orbitron
|
||||||
//$body-font-family: 'Victor Mono', monospace, sans-serif;
|
//$body-font-family: 'Victor Mono', monospace, sans-serif;
|
||||||
$body-font-family: 'Manrope', monospace, sans-serif;
|
//$body-font-family: 'Space Grotesk', monospace, sans-serif; // silly serifs on caps
|
||||||
|
//$body-font-family: 'Rajdhani', monospace, sans-serif; // very thin a little hard to read
|
||||||
|
//$body-font-family: 'Dosis', monospace, sans-serif; // a little informal, to Dilbert-y
|
||||||
|
//$body-font-family: 'PT Sans', monospace, sans-serif; // not bad but numbers are too seriffy
|
||||||
|
//$body-font-family: 'Cairo', monospace, sans-serif; // number baseline is jagged
|
||||||
//$body-font-family: 'Nunito', monospace, sans-serif;
|
//$body-font-family: 'Nunito', monospace, sans-serif;
|
||||||
//$body-font-family: 'Sofia Sans', monospace, sans-serif;
|
//$body-font-family: 'Sofia Sans', monospace, sans-serif;
|
||||||
//$body-font-family: 'Quantico', sans-serif;
|
//$body-font-family: 'Quantico', sans-serif;
|
||||||
@@ -43,4 +54,4 @@ $heading-font-family: 'Orbitron', sans-serif;
|
|||||||
//$heading-font-family: 'Chakra Petch', sans-serif;
|
//$heading-font-family: 'Chakra Petch', sans-serif;
|
||||||
|
|
||||||
$sm-breakpoint: 600px;
|
$sm-breakpoint: 600px;
|
||||||
$card-maxw: 30rem;
|
$card-maxw: 40em;
|
||||||
|
|||||||
94
src/tokens.js
Normal file
94
src/tokens.js
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
export const knownTokens = {};
|
||||||
|
|
||||||
|
const known_chains = [
|
||||||
|
{
|
||||||
|
name: 'Arbitrum One',
|
||||||
|
id: 42161,
|
||||||
|
icon: null,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const arbitrumTokens = [
|
||||||
|
{
|
||||||
|
name: 'Wrapped Ether',
|
||||||
|
symbol: 'WETH',
|
||||||
|
decimals: 18,
|
||||||
|
icon: null,
|
||||||
|
address: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Tether USD',
|
||||||
|
symbol: 'USDT',
|
||||||
|
decimals: 6,
|
||||||
|
icon: null,
|
||||||
|
address: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'USD Coin',
|
||||||
|
symbol: 'USDC',
|
||||||
|
decimals: 6,
|
||||||
|
icon: null,
|
||||||
|
address: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Wrapped Bitcoin',
|
||||||
|
symbol: 'WBTC',
|
||||||
|
decimals: 8,
|
||||||
|
icon: null,
|
||||||
|
address: '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DAI Stablecoin',
|
||||||
|
symbol: 'DAI',
|
||||||
|
decimals: 18,
|
||||||
|
icon: null,
|
||||||
|
address: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Uniswap',
|
||||||
|
symbol: 'UNI',
|
||||||
|
decimals: 18,
|
||||||
|
icon: null,
|
||||||
|
address: '0xFa7F8980b0f1E64A2062791cc3b0871572f1F7f0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Chainlink Token',
|
||||||
|
symbol: 'LINK',
|
||||||
|
decimals: 18,
|
||||||
|
icon: null,
|
||||||
|
address: '0xf97f4df75117a78c1A5a0DBb814Af92458539FB4',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TrueUSD',
|
||||||
|
symbol: 'TUSD',
|
||||||
|
decimals: 18,
|
||||||
|
icon: null,
|
||||||
|
address: '0x4D15a3A2286D883AF0AA1B3f21367843FAc63E07',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Lido DAO Token',
|
||||||
|
symbol: 'LDO',
|
||||||
|
decimals: 18,
|
||||||
|
icon: null,
|
||||||
|
address: '0x13Ad51ed4F1B7e9Dc168d8a00cB3f4dDD85EfA60',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Arbitrum',
|
||||||
|
symbol: 'ARB',
|
||||||
|
decimals: 18,
|
||||||
|
icon: null,
|
||||||
|
address: '0x912CE59144191C1204E64559FE8253a0e49E6548',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const _knownTokens = {
|
||||||
|
42161: arbitrumTokens,
|
||||||
|
1338: arbitrumTokens,
|
||||||
|
};
|
||||||
|
|
||||||
|
for( const chainId in _knownTokens ) {
|
||||||
|
knownTokens[chainId] = {}
|
||||||
|
for( const info of _knownTokens[chainId] )
|
||||||
|
knownTokens[chainId][info.address] = info
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user