Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sync / gateway development -> staging #76

Merged
merged 170 commits into from
Mar 28, 2023
Merged
Changes from 1 commit
Commits
Show all changes
170 commits
Select commit Hold shift + click to select a range
3622b90
injective gateway changes
vic-en Feb 15, 2023
9d3b448
Merge development branch
Feb 21, 2023
6c81037
Batch order creation and cancelation for CLOB and injective
Feb 21, 2023
98c392d
fixes
vic-en Feb 21, 2023
c9e0a79
Merge branch 'development' into feat/injective_protocol
fengtality Feb 21, 2023
ec85187
Batch order creation and cancelation for CLOB and injective
Feb 21, 2023
166de8b
initial commit
vic-en Feb 23, 2023
26cb16c
injective gateway changes
vic-en Feb 15, 2023
97879e4
fixes
vic-en Feb 21, 2023
4f8b4f2
Merge branch 'development' of https://github.com/hummingbot/gateway i…
vic-en Feb 23, 2023
1ce4e44
Move code over
Feb 14, 2023
6e56e33
Clean up
Feb 14, 2023
fcd509f
Clean up unwanted changes
Feb 14, 2023
5329137
Compiles now
Feb 14, 2023
8f71a55
Bring patches over
Feb 14, 2023
1ed97bc
Add xdc to root
Feb 14, 2023
3777af2
Fix https
Feb 14, 2023
04a57db
Start unit tests for xdc chain
Feb 14, 2023
e8eb24f
validator test
Feb 14, 2023
0f1fcde
Add route tests
Feb 14, 2023
d3df317
Support xdc addresses in evm routes only when the chain is xdc
Feb 15, 2023
97f6206
Unit test for allowances
Feb 15, 2023
b938d21
Remove commented code
Feb 15, 2023
ab4a877
Use xdc prefixed address as wallet address
Feb 16, 2023
6ec0c8f
Update
Feb 16, 2023
30b71e4
remove logs
Feb 16, 2023
c126d81
Update
Feb 16, 2023
626e7b6
Drop patch packages
Feb 17, 2023
3ff6179
Fork of ethers so it is compatible with XDC and doesn't interfere wit…
james-hummingbot Feb 20, 2023
7654b4d
Add Xdcish
Feb 20, 2023
77fc380
It compiles
Feb 20, 2023
fc4888e
Fix wallet controller
Feb 20, 2023
832df40
(fix) update patch files and package.json
CrimsonJacket Feb 15, 2023
1ba0ebd
(feat) add xsswap
CrimsonJacket Feb 15, 2023
3578005
(fix/test) address issues with gateway start up and add manual token …
CrimsonJacket Feb 15, 2023
61a97e6
wip
CrimsonJacket Feb 17, 2023
c7483c8
(fix/test) remove redundant private key transformation and finalize u…
CrimsonJacket Feb 17, 2023
2df0e70
(update) update yarn.lock
CrimsonJacket Feb 21, 2023
9ec26a3
(fix/feat) resolve WXDC token address conflicts and add xsswap connector
CrimsonJacket Feb 21, 2023
d3cb225
Clean up
Feb 21, 2023
e97d4b7
(fix) address incorrect token address used in XDCSwap and ethers package
CrimsonJacket Feb 23, 2023
f292c38
(refactor) include token list for xinfin network
CrimsonJacket Feb 24, 2023
0866f3a
(remove) xdcswap
CrimsonJacket Feb 24, 2023
57239b8
(cleanup) fix merge issues with erc20 tokenlist file
CrimsonJacket Feb 24, 2023
d06e7f1
(remove) remove patches
CrimsonJacket Feb 24, 2023
303d07e
(fix) increase node space size to 10240 for tests
fengtality Feb 24, 2023
53865ff
resolve conflict
vic-en Feb 24, 2023
632df4f
Merge branch 'development' of https://github.com/hummingbot/gateway i…
vic-en Feb 24, 2023
d9365c3
Set permissions for hummingbot user to conf directory
klpanagi Feb 26, 2023
872845c
Merge branch 'development' into feat/xdc_chain_and_xsswap
james-hummingbot Feb 26, 2023
69e89ee
final update
vic-en Feb 27, 2023
5a23153
Merge branch 'feat/injective_w_batch_update' of https://github.com/Co…
vic-en Feb 27, 2023
324a739
(fix) replace cronos nodeURL in workflow
fengtality Feb 27, 2023
6f18f08
Merge branch 'development' into feat/xdc_chain_and_xsswap
vic-en Feb 28, 2023
4dfa75d
initial tests
vic-en Feb 28, 2023
56c3a18
update
vic-en Feb 28, 2023
bfd9069
Merge branch 'development' into feat/injective_protocol
vic-en Feb 28, 2023
da05d49
update
vic-en Mar 1, 2023
d0b90f2
clear chain metric timer
vic-en Mar 1, 2023
ff2a592
point node addressees in nonce test to hardhat
vic-en Mar 1, 2023
31e5138
assign id to logger metric timer
vic-en Mar 1, 2023
9b9ba42
remove test
vic-en Mar 1, 2023
26b8e2a
remove test
vic-en Mar 1, 2023
b310fbc
Merge branch 'feat/injective_protocol' of https://github.com/CoinAlph…
vic-en Mar 1, 2023
812104b
Revert "remove test"
vic-en Mar 1, 2023
53ed086
custom test
vic-en Mar 2, 2023
82ec200
update workflow
vic-en Mar 2, 2023
af0798b
increase memory
vic-en Mar 2, 2023
46b1dca
diff in src dir only
vic-en Mar 2, 2023
14f8bf1
update order
vic-en Mar 2, 2023
0b427b6
update workflow
vic-en Mar 2, 2023
dfd7151
fix outstanding issues
vic-en Mar 2, 2023
212eef5
fix defikingdoms test
vic-en Mar 2, 2023
605e63d
update defikingdom unit test
vic-en Mar 2, 2023
eb9a6d6
minor update
vic-en Mar 2, 2023
97eb092
workflow test
vic-en Mar 2, 2023
0ff7c5f
Revert "workflow test"
vic-en Mar 2, 2023
c3c191d
injective balance update
vic-en Mar 2, 2023
505178e
workflow test
vic-en Mar 2, 2023
ef1d308
update jest to use 50%
vic-en Mar 2, 2023
ae055da
Revert "update jest to use 50%"
vic-en Mar 3, 2023
717eb81
Revert "workflow test"
vic-en Mar 3, 2023
1889419
(fixes) Several dexalot-related fixes
petioptrv Mar 3, 2023
f81b924
decode tx log event
vic-en Mar 3, 2023
267672c
remove endpoint not implemented in swagger
vic-en Mar 6, 2023
e38c9ac
correct typo
vic-en Mar 6, 2023
7348476
update yarn
vic-en Mar 6, 2023
8ed804c
remove endpoint not implemented in swagger
vic-en Mar 6, 2023
5256f08
correct typo
vic-en Mar 6, 2023
18b97b5
update yarn
vic-en Mar 6, 2023
1c7cdc5
Merge branch 'development' of github.com:CoinAlpha/gateway into feat/…
Mar 6, 2023
fbc59da
Merge branch 'feat/xdc_chain_and_xsswap' of github.com:CoinAlpha/gate…
Mar 6, 2023
6aae11f
move libs to devDependencies
fengtality Mar 7, 2023
13acef8
chore: remove unused route
sytranvn Mar 7, 2023
d384298
Apply yarn format to reduce diffs
Mar 7, 2023
bfdeadc
update get order
vic-en Mar 7, 2023
fd78603
add /wallet/sign
vic-en Mar 7, 2023
a088aae
return toke symbols as sent in bal request
vic-en Mar 8, 2023
7fe694c
Merge pull request #51 from hummingbot/feat/injective_with_diff_test_…
fengtality Mar 8, 2023
79a06a1
Merge branch 'development' into chore/remove-unused-route
fengtality Mar 8, 2023
5a34350
Merge branch 'development' into fix/format
fengtality Mar 8, 2023
d90f54d
Merge pull request #52 from sytranvn/chore/remove-unused-route
fengtality Mar 8, 2023
101cd14
Merge branch 'development' into fix/format
fengtality Mar 8, 2023
aa9ef71
Merge pull request #53 from CoinAlpha/fix/format
fengtality Mar 8, 2023
c18bddf
Merge development branch
Mar 8, 2023
0176b34
Merge branch 'development' of github.com:hummingbot/gateway into feat…
Mar 8, 2023
2483015
Merge development
Mar 8, 2023
24c4831
typo
Mar 8, 2023
1355975
Merge branch 'feat/injective_protocol' of https://github.com/CoinAlph…
vic-en Mar 8, 2023
acee342
remove endpoint not implemented in swagger
vic-en Mar 6, 2023
12ebf1b
fix unit test
vic-en Mar 8, 2023
07bf97c
Merge branch 'feat/injective_w_batch_update' of https://github.com/Co…
vic-en Mar 8, 2023
661a9c9
(refactor) Adds the API Key request for Dexalot
petioptrv Mar 9, 2023
ac959ac
refactor balances controller
vic-en Mar 9, 2023
3f40797
Remove unnecessary console.log
Mar 9, 2023
f955da0
Merge pull request #45 from CoinAlpha/feat/xdc_chain_and_xsswap
fengtality Mar 9, 2023
f8ebd74
Merge branch 'development' into feat/injective_w_batch_update
fengtality Mar 9, 2023
b167c81
upgrade
vic-en Mar 9, 2023
78310a5
fix fees
vic-en Mar 10, 2023
5f6dc1d
Merge branch 'feat/injective_w_batch_update' of https://github.com/Co…
vic-en Mar 10, 2023
696b2cd
change BNB default rpc to ankr
fengtality Mar 12, 2023
5df9c20
Merge pull request #55 from CoinAlpha/feat/injective_w_batch_update
rapcmia Mar 13, 2023
85d7117
(feat) add dMiner tokens to BSC list
fengtality Mar 13, 2023
87ae3e8
Merge branch 'development' of github.com:hummingbot/gateway into deve…
fengtality Mar 13, 2023
59873ef
(fix) fix decimals for FIRO BLOCK
fengtality Mar 13, 2023
680ff17
(fix) don't decrement nonce
fengtality Mar 14, 2023
226f359
(test) fix tests related to nonce change
fengtality Mar 14, 2023
59164c3
(fix) escape harmony mainnet node from workflow
fengtality Mar 14, 2023
b809a21
(feat) add sushi avalanche
fengtality Mar 14, 2023
7881476
(fix) fix bug with existing BSC and MATIC sushi connectors
fengtality Mar 14, 2023
e33cb70
(feat) add harmony arbitrum sushiswap
fengtality Mar 14, 2023
abc7c43
(fix) revert and make fix to ethereum-base
fengtality Mar 15, 2023
0be3dcc
Merge branch 'fix/fix-nonce' of github.com:hummingbot/gateway into fe…
fengtality Mar 15, 2023
4a3ae3f
Merge pull request #58 from hummingbot/fix/fix-nonce
fengtality Mar 15, 2023
686fe52
Merge branch 'development' of github.com:hummingbot/gateway into feat…
fengtality Mar 15, 2023
cae292d
(fix) fix avalanche typo
fengtality Mar 15, 2023
6b61a76
(fix) fix tests
fengtality Mar 15, 2023
1b2eb5c
(fix) replace new BSC default nodes in workflow
fengtality Mar 15, 2023
2daff0d
Merge pull request #60 from hummingbot/feat/add-sushiswap-networks
fengtality Mar 15, 2023
15e496e
remove comments
vic-en Mar 16, 2023
6e541a8
address comments
vic-en Mar 16, 2023
d6ffec3
change networks for mainnet and testnet
vic-en Mar 16, 2023
06a688a
update dexalot test
vic-en Mar 16, 2023
9875184
Merge branch 'development' into feat/dexalot
vic-en Mar 16, 2023
25f55d3
(fix) change to EIP 1559 for gas calcs
fengtality Mar 16, 2023
dabe9d3
fix tests
fengtality Mar 16, 2023
4ccbde7
upgrade hardhat to try to fix tests
fengtality Mar 17, 2023
0b82c90
Merge pull request #66 from hummingbot/fix/use-1559
fengtality Mar 17, 2023
81b980a
Merge branch 'development' into feat/dexalot
fengtality Mar 17, 2023
58c0769
Merge branch 'development' into fix/injective_issues
vic-en Mar 17, 2023
52ccd39
(fix) test using hardhat with pinned block
fengtality Mar 19, 2023
ede9013
fix typo
fengtality Mar 19, 2023
de0308c
Merge pull request #68 from hummingbot/test/pinned-block-workflow
fengtality Mar 20, 2023
3525f5e
Merge branch 'development' into feat/dexalot
fengtality Mar 20, 2023
4707c28
Merge branch 'development' into fix/injective_issues
fengtality Mar 20, 2023
e68d562
(fix) revert eth gas calc and workflow change
fengtality Mar 20, 2023
8a7e302
Merge branch 'development' into feat/dexalot
fengtality Mar 20, 2023
942dd91
Merge branch 'development' into fix/injective_issues
fengtality Mar 20, 2023
98d087f
dexalot curl commands update
vic-en Mar 20, 2023
0311124
update network/balances swagger doc to include connector field
vic-en Mar 20, 2023
33df6bc
Merge branch 'feat/dexalot' of https://github.com/CoinAlpha/gateway i…
vic-en Mar 20, 2023
3ab615f
update network maps
vic-en Mar 21, 2023
71b55ca
Merge pull request #65 from CoinAlpha/feat/dexalot
fengtality Mar 23, 2023
c63c5ed
Merge branch 'development' into fix/injective_issues
fengtality Mar 24, 2023
226cc84
Merge remote-tracking branch 'origin/development' into fix/docker_per…
klpanagi Mar 26, 2023
a7103ee
Merge pull request #72 from klpanagi/fix/docker_permissions_conf_dir
fengtality Mar 26, 2023
465770e
Merge branch 'development' into fix/injective_issues
fengtality Mar 26, 2023
54e4871
Merge pull request #67 from CoinAlpha/fix/injective_issues
fengtality Mar 26, 2023
292960e
Update workflow.yml
nikspz Mar 28, 2023
673f7a0
Merge pull request #78 from hummingbot/nikspz-patch-1
nikspz Mar 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
initial commit
vic-en committed Feb 23, 2023
commit 166de8b761011cec578ae7624c49978001ea08d2
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
/.idea
package-lock.json
/certs
/coverage
/node_modules
/coverage
**/.DS_Store
159 changes: 159 additions & 0 deletions src/chains/ethereum/evm.broadcaster.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import {
TransactionReceipt,
TransactionRequest,
TransactionResponse,
} from '@ethersproject/providers';
import { BigNumber, utils, Wallet } from 'ethers';
import LRUCache from 'lru-cache';
import { Ethereumish } from '../../services/common-interfaces';
import { logger } from '../../services/logger';

