vault upgrades; fees; refactoring

This commit is contained in:
Tim
2024-06-17 02:39:12 -04:00
parent fd8f20c4d4
commit 104b798d4f
15 changed files with 260 additions and 86 deletions

View File

@@ -1,5 +1,14 @@
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 = [
@@ -55,11 +64,11 @@ const Tranche = `(
bool minIsBarrier,
bool maxIsBarrier,
bool marketOrder,
bool _reserved5,
bool _reserved6,
bool minIsRatio,
bool maxIsRatio,
bool _reserved7,
uint8 _reserved8,
uint32 _reserved16,
uint16 rateLimitFraction,
uint24 rateLimitPeriod,
uint32 startTime,
uint32 endTime,
uint32 minIntercept,
@@ -67,6 +76,7 @@ const Tranche = `(
uint32 maxIntercept,
uint32 maxSlope
)`
const SwapOrder = `(
address tokenIn,
address tokenOut,
@@ -75,19 +85,23 @@ const SwapOrder = `(
uint256 minFillAmount,
bool amountIsInput,
bool outputDirectlyToOwner,
uint64 chainOrder,
uint64 conditionalOrder,
${Tranche}[] tranches
)`
export const vaultAbi = [
'function version() view returns (uint8)',
'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 placeDexorder(${SwapOrder})`,
`function placeDexorders(${SwapOrder}[], uint8 ocoMode)`,
`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)`,

View File

@@ -2,7 +2,7 @@ import {uint32max, uint64max} from "@/misc.js";
import {decodeIEE754, encodeIEE754} from "@/common.js";
export const MAX_FRACTION = 65535;
export const NO_CHAIN = uint64max;
export const NO_CONDITIONAL_ORDER = uint64max;
export const NO_OCO = uint64max;
export const DISTANT_PAST = 0
export const DISTANT_FUTURE = uint32max
@@ -15,7 +15,7 @@ export const DISTANT_FUTURE = uint32max
// uint256 minFillAmount; // if a tranche has less than this amount available to fill, it is considered completed
// bool amountIsInput;
// bool outputDirectlyToOwner;
// uint64 chainOrder; // use NO_CHAIN for no chaining. chainOrder index must be < than this order's index for safety (written first) and chainOrder state must be Template
// uint64 conditionalOrder; // use NO_CONDITIONAL_ORDER for no chaining. conditionalOrder index must be < than this order's index for safety (written first) and conditionalOrder state must be Template
// Tranche[] tranches;
// }
// struct Route {
@@ -23,7 +23,7 @@ export const DISTANT_FUTURE = uint32max
// uint24 fee;
// }
export function newOrder(tokenIn, tokenOut, exchange, fee, amount, amountIsInput, tranches,
minFillAmount=null, outputDirectlyToOwner = false, chainOrder = NO_CHAIN) {
minFillAmount=null, outputDirectlyToOwner = false, conditionalOrder = NO_CONDITIONAL_ORDER) {
amountIsInput = !!amountIsInput // force convert to bool
outputDirectlyToOwner = !!outputDirectlyToOwner // force convert to bool
amount = BigInt(amount)
@@ -34,7 +34,7 @@ export function newOrder(tokenIn, tokenOut, exchange, fee, amount, amountIsInput
return {
tokenIn, tokenOut, route:{exchange, fee},
amount, minFillAmount, amountIsInput,
outputDirectlyToOwner, chainOrder, tranches
outputDirectlyToOwner, conditionalOrder, tranches
}
}
@@ -46,11 +46,11 @@ export function newOrder(tokenIn, tokenOut, exchange, fee, amount, amountIsInput
// bool minIsBarrier;
// bool maxIsBarrier;
// bool marketOrder; // if true, both min and max lines are ignored, and minIntercept is treated as a maximum slippage value (use positive numbers)
// bool _reserved5;
// bool _reserved6;
// bool minIsRatio;
// bool maxIsRatio;
// bool _reserved7;
// uint8 _reserved8;
// uint32 _reserved16;
// uint16 rateLimitFraction;
// uint24 rateLimitPeriod;
//
// uint32 startTime; // use DISTANT_PAST to disable
// uint32 endTime; // use DISTANT_FUTURE to disable
@@ -69,12 +69,16 @@ export function newTranche({
endTimeIsRelative = false,
endTime = DISTANT_FUTURE,
minIsBarrier = false,
minIsRatio = false,
minIntercept = 0,
slippage = 0, // may also set minIntercept instead
minSlope = 0,
maxIsBarrier = false,
maxIsRatio = false,
maxIntercept = 0,
maxSlope = 0,
rateLimitFraction = 0,
rateLimitPeriod = 0,
} = {}) {
if( minIntercept === 0 && minSlope === 0 && maxIntercept === 0 && maxSlope === 0 )
marketOrder = true
@@ -90,7 +94,7 @@ export function newTranche({
fraction: Math.min(MAX_FRACTION, Math.round(fraction)), marketOrder,
startTimeIsRelative, startTime, endTimeIsRelative, endTime,
minIsBarrier, minIntercept, minSlope, maxIsBarrier, maxIntercept, maxSlope,
_reserved5: false, _reserved6: false, _reserved7: false, _reserved8: 0, _reserved16: 0,
minIsRatio, maxIsRatio, _reserved7: false, rateLimitFraction, rateLimitPeriod,
}
}
@@ -110,10 +114,6 @@ export const OrderState = {
Error: 99,
}
export function orderIsOpen(order) {
return isOpen(order.state)
}
export function isOpen(state) {
return state >= 1 && state < 3
}
@@ -121,7 +121,7 @@ export function isOpen(state) {
export function parseOrderStatus(chainId, status) {
let [
order,
fillFeeBP,
fillFeeHalfBps,
state,
start,
ocoGroup,
@@ -129,14 +129,17 @@ export function parseOrderStatus(chainId, status) {
filledOut,
trancheFilledIn,
trancheFilledOut,
trancheActivationTime,
] = status
order = parseOrder(order)
filledIn = BigInt(filledIn)
filledOut = BigInt(filledOut)
trancheFilledIn = trancheFilledIn.map((f)=>BigInt(f))
trancheFilledOut = trancheFilledOut.map((f)=>BigInt(f))
trancheActivationTime = trancheActivationTime.map((v)=>Number(v))
return {
chainId, order, fillFeeBP, state, start, ocoGroup, filledIn, filledOut, trancheFilledIn, trancheFilledOut,
chainId, order, fillFeeHalfBps, state, start, ocoGroup,
filledIn, filledOut, trancheFilledIn, trancheFilledOut, trancheActivationTime
}
}
@@ -150,7 +153,7 @@ export function parseOrder(order) {
minFillAmount,
amountIsInput,
outputDirectlyToOwner,
chainOrder,
conditionalOrder,
tranches,
] = order
route = parseRoute(route)
@@ -158,7 +161,7 @@ export function parseOrder(order) {
minFillAmount = BigInt(minFillAmount)
tranches = tranches.map(parseTranche)
return {
tokenIn, tokenOut, route, amount, minFillAmount, amountIsInput, outputDirectlyToOwner, chainOrder, tranches
tokenIn, tokenOut, route, amount, minFillAmount, amountIsInput, outputDirectlyToOwner, conditionalOrder, tranches
}
}
@@ -175,11 +178,11 @@ export function parseTranche(tranche) {
minIsBarrier,
maxIsBarrier,
marketOrder,
_reserved5,
_reserved6,
minIsRatio,
maxIsRatio,
_reserved7,
_reserved8,
_reserved16,
rateLimitFraction,
rateLimitPeriod,
startTime,
endTime,
minIntercept,
@@ -193,6 +196,7 @@ export function parseTranche(tranche) {
maxSlope = decodeIEE754(maxSlope)
return {
fraction, startTimeIsRelative, endTimeIsRelative, minIsBarrier, maxIsBarrier, marketOrder,
minIsRatio, maxIsRatio, rateLimitFraction, rateLimitPeriod,
startTime, endTime, minIntercept, minSlope, maxIntercept, maxSlope,
}
}

View File

@@ -3,10 +3,10 @@ import {useStore} from "@/store/store";
import {socket} from "@/socket.js";
import {contractOrNull, vaultAddress} from "@/blockchain/contract.js";
import {SingletonCoroutine, uuid} from "@/misc.js";
import {vaultAbi} from "@/blockchain/abi.js";
import {factoryAbi, vaultAbi} from "@/blockchain/abi.js";
import {defineStore} from "pinia";
import {ref} from "vue";
import {metadataMap} from "@/version.js";
import {metadataMap, version} from "@/version.js";
export let provider = null
@@ -198,6 +198,7 @@ function discoverVaults(owner) {
const doDiscoverVaults = new SingletonCoroutine(_discoverVaults, 50, false)
async function _discoverVaults(owner) {
const result = []
const versions = []
const s = useStore()
if( !owner || !s.chainId || !s.account) {
s.vaults = []
@@ -205,38 +206,34 @@ async function _discoverVaults(owner) {
}
// todo multi-vault scan
// console.log('_discoverVaults',owner)
const num = 0
const addr = vaultAddress(s.factory, s.vaultInitCodeHash, owner, num)
// console.log(`vault ${num} at`, addr)
if( addr === null ) {
s.vaults = []
return
}
console.log('provider', provider)
if (!provider) {
console.log('No provider')
return // do not change whatever was already found
}
const vault = new ethers.Contract(addr, vaultAbi, provider)
let version = -1
try {
version = await vault.version();
if( Number(version) === 1 ) {
console.log(`found vault ${num} at ${addr}`)
result.push(addr)
for (let num=0; num < 1; num++) {
const num = 0
const addr = vaultAddress(s.factory, s.vaultInitCodeHash, owner, num)
// console.log(`vault ${num} at`, addr)
if (addr === null) // no more vaults
break
console.log('provider', provider)
if (!provider) {
console.log('No provider')
return // do not change whatever was already found
}
const vault = new ethers.Contract(addr, vaultAbi, provider)
try {
const version = Number(await vault.version())
console.log(`found vault #${num} v${version} at ${addr}`)
result.push(addr)
versions.push(version)
} catch (e) {
if (e.value === '0x' && e.code === 'BAD_DATA' || e.revert === null && e.code === 'CALL_EXCEPTION')
console.log(`no vault ${num} at ${addr}`)
else
console.error(`discoverVaults failed`, e)
return // do not change what was already found todo is this correct?
}
else
console.error(`bad vault version ${version}`)
}
catch (e) {
if( e.value==='0x' && e.code==='BAD_DATA' || e.revert===null && e.code==='CALL_EXCEPTION' )
console.log(`no vault ${num} at ${addr}`)
else
console.error(`discoverVaults failed`, e)
return // do not change what was already found todo is this correct?
}
if( s.account === owner ) { // double-check the account since it could have changed during our await
s.vaults = result
s.vaultVersions = versions
if( useWalletStore().pendingOrders.length ) {
if (result.length)
flushOrders(result[0])
@@ -287,7 +284,6 @@ async function doEnsureVault(chainId, owner, num) {
socket.emit('ensureVault', chainId, owner, num)
}
}
// await sleep(5000) // prevent this process from running more than once every 5 seconds
const ensureVaultRoutine = new SingletonCoroutine(doEnsureVault, 100)
@@ -300,18 +296,27 @@ export const PendingOrderState = {
}
export async function pendOrder(order) {
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)
return [orderFee, gasFee]
}
export async function pendOrder(order, fee=null) {
const s = useStore()
const pend = {
id: uuid(),
chainId: s.chainId,
placementTime: Date.now()/1000,
fee: fee,
vault: s.vaults.length ? s.vaults[0] : null,
state: PendingOrderState.Submitted,
order
};
useWalletStore().pendingOrders.splice(0,0, pend)
console.log('pending order', pend.id, JSON.stringify(order))
console.log('pended order', pend.id, JSON.stringify(order))
ensureVault()
}
@@ -378,8 +383,12 @@ function pendOrderAsTransaction(pend) {
return null
}
}
if (pend.fee === null) {
const [orderFee, gasFee] = await placementFee(pend.vault, pend.order)
pend.fee = orderFee + gasFee
}
console.log('placing order', pend.id, pend.order)
const tx = await contract.placeDexorder(pend.order)
const tx = await contract.placeDexorder(pend.order, {value:pend.fee})
pend.tx = tx
pend.state = PendingOrderState.Sent
console.log(`order ${pend.id} sent transaction`, tx)
@@ -400,7 +409,7 @@ function pendOrderAsTransaction(pend) {
})
}
0
export function pendTransaction(sender, errHandler) {
const s = useStore()
s.transactionSenders.push([sender,errHandler])
@@ -462,3 +471,60 @@ function doSendTransaction(sender, signer, errHandler) {
}
})
}
export async function detectUpgrade() {
if (!provider) {
console.log('no provider!')
return 0
}
const s = useStore()
if (!s.vault) {
console.log('no vault logged in')
return 0
}
const info = version.chainInfo[s.chainId]
if (!info) {
console.log(`couldn't get chainInfo for ${s.chainId}`)
return 0
}
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 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 version = await logic.version()
console.log(`found vault version ${version}`)
return version
}
}
catch (e) {
console.error('ignorable error while querying for an upgrade', e)
}
return 0
}
function upgradeSender(vault, logic) {
return async function (signer) {
const v = new ethers.Contract(vault, vaultAbi, signer)
v.upgrade(logic)
}
}
function upgradeError(e) {
console.error('error while upgrading vault', e)
}
export async function upgradeVault(vault, logic) {
pendTransaction(upgradeSender(vault, logic), upgradeError)
}