mirror of
https://github.com/ZigZagExchange/zksync-lite-market-maker.git
synced 2025-12-17 07:04:23 +01:00
fully functioning general pair marketmaker
This commit is contained in:
14
.env.EXAMPLE
14
.env.EXAMPLE
@@ -1,14 +0,0 @@
|
|||||||
ZIGZAG_WS_URL=wss://zigzag-exchange.herokuapp.com
|
|
||||||
ETH_PRIVKEY=myprivkey
|
|
||||||
|
|
||||||
# mainnet or rinkeby
|
|
||||||
ETH_NETWORK=mainnet
|
|
||||||
|
|
||||||
# See https://github.com/ZigZagExchange/backend/README.md for up to date Chain IDs
|
|
||||||
# 1 = zksync-mainnet, 1000 = zksync-rinkeby
|
|
||||||
CHAIN_ID=1
|
|
||||||
MIN_DOLLAR_SIZE=0
|
|
||||||
MAX_DOLLAR_SIZE=1000000
|
|
||||||
|
|
||||||
# Comma separated list of supported pairs you want to market make for
|
|
||||||
PAIR_WHITELIST=ETH-USDT,ETH-USDC
|
|
||||||
@@ -14,4 +14,6 @@ If your hosting service requires you to pass in configs via environment variable
|
|||||||
cat config.json | tr -d ' ' | tr -d '\n'
|
cat config.json | tr -d ' ' | tr -d '\n'
|
||||||
```
|
```
|
||||||
|
|
||||||
and set it to the value of the `MM_CONFIG` environment variable to override the config file
|
and set it to the value of the `MM_CONFIG` environment variable to override the config file.
|
||||||
|
|
||||||
|
You can also override the private key in the config file with the `ETH_PRIVKEY` environment variable.
|
||||||
|
|||||||
@@ -1,16 +1,107 @@
|
|||||||
{
|
{
|
||||||
"eth_privkey": "aaaaaaaaaa.......",
|
"ethPrivKey": "aaaa....",
|
||||||
"zigzag_chain_id": 1000,
|
"zigzagChainId": 1000,
|
||||||
"zigzag_ws_url": "wss://zigzag-exchange.herokuapp.com",
|
"zigzagWsUrl": "wss://zigzag-exchange.herokuapp.com",
|
||||||
"pairs": {
|
"pairs": {
|
||||||
"ETH-USDC": {
|
"ETH-USDC": {
|
||||||
"priceFeedPrimary": "cryptowatch:6631",
|
"priceFeedPrimary": "cryptowatch:6631",
|
||||||
"priceFeedSecondary": null,
|
"priceFeedSecondary": null,
|
||||||
"slippageRate": 1e-9,
|
"slippageRate": 1e-5,
|
||||||
"active": true,
|
"active": true,
|
||||||
"maxSize": 100,
|
"maxSize": 100,
|
||||||
"minSize": 0.0003,
|
"minSize": 0.0003,
|
||||||
"minSpread": 0.0005
|
"minSpread": 0.0005,
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
"ETH-USDT": {
|
||||||
|
"priceFeedPrimary": "cryptowatch:588",
|
||||||
|
"priceFeedSecondary": null,
|
||||||
|
"slippageRate": 1e-5,
|
||||||
|
"active": true,
|
||||||
|
"maxSize": 100,
|
||||||
|
"minSize": 0.0003,
|
||||||
|
"minSpread": 0.0005,
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
"ETH-DAI": {
|
||||||
|
"priceFeedPrimary": "cryptowatch:63533",
|
||||||
|
"priceFeedSecondary": null,
|
||||||
|
"slippageRate": 1e-5,
|
||||||
|
"active": true,
|
||||||
|
"maxSize": 100,
|
||||||
|
"minSize": 0.0003,
|
||||||
|
"minSpread": 0.0005,
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
"ETH-FRAX": {
|
||||||
|
"priceFeedPrimary": "cryptowatch:6631",
|
||||||
|
"priceFeedSecondary": null,
|
||||||
|
"slippageRate": 1e-5,
|
||||||
|
"active": true,
|
||||||
|
"maxSize": 100,
|
||||||
|
"minSize": 0.0003,
|
||||||
|
"minSpread": 0.0005,
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
"WBTC-USDT": {
|
||||||
|
"priceFeedPrimary": "cryptowatch:579",
|
||||||
|
"priceFeedSecondary": null,
|
||||||
|
"slippageRate": 1e-4,
|
||||||
|
"active": true,
|
||||||
|
"maxSize": 10,
|
||||||
|
"minSize": 0.00003,
|
||||||
|
"minSpread": 0.0005,
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
"WBTC-USDC": {
|
||||||
|
"priceFeedPrimary": "cryptowatch:6630",
|
||||||
|
"priceFeedSecondary": null,
|
||||||
|
"slippageRate": 1e-4,
|
||||||
|
"active": true,
|
||||||
|
"maxSize": 10,
|
||||||
|
"minSize": 0.00003,
|
||||||
|
"minSpread": 0.0005,
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
"WBTC-DAI": {
|
||||||
|
"priceFeedPrimary": "cryptowatch:63532",
|
||||||
|
"priceFeedSecondary": null,
|
||||||
|
"slippageRate": 1e-4,
|
||||||
|
"active": true,
|
||||||
|
"maxSize": 10,
|
||||||
|
"minSize": 0.00003,
|
||||||
|
"minSpread": 0.0005,
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
"USDC-USDT": {
|
||||||
|
"priceFeedPrimary": "cryptowatch:6636",
|
||||||
|
"priceFeedSecondary": null,
|
||||||
|
"slippageRate": 1e-9,
|
||||||
|
"active": true,
|
||||||
|
"maxSize": 100000,
|
||||||
|
"minSize": 1,
|
||||||
|
"minSpread": 0.0003,
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
"DAI-USDT": {
|
||||||
|
"priceFeedPrimary": "cryptowatch:63349",
|
||||||
|
"priceFeedSecondary": null,
|
||||||
|
"slippageRate": 1e-9,
|
||||||
|
"active": true,
|
||||||
|
"maxSize": 100000,
|
||||||
|
"minSize": 1,
|
||||||
|
"minSpread": 0.0003,
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
"DAI-USDC": {
|
||||||
|
"priceFeedPrimary": "cryptowatch:61485",
|
||||||
|
"priceFeedSecondary": null,
|
||||||
|
"slippageRate": 1e-9,
|
||||||
|
"active": true,
|
||||||
|
"maxSize": 100000,
|
||||||
|
"minSize": 1,
|
||||||
|
"minSpread": 0.0003,
|
||||||
|
"active": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,14 @@ else {
|
|||||||
const mmConfigFile = fs.readFileSync("config.json", "utf8");
|
const mmConfigFile = fs.readFileSync("config.json", "utf8");
|
||||||
MM_CONFIG = JSON.parse(mmConfigFile);
|
MM_CONFIG = JSON.parse(mmConfigFile);
|
||||||
}
|
}
|
||||||
console.log(MM_CONFIG);
|
let activePairs = [];
|
||||||
const activePairs = Object.keys(MM_CONFIG.pairs).join(',');
|
for (let marketId in MM_CONFIG.pairs) {
|
||||||
|
const pair = MM_CONFIG.pairs[marketId];
|
||||||
|
if (pair.active) {
|
||||||
|
activePairs.push(marketId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("ACTIVE PAIRS", activePairs);
|
||||||
|
|
||||||
// Initiate fill loop
|
// Initiate fill loop
|
||||||
let ORDER_BROADCASTING = false;
|
let ORDER_BROADCASTING = false;
|
||||||
@@ -32,7 +38,7 @@ let syncWallet, ethersProvider, syncProvider, ethWallet,
|
|||||||
ethersProvider = ethers.getDefaultProvider(ETH_NETWORK);
|
ethersProvider = ethers.getDefaultProvider(ETH_NETWORK);
|
||||||
try {
|
try {
|
||||||
syncProvider = await zksync.getDefaultProvider(ETH_NETWORK);
|
syncProvider = await zksync.getDefaultProvider(ETH_NETWORK);
|
||||||
ethWallet = new ethers.Wallet(MM_CONFIG.ethPrivKey);
|
ethWallet = new ethers.Wallet(process.env.ETH_PRIVKEY || MM_CONFIG.ethPrivKey);
|
||||||
syncWallet = await zksync.Wallet.fromEthSigner(ethWallet, syncProvider);
|
syncWallet = await zksync.Wallet.fromEthSigner(ethWallet, syncProvider);
|
||||||
if (!(await syncWallet.isSigningKeySet())) {
|
if (!(await syncWallet.isSigningKeySet())) {
|
||||||
console.log("setting sign key");
|
console.log("setting sign key");
|
||||||
@@ -52,9 +58,13 @@ const SPOT_PRICES = {};
|
|||||||
const OPEN_ORDERS = {};
|
const OPEN_ORDERS = {};
|
||||||
|
|
||||||
// Get markets info
|
// Get markets info
|
||||||
const markets_url = `https://zigzag-markets.herokuapp.com/markets?chainid=${CHAIN_ID}&id=${activePairs}`
|
const activePairsText = activePairs.join(',');
|
||||||
|
const markets_url = `https://zigzag-markets.herokuapp.com/markets?chainid=${CHAIN_ID}&id=${activePairsText}`
|
||||||
const markets = await fetch(markets_url).then(r => r.json());
|
const markets = await fetch(markets_url).then(r => r.json());
|
||||||
console.log(markets);
|
if (markets.error) {
|
||||||
|
console.error(markets);
|
||||||
|
throw new Error(markets.error);
|
||||||
|
}
|
||||||
const MARKETS = {};
|
const MARKETS = {};
|
||||||
for (let i in markets) {
|
for (let i in markets) {
|
||||||
const market = markets[i];
|
const market = markets[i];
|
||||||
@@ -72,8 +82,10 @@ function onWsOpen() {
|
|||||||
zigzagws.on('close', onWsClose);
|
zigzagws.on('close', onWsClose);
|
||||||
fillOrdersInterval = setInterval(fillOpenOrders, 5000);
|
fillOrdersInterval = setInterval(fillOpenOrders, 5000);
|
||||||
for (let market in MM_CONFIG.pairs) {
|
for (let market in MM_CONFIG.pairs) {
|
||||||
const msg = {op:"subscribemarket", args:[CHAIN_ID, market]};
|
if (MM_CONFIG.pairs[market].active) {
|
||||||
zigzagws.send(JSON.stringify(msg));
|
const msg = {op:"subscribemarket", args:[CHAIN_ID, market]};
|
||||||
|
zigzagws.send(JSON.stringify(msg));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,6 +151,7 @@ function isOrderFillable(order) {
|
|||||||
const mmConfig = MM_CONFIG.pairs[market_id];
|
const mmConfig = MM_CONFIG.pairs[market_id];
|
||||||
if (chainid != CHAIN_ID) return { fillable: false, reason: "badchain" }
|
if (chainid != CHAIN_ID) return { fillable: false, reason: "badchain" }
|
||||||
if (!market) return { fillable: false, reason: "badmarket" }
|
if (!market) return { fillable: false, reason: "badmarket" }
|
||||||
|
if (!mmConfig.active) return { fillable: false, reason: "inactivemarket" }
|
||||||
|
|
||||||
const baseQuantity = order[5];
|
const baseQuantity = order[5];
|
||||||
const quoteQuantity = order[6];
|
const quoteQuantity = order[6];
|
||||||
@@ -164,10 +177,10 @@ function isOrderFillable(order) {
|
|||||||
return { fillable: false, reason: e.message }
|
return { fillable: false, reason: e.message }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (side == 's' && price > quote.hardPrice) {
|
if (side == 's' && price > quote.quotePrice) {
|
||||||
return { fillable: false, reason: "badprice" };
|
return { fillable: false, reason: "badprice" };
|
||||||
}
|
}
|
||||||
else if (side == 'b' && price < quote.hardPrice) {
|
else if (side == 'b' && price < quote.quotePrice) {
|
||||||
return { fillable: false, reason: "badprice" };
|
return { fillable: false, reason: "badprice" };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,34 +218,37 @@ async function sendfillrequest(orderreceipt) {
|
|||||||
const baseCurrency = market.baseAssetId;
|
const baseCurrency = market.baseAssetId;
|
||||||
const quoteCurrency = market.quoteAssetId;
|
const quoteCurrency = market.quoteAssetId;
|
||||||
const side = orderreceipt[3];
|
const side = orderreceipt[3];
|
||||||
let price = orderreceipt[4];
|
|
||||||
const baseQuantity = orderreceipt[5];
|
const baseQuantity = orderreceipt[5];
|
||||||
const quoteQuantity = orderreceipt[6];
|
const quoteQuantity = orderreceipt[6];
|
||||||
let tokenSell, tokenBuy, sellQuantity;
|
const quote = genquote(chainId, market_id, side, baseQuantity);
|
||||||
|
let tokenSell, tokenBuy, sellQuantity, buyQuantity;
|
||||||
if (side === "b") {
|
if (side === "b") {
|
||||||
tokenSell = market.baseAssetId;
|
tokenSell = market.baseAssetId;
|
||||||
tokenBuy = market.quoteAssetId;
|
tokenBuy = market.quoteAssetId;
|
||||||
sellQuantity = baseQuantity.toFixed(market.baseAsset.decimals);
|
// Add 1 bip to to protect against rounding errors
|
||||||
|
sellQuantity = (baseQuantity * 1.0001).toFixed(market.baseAsset.decimals);
|
||||||
|
buyQuantity = quote.quoteQuantity.toFixed(market.quoteAsset.decimals);
|
||||||
} else if (side === "s") {
|
} else if (side === "s") {
|
||||||
tokenSell = market.quoteAssetId;
|
tokenSell = market.quoteAssetId;
|
||||||
tokenBuy = market.baseAssetId;
|
tokenBuy = market.baseAssetId;
|
||||||
sellQuantity = quoteQuantity.toFixed(market.quoteAsset.decimals);
|
// Add 1 bip to to protect against rounding errors
|
||||||
|
sellQuantity = (quote.quoteQuantity * 1.0001).toFixed(market.quoteAsset.decimals);
|
||||||
|
buyQuantity = baseQuantity.toFixed(market.baseAsset.decimals);
|
||||||
}
|
}
|
||||||
sellQuantity = syncProvider.tokenSet.parseToken(
|
const sellQuantityParsed = syncProvider.tokenSet.parseToken(
|
||||||
tokenSell,
|
tokenSell,
|
||||||
sellQuantity
|
sellQuantity
|
||||||
);
|
);
|
||||||
sellQuantity = zksync.utils.closestPackableTransactionAmount(sellQuantity);
|
const sellQuantityPacked = zksync.utils.closestPackableTransactionAmount(sellQuantityParsed);
|
||||||
const tokenRatio = {};
|
const tokenRatio = {};
|
||||||
tokenRatio[baseCurrency] = baseQuantity.toFixed(market.baseAsset.decimals);
|
tokenRatio[tokenBuy] = buyQuantity;
|
||||||
tokenRatio[quoteCurrency] = quoteQuantity.toFixed(market.quoteAsset.decimals);
|
tokenRatio[tokenSell] = sellQuantity;
|
||||||
console.log(tokenRatio);
|
console.log(tokenRatio);
|
||||||
console.log(sellQuantity.toString());
|
|
||||||
const one_min_expiry = (Date.now() / 1000 | 0) + 60;
|
const one_min_expiry = (Date.now() / 1000 | 0) + 60;
|
||||||
const orderDetails = {
|
const orderDetails = {
|
||||||
tokenSell,
|
tokenSell,
|
||||||
tokenBuy,
|
tokenBuy,
|
||||||
amount: sellQuantity,
|
amount: sellQuantityPacked,
|
||||||
ratio: zksync.utils.tokenRatio(tokenRatio),
|
ratio: zksync.utils.tokenRatio(tokenRatio),
|
||||||
validUntil: one_min_expiry
|
validUntil: one_min_expiry
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user