This repository has been archived by the owner on Jan 29, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add OrderBook entity
- Loading branch information
Valentin Stavetski
committed
Apr 28, 2018
1 parent
8344285
commit 9700c0a
Showing
7 changed files
with
256 additions
and
193 deletions.
There are no files selected for viewing
7 changes: 7 additions & 0 deletions
7
common/src/main/kotlin/fund/cyber/markets/common/model/OrderBook.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package fund.cyber.markets.common.model | ||
|
||
data class OrderBook( | ||
val asks: MutableList<Order>, | ||
val bids: MutableList<Order>, | ||
val timestamp: Long | ||
) |
19 changes: 0 additions & 19 deletions
19
...r/src/main/kotlin/fund/cyber/markets/connector/configuration/ConnectorAppConfiguration.kt
This file was deleted.
Oops, something went wrong.
30 changes: 30 additions & 0 deletions
30
...ctor/src/main/kotlin/fund/cyber/markets/connector/configuration/ConnectorConfiguration.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package fund.cyber.markets.connector.configuration | ||
|
||
import fund.cyber.markets.common.EXCHANGES | ||
import fund.cyber.markets.common.EXCHANGES_DEFAULT | ||
import fund.cyber.markets.common.PARITY_URL | ||
import fund.cyber.markets.common.PARITY_URL_DEFAULT | ||
import org.springframework.beans.factory.annotation.Value | ||
import org.springframework.context.annotation.Bean | ||
import org.springframework.context.annotation.Configuration | ||
import org.web3j.protocol.Web3j | ||
import org.web3j.protocol.http.HttpService | ||
import org.web3j.utils.Async | ||
|
||
const val WEB3J_POLLING_INTERVAL = 5 * 1000L | ||
|
||
@Configuration | ||
class ConnectorConfiguration( | ||
@Value("\${$EXCHANGES:$EXCHANGES_DEFAULT}") | ||
private val exchangesProperty: String, | ||
|
||
@Value("\${$PARITY_URL:$PARITY_URL_DEFAULT}") | ||
val parityUrl: String | ||
) { | ||
val exchanges: Set<String> = exchangesProperty.split(",").map { it.trim().toUpperCase() }.toSet() | ||
|
||
@Bean | ||
fun web3j(): Web3j { | ||
return Web3j.build(HttpService(parityUrl), WEB3J_POLLING_INTERVAL, Async.defaultExecutorService()) | ||
} | ||
} |
196 changes: 196 additions & 0 deletions
196
...nector/src/main/kotlin/fund/cyber/markets/connector/etherdelta/EtherdeltaTokenResolver.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
package fund.cyber.markets.connector.etherdelta | ||
|
||
import com.fasterxml.jackson.databind.JsonNode | ||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import com.fasterxml.jackson.module.kotlin.readValue | ||
import org.apache.commons.io.IOUtils | ||
import org.slf4j.LoggerFactory | ||
import org.springframework.beans.factory.annotation.Autowired | ||
import org.springframework.context.support.GenericApplicationContext | ||
import org.springframework.stereotype.Component | ||
import org.web3j.protocol.Web3j | ||
import org.web3j.tx.Contract | ||
import org.web3j.tx.ReadonlyTransactionManager | ||
import java.math.BigInteger | ||
import java.net.URL | ||
import java.nio.charset.Charset | ||
import java.util.regex.Pattern | ||
|
||
private const val ETHERDELTA_CONFIG_URL = "https://raw.githubusercontent.com/etherdelta/etherdelta.github.io/master/config/main.json" | ||
private const val PARITY_TOKEN_REGISTRY_CONTRACT_ADDRESS = "0x5F0281910Af44bFb5fC7e86A404d0304B0e042F1" | ||
private const val PARITY_TOKEN_REGISTRY_EMPTY_ADDRESS = "0x0000000000000000000000000000000000000000" | ||
|
||
@Component | ||
class EtherdeltaTokenResolver { | ||
private val log = LoggerFactory.getLogger(EtherdeltaTokenResolver::class.java)!! | ||
|
||
private lateinit var exchangeTokensPairs: MutableMap<String, EtherdeltaToken> | ||
|
||
@Autowired | ||
private lateinit var web3j: Web3j | ||
|
||
@Autowired | ||
private lateinit var resourceLoader: GenericApplicationContext | ||
|
||
/** | ||
* Get ERC20 token definitions from different sources | ||
*/ | ||
fun updateTokensPairs() { | ||
log.info("Updating Etherdelta tokens pairs") | ||
|
||
val parityTokenRegistryTokens = getParityTokenRegistryTokens() | ||
val etherdeltaConfigTokens = getEtherdeltaConfigTokens() | ||
val myEtherWalletTokens = getMyEtherWalletTokens() | ||
exchangeTokensPairs = parityTokenRegistryTokens | ||
exchangeTokensPairs.putAll(etherdeltaConfigTokens) | ||
exchangeTokensPairs.putAll(myEtherWalletTokens) | ||
|
||
log.info("Etherdelta tokens pairs updated. Count: ${exchangeTokensPairs.size}") | ||
} | ||
|
||
/** | ||
* Get ERC20 token definitions from etherdelta configuration JSON file. | ||
* @return a map of ERC20 token address and token definition. | ||
*/ | ||
private fun getEtherdeltaConfigTokens(): MutableMap<String, EtherdeltaToken> { | ||
val mapper = ObjectMapper() | ||
val tokens = mutableMapOf<String, EtherdeltaToken>() | ||
val base = BigInteger.TEN | ||
|
||
try { | ||
val configTree = mapper.readTree(URL(ETHERDELTA_CONFIG_URL)) | ||
configTree.get("tokens").asIterable().forEach { tokenNode -> | ||
|
||
if (tokenNode.get("decimals").asInt() != 0) { | ||
val tokenContractAddress = tokenNode.get("addr").asText() | ||
val tokenSymbol = tokenNode.get("name").asText() | ||
val tokenDecimal = tokenNode.get("decimals").asInt() | ||
val tokenBase = base.pow(tokenDecimal) | ||
|
||
tokens[tokenContractAddress] = EtherdeltaToken(tokenSymbol, tokenBase, tokenDecimal) | ||
} | ||
} | ||
} catch (e: Exception) { | ||
log.warn("Cant get tokens definitions from Etherdelta config") | ||
} | ||
|
||
return tokens | ||
} | ||
|
||
/** | ||
* Get ERC20 token definitions from MyEtherWallet token list. | ||
* @return a map of ERC20 token address and token definition. | ||
*/ | ||
private fun getMyEtherWalletTokens(): MutableMap<String, EtherdeltaToken> { | ||
val mapper = ObjectMapper() | ||
val tokens = mutableMapOf<String, EtherdeltaToken>() | ||
val base = BigInteger.TEN | ||
|
||
try { | ||
val tokenDefinitionInputStream = resourceLoader.getResource("classpath:tokens-eth.json").inputStream | ||
val tokenDefinitionString = IOUtils.toString(tokenDefinitionInputStream, Charset.forName("UTF-8")) | ||
|
||
val tokenListTree = mapper.readValue<Iterable<JsonNode>>(tokenDefinitionString) | ||
tokenListTree.asIterable().forEach { tokenNode -> | ||
|
||
if (tokenNode.get("decimals").asInt() != 0) { | ||
val tokenContractAddress = tokenNode.get("address").asText() | ||
val tokenSymbol = tokenNode.get("symbol").asText() | ||
val tokenDecimal = tokenNode.get("decimals").asInt() | ||
val tokenBase = base.pow(tokenDecimal) | ||
|
||
tokens[tokenContractAddress] = EtherdeltaToken(tokenSymbol, tokenBase, tokenDecimal) | ||
} | ||
} | ||
} catch (e: Exception) { | ||
log.warn("Cant get tokens definitions from MyEtherWallet config") | ||
} | ||
|
||
return tokens | ||
} | ||
|
||
/** | ||
* Get ERC20 token definitions from parity token registry smart contract. | ||
* @return a map of ERC20 token address and token definition. | ||
*/ | ||
private fun getParityTokenRegistryTokens(): MutableMap<String, EtherdeltaToken> { | ||
val tokens = mutableMapOf<String, EtherdeltaToken>() | ||
|
||
try { | ||
val transactionManager = ReadonlyTransactionManager(web3j, PARITY_TOKEN_REGISTRY_CONTRACT_ADDRESS) | ||
val parityTokenRegistryContract = ParityTokenRegistryContract.load(PARITY_TOKEN_REGISTRY_CONTRACT_ADDRESS, | ||
web3j, transactionManager, Contract.GAS_PRICE, Contract.GAS_LIMIT) | ||
|
||
val tokensCount = parityTokenRegistryContract.tokenCount().send().toLong() | ||
for (index in 0 until tokensCount) { | ||
val token = parityTokenRegistryContract.token(BigInteger.valueOf(index)).send() | ||
|
||
if (token.value1 == PARITY_TOKEN_REGISTRY_EMPTY_ADDRESS || !validBase(token.value3.toString())) { | ||
continue | ||
} | ||
|
||
val tokenContractAddress = token.value1 | ||
val tokenSymbol = token.value2 | ||
val tokenBase = token.value3 | ||
val tokenDecimals = tokenBase.toString().length - 1 | ||
|
||
tokens[tokenContractAddress] = EtherdeltaToken(tokenSymbol, tokenBase, tokenDecimals) | ||
} | ||
} catch (e: Exception) { | ||
log.warn("Cant get parity token registry tokens") | ||
} | ||
|
||
return tokens | ||
} | ||
|
||
/** | ||
* Get ERC20 token definitions from its own smart contract. | ||
* @return a map of ERC20 token address and token definition. | ||
*/ | ||
private fun getTokenDefinitionByAddress(address: String): EtherdeltaToken? { | ||
var tokenDefinition: EtherdeltaToken? = null | ||
|
||
try { | ||
val transactionManager = ReadonlyTransactionManager(web3j, address) | ||
val erc20Contract = Erc20Contract.load(address, web3j, transactionManager, Contract.GAS_PRICE, Contract.GAS_LIMIT) | ||
|
||
val tokenSymbol = erc20Contract.symbol().send().trim() | ||
val tokenDecimals = erc20Contract.decimals().send().intValueExact() | ||
val tokenBase = BigInteger.TEN.pow(tokenDecimals) | ||
|
||
tokenDefinition = EtherdeltaToken(tokenSymbol, tokenBase, tokenDecimals) | ||
|
||
log.info("Resolve new token: symbol=$tokenSymbol, decimals=$tokenDecimals") | ||
exchangeTokensPairs[address] = tokenDefinition | ||
} catch (e: Exception) { | ||
log.info("Cant get token definition from address: $address") | ||
} | ||
|
||
return tokenDefinition | ||
} | ||
|
||
fun resolveToken(address: String?): EtherdeltaToken? { | ||
val tokenDefiniton = exchangeTokensPairs[address] | ||
|
||
if (tokenDefiniton == null && address != null) { | ||
getTokenDefinitionByAddress(address) | ||
} | ||
|
||
return tokenDefiniton | ||
} | ||
|
||
/** | ||
* Check that @param tokenBase is valid power of 10. | ||
* We need check this because parity token registry contains not valid data. | ||
* @return boolean result | ||
*/ | ||
private fun validBase(tokenBase: String): Boolean { | ||
val pattern = Pattern.compile("10*") | ||
val matcher = pattern.matcher(tokenBase) | ||
|
||
val result = (matcher.regionEnd() - matcher.regionStart()) == tokenBase.length || tokenBase == "0" | ||
|
||
return result | ||
} | ||
|
||
} |
2 changes: 2 additions & 0 deletions
2
...es-connector/src/main/kotlin/fund/cyber/markets/connector/orderbook/OrderbookConnector.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,11 @@ | ||
package fund.cyber.markets.connector.orderbook | ||
|
||
import fund.cyber.markets.common.model.TokensPair | ||
import fund.cyber.markets.connector.Connector | ||
import org.knowm.xchange.currency.CurrencyPair | ||
import org.knowm.xchange.dto.marketdata.OrderBook | ||
|
||
interface OrderbookConnector : Connector { | ||
var orderbooks: MutableMap<CurrencyPair, OrderBook> | ||
fun getOrderBookSnapshot(pair: TokensPair): fund.cyber.markets.common.model.OrderBook | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.