From c9ccb89d8ee2d15a83840690f2ecf78714e552c8 Mon Sep 17 00:00:00 2001 From: Tim Olson <> Date: Mon, 4 Dec 2023 17:59:57 -0400 Subject: [PATCH] TimedOrder submission works --- src/blockchain/abi.js | 53 +++++++++--- src/blockchain/common.js | 7 +- src/blockchain/orderlib.js | 144 +++++++++++++++------------------ src/components/LadderOrder.vue | 4 +- src/components/Order.vue | 2 +- src/components/TimedOrder.vue | 9 ++- src/misc.js | 2 +- src/orderbuild.js | 31 ++++--- 8 files changed, 138 insertions(+), 114 deletions(-) diff --git a/src/blockchain/abi.js b/src/blockchain/abi.js index 54a9379..28d00d1 100644 --- a/src/blockchain/abi.js +++ b/src/blockchain/abi.js @@ -47,17 +47,48 @@ export const mockErc20Abi = [...erc20Abi, 'function mint(address,uint256)', ] -const Route = '(uint8,uint24)' -const Constraint = '(uint8,bytes)' -const Tranche = `(uint64,${Constraint}[])` -const SwapOrder = `(address,address,${Route},uint256,bool,bool,uint64,${Tranche}[])` +const Route = '(uint8 exchange, uint24 fee)' +const Tranche = `( + uint16 fraction, + bool startTimeIsRelative, + bool endTimeIsRelative, + bool minIsBarrier, + bool maxIsBarrier, + bool marketOrder, + bool _reserved5, + bool _reserved6, + bool _reserved7, + uint8 _reserved8, + uint32 _reserved16, + 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 chainOrder, + ${Tranche}[] tranches +)` export const vaultAbi = [ - 'function withdraw(uint256)', - 'function withdrawTo(address payable,uint256)', - 'function withdraw(address,uint256)', - 'function withdrawTo(address,address,uint256)', - 'function placeOrder((address,address,(uint8,uint24),uint256,bool,bool,uint64,(uint16,(uint8,bytes)[])[]))', - 'function placeOrders((address,address,(uint8,uint24),uint256,bool,bool,uint64,(uint16,(uint8,bytes)[])[])[],uint8)', - 'function cancelOrder(uint64)', + '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 placeOrder(${SwapOrder})`, + `function placeOrders(${SwapOrder}[], uint8 ocoMode)`, + 'function cancelOrder(uint64 orderIndex)', + 'function cancelAll()', + // `function swapOrderStatus(uint64 orderIndex) view returns (${SwapOrderStatus} memory status)`, + 'function orderCanceled(uint64 orderIndex) view returns (bool)', ] diff --git a/src/blockchain/common.js b/src/blockchain/common.js index 114b516..f66ae7a 100644 --- a/src/blockchain/common.js +++ b/src/blockchain/common.js @@ -12,9 +12,10 @@ export function applyFills( orderStatus, filled ) { export function encodeIEE754(value) { - const buffer = new ArrayBuffer(2); - new DataView(buffer).setFloat32(0, value, false /* big endian */); - return buffer; + const buffer = new ArrayBuffer(4); + const view = new DataView(buffer); + view.setFloat32(0, value, false /* big endian */); + return view.getUint32(0, false); } diff --git a/src/blockchain/orderlib.js b/src/blockchain/orderlib.js index c0d409f..c61d991 100644 --- a/src/blockchain/orderlib.js +++ b/src/blockchain/orderlib.js @@ -1,14 +1,18 @@ import {uint32max, uint64max} from "@/misc.js"; -import {ethers} from "ethers"; +import {encodeIEE754} from "@/blockchain/common.js"; +export const MAX_FRACTION = 65535; export const NO_CHAIN = uint64max; export const NO_OCO = uint64max; +export const DISTANT_PAST = 0 +export const DISTANT_FUTURE = uint32max // struct SwapOrder { // address tokenIn; // address tokenOut; // Route route; // uint256 amount; +// 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 @@ -18,24 +22,68 @@ export const NO_OCO = uint64max; // Exchange exchange; // uint24 fee; // } -export function newOrder(tokenIn, tokenOut, exchange, fee, amount, amountIsInput, tranches, - outputToOwner=false, chainOrder=NO_CHAIN) { - if(!tranches) - tranches = [newTranche(1,[])] // todo this is just a swap: issue warning? - return [ - tokenIn, tokenOut, [exchange,fee], amount, amountIsInput, outputToOwner, chainOrder, tranches - ] +export function newOrder(tokenIn, tokenOut, exchange, fee, amount, amountIsInput, tranches, minAmount=null, + outputToOwner = false, chainOrder = NO_CHAIN) { + if (!tranches) + tranches = [newTranche({marketOrder: true})] // todo this is just a swap: issue warning? + if( minAmount === null ) + minAmount = BigInt(amount) / 100n // default to min trade size of 1% + return [tokenIn, tokenOut, [exchange, fee], amount, minAmount, amountIsInput, outputToOwner, chainOrder, tranches] } // struct Tranche { -// uint64 fraction; -// Constraint[] constraints; +// uint16 fraction; +// +// bool startTimeIsRelative; +// bool endTimeIsRelative; +// 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 _reserved7; +// uint8 _reserved8; +// uint32 _reserved16; +// +// uint32 startTime; // use DISTANT_PAST to disable +// uint32 endTime; // use DISTANT_FUTURE to disable +// +// // if intercept and slope are both 0 the line is disabled +// float minIntercept; // if marketOrder==true, this is the (positive) max slippage amount +// float minSlope; +// float maxIntercept; +// float maxSlope; // } -export function newTranche(amountRatio, constraints) { - return [ - BigInt(Math.min(65535, Math.ceil(amountRatio * 65535))), // we use ceil to make sure the sum of tranche fractions doesn't round below 1 - constraints - ] +export function newTranche({ + fraction = MAX_FRACTION, + marketOrder = false, + startTimeIsRelative = false, + startTime = DISTANT_PAST, + endTimeIsRelative = false, + endTime = DISTANT_FUTURE, + minIsBarrier = false, + minIntercept = 0, + minSlope = 0, + maxIsBarrier = false, + maxIntercept = 0, + maxSlope = 0, + } = {}) { + if( minIntercept === 0 && minSlope === 0 && maxIntercept === 0 && maxSlope === 0 ) + marketOrder = true + if( marketOrder ) + minIntercept = encodeIEE754(minIntercept) // this is the slippage field for market orders + else { + minIntercept = encodeIEE754(minIntercept) + minSlope = encodeIEE754(minSlope) + maxIntercept = encodeIEE754(maxIntercept) + maxSlope = encodeIEE754(maxSlope) + } + return { + 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, + } } // enum Exchange { @@ -47,72 +95,6 @@ export const Exchange = { UniswapV3: 1, } -// enum ConstraintMode { -// Time, // 0 -// Line, // 1 -// Barrier // 2 -// } -export const ConstraintMode = { - Time: 0, - Line: 1, - Barrier: 2, -} - -// struct Constraint { -// ConstraintMode mode; -// bytes constraint; // abi-encoded constraint struct -// } - -function encodeConstraint( constraintMode, types, values ) { - return [constraintMode, ethers.AbiCoder.defaultAbiCoder().encode(types,values)] -} - - -export const TimeMode = { - Timestamp:0, - SinceOrderStart:1, -} - -export const DISTANT_PAST = 0 -export const DISTANT_FUTURE = uint32max - -// struct Time { -// TimeMode mode; -// uint32 time; -// } -// struct TimeConstraint { -// Time earliest; -// Time latest; -// } -export function newTimeConstraint(startMode, start, endMode, end) { - // absolute time - return encodeConstraint( - ConstraintMode.Time, - ['uint8', 'uint32', 'uint8', 'uint32'], - [startMode, start, endMode, end] - ) -} - -// struct LineConstraint { -// bool isAbove; -// bool isRatio; -// uint32 time; -// uint160 valueSqrtX96; -// int160 slopeSqrtX96; // price change per second -// } - -export function newLimitConstraint( isAbove, isRatio, price ) { - return newLineConstraint(isAbove, isRatio, 0, price, 0) -} - -export function newLineConstraint( isAbove, isRatio, time, price, slope ) { - return encodeConstraint( - ConstraintMode.Line, - ['bool', 'bool', 'uint32', 'uint160', 'int160'], - [isAbove, isRatio, time, sqrtX96(price), sqrtX96(slope)] - ) -} - export function sqrtX96(value) { return BigInt(Math.round(Math.sqrt(value * 2 ** (96*2)))) } diff --git a/src/components/LadderOrder.vue b/src/components/LadderOrder.vue index 6af30c7..ed7ec42 100644 --- a/src/components/LadderOrder.vue +++ b/src/components/LadderOrder.vue @@ -29,7 +29,7 @@ import {useOrderStore} from "@/store/store"; import LimitPrice from "@/components/LimitPrice.vue"; import Order from "@/components/Order.vue"; import {computed, ref} from "vue"; -import {limitConstraint, maxFraction} from "@/orderbuild.js"; +import {applyLimit, maxFraction} from "@/orderbuild.js"; import {validateRequired, validateTranches} from "@/validate.js"; const os = useOrderStore() @@ -82,7 +82,7 @@ function buildTranches() { const mf = Number(maxFraction) for( let i=0; i setTimeout(() => input.select(), 0) } } -export const uint64max = 18446744073709551615n export const uint32max = 4294967295n +export const uint64max = 18446744073709551615n export function tokenNumber(token, balance) { return FixedNumber.fromValue(balance, token.decimals, {decimals:token.decimals, width: 256}) diff --git a/src/orderbuild.js b/src/orderbuild.js index 2030e40..42951ba 100644 --- a/src/orderbuild.js +++ b/src/orderbuild.js @@ -1,17 +1,14 @@ import {routeInverted} from "@/misc.js"; -import {newLimitConstraint, newTimeConstraint, TimeMode} from "@/blockchain/orderlib.js"; +import {MAX_FRACTION, newTranche} from "@/blockchain/orderlib.js"; import {useOrderStore} from "@/store/store.js"; -export const maxFraction = 65535n // by contract definition of uint16 fraction - - -export function limitConstraint(price=null) { +export function applyLimit(tranche, price=null) { const os = useOrderStore() if( price === null ) { price = os.limitPrice if (!price) - return null + return } const route = os.route const inverted = routeInverted(route) @@ -19,7 +16,15 @@ export function limitConstraint(price=null) { const isRatio = false // todo ratios const decimals = 10 ** (os.tokenA.decimals - os.tokenB.decimals) const limit = inverted ? decimals / price : price / decimals - return newLimitConstraint(isAbove, isRatio, limit) + tranche.marketOrder = false; + if( isAbove ) { + tranche.minIntercept = limit; + tranche.minSlope = 0; + } + else { + tranche.maxIntercept = limit; + tranche.maxSlope = 0; + } } export function timesliceTranches() { @@ -39,14 +44,18 @@ export function timesliceTranches() { } else { window = Math.round(duration / n) } - const ceil = maxFraction % BigInt(n) ? 1n : 0n - const amtPerTranche = maxFraction / BigInt(n) + ceil + const amtPerTranche = Math.ceil(MAX_FRACTION / n) duration -= 15 // subtract 15 seconds so the last tranche completes before the deadline for (let i = 0; i < n; i++) { const start = Math.floor(i * (duration / Math.max((n - 1), 1))) const end = start + window - const cs = [newTimeConstraint(TimeMode.SinceOrderStart, start, TimeMode.SinceOrderStart, end)] - ts.push([amtPerTranche, cs]) + ts.push(newTranche({ + fraction: amtPerTranche, + startTimeIsRelative: true, + startTime: start, + endTimeIsRelative: true, + endTime: end, + })) } return ts }