fees.js; DCABuilder gas warning
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
"lint": "eslint . --fix --ignore-path .gitignore"
|
"lint": "eslint . --fix --ignore-path .gitignore"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@isaacs/ttlcache": "^1.4.1",
|
||||||
"@mdi/font": "6.9.96",
|
"@mdi/font": "6.9.96",
|
||||||
"color": "^4.2.3",
|
"color": "^4.2.3",
|
||||||
"core-js": "^3.29.0",
|
"core-js": "^3.29.0",
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import {nav, uuid} from "@/misc.js";
|
import {nav, uuid} from "@/misc.js";
|
||||||
import {newContract, vaultContract} from "@/blockchain/contract.js";
|
import {vaultContract} from "@/blockchain/contract.js";
|
||||||
import {ensureVault, provider, switchChain, useWalletStore} from "@/blockchain/wallet.js";
|
import {ensureVault, provider, switchChain, useWalletStore} from "@/blockchain/wallet.js";
|
||||||
import {toRaw} from "vue";
|
import {toRaw} from "vue";
|
||||||
import {useChartOrderStore} from "@/orderbuild.js";
|
import {useChartOrderStore} from "@/orderbuild.js";
|
||||||
import {timestamp} from "@/common.js";
|
import {placementFee} from "@/fees.js";
|
||||||
|
|
||||||
export const TransactionState = {
|
export const TransactionState = {
|
||||||
Created: 0, // user requested a transaction
|
Created: 0, // user requested a transaction
|
||||||
@@ -176,29 +176,6 @@ export class PlaceOrderTransaction extends Transaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// todo move to orderlib
|
|
||||||
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)
|
|
||||||
// single order placement selector
|
|
||||||
const placementFeeSelector = 'placementFee((address,address,(uint8,uint24),uint256,uint256,bool,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))'
|
|
||||||
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]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export class CancelOrderTransaction extends Transaction {
|
export class CancelOrderTransaction extends Transaction {
|
||||||
constructor(chainId, index) {
|
constructor(chainId, index) {
|
||||||
super(chainId, TransactionType.CancelOrder)
|
super(chainId, TransactionType.CancelOrder)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<v-text-field label="Split into" type="number" variant="outlined"
|
<v-text-field label="Split into" type="number" variant="outlined"
|
||||||
aria-valuemin="1" aria-valuemax="100" min="1" max="1000" step="1"
|
aria-valuemin="1" aria-valuemax="100" min="1" max="1000" step="1"
|
||||||
|
:hint="partsGasHint" :persistent-hint="true"
|
||||||
v-model="parts" v-auto-select class="parts mr-3">
|
v-model="parts" v-auto-select class="parts mr-3">
|
||||||
<template v-slot:append-inner>
|
<template v-slot:append-inner>
|
||||||
parts
|
parts
|
||||||
@@ -45,16 +46,17 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {builderDefaults, DEFAULT_SLIPPAGE, useChartOrderStore} from "@/orderbuild.js";
|
import {builderDefaults, DEFAULT_SLIPPAGE, useChartOrderStore} from "@/orderbuild.js";
|
||||||
import {allocationText, ShapeType} from "@/charts/shape.js";
|
import {allocationText, ShapeType} from "@/charts/shape.js";
|
||||||
import {sideColor, vAutoSelect} from "@/misc.js";
|
import {sideColor, SingletonCoroutine, toPrecision, vAutoSelect} from "@/misc.js";
|
||||||
import {useStore} from "@/store/store.js";
|
import {useStore} from "@/store/store.js";
|
||||||
import {computed, ref, watchEffect} from "vue";
|
import {computed, ref, watchEffect} from "vue";
|
||||||
import {createShape, deleteShapeId, widget} from "@/charts/chart.js";
|
import {createShape, deleteShapeId, dragging, widget} from "@/charts/chart.js";
|
||||||
import AbsoluteTimeEntry from "@/components/AbsoluteTimeEntry.vue";
|
import AbsoluteTimeEntry from "@/components/AbsoluteTimeEntry.vue";
|
||||||
import BuilderPanel from "@/components/chart/BuilderPanel.vue";
|
import BuilderPanel from "@/components/chart/BuilderPanel.vue";
|
||||||
import {ohlcStart} from "@/charts/chart-misc.js";
|
import {ohlcStart} from "@/charts/chart-misc.js";
|
||||||
import Color from "color";
|
import Color from "color";
|
||||||
import OrderAmount from "@/components/chart/OrderAmount.vue";
|
import OrderAmount from "@/components/chart/OrderAmount.vue";
|
||||||
import {MAX_FRACTION, newTranche} from "@/blockchain/orderlib.js";
|
import {MAX_FRACTION, newTranche} from "@/blockchain/orderlib.js";
|
||||||
|
import {getFeeSchedule} from "@/fees.js";
|
||||||
|
|
||||||
const s = useStore()
|
const s = useStore()
|
||||||
const co = useChartOrderStore()
|
const co = useChartOrderStore()
|
||||||
@@ -72,6 +74,7 @@ const defaultTranches = 10
|
|||||||
|
|
||||||
builderDefaults(props.builder, {
|
builderDefaults(props.builder, {
|
||||||
startTime: s.clock, // todo relative 0
|
startTime: s.clock, // todo relative 0
|
||||||
|
endTime: s.clock + defaultTranches * co.intervalSecs,
|
||||||
interval: co.intervalSecs,
|
interval: co.intervalSecs,
|
||||||
tranches: defaultTranches,
|
tranches: defaultTranches,
|
||||||
percentage: 100/defaultTranches,
|
percentage: 100/defaultTranches,
|
||||||
@@ -116,6 +119,17 @@ const parts = computed({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const sched = ref(null)
|
||||||
|
const schedFetcher = new SingletonCoroutine(async (vault)=>sched.value = vault === null ? null : await getFeeSchedule(vault))
|
||||||
|
|
||||||
|
const partsGasHint = computed(()=>{
|
||||||
|
if (sched.value === null) {
|
||||||
|
schedFetcher.invoke(s.vault)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return toPrecision(Number(sched.value.gasFee) * parts.value / 1e18) + ' ETH gas fee'
|
||||||
|
})
|
||||||
|
|
||||||
const intervalIsTotal = ref(false)
|
const intervalIsTotal = ref(false)
|
||||||
const displayedInterval = computed({
|
const displayedInterval = computed({
|
||||||
get() {
|
get() {
|
||||||
@@ -165,7 +179,6 @@ const endTime = computed({
|
|||||||
|
|
||||||
const barStart = computed(()=>ohlcStart(startTime.value, props.builder.interval))
|
const barStart = computed(()=>ohlcStart(startTime.value, props.builder.interval))
|
||||||
const barEnd = computed(()=>ohlcStart(endTime.value, props.builder.interval))
|
const barEnd = computed(()=>ohlcStart(endTime.value, props.builder.interval))
|
||||||
let shapeWidth = barEnd.value - barStart.value // NOT reactively computed. We compare in onPoints()
|
|
||||||
|
|
||||||
function emitUpdatedPoints(a, b) {
|
function emitUpdatedPoints(a, b) {
|
||||||
const updates = {}
|
const updates = {}
|
||||||
@@ -234,7 +247,6 @@ function emitUpdate(changes) {
|
|||||||
|
|
||||||
const text = computed(()=>{
|
const text = computed(()=>{
|
||||||
const o = props.order
|
const o = props.order
|
||||||
console.log('check text', o.buy, o.amountIsTokenA)
|
|
||||||
return allocationText(o.buy, 1, o.amount, co.selectedSymbol.base.s,
|
return allocationText(o.buy, 1, o.amount, co.selectedSymbol.base.s,
|
||||||
o.amountIsTokenA ? null : co.selectedSymbol.quote.s, parts.value, '\n')
|
o.amountIsTokenA ? null : co.selectedSymbol.quote.s, parts.value, '\n')
|
||||||
})
|
})
|
||||||
@@ -260,6 +272,14 @@ setProperties()
|
|||||||
|
|
||||||
watchEffect(setProperties)
|
watchEffect(setProperties)
|
||||||
|
|
||||||
|
watchEffect(()=>{
|
||||||
|
const curBarStart = ohlcStart(s.clock, co.intervalSecs);
|
||||||
|
if (curBarStart > barStart.value && !dragging) { // check dragging late to ensure reactivity on bar start
|
||||||
|
const delta = curBarStart - props.builder.startTime
|
||||||
|
setPoints([{time: props.builder.startTime + delta}, {time: props.builder.endTime + delta}])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
function deleteShapes() {
|
function deleteShapes() {
|
||||||
deleteShapeId(shapeId)
|
deleteShapeId(shapeId)
|
||||||
}
|
}
|
||||||
@@ -272,7 +292,7 @@ td.weight {
|
|||||||
padding-right: 1em;
|
padding-right: 1em;
|
||||||
}
|
}
|
||||||
.parts {
|
.parts {
|
||||||
width: 8em;
|
width: 10em;
|
||||||
}
|
}
|
||||||
.interval {
|
.interval {
|
||||||
width: 22em;
|
width: 22em;
|
||||||
|
|||||||
11
src/components/chart/GasMeter.vue
Normal file
11
src/components/chart/GasMeter.vue
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
</style>
|
||||||
87
src/fees.js
Normal file
87
src/fees.js
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import {newContract, vaultContract} from "@/blockchain/contract.js";
|
||||||
|
import {provider} from "@/blockchain/wallet.js";
|
||||||
|
import {timestamp} from "@/common.js";
|
||||||
|
import TTLCache from "@isaacs/ttlcache";
|
||||||
|
|
||||||
|
|
||||||
|
async function getFeeManagerContract(vaultContract) {
|
||||||
|
const feeManagerAddr = await vaultContract.feeManager()
|
||||||
|
return await newContract(feeManagerAddr, 'IFeeManager', provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function getFeeSchedule(vaultAddr) {
|
||||||
|
if (feeSchedCache.has(vaultAddr))
|
||||||
|
return feeSchedCache.get(vaultAddr)
|
||||||
|
const vault = await vaultContract(vaultAddr, provider)
|
||||||
|
const feeManager = await getFeeManagerContract(vault);
|
||||||
|
const [sched, changeTimestamp] = await Promise.all([feeManager.fees(), feeManager.proposedFeeActivationTime()])
|
||||||
|
const changing = Number(changeTimestamp)
|
||||||
|
const newSched = !changing ? null : await feeManager.proposedFees()
|
||||||
|
// if it's not changing, we have an hour (wait 55 minutes) until another fee change could happen
|
||||||
|
// otherwise, set the TTL to be a long TTL after the changeover
|
||||||
|
const noticePeriod = 55*60
|
||||||
|
const ttl = (!changing ? noticePeriod : (changing - timestamp() + noticePeriod))*1000 // milliseconds
|
||||||
|
const schedule = new FeeSchedule(sched, newSched);
|
||||||
|
feeSchedCache.set(vaultAddr, schedule, {ttl})
|
||||||
|
return schedule
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function placementFee(vaultAddr, 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 vault = await vaultContract(vaultAddr, provider)
|
||||||
|
const feeManager = await getFeeManagerContract(vault);
|
||||||
|
const [sched, changeTimestamp] = await Promise.all([feeManager.fees(), feeManager.proposedFeeActivationTime()])
|
||||||
|
console.log('sched', order, sched)
|
||||||
|
// single order placement selector
|
||||||
|
const placementFeeSelector = 'placementFee((address,address,(uint8,uint24),uint256,uint256,bool,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))'
|
||||||
|
let [orderFee, gasFee] = await vault[placementFeeSelector](order, [...sched])
|
||||||
|
console.log('placementFee', orderFee, gasFee)
|
||||||
|
if (Number(changeTimestamp) - timestamp() < window) {
|
||||||
|
const nextSched = await feeManager.proposedFees()
|
||||||
|
const [nextOrderFee, nextGasFee] = await vault[placementFeeSelector](order, [...nextSched])
|
||||||
|
if (nextOrderFee + nextGasFee > orderFee + gasFee)
|
||||||
|
[orderFee, gasFee] = [nextOrderFee, nextGasFee]
|
||||||
|
}
|
||||||
|
return [orderFee, gasFee]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function schedToDict(sched) {
|
||||||
|
if (sched===null)
|
||||||
|
return null
|
||||||
|
const [ofee, oexp, gfee, gexp, ffee] = sched
|
||||||
|
console.log('sched', ofee, oexp, gfee, gexp, ffee)
|
||||||
|
return {
|
||||||
|
orderFee: BigInt(ofee) << BigInt(oexp), // in wei
|
||||||
|
gasFee: BigInt(gfee) << BigInt(gexp), // in wei
|
||||||
|
fillFee: Number(ffee) / 200, // float coefficient
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class FeeSchedule {
|
||||||
|
constructor(sched, nextSched=null) {
|
||||||
|
// if nextSched is set, the more expensive of the two fees will be returned
|
||||||
|
this.sched = schedToDict(sched)
|
||||||
|
this.nextSched = schedToDict(nextSched)
|
||||||
|
|
||||||
|
const _max = (method, ...args) => {
|
||||||
|
const curVal = this[method](this.sched,...args);
|
||||||
|
return this.nextSched === null ? curVal : Math.max(curVal, this[method](this.nextSched,...args))
|
||||||
|
}
|
||||||
|
|
||||||
|
this.gasFee = _max( '_gasFee')
|
||||||
|
this.orderFee = _max('_orderFee')
|
||||||
|
this.fillFee = _max('_fillFee')
|
||||||
|
}
|
||||||
|
|
||||||
|
_gasFee(sched) {return sched.gasFee}
|
||||||
|
_orderFee(sched) {return sched.orderFee}
|
||||||
|
_fillFee(sched) {return sched.fillFee}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const feeSchedCache = new TTLCache()
|
||||||
@@ -315,6 +315,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3"
|
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3"
|
||||||
integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==
|
integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==
|
||||||
|
|
||||||
|
"@isaacs/ttlcache@^1.4.1":
|
||||||
|
version "1.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz#21fb23db34e9b6220c6ba023a0118a2dd3461ea2"
|
||||||
|
integrity sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==
|
||||||
|
|
||||||
"@jridgewell/gen-mapping@^0.3.5":
|
"@jridgewell/gen-mapping@^0.3.5":
|
||||||
version "0.3.8"
|
version "0.3.8"
|
||||||
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142"
|
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142"
|
||||||
|
|||||||
Reference in New Issue
Block a user