diff --git a/src/blockchain/abi.js b/src/blockchain/abi.js
index 8bfd844..f090ae9 100644
--- a/src/blockchain/abi.js
+++ b/src/blockchain/abi.js
@@ -6,7 +6,7 @@ export const queryHelperAbi = [
'function getRoutes(address tokenA,address tokenB) view returns((uint8,uint24,address)[])',
]
-export const poolAbi = [
+export const uniswapV3PoolAbi = [
// {
// // the current price
// uint160 sqrtPriceX96;
diff --git a/src/blockchain/contract.js b/src/blockchain/contract.js
index 6a6268f..a16a02a 100644
--- a/src/blockchain/contract.js
+++ b/src/blockchain/contract.js
@@ -4,6 +4,8 @@ import {useStore} from "@/store/store.js";
export function vaultAddress( owner, num=0) {
+ if( !owner )
+ return null
const s = useStore()
const salt = ethers.solidityPackedKeccak256(['address','uint8'],[owner,num])
return ethers.getCreate2Address(s.factory, salt, s.vaultInitCodeHash)
@@ -31,7 +33,7 @@ export async function queryHelperContract() {
export async function poolContract(addr) {
const s = useStore()
- return contractOrNull(addr, poolAbi, s.provider)
+ return contractOrNull(addr, uniswapV3PoolAbi, s.provider)
}
export async function vaultContract(num, signer) {
diff --git a/src/blockchain/prices.js b/src/blockchain/prices.js
new file mode 100644
index 0000000..df509f9
--- /dev/null
+++ b/src/blockchain/prices.js
@@ -0,0 +1,84 @@
+import {socket} from "@/socket.js";
+import {useStore} from "@/store/store.js";
+import {Exchange} from "@/blockchain/orderlib.js";
+import {uniswapV3PoolAddress} from "@/blockchain/uniswap.js";
+import {ethers, FixedNumber} from "ethers";
+import {erc20Abi, uniswapV3PoolAbi} from "@/blockchain/abi.js";
+
+const subs = {} // key is route and value is a subscription counter
+
+
+export function subPrices( routes ) {
+ const subRoutes = []
+ let chainId = null
+ for( const route of routes ) {
+ if( !(route in subRoutes) || subRoutes[route] === 0 ) {
+ subRoutes[route] = 1
+ console.log('subscribing to pool', route.pool)
+ subRoutes.push(route)
+ }
+ else {
+ subRoutes[route]++
+ if( chainId !== null && route.chainId !== chainId )
+ throw Error('cannot mix chainIds in a subscription list')
+ chainId = route.chainId
+ }
+ }
+ if( subRoutes.length ) {
+ socket.emit('subPools', chainId, routes.map((r)=>r.address) )
+ // perform a local query if necessary
+ for( const route of subRoutes ) {
+ const s = useStore()
+ if( !(route.address in s.poolPrices) ) {
+ getPriceForRoute(route).then((price)=>s.poolPrices[route.address]=price)
+ }
+ }
+ }
+}
+
+export function unsubPrices( routes ) {
+ let chainId = null
+ const unsubAddrs = []
+ for( const route of routes ) {
+ if( !(route in subs) ) {
+ console.error('unsubscribed to a nonexistent route', route)
+ }
+ else {
+ subs[route]--
+ if( subs[route] === 0 ) {
+ unsubAddrs.push(route.pool)
+ if( chainId !== null && route.chainId !== chainId )
+ throw Error('cannot mix chainIds in a subscription list')
+ console.log('unsubscribing from pool', route.pool)
+ chainId = route.chainId
+ }
+ else if( subs[route] < 0 ) {
+ console.error('unsubscribed to an already unsubbed route', route)
+ subs[route] = 0 // fix
+ }
+ }
+ }
+ if( unsubAddrs.length )
+ socket.emit('unsubPool', chainId, unsubAddrs )
+}
+
+
+async function getPriceForRoute(route) {
+ console.log('route is',route)
+ if( route.exchange === Exchange.UniswapV3 ) {
+ const addr = uniswapV3PoolAddress(route.chainId, route.token0.address, route.token1.address, route.fee)
+ const provider = useStore().provider;
+ if( provider === null ) {
+ console.error('provider was null during getPriceForRoute')
+ return null
+ }
+ const pool = new ethers.Contract(addr, uniswapV3PoolAbi, provider)
+ const got = await pool.slot0()
+ const [sqrtPrice,,,,,,] = got
+ const spn = Number(sqrtPrice)
+ const price = spn*spn/2**(96*2) * 10**(route.token0.decimals-route.token1.decimals)
+ console.log(`price for ${route.token0.symbol}/${route.token1.symbol}`,price)
+ }
+ else
+ throw Error(`Unsupported exchange ${route.exchange}`)
+}
diff --git a/src/blockchain/route.js b/src/blockchain/route.js
new file mode 100644
index 0000000..0eb6cd3
--- /dev/null
+++ b/src/blockchain/route.js
@@ -0,0 +1,29 @@
+import {queryHelperContract} from "@/blockchain/contract.js";
+import {Exchange} from "@/blockchain/orderlib.js";
+import {useStore} from "@/store/store.js";
+
+
+export async function findRoute(tokenA, tokenB) {
+ const helper = await queryHelperContract()
+ if (!helper)
+ throw Error('no helper')
+ const chainId = useStore().chainId
+ const rawRoutes = await helper.getRoutes(tokenA.address, tokenB.address)
+ // 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
+ const [token0, token1] = tokenA.address < tokenB.address ? [tokenA, tokenB] : [tokenB, tokenA]
+ result = {chainId, exchange: Exchange.UniswapV3, pool, fee, token0, token1}
+ break
+ }
+ }
+ }
+ return [result]
+}
\ No newline at end of file
diff --git a/src/blockchain/uniswap.js b/src/blockchain/uniswap.js
new file mode 100644
index 0000000..63bfd71
--- /dev/null
+++ b/src/blockchain/uniswap.js
@@ -0,0 +1,24 @@
+import {ethers} from "ethers";
+
+const UNISWAPV3_POOL_INIT_CODE_HASH = '0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54'
+const uniswapV3Addresses = {
+ 42161: {
+ factory: '0x1F98431c8aD98523631AE4a59f267346ea31F984',
+ },
+ 31337: {
+ factory: '0x1F98431c8aD98523631AE4a59f267346ea31F984',
+ },
+}
+
+export function uniswapV3PoolAddress(chainId, tokenAddrA, tokenAddrB, fee) {
+ const [addr0, addr1] = tokenAddrA < tokenAddrB ? [tokenAddrA, tokenAddrB] : [tokenAddrB, tokenAddrA]
+ const encoded = ethers.AbiCoder.defaultAbiCoder().encode(['address', 'address', 'uint24'], [addr0, addr1, fee]);
+ const salt = ethers.keccak256(encoded)
+ const factory = uniswapV3Addresses[chainId]?.factory
+ console.log('uni3addr', addr0, addr1, fee, salt, factory)
+ if (!factory) {
+ console.log('no uniswap factory for chain', chainId)
+ return null
+ }
+ return ethers.getCreate2Address(factory, salt, UNISWAPV3_POOL_INIT_CODE_HASH)
+}
\ No newline at end of file
diff --git a/src/blockchain/wallet.js b/src/blockchain/wallet.js
index a96bb07..4c0eb9e 100644
--- a/src/blockchain/wallet.js
+++ b/src/blockchain/wallet.js
@@ -81,8 +81,9 @@ const errorHandlingProxy = {
export async function connectWallet() {
- console.log('TODO connect wallet')
// eth_getaccounts
+ const s = useStore()
+ await s.provider.getSigner()
}
const pendingOrders = []
diff --git a/src/components/NeedsSigner.vue b/src/components/NeedsSigner.vue
new file mode 100644
index 0000000..406f14b
--- /dev/null
+++ b/src/components/NeedsSigner.vue
@@ -0,0 +1,32 @@
+
+
+
+
+ Connect Wallet
+
+ Please select an account to use from your wallet.
+
+
+ Connect Wallet
+
+
+
+
+
+
+
+
diff --git a/src/components/TimedOrderEntry.vue b/src/components/TimedOrderEntry.vue
index e4e3180..4d61e3e 100644
--- a/src/components/TimedOrderEntry.vue
+++ b/src/components/TimedOrderEntry.vue
@@ -84,16 +84,17 @@