ABI's from //contract/out URLs; arbsep; placement fee considers upcoming fee changes; vault detection bugfixes; order placement bugfixes; BETA

This commit is contained in:
Tim
2024-07-03 16:18:29 -04:00
parent 104b798d4f
commit d38baccd49
18 changed files with 200 additions and 170 deletions

View File

@@ -1,109 +0,0 @@
export const factoryAbi = [
'function deployVault() returns (address payable vault)',
'function deployVault(uint8 num) returns (address payable vault)',
'function deployVault(address owner) returns (address payable vault)',
'function deployVault(address owner, uint8 num) returns (address payable vault)',
'function logic() view returns (address)',
'function upgrader() view returns (address)',
'function proposedLogicActivationTimestamp() view returns (uint32)',
'function proposedLogic() view returns (address)',
'function upgradeLogic(address newLogic)',
]
export const queryHelperAbi = [
'function getRoutes(address tokenA,address tokenB) view returns((uint8,uint24,address)[])',
]
export const uniswapV3PoolAbi = [
// {
// // 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)',
'function decimals() view returns (uint8)',
'function totalSupply() view returns (uint256)',
'function balanceOf(address) view returns (uint256)',
'function transfer(address,uint256) returns (bool)',
'function transferFrom(address,address,uint256) returns (bool)',
'function approve(address,uint256) returns (bool success)',
'function allowance(address,address) view returns (uint256)',
'event Transfer(address indexed,address indexed,uint256)',
'event Approval(address indexed,address indexed,uint256)',
]
export const mockErc20Abi = [...erc20Abi,
'function mint(address,uint256)',
]
const Route = '(uint8 exchange, uint24 fee)'
const Tranche = `(
uint16 fraction,
bool startTimeIsRelative,
bool endTimeIsRelative,
bool minIsBarrier,
bool maxIsBarrier,
bool marketOrder,
bool minIsRatio,
bool maxIsRatio,
bool _reserved7,
uint16 rateLimitFraction,
uint24 rateLimitPeriod,
uint32 startTime,
uint32 endTime,
uint32 minIntercept,
uint32 minSlope,
uint32 maxIntercept,
uint32 maxSlope
)`
const SwapOrder = `(
address tokenIn,
address tokenOut,
${Route} route,
uint256 amount,
uint256 minFillAmount,
bool amountIsInput,
bool outputDirectlyToOwner,
uint64 conditionalOrder,
${Tranche}[] tranches
)`
export const vaultAbi = [
'function version() pure returns (uint8)',
'function logic() view returns (address)',
'function upgrade(address)',
'function feeManager() view returns (address)',
'function withdraw(uint256 amount)',
'function withdrawTo(address payable recipient, uint256 amount)',
'function withdraw(address token, uint256 amount)',
'function withdrawTo(address token, address recipient, uint256 amount)',
'function numSwapOrders() view returns (uint64 num)',
`function placementFee(${SwapOrder} order) view returns (uint256 orderFee, uint256 gasFee)`,
`function placeDexorder(${SwapOrder}) payable`,
`function placeDexorders(${SwapOrder}[], uint8 ocoMode) payable`,
'function cancelDexorder(uint64 orderIndex)',
'function cancelAllDexorders()',
// `function swapOrderStatus(uint64 orderIndex) view returns (${SwapOrderStatus} memory status)`,
'function orderCanceled(uint64 orderIndex) view returns (bool)',
]

View File