/**
* This class is used to broadcast transactions
* using a privateKey as a signer
* for the transactions and broadcasting
* the transactions directly to the node
*
* Mainly used for working in a Node Environment
*/
export class EVMTxBroadcaster {
private _chain: Ethereumish;
private _isBlocked: boolean;
private _txQueue: TransactionRequest[];
private static _instances: LRUCache<string, EVMTxBroadcaster>;
private _wallet_address: string;
private _wallet: Wallet | undefined;

constructor(chain: Ethereumish, wallet_address: string) {
this._chain = chain;
this._wallet_address = wallet_address;
this._isBlocked = false;
this._txQueue = [];
}

public static getInstance(
chain: Ethereumish,
wallet_address: string
): EVMTxBroadcaster {
if (EVMTxBroadcaster._instances === undefined) {
EVMTxBroadcaster._instances = new LRUCache<string, EVMTxBroadcaster>({
max: 50,
});
}
const instanceKey = chain.chainName + chain.chainId + wallet_address;
if (!EVMTxBroadcaster._instances.has(instanceKey)) {
EVMTxBroadcaster._instances.set(
instanceKey,
new EVMTxBroadcaster(chain, wallet_address)
);
}

return EVMTxBroadcaster._instances.get(instanceKey) as EVMTxBroadcaster;
}

isNextTx(tx: TransactionRequest): boolean {
return !this._isBlocked && tx === this._txQueue[0];
}

async sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

/**
* Broadcasting the transaction using the client
*
* @param transaction
* @returns {string} transaction hash
*/
async broadcast(
transaction: TransactionRequest
): Promise<TransactionResponse> {
let txResponse: TransactionResponse = {
hash: '',
confirmations: 0,
from: '',
wait: function (
confirmations?: number | undefined
): Promise<TransactionReceipt> {
throw new Error(confirmations + 'Function not implemented.');
},
nonce: 0,
gasLimit: BigNumber.from('0'),
data: '',
value: BigNumber.from('0'),
chainId: 0,
};

this._txQueue.push(transaction);

try {
while (!this.isNextTx(transaction)) {
await this.sleep(10); // sleep
}
this._isBlocked = true;

/** Prepare the Transaction * */
const currentNonce = await this._chain.nonceManager.getNextNonce(
this._wallet_address
);
txResponse = await this.createAndSend(transaction, currentNonce);

await this._chain.nonceManager.commitNonce(
this._wallet_address,
currentNonce
);
} catch (e) {
if (e instanceof Error) {
const expectedSequence = Number(
e.message.split('current nonce (')[1].split(')')[0]
);
logger.info(`Expected nonce: ${expectedSequence}`);
await this._chain.nonceManager.overridePendingNonce(
this._wallet_address,
expectedSequence
);
txResponse = await this.createAndSend(transaction, expectedSequence);
}
} finally {
this._txQueue.shift();
this._isBlocked = false;
}
logger.error(await this.getRevertReason(txResponse));
return txResponse;
}

async createAndSend(
tx: TransactionRequest,
nonce: number
): Promise<TransactionResponse> {
tx.nonce = nonce;
if (this._wallet === undefined)
this._wallet = await this._chain.getWallet(this._wallet_address);

/** Broadcast transaction */
return await this._wallet.sendTransaction(tx);
}

async getRevertReason(err: any) {
let reason;
try {
await err.wait();
} catch (error: any) {
if (!error.transaction) {
logger.error('getRevertReason: error.transaction is undefined');
} else {
// https://gist.github.com/gluk64/fdea559472d957f1138ed93bcbc6f78a
const code = await this._chain.provider.call(
error.transaction,
error.blockNumber || error.receipt.blockNumber
);
reason = utils.toUtf8String('0x' + code.substring(138));
const i = reason.indexOf('0'); // delete all null characters after the
if (i > -1) {
return reason.substring(0, i);
}
}
}
return reason;
}
}
7 changes: 5 additions & 2 deletions src/clob/clob.requests.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { NetworkSelectionRequest } from '../services/common-interfaces';
import { OrderType, Side } from '../amm/amm.requests';
import { Orderbook, SpotMarket } from '@injectivelabs/sdk-ts';
import { Orderbook } from '@injectivelabs/sdk-ts';

