order placement works, including automatic vault creation

This commit is contained in:
Tim Olson
2023-10-06 19:49:31 -04:00
parent c206607547
commit c637e82ac3
8 changed files with 279 additions and 56 deletions

View File

@@ -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,
}

View File

@@ -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
View 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
// }

View File

@@ -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()
})