TimedOrder submission works
This commit is contained in:
@@ -47,17 +47,48 @@ export const mockErc20Abi = [...erc20Abi,
|
|||||||
'function mint(address,uint256)',
|
'function mint(address,uint256)',
|
||||||
]
|
]
|
||||||
|
|
||||||
const Route = '(uint8,uint24)'
|
const Route = '(uint8 exchange, uint24 fee)'
|
||||||
const Constraint = '(uint8,bytes)'
|
const Tranche = `(
|
||||||
const Tranche = `(uint64,${Constraint}[])`
|
uint16 fraction,
|
||||||
const SwapOrder = `(address,address,${Route},uint256,bool,bool,uint64,${Tranche}[])`
|
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 = [
|
export const vaultAbi = [
|
||||||
'function withdraw(uint256)',
|
'function withdraw(uint256 amount)',
|
||||||
'function withdrawTo(address payable,uint256)',
|
'function withdrawTo(address payable recipient, uint256 amount)',
|
||||||
'function withdraw(address,uint256)',
|
'function withdraw(address token, uint256 amount)',
|
||||||
'function withdrawTo(address,address,uint256)',
|
'function withdrawTo(address token, address recipient, uint256 amount)',
|
||||||
'function placeOrder((address,address,(uint8,uint24),uint256,bool,bool,uint64,(uint16,(uint8,bytes)[])[]))',
|
'function numSwapOrders() view returns (uint64 num)',
|
||||||
'function placeOrders((address,address,(uint8,uint24),uint256,bool,bool,uint64,(uint16,(uint8,bytes)[])[])[],uint8)',
|
`function placeOrder(${SwapOrder})`,
|
||||||
'function cancelOrder(uint64)',
|
`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)',
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ export function applyFills( orderStatus, filled ) {
|
|||||||
|
|
||||||
|
|
||||||
export function encodeIEE754(value) {
|
export function encodeIEE754(value) {
|
||||||
const buffer = new ArrayBuffer(2);
|
const buffer = new ArrayBuffer(4);
|
||||||
new DataView(buffer).setFloat32(0, value, false /* big endian */);
|
const view = new DataView(buffer);
|
||||||
return buffer;
|
view.setFloat32(0, value, false /* big endian */);
|
||||||
|
return view.getUint32(0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
import {uint32max, uint64max} from "@/misc.js";
|
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_CHAIN = uint64max;
|
||||||
export const NO_OCO = uint64max;
|
export const NO_OCO = uint64max;
|
||||||
|
export const DISTANT_PAST = 0
|
||||||
|
export const DISTANT_FUTURE = uint32max
|
||||||
|
|
||||||
// struct SwapOrder {
|
// struct SwapOrder {
|
||||||
// address tokenIn;
|
// address tokenIn;
|
||||||
// address tokenOut;
|
// address tokenOut;
|
||||||
// Route route;
|
// Route route;
|
||||||
// uint256 amount;
|
// uint256 amount;
|
||||||
|
// uint256 minFillAmount; // if a tranche has less than this amount available to fill, it is considered completed
|
||||||
// bool amountIsInput;
|
// bool amountIsInput;
|
||||||
// bool outputDirectlyToOwner;
|
// 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 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;
|
// Exchange exchange;
|
||||||
// uint24 fee;
|
// uint24 fee;
|
||||||
// }
|
// }
|
||||||
export function newOrder(tokenIn, tokenOut, exchange, fee, amount, amountIsInput, tranches,
|
export function newOrder(tokenIn, tokenOut, exchange, fee, amount, amountIsInput, tranches, minAmount=null,
|
||||||
outputToOwner = false, chainOrder = NO_CHAIN) {
|
outputToOwner = false, chainOrder = NO_CHAIN) {
|
||||||
if (!tranches)
|
if (!tranches)
|
||||||
tranches = [newTranche(1,[])] // todo this is just a swap: issue warning?
|
tranches = [newTranche({marketOrder: true})] // todo this is just a swap: issue warning?
|
||||||
return [
|
if( minAmount === null )
|
||||||
tokenIn, tokenOut, [exchange,fee], amount, amountIsInput, outputToOwner, chainOrder, tranches
|
minAmount = BigInt(amount) / 100n // default to min trade size of 1%
|
||||||
]
|
return [tokenIn, tokenOut, [exchange, fee], amount, minAmount, amountIsInput, outputToOwner, chainOrder, tranches]
|
||||||
}
|
}
|
||||||
|
|
||||||
// struct Tranche {
|
// struct Tranche {
|
||||||
// uint64 fraction;
|
// uint16 fraction;
|
||||||
// Constraint[] constraints;
|
//
|
||||||
|
// 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) {
|
export function newTranche({
|
||||||
return [
|
fraction = MAX_FRACTION,
|
||||||
BigInt(Math.min(65535, Math.ceil(amountRatio * 65535))), // we use ceil to make sure the sum of tranche fractions doesn't round below 1
|
marketOrder = false,
|
||||||
constraints
|
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 {
|
// enum Exchange {
|
||||||
@@ -47,72 +95,6 @@ export const Exchange = {
|
|||||||
UniswapV3: 1,
|
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) {
|
export function sqrtX96(value) {
|
||||||
return BigInt(Math.round(Math.sqrt(value * 2 ** (96*2))))
|
return BigInt(Math.round(Math.sqrt(value * 2 ** (96*2))))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import {useOrderStore} from "@/store/store";
|
|||||||
import LimitPrice from "@/components/LimitPrice.vue";
|
import LimitPrice from "@/components/LimitPrice.vue";
|
||||||
import Order from "@/components/Order.vue";
|
import Order from "@/components/Order.vue";
|
||||||
import {computed, ref} from "vue";
|
import {computed, ref} from "vue";
|
||||||
import {limitConstraint, maxFraction} from "@/orderbuild.js";
|
import {applyLimit, maxFraction} from "@/orderbuild.js";
|
||||||
import {validateRequired, validateTranches} from "@/validate.js";
|
import {validateRequired, validateTranches} from "@/validate.js";
|
||||||
|
|
||||||
const os = useOrderStore()
|
const os = useOrderStore()
|
||||||
@@ -82,7 +82,7 @@ function buildTranches() {
|
|||||||
const mf = Number(maxFraction)
|
const mf = Number(maxFraction)
|
||||||
for( let i=0; i<n; i++ ) {
|
for( let i=0; i<n; i++ ) {
|
||||||
// todo optional deadline
|
// todo optional deadline
|
||||||
const cs = [limitConstraint(rungs.value[i])]
|
const cs = [applyLimit(rungs.value[i])]
|
||||||
const fraction = Math.min(mf, Math.ceil(mf * fractions.value[i]) )
|
const fraction = Math.min(mf, Math.ceil(mf * fractions.value[i]) )
|
||||||
ts.push([fraction, cs])
|
ts.push([fraction, cs])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ function placeOrder() {
|
|||||||
const route = os.route
|
const route = os.route
|
||||||
const amt = FixedNumber.fromString(os.totalAmount.toString(), {decimals: os.amountToken.decimals}).value
|
const amt = FixedNumber.fromString(os.totalAmount.toString(), {decimals: os.amountToken.decimals}).value
|
||||||
const ts = props.tranches()
|
const ts = props.tranches()
|
||||||
const order = newOrder(tokenIn, tokenOut, route.exchange, route.fee, amt, os.amountIsInput, ts)
|
const order = newOrder(tokenIn, tokenOut, route.exchange, route.fee, amt, os.amountIsInput, ts) // todo: minAmount, outputToOwner, chainOrder
|
||||||
pendOrder(order)
|
pendOrder(order)
|
||||||
router.push('/orders')
|
router.push('/orders')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
import {routeInverted, SingletonCoroutine, vAutoSelect} from "@/misc.js";
|
import {routeInverted, SingletonCoroutine, vAutoSelect} from "@/misc.js";
|
||||||
import Order from "@/components/Order.vue";
|
import Order from "@/components/Order.vue";
|
||||||
import LimitPrice from "@/components/LimitPrice.vue";
|
import LimitPrice from "@/components/LimitPrice.vue";
|
||||||
import {timesliceTranches, limitConstraint} from "@/orderbuild.js";
|
import {timesliceTranches, applyLimit} from "@/orderbuild.js";
|
||||||
import Tranches from "@/components/Tranches.vue";
|
import Tranches from "@/components/Tranches.vue";
|
||||||
import {useOrderStore} from "@/store/store.js";
|
import {useOrderStore} from "@/store/store.js";
|
||||||
|
|
||||||
@@ -18,10 +18,11 @@ const os = useOrderStore()
|
|||||||
|
|
||||||
function buildTranches() {
|
function buildTranches() {
|
||||||
const ts = timesliceTranches();
|
const ts = timesliceTranches();
|
||||||
const priceConstraint = limitConstraint();
|
const os = useOrderStore()
|
||||||
if( priceConstraint !== null )
|
const price = os.limitPrice
|
||||||
|
if( price )
|
||||||
for( let i=0; i<ts.length; i++)
|
for( let i=0; i<ts.length; i++)
|
||||||
ts[i][1].push(priceConstraint)
|
applyLimit(ts[i], price)
|
||||||
return ts
|
return ts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ export const vAutoSelect = {
|
|||||||
input.onfocus = () => setTimeout(() => input.select(), 0)
|
input.onfocus = () => setTimeout(() => input.select(), 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export const uint64max = 18446744073709551615n
|
|
||||||
export const uint32max = 4294967295n
|
export const uint32max = 4294967295n
|
||||||
|
export const uint64max = 18446744073709551615n
|
||||||
|
|
||||||
export function tokenNumber(token, balance) {
|
export function tokenNumber(token, balance) {
|
||||||
return FixedNumber.fromValue(balance, token.decimals, {decimals:token.decimals, width: 256})
|
return FixedNumber.fromValue(balance, token.decimals, {decimals:token.decimals, width: 256})
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
import {routeInverted} from "@/misc.js";
|
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";
|
import {useOrderStore} from "@/store/store.js";
|
||||||
|
|
||||||
|
|
||||||
export const maxFraction = 65535n // by contract definition of uint16 fraction
|
export function applyLimit(tranche, price=null) {
|
||||||
|
|
||||||
|
|
||||||
export function limitConstraint(price=null) {
|
|
||||||
const os = useOrderStore()
|
const os = useOrderStore()
|
||||||
if( price === null ) {
|
if( price === null ) {
|
||||||
price = os.limitPrice
|
price = os.limitPrice
|
||||||
if (!price)
|
if (!price)
|
||||||
return null
|
return
|
||||||
}
|
}
|
||||||
const route = os.route
|
const route = os.route
|
||||||
const inverted = routeInverted(route)
|
const inverted = routeInverted(route)
|
||||||
@@ -19,7 +16,15 @@ export function limitConstraint(price=null) {
|
|||||||
const isRatio = false // todo ratios
|
const isRatio = false // todo ratios
|
||||||
const decimals = 10 ** (os.tokenA.decimals - os.tokenB.decimals)
|
const decimals = 10 ** (os.tokenA.decimals - os.tokenB.decimals)
|
||||||
const limit = inverted ? decimals / price : price / 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() {
|
export function timesliceTranches() {
|
||||||
@@ -39,14 +44,18 @@ export function timesliceTranches() {
|
|||||||
} else {
|
} else {
|
||||||
window = Math.round(duration / n)
|
window = Math.round(duration / n)
|
||||||
}
|
}
|
||||||
const ceil = maxFraction % BigInt(n) ? 1n : 0n
|
const amtPerTranche = Math.ceil(MAX_FRACTION / n)
|
||||||
const amtPerTranche = maxFraction / BigInt(n) + ceil
|
|
||||||
duration -= 15 // subtract 15 seconds so the last tranche completes before the deadline
|
duration -= 15 // subtract 15 seconds so the last tranche completes before the deadline
|
||||||
for (let i = 0; i < n; i++) {
|
for (let i = 0; i < n; i++) {
|
||||||
const start = Math.floor(i * (duration / Math.max((n - 1), 1)))
|
const start = Math.floor(i * (duration / Math.max((n - 1), 1)))
|
||||||
const end = start + window
|
const end = start + window
|
||||||
const cs = [newTimeConstraint(TimeMode.SinceOrderStart, start, TimeMode.SinceOrderStart, end)]
|
ts.push(newTranche({
|
||||||
ts.push([amtPerTranche, cs])
|
fraction: amtPerTranche,
|
||||||
|
startTimeIsRelative: true,
|
||||||
|
startTime: start,
|
||||||
|
endTimeIsRelative: true,
|
||||||
|
endTime: end,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
return ts
|
return ts
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user