order placement works, including automatic vault creation
This commit is contained in:
@@ -43,32 +43,16 @@ export const erc20Abi = [
|
||||
'event Approval(address indexed,address indexed,uint256)',
|
||||
]
|
||||
|
||||
const Route = '(uint8,uint24)'
|
||||
const Constraint = '(uint8,bytes)'
|
||||
const Tranche = `(uint64,${Constraint}[])`
|
||||
const SwapOrder = `(address,address,${Route},uint256,bool,bool,uint64,${Tranche}[])`
|
||||
|
||||
const TimedOrderSpec = '(' +
|
||||
'address tokenIn,' +
|
||||
'address tokenOut,' +
|
||||
'uint24 fee,' +
|
||||
'uint32 deadline,' +
|
||||
'uint32 leeway,' +
|
||||
'uint160 minSqrtPriceX96,' +
|
||||
'uint160 maxSqrtPriceX96,' +
|
||||
'uint8 numTranches,' +
|
||||
'uint256 amount,' +
|
||||
'bool amountIsInput' +
|
||||
')'
|
||||
|
||||
export const timedOrderAbi = [
|
||||
'event TimedOrderCreated (address owner, uint64 index, Spec spec)',
|
||||
'event TimedOrderFilled (address owner, uint64 index, uint256 amountIn, uint256 amountOut)',
|
||||
'event TimedOrderCompleted (address owner, uint64 index)',
|
||||
'event TimedOrderError (address owner, uint64 index, string reason)',
|
||||
`timedOrder(${TimedOrderSpec}) returns (uint64 index)`,
|
||||
export const vaultAbi = [
|
||||
'function withdraw(uint256) public',
|
||||
'function withdrawTo(address payable,uint256) public',
|
||||
'function withdraw(address,uint256) public',
|
||||
'function withdrawTo(address,address,uint256) public',
|
||||
`function placeOrder(${SwapOrder}) public`,
|
||||
`function placeOrders(${SwapOrder}[],uint8) public`,
|
||||
]
|
||||
|
||||
|
||||
export const abi = {
|
||||
'ERC20': erc20Abi,
|
||||
'TimedOrder': timedOrderAbi,
|
||||
'QueryHelper': queryHelperAbi,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,48 @@
|
||||
import {ethers} from "ethers";
|
||||
import {factoryAbi, queryHelperAbi} from "@/blockchain/abi.js";
|
||||
import {factoryAbi, queryHelperAbi, vaultAbi} from "@/blockchain/abi.js";
|
||||
import {useStore} from "@/store/store.js";
|
||||
import {provider} from "@/blockchain/wallet.js";
|
||||
|
||||
|
||||
export function vaultAddress( owner, num ) {
|
||||
const s = useStore()
|
||||
if( s.vaultInitCodeHash === null || s.factory === null )
|
||||
return null
|
||||
const abiCoder = ethers.AbiCoder.defaultAbiCoder();
|
||||
console.log('vaultAddress owner', owner)
|
||||
const salt = ethers.keccak256(abiCoder.encode(['address','uint8'],[owner,num]))
|
||||
const result = ethers.getCreate2Address(s.factory, salt, s.vaultInitCodeHash)
|
||||
console.log('vaultAddress', result, s.factory, salt, s.vaultInitCodeHash)
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
function contractOrNull(addr,abi,provider) {
|
||||
try {
|
||||
return new ethers.Contract(addr,abi,provider)
|
||||
}
|
||||
catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export async function factoryContract() {
|
||||
const s = useStore()
|
||||
return new ethers.Contract(s.helper, factoryAbi, provider)
|
||||
return contractOrNull(s.factory, factoryAbi, provider)
|
||||
}
|
||||
|
||||
export async function queryHelperContract() {
|
||||
const s = useStore()
|
||||
return new ethers.Contract(s.helper, queryHelperAbi, provider)
|
||||
return contractOrNull(s.helper, queryHelperAbi, provider)
|
||||
}
|
||||
|
||||
export async function poolContract(addr) {
|
||||
const s = useStore()
|
||||
return new ethers.Contract(addr, poolAbi, provider)
|
||||
return contractOrNull(addr, poolAbi, provider)
|
||||
}
|
||||
|
||||
export async function vaultContract(num, signer) {
|
||||
const s = useStore()
|
||||
if( num >= s.vaults.length )
|
||||
return null
|
||||
return contractOrNull(s.vaults[num], vaultAbi, signer)
|
||||
}
|
||||
|
||||
114
src/blockchain/orderlib.js
Normal file
114
src/blockchain/orderlib.js
Normal file
@@ -0,0 +1,114 @@
|
||||
|
||||
import {uint32max, uint64max} from "@/misc.js";
|
||||
import {ethers} from "ethers";
|
||||
|
||||
export const NO_CHAIN = uint64max;
|
||||
export const NO_OCO = uint64max;
|
||||
|
||||
// struct SwapOrder {
|
||||
// address tokenIn;
|
||||
// address tokenOut;
|
||||
// Route route;
|
||||
// uint256 amount;
|
||||
// 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
|
||||
// Tranche[] tranches;
|
||||
// }
|
||||
// struct Route {
|
||||
// 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
|
||||
]
|
||||
}
|
||||
|
||||
// struct Tranche {
|
||||
// uint64 fraction; // 18-decimal fraction of the order amount which is available to this tranche. must be <= 1
|
||||
// Constraint[] constraints;
|
||||
// }
|
||||
export function newTranche(amountRatio, constraints) {
|
||||
return [
|
||||
BigInt(Math.ceil(amountRatio * 10**18)), // we use ceil to make sure the sum of tranche fractions doesn't round below 1
|
||||
constraints
|
||||
]
|
||||
}
|
||||
|
||||
// enum Exchange {
|
||||
// UniswapV2,
|
||||
// UniswapV3
|
||||
// }
|
||||
export const Exchange = {
|
||||
UniswapV2: 0,
|
||||
UniswapV3: 1,
|
||||
}
|
||||
|
||||
// enum ConstraintMode {
|
||||
// Time, // 0
|
||||
// Limit, // 1
|
||||
// Trailing, // 2
|
||||
// Barrier, // 3
|
||||
// Line // 4
|
||||
// }
|
||||
export const ConstraintMode = {
|
||||
Time: 0,
|
||||
Limit: 1,
|
||||
Trailing: 2,
|
||||
Barrier: 3,
|
||||
Line: 4,
|
||||
}
|
||||
|
||||
// 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 PriceConstraint {
|
||||
// bool isAbove;
|
||||
// bool isRatio;
|
||||
// uint160 valueSqrtX96;
|
||||
// }
|
||||
|
||||
// struct LineConstraint {
|
||||
// bool isAbove;
|
||||
// bool isRatio;
|
||||
// uint32 time;
|
||||
// uint160 valueSqrtX96;
|
||||
// int160 slopeSqrtX96; // price change per second
|
||||
// }
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import {ethers} from "ethers";
|
||||
import {useStore} from "@/store/store";
|
||||
import {socket} from "@/socket.js";
|
||||
import {vaultContract} from "@/blockchain/contract.js";
|
||||
|
||||
export let provider = null
|
||||
|
||||
function onChainChanged(chainId) {
|
||||
export function onChainChanged(chainId) {
|
||||
chainId = Number(chainId)
|
||||
// console.log('chain changed', chainId)
|
||||
const store = useStore()
|
||||
store.chainId = chainId
|
||||
store.account = null
|
||||
provider = new ethers.BrowserProvider(window.ethereum, chainId)
|
||||
provider.listAccounts().then(onAccountsChanged)
|
||||
}
|
||||
|
||||
function onAccountsChanged(accounts) {
|
||||
@@ -18,8 +22,10 @@ function onAccountsChanged(accounts) {
|
||||
store.account = null
|
||||
}
|
||||
else if (accounts[0] !== store.account) {
|
||||
store.account = accounts[0]
|
||||
store.account = accounts[0].address
|
||||
flushOrders()
|
||||
}
|
||||
socket.emit('address', store.chainId, accounts[0].address)
|
||||
}
|
||||
|
||||
export async function watchWallet() {
|
||||
@@ -62,4 +68,48 @@ const errorHandlingProxy = {
|
||||
}
|
||||
|
||||
|
||||
// const wallet = new Proxy(new Wallet(), errorHandlingProxy);
|
||||
export async function connectWallet() {
|
||||
return provider.getSigner()
|
||||
}
|
||||
|
||||
|
||||
export async function pendOrder(order) {
|
||||
console.log('order', order)
|
||||
const s = useStore()
|
||||
s.pendingOrders.push(order)
|
||||
const signer = await connectWallet()
|
||||
if (!s.vaults.length)
|
||||
socket.emit('ensureVault', s.chainId, await signer.getAddress(), 0)
|
||||
else
|
||||
flushOrders()
|
||||
}
|
||||
|
||||
|
||||
export function flushOrders() {
|
||||
// noinspection JSIgnoredPromiseFromCall
|
||||
asyncFlushOrders()
|
||||
}
|
||||
|
||||
export async function asyncFlushOrders() {
|
||||
const s = useStore()
|
||||
const orders = s.pendingOrders
|
||||
if(!orders.length || !s.account)
|
||||
return
|
||||
const contract = await vaultContract(0, await provider.getSigner())
|
||||
if( !contract )
|
||||
return
|
||||
const proms = []
|
||||
for (const order of orders)
|
||||
proms.push(contract.placeOrder(order))
|
||||
s.pendingOrders = []
|
||||
const txs = await Promise.all(proms)
|
||||
for( const tx of txs )
|
||||
console.log('placed order', tx)
|
||||
}
|
||||
|
||||
socket.on('vaults', (vaults)=>{
|
||||
const s = useStore()
|
||||
console.log('vaults', vaults)
|
||||
s.vaults = vaults
|
||||
flushOrders()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user