From 104b798d4faecca54879326d648c002221549022 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 17 Jun 2024 02:39:12 -0400 Subject: [PATCH] vault upgrades; fees; refactoring --- index.html | 3 - src/App.vue | 4 +- src/blockchain/abi.js | 30 +++-- src/blockchain/orderlib.js | 46 ++++---- src/blockchain/wallet.js | 136 +++++++++++++++++------ src/charts/chart.js | 2 +- src/components/HubSpot.vue | 17 +++ src/components/Order.vue | 2 +- src/components/Status.vue | 12 +- src/components/UpgradeAlert.vue | 72 ++++++++++++ src/components/chart/ChartOrder.vue | 2 +- src/components/chart/DiagonalBuilder.vue | 2 - src/components/chart/ToolbarPane.vue | 2 + src/layouts/chart/MainView.vue | 2 - src/store/store.js | 14 ++- 15 files changed, 260 insertions(+), 86 deletions(-) create mode 100644 src/components/HubSpot.vue create mode 100644 src/components/UpgradeAlert.vue diff --git a/index.html b/index.html index 6781f6e..4f72233 100644 --- a/index.html +++ b/index.html @@ -16,9 +16,6 @@
- - - diff --git a/src/App.vue b/src/App.vue index 6570c92..1896b07 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,6 +1,8 @@ diff --git a/src/blockchain/abi.js b/src/blockchain/abi.js index 7415299..b0bf605 100644 --- a/src/blockchain/abi.js +++ b/src/blockchain/abi.js @@ -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)`, diff --git a/src/blockchain/orderlib.js b/src/blockchain/orderlib.js index 11fd39f..207cd51 100644 --- a/src/blockchain/orderlib.js +++ b/src/blockchain/orderlib.js @@ -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, } } diff --git a/src/blockchain/wallet.js b/src/blockchain/wallet.js index 199d24e..6a75965 100644 --- a/src/blockchain/wallet.js +++ b/src/blockchain/wallet.js @@ -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) +} + diff --git a/src/charts/chart.js b/src/charts/chart.js index d43675a..77f278e 100644 --- a/src/charts/chart.js +++ b/src/charts/chart.js @@ -359,7 +359,7 @@ function doHandleDrawingEvent(id, event) { if (id in shapeCallbacks) { invokeCallbacks(shapeCallbacks[id], 'onHide', id, shape) } - } else if (event === 'show') { + } else if (event === 'showDialog') { if (id in shapeCallbacks) { invokeCallbacks(shapeCallbacks[id], 'onShow', id, shape) } diff --git a/src/components/HubSpot.vue b/src/components/HubSpot.vue new file mode 100644 index 0000000..4d1b962 --- /dev/null +++ b/src/components/HubSpot.vue @@ -0,0 +1,17 @@ + + + diff --git a/src/components/Order.vue b/src/components/Order.vue index 01e3141..52c86d9 100644 --- a/src/components/Order.vue +++ b/src/components/Order.vue @@ -43,7 +43,7 @@ function placeOrder() { const route = os.route const amt = FixedNumber.fromString(os.totalAmount.toString(), {decimals: os.amountToken.decimals}).value const ts = props.tranches() - const order = newOrder(tokenIn, tokenOut, route.exchange, route.fee, amt, os.amountIsInput, ts) // todo: minAmount, outputToOwner, chainOrder + const order = newOrder(tokenIn, tokenOut, route.exchange, route.fee, amt, os.amountIsInput, ts) // todo: minAmount, outputToOwner, conditionalOrder pendOrder(order) route('Status') } diff --git a/src/components/Status.vue b/src/components/Status.vue index ff30ecd..fd3eda2 100644 --- a/src/components/Status.vue +++ b/src/components/Status.vue @@ -115,6 +115,7 @@ +
{{ describeTrancheTime(item, true, t) }}
{{ describeTrancheTime(item, false, t) }}
@@ -160,7 +161,6 @@ const s = useStore() const ws = useWalletStore() const props = defineProps(['vault']) const vaultAddr = computed(()=>props.vault?props.vault:s.vault) -const inverted = ref({}) const datatableHeaders = [ @@ -191,11 +191,11 @@ const orders = computed(()=>{ // false, min is barrier // false, max is barrier // false, market order - // false, reserved - // false, reserved - // false, reserved - // 0, reserved - // 0, reserved + // 0, minIsRatio + // 0, maxIsRatio + // false, _reserved7 + // 0, rateLimitFraction + // 0, rateLimitPeriod // 0, start time // 20, end time // 730643660, min intercept diff --git a/src/components/UpgradeAlert.vue b/src/components/UpgradeAlert.vue new file mode 100644 index 0000000..ae1fecb --- /dev/null +++ b/src/components/UpgradeAlert.vue @@ -0,0 +1,72 @@ + + + + + diff --git a/src/components/chart/ChartOrder.vue b/src/components/chart/ChartOrder.vue index a20a4ad..4acf867 100644 --- a/src/components/chart/ChartOrder.vue +++ b/src/components/chart/ChartOrder.vue @@ -108,7 +108,7 @@ function buildOrder() { // 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; // } const symbol = co.selectedSymbol diff --git a/src/components/chart/DiagonalBuilder.vue b/src/components/chart/DiagonalBuilder.vue index 8d8c000..fc7ebee 100644 --- a/src/components/chart/DiagonalBuilder.vue +++ b/src/components/chart/DiagonalBuilder.vue @@ -303,9 +303,7 @@ const _extendLeft = ref(false) const extendLeft = computed({ get() {return _extendLeft.value}, set(v) { - console.log('set extendLeft', v) if (v !== _extendLeft.value) { - console.log('DO set extendLeft') _extendLeft.value = v; const b = {...props.builder} b.extendLeft = v diff --git a/src/components/chart/ToolbarPane.vue b/src/components/chart/ToolbarPane.vue index bc65abf..86b9a14 100644 --- a/src/components/chart/ToolbarPane.vue +++ b/src/components/chart/ToolbarPane.vue @@ -3,6 +3,7 @@ +
@@ -12,6 +13,7 @@