@@ -1,5 +1,7 @@
import {ethers} from "ethers";
import {queryHelperAbi} from "@/blockchain/abi.js";
import {AbiURLCache} from "../common.js";
export const abiCache = new AbiURLCache('/contract/out/')
export function vaultAddress( factory, vaultInitCodeHash, owner, num=0) {
@@ -20,6 +22,23 @@ export function contractOrNull(addr,abi,provider) {
}
export async function queryHelperContract(helper, provider) {
return contractOrNull(helper, queryHelperAbi, provider)
return newContract(helper, 'QueryHelper', provider)
}
// do not supply extensions with name or file: e.g.
// use newContract(addr, 'IVaultLogic', provider, 'IVault') to get the ABI from IVault.sol/IVaultLogic.json
export async function newContract(addr, name, provider) {
const abi = await abiCache.get(name)
return new ethers.Contract(addr, abi, provider)
}
export async function erc20Contract(addr, provider) {
return newContract(addr, 'IERC20Metadata', provider)
}
export async function vaultContract(addr, provider) {
return await newContract(addr, 'IVault', provider)
}

View File

@@ -70,8 +70,8 @@ export function newTranche({
endTime = DISTANT_FUTURE,
minIsBarrier = false,
minIsRatio = false,
minIntercept = 0,
slippage = 0, // may also set minIntercept instead
minIntercept = 0,
minSlope = 0,
maxIsBarrier = false,
maxIsRatio = false,
@@ -200,3 +200,14 @@ export function parseTranche(tranche) {
startTime, endTime, minIntercept, minSlope, maxIntercept, maxSlope,
}
}
export function parseFeeSchedule(sched) {
const [orderFee, orderExp, gasFee, gasExp, fillFeeHalfBps] = sched
return {
orderFee: orderFee << orderExp, // orderFee is in native (ETH) currency
gasFee: gasFee << gasExp, // gasFee is in native (ETH) currency
fillFee: fillFeeHalfBps/1_000_000 // fillFee is a multiplier on the filled volume. 0.0001 = 0.1% of the output token taken as a fee
}
}

View File

@@ -2,10 +2,9 @@ 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 {uniswapV3PoolAbi} from "@/blockchain/abi.js";
import {subOHLCs} from "@/blockchain/ohlcs.js";
import {FixedNumber} from "ethers";
import {provider} from "@/blockchain/wallet.js";
import {newContract} from "@/blockchain/contract.js";
const subscriptionCounts = {} // key is route and value is a subscription counter
export const WIDE_PRICE_FORMAT = {decimals:38, width:512, signed:false}; // 38 decimals is 127 bits
@@ -84,7 +83,7 @@ async function getPriceForRoute(route) {
console.error('provider was null during getPriceForRoute')
return null
}
const pool = new ethers.Contract(addr, uniswapV3PoolAbi, provider)
const pool = newContract(addr, 'IUniswapV3Pool', provider)
const got = await pool.slot0()
const [sqrtPrice,,,,,,] = got
const spn = BigInt(sqrtPrice)

View File

@@ -1,9 +1,8 @@
import {socket} from "@/socket.js";
import {useStore} from "@/store/store.js";
import {erc20Abi} from "@/blockchain/abi.js";
import {ethers} from "ethers";
import {metadataMap} from "@/version.js";
import {provider} from "@/blockchain/wallet.js";
import {newContract} from "@/blockchain/contract.js";
// synchronous version may return null but will trigger a lookup
@@ -64,7 +63,7 @@ export async function addExtraToken(chainId, addr) {
resolve(null)
}
else {
const token = new ethers.Contract(addr, erc20Abi, provider)
const token = newContract(addr, 'IERC20Metadata', provider)
Promise.all( [token.name(), token.symbol(), token.decimals()] ).then((name,symbol,decimals)=>{
info = {
a: addr,

View File

@@ -2,13 +2,16 @@ import {ethers} from "ethers";
const UNISWAPV3_POOL_INIT_CODE_HASH = '0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54'
const uniswapV3Addresses = {
1337: {
31337: { // Mockchain
factory: '0x1F98431c8aD98523631AE4a59f267346ea31F984',
},
42161: {
1337: { // Dexorder Alpha
factory: '0x1F98431c8aD98523631AE4a59f267346ea31F984',
},
31337: {
421614: { // Arbitrum Sepolia
factory: '0x1F98431c8aD98523631AE4a59f267346ea31F984',
},
42161: { // Arbitrum
factory: '0x1F98431c8aD98523631AE4a59f267346ea31F984',
},
}

View File

@@ -1,9 +1,8 @@
import {BrowserProvider, ethers} from "ethers";
import {useStore} from "@/store/store";
import {socket} from "@/socket.js";
import {contractOrNull, vaultAddress} from "@/blockchain/contract.js";
import {SingletonCoroutine, uuid} from "@/misc.js";
import {factoryAbi, vaultAbi} from "@/blockchain/abi.js";
import {newContract, vaultAddress, vaultContract} from "@/blockchain/contract.js";
import {SingletonCoroutine, timestamp, uuid} from "@/misc.js";
import {defineStore} from "pinia";
import {ref} from "vue";
import {metadataMap, version} from "@/version.js";
@@ -217,7 +216,7 @@ async function _discoverVaults(owner) {
console.log('No provider')
return // do not change whatever was already found
}
const vault = new ethers.Contract(addr, vaultAbi, provider)
const vault = await vaultContract(addr, provider)
try {
const version = Number(await vault.version())
console.log(`found vault #${num} v${version} at ${addr}`)
@@ -295,11 +294,24 @@ export const PendingOrderState = {
Sent: -102, // tx is awaiting blockchain mining
}
const placementFeeSelector = 'placementFee((address,address,(uint8,uint24),uint256,uint256,bool,bool,uint64,(uint16,bool,bool,bool,bool,bool,bool,bool,bool,uint16,uint24,uint32,uint32,uint32,uint32,uint32,uint32)[]),(uint8,uint8,uint8,uint8,uint8))'
export async function placementFee(vault, order) {
const v = new ethers.Contract(vault, vaultAbi, provider)
const [orderFee, gasFee] = await v.placementFee(order)
console.log('computed fees', orderFee, gasFee)
export async function placementFee(vault, order, window=300) {
// If the fees are about to change within `window` seconds of now, we send the higher native amount of the two fees.
// If the fees sent are too much, the vault will refund the sender.
const v = await vaultContract(vault, provider)
const feeManagerAddr = await v.feeManager()
const feeManager = await newContract(feeManagerAddr, 'IFeeManager', provider)
const [sched, changeTimestamp] = await Promise.all([feeManager.fees(), feeManager.proposedFeeActivationTime()])
console.log('sched', order, sched)
let [orderFee, gasFee] = await v[placementFeeSelector](order, [...sched])
console.log('placementFee', orderFee, gasFee)
if (Number(changeTimestamp) - timestamp() < window) {
const nextSched = await feeManager.proposedFees()
const [nextOrderFee, nextGasFee] = await v[placementFeeSelector](order, [...nextSched])
if (nextOrderFee + nextGasFee > orderFee + gasFee)
[orderFee, gasFee] = [nextOrderFee, nextGasFee]
}
return [orderFee, gasFee]
}
@@ -324,7 +336,7 @@ export async function pendOrder(order, fee=null) {
export async function cancelOrder(vault, orderIndex) {
console.log('cancel order', vault, orderIndex)
pendTransaction(async (signer)=> {
const contract = contractOrNull(vault, vaultAbi, signer)
const contract = await vaultContract(vault, signer)
if( contract === null ) {
console.error('vault contract was null while canceling order', vault, orderIndex)
return null
@@ -335,7 +347,7 @@ export async function cancelOrder(vault, orderIndex) {
export async function cancelAll(vault) {
pendTransaction(async (signer)=> {
const contract = contractOrNull(vault, vaultAbi, signer)
const contract = await vaultContract(vault, signer)
if( contract === null ) {
console.error('vault contract was null while canceling order', vault)
return null
@@ -364,8 +376,12 @@ export function flushOrders(vault) {
function pendOrderAsTransaction(pend) {
pendTransaction(async (signer)=> {
const contract = contractOrNull(pend.vault, vaultAbi, signer)
if( contract === null ) {
// console.log('pendTransaction')
let contract
try {
contract = await vaultContract(pend.vault, signer)
}
catch (e) {
console.error('vault contract was null while sending order transaction', pend.vault)
return null
}
@@ -491,15 +507,17 @@ export async function detectUpgrade() {
}
try {
console.log('factory', info.factory)
const factory = new ethers.Contract(info.factory, factoryAbi, provider)
const vault = new ethers.Contract(s.vault, vaultAbi, provider)
const [factory, vault] = await Promise.all([
newContract(info.factory, 'IVaultFactory', provider),
newContract(s.vault, 'IVault', provider),
])
const vaultLogic = await vault.logic()
const latestLogic = await factory.logic()
// const [vaultLogic, latestLogic] = await Promise.all( vault.logic(), factory.logic() )
console.log('vaultLogic / latestLogic', vaultLogic, latestLogic)
if ( vaultLogic !== latestLogic ) {
s.upgrade = latestLogic
const logic = new ethers.Contract(latestLogic, vaultAbi, provider)
const logic = await newContract(latestLogic, 'IVault', provider)
const version = await logic.version()
console.log(`found vault version ${version}`)
return version
@@ -514,7 +532,7 @@ export async function detectUpgrade() {
function upgradeSender(vault, logic) {
return async function (signer) {
const v = new ethers.Contract(vault, vaultAbi, signer)
const v = await vaultContract(vault, signer)
v.upgrade(logic)
}
}