From 72399878679a9d11ad764c22449eba647097709f Mon Sep 17 00:00:00 2001 From: Tim Olson <> Date: Wed, 4 Oct 2023 03:40:47 -0400 Subject: [PATCH] chainInfo from server, routes, wallet rework --- index.html | 1 - public/generated.js | 91 ----------- src/blockchain/abi.js | 32 ++++ src/blockchain/contract.js | 20 +++ src/blockchain/wallet.js | 103 ++++-------- src/components/Alerts.vue | 13 ++ src/components/ConnectWallet.vue | 24 +++ src/components/NeedsQueryHelper.vue | 18 +++ src/components/NeedsWallet.vue | 15 ++ src/components/PairEntry.vue | 59 +++++-- src/components/PhoneCard.vue | 19 +++ src/components/TimedOrderEntry.vue | 242 +++++++++++++++++++--------- src/components/TokenChoice.vue | 10 +- src/components/Vault.vue | 1 - src/layouts/default/AppBar.vue | 13 +- src/layouts/default/Default.vue | 2 +- src/layouts/default/View.vue | 18 ++- src/main.js | 9 +- src/misc.js | 35 ++++ src/plugins/webfontloader.js | 4 +- src/socket.js | 5 + src/store/store.js | 41 +++-- src/styles/settings.scss | 2 + src/styles/style.scss | 14 +- src/styles/vars.scss | 15 +- src/tokens.js | 94 +++++++++++ 26 files changed, 603 insertions(+), 297 deletions(-) delete mode 100644 public/generated.js create mode 100644 src/blockchain/contract.js create mode 100644 src/components/Alerts.vue create mode 100644 src/components/ConnectWallet.vue create mode 100644 src/components/NeedsQueryHelper.vue create mode 100644 src/components/NeedsWallet.vue create mode 100644 src/components/PhoneCard.vue create mode 100644 src/misc.js create mode 100644 src/tokens.js diff --git a/index.html b/index.html index ad36e49..cd32b0b 100644 --- a/index.html +++ b/index.html @@ -11,7 +11,6 @@
- diff --git a/public/generated.js b/public/generated.js deleted file mode 100644 index dcd071c..0000000 --- a/public/generated.js +++ /dev/null @@ -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 -} diff --git a/src/blockchain/abi.js b/src/blockchain/abi.js index 4e758b1..89be73b 100644 --- a/src/blockchain/abi.js +++ b/src/blockchain/abi.js @@ -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 = [ 'function name() view returns (string)', 'function symbol() view returns (string)', @@ -38,5 +69,6 @@ export const timedOrderAbi = [ export const abi = { 'ERC20': erc20Abi, 'TimedOrder': timedOrderAbi, + 'QueryHelper': queryHelperAbi, } diff --git a/src/blockchain/contract.js b/src/blockchain/contract.js new file mode 100644 index 0000000..6983e1f --- /dev/null +++ b/src/blockchain/contract.js @@ -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) +} diff --git a/src/blockchain/wallet.js b/src/blockchain/wallet.js index f4d5be1..710ac99 100644 --- a/src/blockchain/wallet.js +++ b/src/blockchain/wallet.js @@ -1,86 +1,36 @@ import {ethers} from "ethers"; import {useStore} from "@/store/store"; -const store = useStore() +export let provider = null +function onChainChanged(chainId) { + chainId = Number(chainId) + console.log('chain changed', chainId) + 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() +function onAccountsChanged(accounts) { + console.log('accounts changed', accounts) + const store = useStore() + if( accounts.length === 0 ) { + store.account = null } - - async provider() { - if (this._provider === null && this._enabled) { - console.log('creating provider') - const provider = new ethers.BrowserProvider(window.ethereum, store.chain.id) - await provider.getNetwork() // this invokes a check on having the correct network connected - this._provider = provider - console.log('wallet connected') - } - 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 + else if (accounts[0] !== store.account) { + store.account = accounts[0] } +} +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) { const got = Reflect.get(target, prop, proxy); if( typeof got !== 'function' ) { @@ -95,8 +45,11 @@ const handler = { target._connected(false) target._enabled = false 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.') - console.log('wallet network error', x) + // todo available chain names + // 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 { console.log('wallet error') @@ -109,4 +62,4 @@ const handler = { } -export default new Proxy(new Wallet(), handler) +// const wallet = new Proxy(new Wallet(), errorHandlingProxy); diff --git a/src/components/Alerts.vue b/src/components/Alerts.vue new file mode 100644 index 0000000..0022ef5 --- /dev/null +++ b/src/components/Alerts.vue @@ -0,0 +1,13 @@ + + + + + diff --git a/src/components/ConnectWallet.vue b/src/components/ConnectWallet.vue new file mode 100644 index 0000000..ec154dd --- /dev/null +++ b/src/components/ConnectWallet.vue @@ -0,0 +1,24 @@ + + + + + diff --git a/src/components/NeedsQueryHelper.vue b/src/components/NeedsQueryHelper.vue new file mode 100644 index 0000000..0668896 --- /dev/null +++ b/src/components/NeedsQueryHelper.vue @@ -0,0 +1,18 @@ + + + + + diff --git a/src/components/NeedsWallet.vue b/src/components/NeedsWallet.vue new file mode 100644 index 0000000..bdc5bc8 --- /dev/null +++ b/src/components/NeedsWallet.vue @@ -0,0 +1,15 @@ + + + + + diff --git a/src/components/PairEntry.vue b/src/components/PairEntry.vue index b2b0c99..c485a82 100644 --- a/src/components/PairEntry.vue +++ b/src/components/PairEntry.vue @@ -1,14 +1,14 @@ @@ -16,15 +16,52 @@ diff --git a/src/components/PhoneCard.vue b/src/components/PhoneCard.vue new file mode 100644 index 0000000..a584187 --- /dev/null +++ b/src/components/PhoneCard.vue @@ -0,0 +1,19 @@ + + + + + diff --git a/src/components/TimedOrderEntry.vue b/src/components/TimedOrderEntry.vue index 279b284..c11ddb8 100644 --- a/src/components/TimedOrderEntry.vue +++ b/src/components/TimedOrderEntry.vue @@ -1,80 +1,124 @@ + + diff --git a/src/main.js b/src/main.js index f90c7aa..37b67a9 100644 --- a/src/main.js +++ b/src/main.js @@ -13,14 +13,11 @@ import { createApp } from 'vue' // Plugins import { registerPlugins } from '@/plugins' import '@/styles/style.scss' -import {useStore} from "@/store/store.js"; +import {watchWallet} from "@/blockchain/wallet.js"; +import "./socket.js" const app = createApp(App) registerPlugins(app) -const s = useStore() -s.chains = known_chains -s.chain = known_chains[0] - app.mount('#app') - +await watchWallet() diff --git a/src/misc.js b/src/misc.js new file mode 100644 index 0000000..3636d89 --- /dev/null +++ b/src/misc.js @@ -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) + } + } + +} diff --git a/src/plugins/webfontloader.js b/src/plugins/webfontloader.js index ab2fa89..0db0831 100644 --- a/src/plugins/webfontloader.js +++ b/src/plugins/webfontloader.js @@ -5,8 +5,8 @@ */ // todo choose -const displayFonts = ['Tektur', 'Turret Road', 'Chakra Petch', 'Orbitron', 'Quantico', 'Geo'] -const bodyFonts = ['Victor Mono', 'Nunito', 'Manrope', 'Sofia Sans'] +const displayFonts = ['Orbitron', 'Tektur', 'Turret Road', 'Chakra Petch', 'Quantico', 'Geo'] +const bodyFonts = ['Exo 2', 'Victor Mono', 'Nunito', 'Manrope', 'Sofia Sans', 'Dosis', 'PT Sans', 'Space Grotesk', 'Rajdhani', 'Saira Semi Condensed', 'Jura'] export async function loadFonts () { const webFontLoader = await import(/* webpackChunkName: "webfontloader" */'webfontloader') diff --git a/src/socket.js b/src/socket.js index bc64f4b..7d4a619 100644 --- a/src/socket.js +++ b/src/socket.js @@ -1,4 +1,5 @@ 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"] }) @@ -14,3 +15,7 @@ socket.on('welcome', (data)=>{ console.log('welcome',data) }) +socket.on('chainInfo', async (chainInfo)=>{ + const s = useStore() + s.chainInfo = chainInfo +}) diff --git a/src/store/store.js b/src/store/store.js index aa35c9e..0d9ccd3 100644 --- a/src/store/store.js +++ b/src/store/store.js @@ -1,33 +1,44 @@ // Utilities import { defineStore } from 'pinia' +import {knownTokens} from "@/tokens.js"; export const useStore = defineStore('app', { state: () => ({ - chain: {name:'Not Connected', id:0, icon:''}, - wallet: { - connected: false, - }, - errors: [], - extraTokens: {}, + chainInfo: null, + chainId: null, + account: 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: { + chain: (s)=> !s.chainInfo ? null : (s.chainInfo[s.chainId] || null), tokens: (s)=>{ - const extras = s.extraTokens[s.chain.id] - return extras === undefined ? Object.values(known_tokens[s.chain.id]) - : [...Object.values(known_tokens[s.chain.id]), ...Object.values(extras)] + console.log('tokensget',s) + let known = knownTokens[s.chainId] + 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: { - error(title, message) { - this.errors.push({title:title, message:message}) + error(title, text, closeable=true) { + this.errors.push({title, text, closeable}) }, - closeError(title, message) { - console.log('closing error', title, message) + closeError(title, text) { + console.log('closing error', title, text) 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 }, - } + }, }) diff --git a/src/styles/settings.scss b/src/styles/settings.scss index 724e646..d93f624 100644 --- a/src/styles/settings.scss +++ b/src/styles/settings.scss @@ -11,4 +11,6 @@ //$variable: false, $body-font-family: v.$body-font-family, $heading-font-family: v.$heading-font-family, + $card-title-font-size: 24px, + $app-bar-title-font-size: 32px, ); diff --git a/src/styles/style.scss b/src/styles/style.scss index 93d3248..46dc307 100644 --- a/src/styles/style.scss +++ b/src/styles/style.scss @@ -18,9 +18,17 @@ .v-list-subheader { font-family: v.$heading-font-family; } + + input { + text-align: center; + } + + .v-field__input { + justify-content: center; + } + } - -.sc { - font-family: v.$heading-font-family; +.uniswap-pink { + color: v.$uniswap-pink; } diff --git a/src/styles/vars.scss b/src/styles/vars.scss index 5da18a8..71eced0 100644 --- a/src/styles/vars.scss +++ b/src/styles/vars.scss @@ -7,6 +7,8 @@ $blue: #0033CC; $white: #fffefd; // just a touch green $black: #000102; +$uniswap-pink: #ff007a; + $primary: $blue; $primary-50: transparentize($primary,0.5); $primary-25: transparentize($primary,0.75); @@ -29,10 +31,19 @@ $all-colors: ( green: $green, yellow: $yellow, 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: '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: 'Sofia Sans', monospace, 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; $sm-breakpoint: 600px; -$card-maxw: 30rem; +$card-maxw: 40em; diff --git a/src/tokens.js b/src/tokens.js new file mode 100644 index 0000000..8aca02e --- /dev/null +++ b/src/tokens.js @@ -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 +} +