Merge pull request #70 from ZigZagExchange/dev

update master to dev
This commit is contained in:
Trooper
2022-05-10 23:42:39 +02:00
committed by GitHub
3 changed files with 1698 additions and 43 deletions

View File

@@ -32,6 +32,23 @@ node marketmaker.js
## Settings
#### Fee Token
With the defualt setting the bot will pay the zkSync fee wiht the same token as the user (buy currency for the bot). You can chose to override that by a fixed fee token. Check if your tokens is avalible to pay fees on zkSync [here](https://zkscan.io/explorer/tokens).
```
{
"cryptowatchApiKey": "aaaaxxx",
"ethPrivKeys": [
"",
""
],
"zigzagChainId": 1,
"zigzagWsUrl": "wss://zigzag-exchange.herokuapp.com",
"feeToken": "ETH", <- add this line if you eg. want to pay the fees in Ethereum
"pairs": {
```
#### Mainnet zkSync
- "zigzagWsUrl": "wss://zigzag-exchange.herokuapp.com"
- "zigzagChainId": 1
@@ -99,12 +116,14 @@ Example:
###### Chainlink
With chainlink you have access to price oracles via blockchain. The requests are read-calls to a smart contract. The public ethers provider might be too slow for a higher number of pairs or at times of high demand. Therefore, it might be needed to have access to an Infura account (100000 Requests/Day for free). You can get an endpoint for your market maker (like https://mainnet.infura.io/v3/...), You can add this with the `infuraUrl` field in `config.json`, like this:
```
"ETH-USDC": {
"infuraUrl": "https://mainnet.infura.io/v3/xxxxxxxx",
"zigzagChainId": 1,
"zigzagWsUrl": "wss://zigzag-exchange.herokuapp.com",
....
}
"infuraUrl": "https://mainnet.infura.io/v3/xxxxxxxx",
"pairs": {
"ETH-USDC": {
"zigzagChainId": 1,
"zigzagWsUrl": "wss://zigzag-exchange.herokuapp.com",
....
}
```
You can get the available market contracts [here.](https://docs.chain.link/docs/ethereum-addresses/)Add those to you pair config as "chainlink:<address>", like this:
```
@@ -119,12 +138,13 @@ You can get the available market contracts [here.](https://docs.chain.link/docs/
###### UniswapV3
With uniswapV3 you have access to price feed's via blockchain. The requests are read-calls to a smart contract. The public ethers provider might be too slow for a higher number of pairs or at times of high demand. Therefore, it might be needed to have access to an Infura account (100000 Requests/Day for free). You can get an endpoint for your market maker (like https://mainnet.infura.io/v3/...), You can add this with the `infuraUrl` field in `config.json`, like this:
```
"ETH-USDC": {
"infuraUrl": "https://mainnet.infura.io/v3/xxxxxxxx",
"zigzagChainId": 1,
"zigzagWsUrl": "wss://zigzag-exchange.herokuapp.com",
....
}
"infuraUrl": "https://mainnet.infura.io/v3/xxxxxxxx",
"pairs": {
"ETH-USDC": {
"zigzagChainId": 1,
"zigzagWsUrl": "wss://zigzag-exchange.herokuapp.com",
....
}
```
You can get the available market contracts [here.](https://info.uniswap.org) Select a token and then a pool matching the pair you plan to market make. Make sure base and quote tokens match (USDC-ETH don't work for ETH-USDC). After selecting a pool, you can see the adress in the browser URL. Add that to your pair config as "uniswapv3:<address>", like this:
```

View File

@@ -16,6 +16,8 @@ const MARKETS = {};
const CHAINLINK_PROVIDERS = {};
const UNISWAP_V3_PROVIDERS = {};
const PAST_ORDER_LIST = {};
const FEE_TOKEN_LIST = [];
let FEE_TOKEN = null;
let uniswap_error_counter = 0;
let chainlink_error_counter = 0;
@@ -29,6 +31,9 @@ else {
const mmConfigFile = fs.readFileSync("config.json", "utf8");
MM_CONFIG = JSON.parse(mmConfigFile);
}
if (MM_CONFIG.feeToken) {
FEE_TOKEN = MM_CONFIG.feeToken;
}
let activePairs = [];
for (let marketId in MM_CONFIG.pairs) {
const pair = MM_CONFIG.pairs[marketId];
@@ -152,8 +157,7 @@ async function handleMessage(json) {
console.log(fillable);
if (fillable.fillable) {
sendFillRequest(order, fillable.walletId);
}
else if ([
} else if ([
"sending order already",
"badprice"
].includes(fillable.reason)) {
@@ -195,6 +199,19 @@ async function handleMessage(json) {
const newBaseFee = MARKETS[marketId].baseFee;
const newQuoteFee = MARKETS[marketId].quoteFee;
console.log(`marketinfo ${marketId} - update baseFee ${oldBaseFee} -> ${newBaseFee}, quoteFee ${oldQuoteFee} -> ${newQuoteFee}`);
if (FEE_TOKEN) break
if(
marketInfo.baseAsset.enabledForFees &&
!FEE_TOKEN_LIST.includes(marketInfo.baseAsset.id)
) {
FEE_TOKEN_LIST.push(marketInfo.baseAsset.id);
}
if(
marketInfo.quoteAsset.enabledForFees &&
!FEE_TOKEN_LIST.includes(marketInfo.quoteAsset.id)
) {
FEE_TOKEN_LIST.push(marketInfo.quoteAsset.id);
}
break
default:
break
@@ -269,7 +286,6 @@ function isOrderFillable(order) {
else if (side == 'b' && price < quote.quotePrice) {
return { fillable: false, reason: "badprice" };
}
return { fillable: true, reason: null, walletId: goodWalletIds[0]};
}
@@ -352,6 +368,7 @@ async function sendFillRequest(orderreceipt, accountId) {
if (side === "b") {
tokenSell = market.baseAssetId;
tokenBuy = market.quoteAssetId;
sellSymbol = market.baseAsset.symbol;
buySymbol = market.quoteAsset.symbol;
// Add 1 bip to to protect against rounding errors
@@ -360,6 +377,7 @@ async function sendFillRequest(orderreceipt, accountId) {
} else if (side === "s") {
tokenSell = market.quoteAssetId;
tokenBuy = market.baseAssetId;
sellSymbol = market.quoteAsset.symbol;
buySymbol = market.baseAsset.symbol;
// Add 1 bip to to protect against rounding errors
@@ -414,11 +432,21 @@ async function broadcastFill(chainId, orderId, swapOffer, fillOrder, wallet) {
zigzagws.send(JSON.stringify(orderCommitMsg));
return;
}
// select token to match user's fee token
let feeToken;
if (FEE_TOKEN) {
feeToken = FEE_TOKEN
} else {
feeToken = (FEE_TOKEN_LIST.includes(swapOffer.tokenSell))
? swapOffer.tokenSell
: 'ETH'
}
const randInt = (Math.random()*1000).toFixed(0);
console.time('syncswap' + randInt);
const swap = await wallet['syncWallet'].syncSwap({
orders: [swapOffer, fillOrder],
feeToken: "ETH",
feeToken: feeToken,
nonce: fillOrder.nonce
});
const txHash = swap.txHash.split(":")[1];
@@ -486,6 +514,14 @@ async function setupPriceFeeds() {
}
const primaryPriceFeed = pairConfig.priceFeedPrimary;
const secondaryPriceFeed = pairConfig.priceFeedSecondary;
// parse keys to lower case to match later PRICE_FEED keys
if (primaryPriceFeed) {
MM_CONFIG.pairs[market].priceFeedPrimary = primaryPriceFeed.toLowerCase();
}
if (secondaryPriceFeed) {
MM_CONFIG.pairs[market].priceFeedSecondary = secondaryPriceFeed.toLowerCase();
}
[primaryPriceFeed, secondaryPriceFeed].forEach(priceFeed => {
if(!priceFeed) { return; }
const [provider, id] = priceFeed.split(':');
@@ -639,7 +675,7 @@ async function uniswapV3Setup(uniswapV3Address) {
tokenProvier1.decimals()
]);
const key = 'uniswapV3:' + address;
const key = 'uniswapv3:' + address;
const decimalsRatio = (10**decimals0 / 10**decimals1);
UNISWAP_V3_PROVIDERS[key] = [provider, decimalsRatio];
@@ -710,18 +746,30 @@ function indicateLiquidity (pairs = MM_CONFIG.pairs) {
const maxSellSize = Math.min(baseBalance, mmConfig.maxSize);
const maxBuySize = Math.min(quoteBalance / midPrice, mmConfig.maxSize);
const splits = mmConfig.numOrdersIndicated || 10;
// default splits
let buySplits = mmConfig.numOrdersIndicated || 10;
let sellSplits = mmConfig.numOrdersIndicated || 10;
// check if balance passes the min liquidity size - 10 USD
const usdBaseBalance = baseBalance * marketInfo.baseAsset.usdPrice;
const usdQuoteBalance = quoteBalance * marketInfo.quoteAsset.usdPrice;
if (usdBaseBalance < (10 * buySplits)) buySplits = Math.floor(usdBaseBalance / 10)
if (usdQuoteBalance < (10 * sellSplits)) sellSplits = Math.floor(usdQuoteBalance / 10)
const liquidity = [];
for (let i=1; i <= splits; i++) {
const buyPrice = midPrice * (1 - mmConfig.minSpread - (mmConfig.slippageRate * maxBuySize * i/splits));
const sellPrice = midPrice * (1 + mmConfig.minSpread + (mmConfig.slippageRate * maxSellSize * i/splits));
for (let i=1; i <= buySplits; i++) {
const buyPrice = midPrice * (1 - mmConfig.minSpread - (mmConfig.slippageRate * maxBuySize * i/buySplits));
if ((['b','d']).includes(side)) {
liquidity.push(["b", buyPrice, maxBuySize / splits, expires]);
}
if ((['s','d']).includes(side)) {
liquidity.push(["s", sellPrice, maxSellSize / splits, expires]);
liquidity.push(["b", buyPrice, maxBuySize / buySplits, expires]);
}
}
for (let i=1; i <= sellSplits; i++) {
const sellPrice = midPrice * (1 + mmConfig.minSpread + (mmConfig.slippageRate * maxSellSize * i/sellSplits));
if ((['s','d']).includes(side)) {
liquidity.push(["s", sellPrice, maxSellSize / sellSplits, expires]);
}
}
const msg = { op: "indicateliq2", args: [CHAIN_ID, marketId, liquidity] };
try {
zigzagws.send(JSON.stringify(msg));
@@ -758,11 +806,13 @@ async function afterFill(chainId, orderId, wallet) {
const sellTokenParsed = syncProvider.tokenSet.parseToken (
order.sellSymbol,
order.sellQuantity
);
const oldbuyTokenParsed = ethers.BigNumber.from(account_state[order.buySymbol]);
const oldsellTokenParsed = ethers.BigNumber.from(account_state[order.sellSymbol]);
account_state[order.buySymbol] = (oldbuyTokenParsed.add(buyTokenParsed)).toString();
account_state[order.sellSymbol] = (oldsellTokenParsed.sub(sellTokenParsed)).toString();
);
const oldBuyBalance = account_state[order.buySymbol] ? account_state[order.buySymbol] : '0';
const oldSellBalance = account_state[order.sellSymbol] ? account_state[order.sellSymbol] : '0';
const oldBuyTokenParsed = ethers.BigNumber.from(oldBuyBalance);
const oldSellTokenParsed = ethers.BigNumber.from(oldSellBalance);
account_state[order.buySymbol] = (oldBuyTokenParsed.add(buyTokenParsed)).toString();
account_state[order.sellSymbol] = (oldSellTokenParsed.sub(sellTokenParsed)).toString();
const indicateMarket = {};
indicateMarket[marketId] = mmConfig;

1611
package-lock.json generated

File diff suppressed because it is too large Load Diff