export interface ClobMarketsRequest extends NetworkSelectionRequest {
market?: string;
}

export interface CLOBMarkets {
[key: string]: SpotMarket;
[key: string]: any;
}

export interface ClobMarketResponse {
network: string;
timestamp: number;
@@ -55,6 +56,7 @@ export interface CreateOrderParam {
orderType: OrderType;
side: Side;
market: string;
clientOrderID?: string;
}

export interface ClobPostOrderRequest
@@ -79,6 +81,7 @@ export interface ClobPostOrderResponse {
timestamp: number;
latency: number;
txHash: string;
clientOrderID?: string;
}

export type ClobDeleteOrderRequest = ClobGetOrderRequest;
6 changes: 1 addition & 5 deletions src/clob/clob.validators.ts
Original file line number Diff line number Diff line change
@@ -50,11 +50,7 @@ export const validateWallet: Validator = mkValidator(
'address',
invalidWalletError,
(val) => {
return (
typeof val === 'string' &&
val.length === 66 &&
isAddress(val.slice(0, 42))
);
return typeof val === 'string' && isAddress(val.slice(0, 42));
}
);

39 changes: 39 additions & 0 deletions src/connectors/dexalot/dexalot.clob.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { AvailableNetworks } from '../../services/config-manager-types';
import { ConfigManagerV2 } from '../../services/config-manager-v2';

export namespace DexalotCLOBConfig {
export interface NetworkConfig {
gasLimitEstimate: number;
tradingTypes: (type: string) => Array<string>;
availableNetworks: Array<AvailableNetworks>;
addresses: (network: string) => { [name: string]: string };
maxLRUCacheInstances: number;
}

export const config: NetworkConfig = {
gasLimitEstimate: ConfigManagerV2.getInstance().get(
`dexalot.gasLimitEstimate`
),
maxLRUCacheInstances: 10,
tradingTypes: (type: string) => {
return type === 'spot' ? ['CLOB_SPOT'] : ['CLOB_PERP'];
},
availableNetworks: [
{
chain: 'avalanche',
networks: Object.keys(
ConfigManagerV2.getInstance().get('dexalot.contractAddresses')
).filter((network) =>
Object.keys(
ConfigManagerV2.getInstance().get('avalanche.networks')
).includes(network)
),
},
],
addresses: (network: string) => {
return ConfigManagerV2.getInstance().get(
`dexalot.contractAddresses.${network}`
);
},
};
}
114 changes: 114 additions & 0 deletions src/connectors/dexalot/dexalot.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { utils } from 'ethers';
import { MarketInfo, PriceLevel } from '../../services/common-interfaces';
import { MarketInfoStruct, OrderInfoStruct } from './dexalot.interfaces';

export const OrderStatus = {
NEW: 0,
REJECTED: 1, // not used
PARTIAL: 2,
FILLED: 3,
CANCELED: 4,
EXPIRED: 5, // not used
KILLED: 6,
};

export const OrderSide: any = {
BUY: 0,
SELL: 1,
};

export const OrderType1 = {
MARKET: 0,
LIMIT: 1,
STOP: 2, // not used
STOPLIMIT: 3, // not used
};

export const OrderType2 = {
GTC: 0, // Good Till Cancel,
FOK: 1, // Fill or Kill - requires immediate full fill or reverts
IOC: 2, // Immediate or Cancel - gets any fills & then canceled Remaining will not go in the orderbook
PO: 3, // Post Only - Requires to go in the orderbook without any fills or reverts
};

export const fromUtf8 = (txt: string): string => {
return utils.formatBytes32String(txt);
};

export const toUtf8 = (txt: utils.BytesLike): string => {
return utils.parseBytes32String(txt);
};

export const parseMarkerInfo = (marketInfo: MarketInfoStruct) => {
return {
baseSymbol: toUtf8(marketInfo.baseSymbol),
quoteSymbol: toUtf8(marketInfo.quoteSymbol),
buyBookId: toUtf8(marketInfo.buyBookId),
sellBookId: toUtf8(marketInfo.sellBookId),
minTradeAmount: marketInfo.minTradeAmount.toString(),
maxTradeAmount: marketInfo.maxTradeAmount.toString(),
auctionPrice: marketInfo.auctionPrice.toString(),
auctionMode: marketInfo.auctionMode,
makerRate: marketInfo.makerRate,
takerRate: marketInfo.takerRate,
baseDecimals: marketInfo.baseDecimals,
baseDisplayDecimals: marketInfo.baseDisplayDecimals,
quoteDecimals: marketInfo.quoteDecimals,
quoteDisplayDecimals: marketInfo.quoteDisplayDecimals,
allowedSlippagePercent: marketInfo.allowedSlippagePercent,
addOrderPaused: marketInfo.addOrderPaused,
pairPaused: marketInfo.pairPaused,
postOnly: marketInfo.postOnly,
};
};

export const parseOrderInfo = (orderInfo: OrderInfoStruct) => {
return {
id: orderInfo.id,
clientOrderId: orderInfo.clientOrderId,
tradePairId: orderInfo.tradePairId,
price: orderInfo.price.toString(),
totalAmount: orderInfo.totalAmount.toString(),
quantity: orderInfo.quantity.toString(),
quantityFilled: orderInfo.quantityFilled.toString(),
totalFee: orderInfo.totalFee.toString(),
traderaddress: orderInfo.traderaddress,
side: Object.keys(OrderSide)[
Object.values(OrderSide).indexOf(orderInfo.side)
],
type1:
Object.keys(OrderType1)[
Object.values(OrderType1).indexOf(orderInfo.type1)
],
type2:
Object.keys(OrderType2)[
Object.values(OrderType2).indexOf(orderInfo.type1)
],
status:
Object.keys(OrderStatus)[
Object.values(OrderStatus).indexOf(orderInfo.status)
],
};
};

export const createBook = (
rawBook: string[][],
timestamps: string[],
marketInfo: MarketInfo
): PriceLevel[] => {
const book: PriceLevel[] = [];
if (rawBook[0].length === timestamps.length) {
for (let val = 0; val < rawBook[0].length; val++) {
book.push({
price: utils
.formatUnits(rawBook[0][val], marketInfo.quoteDecimals)
.toString(),
quantity: utils
.formatUnits(rawBook[1][val], marketInfo.baseDecimals)
.toString(),
timestamp: Number(timestamps[val]),
});
}
}
return book;
};
38 changes: 38 additions & 0 deletions src/connectors/dexalot/dexalot.interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { BigNumber, utils } from 'ethers';

export interface OrderInfoStruct {
id: string;
clientOrderId: string;
tradePairId: string;
price: BigNumber;
totalAmount: BigNumber;
quantity: BigNumber;
quantityFilled: BigNumber;
totalFee: BigNumber;
traderaddress: any;
side: number;
type1: number;
type2: number;
status: number;
}

export interface MarketInfoStruct {
baseSymbol: utils.BytesLike;
quoteSymbol: utils.BytesLike;
buyBookId: utils.BytesLike;
sellBookId: utils.BytesLike;
minTradeAmount: BigNumber;
maxTradeAmount: BigNumber;
auctionPrice: BigNumber;
auctionMode: number;
makerRate: number;
takerRate: number;
baseDecimals: number;
baseDisplayDecimals: number;
quoteDecimals: number;
quoteDisplayDecimals: number;
allowedSlippagePercent: number;
addOrderPaused: boolean;
pairPaused: boolean;
postOnly: boolean;
}
293 changes: 293 additions & 0 deletions src/connectors/dexalot/dexalot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
import {
ClobMarketsRequest,
ClobOrderbookRequest,
ClobTickerRequest,
ClobGetOrderRequest,
ClobPostOrderRequest,
ClobDeleteOrderRequest,
ClobGetOrderResponse,
} from '../../clob/clob.requests';
import {
CLOBish,
MarketInfo,
NetworkSelectionRequest,
Orderbook,
} from '../../services/common-interfaces';
import { BigNumber, Contract, utils } from 'ethers';
import { DexalotCLOBConfig } from './dexalot.clob.config';
import LRUCache from 'lru-cache';
import {
floatStringWithDecimalToFixed,
TokenInfo,
TokenValue,
} from '../../services/base';
import { logger } from '../../services/logger';
import { Avalanche } from '../../chains/avalanche/avalanche';
import { EVMTxBroadcaster } from '../../chains/ethereum/evm.broadcaster';
import {
createBook,
fromUtf8,
OrderSide,
OrderType1,
OrderType2,
parseMarkerInfo,
parseOrderInfo,
} from './dexalot.constants';

export class DexalotCLOB implements CLOBish {
private static _instances: LRUCache<string, DexalotCLOB>;
private _chain;
private _ready: boolean = false;
public parsedMarkets: MarketInfo = [];
// private _exchangeContract: Contract;
private _portfolioContract: Contract;
// private _orderBooksContract: Contract;
private _tradePairsContract: Contract;
// private _gasStationContract: Contract;
private _resources: any;
private _conf: DexalotCLOBConfig.NetworkConfig;

private constructor(network: string) {
this._chain = Avalanche.getInstance(network);
this._conf = DexalotCLOBConfig.config;
this._resources = require('./dexalot_mainnet.json');
// this._exchangeContract = this.getContract('ExchangeSub', this._resources);
this._portfolioContract = this.getContract('PortfolioSub', this._resources);
// this._orderBooksContract = this.getContract('OrderBooks', this._resources);
this._tradePairsContract = this.getContract('TradePairs', this._resources);
// this._gasStationContract = this.getContract('GasStation', this._resources);
}

public getContract(name: string, data: any[]): Contract {
const validContractNames = [
'ExchangeSub',
'PortfolioSub',
'OrderBooks',
'TradePairs',
'GasStation',
];
if (!validContractNames.includes(name)) {
logger.error(`${name} has to be one of ${validContractNames.join(',')}`);
throw Error('Invalid contract name.');
}

const info = data.filter((entry) => entry.contract_name === name)[0];
return new Contract(info.address, info.abi.abi, this._chain.provider);
}

public static getInstance(network: string): DexalotCLOB {
if (DexalotCLOB._instances === undefined) {
DexalotCLOB._instances = new LRUCache<string, DexalotCLOB>({
max: DexalotCLOBConfig.config.maxLRUCacheInstances,
});
}
if (!DexalotCLOB._instances.has(network)) {
DexalotCLOB._instances.set(network, new DexalotCLOB(network));
}

return DexalotCLOB._instances.get(network) as DexalotCLOB;
}

public async loadMarkets() {
const rawMarkets = (
await Promise.all(
(
await this._tradePairsContract.getTradePairs()
).map(async (marketId: string) => {
return this._tradePairsContract.getTradePair(marketId);
})
)
).map(parseMarkerInfo);
for (const market of rawMarkets) {
this.parsedMarkets[market.baseSymbol + '-' + market.quoteSymbol] = market;
}
}

public async init() {
if (!this._chain.ready() || Object.keys(this.parsedMarkets).length === 0) {
await this._chain.init();
await this.loadMarkets();
this._ready = true;
}
}

public ready(): boolean {
return this._ready;
}

public async getBalance(
address: string,
symbol: string
): Promise<{ available: TokenValue; total: TokenValue }> {
const bal = this._portfolioContract.getBalance(address, symbol);
const token = <TokenInfo>this._chain.getTokenBySymbol(symbol);
return {
available: { value: bal.available, decimals: token.decimals },
total: { value: bal.total, decimals: token.decimals },
};
}

public async markets(
req: ClobMarketsRequest
): Promise<{ markets: MarketInfo }> {
if (req.market && req.market in this.parsedMarkets)
return { markets: this.parsedMarkets[req.market] };
return { markets: Object.values(this.parsedMarkets) };
}

public async orderBook(req: ClobOrderbookRequest): Promise<Orderbook> {
const markerId = fromUtf8(req.market.replace('-', '/'));
const books = await Promise.all([
this._tradePairsContract.getNBook(
markerId,
OrderSide.BUY,
50,
50,
0,
fromUtf8('')
),
this._tradePairsContract.getNBook(
markerId,
OrderSide.SELL,
50,
50,
0,
fromUtf8('')
),
]);

const buys: string[][] = [
books[0][0].map((value: { toString: () => string }) => {
return value.toString();
}),
books[0][1].map((value: { toString: () => string }) => {
return value.toString();
}),
];
const sells: string[][] = [
books[1][0].map((value: { toString: () => string }) => {
return value.toString();
}),
books[1][1].map((value: { toString: () => string }) => {
return value.toString();
}),
];

const timestamps = [...buys[0]].fill(Date.now().toString());

return {
buys: createBook(buys, timestamps, this.parsedMarkets[req.market]),
sells: createBook(sells, timestamps, this.parsedMarkets[req.market]),
};
}

public async ticker(
req: ClobTickerRequest
): Promise<{ markets: MarketInfo }> {
return await this.markets(req);
}

public async orders(
req: ClobGetOrderRequest
): Promise<{ orders: ClobGetOrderResponse['orders'] }> {
let order;
const marketInfo = this.parsedMarkets[req.market];
if (!req.address) {
order = [await this._tradePairsContract.getOrder(req.orderId)].map(
parseMarkerInfo
)[0];
}
order = [
await this._tradePairsContract.getOrderByClientOrderId(
req.address,
req.orderId
),
].map(parseOrderInfo)[0];
order.price = utils.formatUnits(order.price, marketInfo.quoteDecimals);
order.totalAmount = utils.formatUnits(
order.totalAmount,
marketInfo.baseDecimals
);
order.quantity = utils.formatUnits(order.quantity, marketInfo.baseDecimals);
order.quantityFilled = utils.formatUnits(
order.quantityFilled,
marketInfo.baseDecimals
);
order.totalFee = utils.formatUnits(order.totalFee, marketInfo.baseDecimals);
return {
orders: [order],
};
}

public async postOrder(
req: ClobPostOrderRequest
): Promise<{ txHash: string; clientOrderID: string }> {
const market: MarketInfo = this.parsedMarkets[req.market];
if (market === undefined) throw Error('Invalid market');

const clientOrderID =
req.clientOrderID || (await this.getClientOrderId(req.address));

const txData = await this._tradePairsContract.populateTransaction.addOrder(
req.address,
clientOrderID,
fromUtf8(req.market.replace('-', '/')), // market id
utils.parseUnits(
floatStringWithDecimalToFixed(req.price, market.quoteDisplayDecimals) ||
req.price,
market.quoteDecimals
),
utils.parseUnits(
floatStringWithDecimalToFixed(req.amount, market.baseDisplayDecimals) ||
req.price,
market.baseDecimals
),
OrderSide[req.side.toUpperCase()],
req.orderType.startsWith('LIMIT') ? OrderType1.LIMIT : OrderType1.MARKET,
req.orderType === 'LIMIT_MAKER' ? OrderType2.PO : OrderType2.GTC
);
txData.gasLimit = BigNumber.from(String(this._conf.gasLimitEstimate));
const txResponse = await EVMTxBroadcaster.getInstance(
this._chain,
req.address
).broadcast(txData);
return { txHash: txResponse.hash, clientOrderID };
}

public async deleteOrder(
req: ClobDeleteOrderRequest
): Promise<{ txHash: string }> {
const txData =
await this._tradePairsContract.populateTransaction.cancelOrder(
req.orderId
);
txData.gasLimit = BigNumber.from(String(this._conf.gasLimitEstimate));
const txResponse = await EVMTxBroadcaster.getInstance(
this._chain,
req.address
).broadcast(txData);
return { txHash: txResponse.hash };
}

public estimateGas(_req: NetworkSelectionRequest): {
gasPrice: number;
gasPriceToken: string;
gasLimit: number;
gasCost: number;
} {
return {
gasPrice: this._chain.gasPrice,
gasPriceToken: this._chain.nativeTokenSymbol,
gasLimit: this._conf.gasLimitEstimate,
gasCost: this._chain.gasPrice * this._conf.gasLimitEstimate,
};
}

async getClientOrderId(address: string): Promise<string> {
const blocknumber: number =
(await this._chain.getCurrentBlockNumber()) || 0;
const timestamp = new Date().toISOString();
const id = utils.toUtf8Bytes(`${address}${blocknumber}${timestamp}`);
return utils.keccak256(id);
}
}
5,774 changes: 5,774 additions & 0 deletions src/connectors/dexalot/dexalot_mainnet.json

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions src/connectors/injective/injective.ts
Original file line number Diff line number Diff line change
@@ -20,13 +20,16 @@ import {
CreateOrderParam,
ClobDeleteOrderRequestExtract,
} from '../../clob/clob.requests';
import { NetworkSelectionRequest } from '../../services/common-interfaces';
import {
CLOBish,
NetworkSelectionRequest,
} from '../../services/common-interfaces';
import { InjectiveCLOBConfig } from './injective.clob.config';
import { Injective } from '../../chains/injective/injective';
import LRUCache from 'lru-cache';
import { getInjectiveConfig } from '../../chains/injective/injective.config';

export class InjectiveCLOB {
export class InjectiveCLOB implements CLOBish {
private static _instances: LRUCache<string, InjectiveCLOB>;
private _chain;
public conf;
22 changes: 22 additions & 0 deletions src/services/base.ts
Original file line number Diff line number Diff line change
@@ -144,3 +144,25 @@ export const floatStringWithDecimalToBigNumber = (
return null;
}
};

export const floatStringWithDecimalToFixed = (
floatString: string,
d: number
): string | null => {
if (d < 0) {
return null;
}
const split = floatString.split('.');
const left = split[0];
let right: string;
if (split.length === 2) {
right = split[1].slice(0, d).padEnd(d, '0');
} else {
right = ''.padEnd(d, '0');
}
try {
return left + '.' + right;
} catch (_e) {
return null;
}
};
54 changes: 54 additions & 0 deletions src/services/common-interfaces.ts
Original file line number Diff line number Diff line change
@@ -91,6 +91,15 @@ import { NearBase } from '../chains/near/near.base';
import { Account, Contract as NearContract } from 'near-api-js';
import { EstimateSwapView, TokenMetadata } from 'coinalpha-ref-sdk';
import { FinalExecutionOutcome } from 'near-api-js/lib/providers';
import {
ClobDeleteOrderRequest,
ClobGetOrderRequest,
ClobGetOrderResponse,
ClobMarketsRequest,
ClobOrderbookRequest,
ClobPostOrderRequest,
ClobTickerRequest,
} from '../clob/clob.requests';

// TODO Check the possibility to have clob/solana/serum equivalents here
// Check this link https://hummingbot.org/developers/gateway/building-gateway-connectors/#5-add-sdk-classes-to-uniswapish-interface
@@ -618,6 +627,51 @@ export interface Ethereumish extends BasicChainMethods, EthereumBase {
): Contract;
}

export interface PriceLevel {
price: string;
quantity: string;
timestamp: number;
}
export interface Orderbook {
buys: PriceLevel[];
sells: PriceLevel[];
}

export interface MarketInfo {
[key: string]: any;
}

export interface CLOBish {
parsedMarkets: MarketInfo;

loadMarkets(): Promise<void>;

init(): Promise<void>;

ready(): boolean;

markets(req: ClobMarketsRequest): Promise<{ markets: MarketInfo }>;

orderBook(req: ClobOrderbookRequest): Promise<Orderbook>;

ticker(req: ClobTickerRequest): Promise<{ markets: MarketInfo }>;

orders(
req: ClobGetOrderRequest
): Promise<{ orders: ClobGetOrderResponse['orders'] }>;

postOrder(req: ClobPostOrderRequest): Promise<{ txHash: string }>;

deleteOrder(req: ClobDeleteOrderRequest): Promise<{ txHash: string }>;

estimateGas(_req: NetworkSelectionRequest): {
gasPrice: number;
gasPriceToken: string;
gasLimit: number;
gasCost: number;
};
}

export interface Nearish extends BasicChainMethods, NearBase {
cancelTx(account: Account, nonce: number): Promise<string>;
getContract(tokenAddress: string, account: Account): NearContract;
15 changes: 7 additions & 8 deletions src/services/connection-manager.ts
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@ import { VVSConnector } from '../connectors/vvs/vvs';
import { InjectiveCLOB } from '../connectors/injective/injective';
import { Injective } from '../chains/injective/injective';
import {
CLOBish,
Ethereumish,
Nearish,
Perpish,
@@ -29,6 +30,7 @@ import { Defikingdoms } from '../connectors/defikingdoms/defikingdoms';
import { Defira } from '../connectors/defira/defira';
import { Near } from '../chains/near/near';
import { Ref } from '../connectors/ref/ref';
import { DexalotCLOB } from '../connectors/dexalot/dexalot';

export type ChainUnion = Ethereumish | Nearish | Injective;

@@ -66,12 +68,7 @@ export async function getChain<T>(
return chainInstance as Chain<T>;
}

type ConnectorUnion =
| Uniswapish
| UniswapLPish
| Perpish
| RefAMMish
| InjectiveCLOB;
type ConnectorUnion = Uniswapish | UniswapLPish | Perpish | RefAMMish | CLOBish;

export type Connector<T> = T extends Uniswapish
? Uniswapish
@@ -81,8 +78,8 @@ export type Connector<T> = T extends Uniswapish
? Perpish
: T extends RefAMMish
? RefAMMish
: T extends InjectiveCLOB
? InjectiveCLOB
: T extends CLOBish
? CLOBish
: never;

export async function getConnector<T>(
@@ -129,6 +126,8 @@ export async function getConnector<T>(
connectorInstance = Sushiswap.getInstance(chain, network);
} else if (chain === 'injective' && connector === 'injective') {
connectorInstance = InjectiveCLOB.getInstance(chain, network);
} else if (chain === 'avalanche' && connector === 'dexalot') {
connectorInstance = DexalotCLOB.getInstance(network);
} else {
throw new Error('unsupported chain or connector');
}
41 changes: 41 additions & 0 deletions src/services/schema/dexalot-schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"allowedSlippage": { "type": "string" },
"gasLimitEstimate": { "type": "integer" },
"ttl": { "type": "integer" },
"contractAddresses": {
"type": "object",
"patternProperties": {
"^\\w+$": {
"type": "object",
"properties": {
"ExchangeSub": { "type": "string" },
"PortfolioSub": { "type": "string" },
"OrderBooks": { "type": "string" },
"TradePairs": { "type": "string" },
"GasStation": { "type": "string" }
},
"required": [
"ExchangeSub",
"PortfolioSub",
"OrderBooks",
"TradePairs",
"GasStation"
],
"additionalProperties": false
}
},
"additionalProperties": false
}
},
"additionalProperties": false,
"required": [
"allowedSlippage",
"gasLimitEstimate",
"ttl",
"contractAddresses"
]
}

11 changes: 9 additions & 2 deletions src/templates/avalanche.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
# list the Avalanche networks available to gateway
networks:
fuji:
fuji:
chainID: 43113
nodeURL: https://rpc.ankr.com/avalanche_fuji
tokenListType: 'FILE'
tokenListSource: 'src/chains/avalanche/avalanche_tokens_fuji.json'
nativeCurrencySymbol: 'AVAX'
gasPriceRefreshInterval: 60
avalanche:
avalanche:
chainID: 43114
nodeURL: https://rpc.ankr.com/avalanche
tokenListType: 'FILE'
tokenListSource: 'src/chains/avalanche/avanlanche_tokens.json'
nativeCurrencySymbol: 'AVAX'
gasPriceRefreshInterval: 60
dexalot:
chainID: 432204
nodeURL: https://subnets.avax.network/dexalot/mainnet/rpc
tokenListType: 'FILE'
tokenListSource: 'src/chains/avalanche/avanlanche_tokens.json'
nativeCurrencySymbol: 'ALOT'
gasPriceRefreshInterval: 60

manualGasPrice: 100
gasLimitTransaction: 3000000
18 changes: 18 additions & 0 deletions src/templates/dexalot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# how much the execution price is allowed to move unfavorably from the trade
# execution price. It uses a rational number for precision.
allowedSlippage: '2/100'

# the maximum gas used to estimate gasCost for a transaction.
gasLimitEstimate: 150688

# how long a trade is valid in seconds. After time passes uniswap will not
# perform the trade, but the gas will still be sent.
ttl: 600

contractAddresses:
dexalot:
ExchangeSub: '0x1FD108cf42A59c635bD4703b8DbC8a741ff834Be'
PortfolioSub: '0xa5C079C1986E2335d83fA2d7282e162958e515D5'
OrderBooks: '0xd742da75D37f526Ee9e3A4BA25F8753CE5c89304'
TradePairs: '0x09383137C1eEe3E1A8bc781228E4199f6b4A9bbf'
GasStation: '0xFfD74FD04E66Cc80990eA0BCA36E42462C7A804C'
4 changes: 4 additions & 0 deletions src/templates/root.yml
Original file line number Diff line number Diff line change
@@ -16,6 +16,10 @@ configurations:
configurationPath: avalanche.yml
schemaPath: ethereum-schema.json

$namespace dexalot:
configurationPath: dexalot.yml
schemaPath: dexalot-schema.json

$namespace polygon:
configurationPath: polygon.yml
schemaPath: ethereum-schema.json
6 changes: 6 additions & 0 deletions test-helpers/curl/curl.sh
Original file line number Diff line number Diff line change
@@ -258,3 +258,9 @@ curl -s -X POST -k --key $GATEWAY_KEY --cert $GATEWAY_CERT -H "Content-Type: app
curl -s -X POST -k --key $GATEWAY_KEY --cert $GATEWAY_CERT -H "Content-Type: application/json" -d "$(envsubst < ./requests/injective_transfer_to_bank.json)" https://localhost:15888/injective/transfer/to/bank | jq

curl -s -X POST -k --key $GATEWAY_KEY --cert $GATEWAY_CERT -H "Content-Type: application/json" -d "$(envsubst < ./requests/injective_transfer_to_sub.json)" https://localhost:15888/injective/transfer/to/sub | jq

# dexalot

curl % curl -s -X GET -k --key $GATEWAY_KEY --cert $GATEWAY_CERT "https://localhost:15888/clob/markets?chain=avalanche&network=dexalot&connector=dexalot&market=ALOT-USDC" | jq

curl -s -X GET -k --key $GATEWAY_KEY --cert $GATEWAY_CERT "https://localhost:15888/clob/orderBook?chain=avalanche&network=dexalot&connector=dexalot&market=ALOT-USDC" | jq