diff --git a/package.json b/package.json
index b92ecb3..54c755a 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"lint": "eslint . --fix --ignore-path .gitignore"
},
"dependencies": {
+ "@isaacs/ttlcache": "^1.4.1",
"@mdi/font": "6.9.96",
"color": "^4.2.3",
"core-js": "^3.29.0",
diff --git a/src/blockchain/transaction.js b/src/blockchain/transaction.js
index 4fed97a..03d5810 100644
--- a/src/blockchain/transaction.js
+++ b/src/blockchain/transaction.js
@@ -1,9 +1,9 @@
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 {toRaw} from "vue";
import {useChartOrderStore} from "@/orderbuild.js";
-import {timestamp} from "@/common.js";
+import {placementFee} from "@/fees.js";
export const TransactionState = {
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 {
constructor(chainId, index) {
super(chainId, TransactionType.CancelOrder)
diff --git a/src/components/chart/DCABuilder.vue b/src/components/chart/DCABuilder.vue
index 53b369a..2fae81b 100644
--- a/src/components/chart/DCABuilder.vue
+++ b/src/components/chart/DCABuilder.vue
@@ -11,6 +11,7 @@
parts
@@ -45,16 +46,17 @@
+
+
\ No newline at end of file
diff --git a/src/fees.js b/src/fees.js
new file mode 100644
index 0000000..4d337b7
--- /dev/null
+++ b/src/fees.js
@@ -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()
diff --git a/yarn.lock b/yarn.lock
index 2a08ec9..1abb29a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -315,6 +315,11 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3"
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":
version "0.3.8"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142"