diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 17152d073..46282070c 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -25,7 +25,10 @@ jobs: - name: Build run: mvn -B -T 1C clean install - name: Build Docker images - run: TAG=dev docker-compose -f docker-compose.build.yml build + env: + TAG: dev + PREFERENCES_IDENTIFIER: dev + run: docker-compose -f docker-compose.build.yml build - name: Login to GitHub Container Registry uses: docker/login-action@v1 with: @@ -33,4 +36,7 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Push images to GitHub Container Registry - run: TAG=dev docker-compose -f docker-compose.build.yml push + env: + TAG: dev + PREFERENCES_IDENTIFIER: dev + run: docker-compose -f docker-compose.build.yml push diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 69b0c85c5..7ffad3312 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,7 +25,10 @@ jobs: - name: Build run: mvn -B -T 1C clean install - name: Build Docker images - run: TAG=latest docker-compose -f docker-compose.build.yml build + env: + TAG: latest + PREFERENCES_IDENTIFIER: dev + run: docker-compose -f docker-compose.build.yml build - name: Login to GitHub Container Registry uses: docker/login-action@v1 with: @@ -33,4 +36,7 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Push images to GitHub Container Registry - run: TAG=latest docker-compose -f docker-compose.build.yml push + env: + TAG: dev + PREFERENCES_IDENTIFIER: demo + run: docker-compose -f docker-compose.build.yml push diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 3911048fd..206221671 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -24,4 +24,7 @@ jobs: - name: Build run: mvn -B -T 1C clean install - name: Build Docker images - run: TAG=latest docker-compose -f docker-compose.build.yml build + env: + TAG: dev + PREFERENCES_IDENTIFIER: dev + run: docker-compose -f docker-compose.build.yml build diff --git a/accountant/accountant-app/pom.xml b/accountant/accountant-app/pom.xml index aa8f7f94d..835465fa0 100644 --- a/accountant/accountant-app/pom.xml +++ b/accountant/accountant-app/pom.xml @@ -63,6 +63,10 @@ org.springframework.boot spring-boot-starter-actuator + + co.nilin.opex.utility.preferences + preferences + diff --git a/accountant/accountant-app/src/main/kotlin/co/nilin/opex/accountant/app/config/InitializeService.kt b/accountant/accountant-app/src/main/kotlin/co/nilin/opex/accountant/app/config/InitializeService.kt new file mode 100644 index 000000000..71c857eb0 --- /dev/null +++ b/accountant/accountant-app/src/main/kotlin/co/nilin/opex/accountant/app/config/InitializeService.kt @@ -0,0 +1,54 @@ +package co.nilin.opex.accountant.app.config + +import co.nilin.opex.accountant.ports.postgres.dao.PairConfigRepository +import co.nilin.opex.accountant.ports.postgres.dao.PairFeeConfigRepository +import co.nilin.opex.accountant.ports.postgres.model.PairFeeConfigModel +import co.nilin.opex.utility.preferences.Preferences +import kotlinx.coroutines.reactor.awaitSingleOrNull +import kotlinx.coroutines.runBlocking +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.context.annotation.DependsOn +import org.springframework.stereotype.Component + +@Component +@DependsOn("postgresConfig") +class InitializeService( + private val pairConfigRepository: PairConfigRepository, + private val pairFeeConfigRepository: PairFeeConfigRepository +) { + @Autowired + private lateinit var preferences: Preferences + + @Autowired + fun init() = runBlocking { + preferences.markets.map { + val pair = it.pair ?: "${it.leftSide}_${it.rightSide}" + val leftSideCurrency = preferences.currencies.first { c -> it.leftSide == c.symbol } + val rightSideCurrency = preferences.currencies.first { c -> it.rightSide == c.symbol } + val leftSideFraction = (it.leftSideFraction ?: leftSideCurrency.precision).toDouble() + val rightSideFraction = (it.rightSideFraction ?: rightSideCurrency.precision).toDouble() + pairConfigRepository.insert( + pair, + it.leftSide, + it.rightSide, + leftSideFraction, + rightSideFraction, + 0.0 + ).awaitSingleOrNull() + it.feeConfigs.forEach { f -> + runCatching { + pairFeeConfigRepository.save( + PairFeeConfigModel( + null, + pair, + f.direction, + f.userLevel, + f.makerFee.toDouble(), + f.takerFee.toDouble() + ) + ).awaitSingleOrNull() + } + } + } + } +} diff --git a/accountant/accountant-app/src/main/resources/application.yml b/accountant/accountant-app/src/main/resources/application.yml index 6e5e58484..9bf8bbb6a 100644 --- a/accountant/accountant-app/src/main/resources/application.yml +++ b/accountant/accountant-app/src/main/resources/application.yml @@ -45,7 +45,7 @@ spring: config: import: vault://secret/${spring.application.name} app: - coin: nln + coin: IRT address: 1 wallet: url: lb://opex-wallet/ diff --git a/accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/accountant/ports/postgres/config/PostgresConfig.kt b/accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/accountant/ports/postgres/config/PostgresConfig.kt index 10fe331da..dd0fbf78e 100644 --- a/accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/accountant/ports/postgres/config/PostgresConfig.kt +++ b/accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/accountant/ports/postgres/config/PostgresConfig.kt @@ -11,17 +11,13 @@ import org.springframework.r2dbc.core.DatabaseClient @EnableR2dbcRepositories(basePackages = ["co.nilin.opex"]) class PostgresConfig( db: DatabaseClient, - @Value("classpath:schema.sql") private val schemaResource: Resource, - @Value("classpath:data.sql") private val dataResource: Resource? + @Value("classpath:schema.sql") private val schemaResource: Resource ) { init { val schemaReader = schemaResource.inputStream.reader() val schema = schemaReader.readText().trim() schemaReader.close() - val dataReader = dataResource?.inputStream?.reader() - val data = dataReader?.readText()?.trim() ?: "" - dataReader?.close() - val initDb = db.sql { schema.plus(data) } + val initDb = db.sql { schema } initDb // initialize the database .then() .subscribe() // execute diff --git a/accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/accountant/ports/postgres/dao/PairConfigRepository.kt b/accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/accountant/ports/postgres/dao/PairConfigRepository.kt index 9f76a937b..c3d9ca417 100644 --- a/accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/accountant/ports/postgres/dao/PairConfigRepository.kt +++ b/accountant/accountant-ports/accountant-persister-postgres/src/main/kotlin/co/nilin/opex/accountant/ports/postgres/dao/PairConfigRepository.kt @@ -1,8 +1,20 @@ package co.nilin.opex.accountant.ports.postgres.dao import co.nilin.opex.accountant.ports.postgres.model.PairConfigModel +import org.springframework.data.r2dbc.repository.Query import org.springframework.data.repository.reactive.ReactiveCrudRepository import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono @Repository -interface PairConfigRepository : ReactiveCrudRepository +interface PairConfigRepository : ReactiveCrudRepository { + @Query("insert into pair_config values (:pair, :leftSide, :rightSide, :leftSideFraction, :rightSideFraction, :rate) on conflict do nothing") + fun insert( + pair: String, + leftSide: String, + rightSide: String, + leftSideFraction: Double, + rightSideFraction: Double, + rate: Double + ): Mono +} diff --git a/accountant/accountant-ports/accountant-persister-postgres/src/main/resources/data.sql b/accountant/accountant-ports/accountant-persister-postgres/src/main/resources/data.sql deleted file mode 100644 index 0b209872f..000000000 --- a/accountant/accountant-ports/accountant-persister-postgres/src/main/resources/data.sql +++ /dev/null @@ -1,62 +0,0 @@ -INSERT INTO pair_config -VALUES ('btc_usdt', 'btc', 'usdt', 0.000001, 0.01, 55000), - ('eth_usdt', 'eth', 'usdt', 0.00001, 0.01, 3800), - ('btc_irt', 'btc', 'irt', 0.000001, 0.01, 55000), - ('eth_irt', 'eth', 'irt', 0.00001, 0.01, 3800), - ('bnb_irt', 'bnb', 'irt', 0.00001, 0.01, 3800), - ('bnb_usdt', 'bnb', 'usdt', 0.00001, 0.01, 3800), - ('busd_irt', 'busd', 'irt', 0.00001, 0.01, 3800), - ('nln_usdt', 'nln', 'usdt', 1.0, 0.01, 0.01), - ('nln_btc', 'nln', 'btc', 1.0, 0.000001, 1 / 5500000), - ('nln_eth', 'nln', 'eth', 1.0, 0.000001, 1 / 550000) -ON CONFLICT DO NOTHING; - --- Test pairs -INSERT INTO pair_config -VALUES ('tbtc_tusdt', 'tbtc', 'tusdt', 0.000001, 0.01, 55000), - ('teth_tusdt', 'teth', 'tusdt', 0.00001, 0.01, 3800), - ('nln_tusdt', 'nln', 'tusdt', 1.0, 0.01, 0.01), - ('nln_tbtc', 'nln', 'tbtc', 1.0, 0.000001, 1 / 5500000), - ('nln_teth', 'nln', 'teth', 1.0, 0.000001, 1 / 550000) -ON CONFLICT DO NOTHING; - -INSERT INTO pair_fee_config -VALUES (1, 'btc_usdt', 'ASK', '*', 0.01, 0.01), - (2, 'btc_usdt', 'BID', '*', 0.01, 0.01), - (3, 'nln_usdt', 'ASK', '*', 0.01, 0.01), - (4, 'nln_usdt', 'BID', '*', 0.01, 0.01), - (5, 'nln_btc', 'ASK', '*', 0.01, 0.01), - (6, 'nln_btc', 'BID', '*', 0.01, 0.01), - (7, 'eth_usdt', 'ASK', '*', 0.01, 0.01), - (8, 'eth_usdt', 'BID', '*', 0.01, 0.01), - (9, 'nln_eth', 'ASK', '*', 0.01, 0.01), - (10, 'nln_eth', 'BID', '*', 0.01, 0.01), - (11, 'btc_irt', 'ASK', '*', 0.01, 0.01), - (12, 'btc_irt', 'BID', '*', 0.01, 0.01), - (13, 'eth_irt', 'ASK', '*', 0.01, 0.01), - (14, 'eth_irt', 'BID', '*', 0.01, 0.01), - (15, 'bnb_irt', 'ASK', '*', 0.01, 0.01), - (16, 'bnb_irt', 'BID', '*', 0.01, 0.01), - (17, 'bnb_usdt', 'ASK', '*', 0.01, 0.01), - (18, 'bnb_usdt', 'BID', '*', 0.01, 0.01), - (19, 'busd_irt', 'ASK', '*', 0.01, 0.01), - (20, 'busd_irt', 'BID', '*', 0.01, 0.01) -ON CONFLICT DO NOTHING; - --- Test pair configs -INSERT INTO pair_fee_config -VALUES (21, 'tbtc_tusdt', 'ASK', '*', 0.01, 0.01), - (22, 'tbtc_tusdt', 'BID', '*', 0.01, 0.01), - (23, 'nln_tusdt', 'ASK', '*', 0.01, 0.01), - (24, 'nln_tusdt', 'BID', '*', 0.01, 0.01), - (25, 'nln_tbtc', 'ASK', '*', 0.01, 0.01), - (26, 'nln_tbtc', 'BID', '*', 0.01, 0.01), - (27, 'teth_tusdt', 'ASK', '*', 0.01, 0.01), - (28, 'teth_tusdt', 'BID', '*', 0.01, 0.01), - (29, 'nln_teth', 'ASK', '*', 0.01, 0.01), - (30, 'nln_teth', 'BID', '*', 0.01, 0.01) -ON CONFLICT DO NOTHING; - -SELECT setval(pg_get_serial_sequence('pair_fee_config', 'id'), (SELECT MAX(id) FROM pair_fee_config)); - -COMMIT; diff --git a/accountant/pom.xml b/accountant/pom.xml index 1a20c1ff2..01387a319 100644 --- a/accountant/pom.xml +++ b/accountant/pom.xml @@ -73,6 +73,11 @@ logging-handler ${project.version} + + co.nilin.opex.utility.preferences + preferences + ${project.version} + org.springframework.cloud spring-cloud-dependencies diff --git a/api/api-app/pom.xml b/api/api-app/pom.xml index 00529ca01..f7324038b 100644 --- a/api/api-app/pom.xml +++ b/api/api-app/pom.xml @@ -68,6 +68,10 @@ org.springframework.cloud spring-cloud-starter-vault-config + + co.nilin.opex.utility.preferences + preferences + diff --git a/api/api-app/src/main/kotlin/co/nilin/opex/api/app/config/InitializeService.kt b/api/api-app/src/main/kotlin/co/nilin/opex/api/app/config/InitializeService.kt new file mode 100644 index 000000000..63a9291b3 --- /dev/null +++ b/api/api-app/src/main/kotlin/co/nilin/opex/api/app/config/InitializeService.kt @@ -0,0 +1,26 @@ +package co.nilin.opex.api.app.config + +import co.nilin.opex.api.ports.postgres.dao.SymbolMapRepository +import co.nilin.opex.api.ports.postgres.model.SymbolMapModel +import co.nilin.opex.utility.preferences.Preferences +import kotlinx.coroutines.reactor.awaitSingleOrNull +import kotlinx.coroutines.runBlocking +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.context.annotation.DependsOn +import org.springframework.stereotype.Component + +@Component +@DependsOn("postgresConfig") +class InitializeService(private val symbolMapRepository: SymbolMapRepository) { + @Autowired + private lateinit var preferences: Preferences + + @Autowired + fun init() = runBlocking { + preferences.markets.map { + val pair = it.pair ?: "${it.leftSide}_${it.rightSide}" + val items = it.aliases.map { a -> SymbolMapModel(null, pair, a.key, a.alias) } + runCatching { symbolMapRepository.saveAll(items).collectList().awaitSingleOrNull() } + } + } +} diff --git a/api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/SymbolMapper.kt b/api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/SymbolMapper.kt index 6e36e717c..893080836 100644 --- a/api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/SymbolMapper.kt +++ b/api/api-core/src/main/kotlin/co/nilin/opex/api/core/spi/SymbolMapper.kt @@ -4,7 +4,7 @@ interface SymbolMapper { suspend fun map(symbol: String?): String? - suspend fun unmap(value: String?): String? + suspend fun unmap(alias: String?): String? - suspend fun getKeyValues(): Map -} \ No newline at end of file + suspend fun getAll(): Map +} diff --git a/api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/api/ports/binance/controller/FiltersController.kt b/api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/api/ports/binance/controller/FiltersController.kt index 46db1b010..a8bf2d9ca 100644 --- a/api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/api/ports/binance/controller/FiltersController.kt +++ b/api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/api/ports/binance/controller/FiltersController.kt @@ -3,5 +3,4 @@ package co.nilin.opex.api.ports.binance.controller import org.springframework.web.bind.annotation.RestController @RestController -class FiltersController { -} \ No newline at end of file +class FiltersController diff --git a/api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/api/ports/binance/controller/MarketController.kt b/api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/api/ports/binance/controller/MarketController.kt index 92899a607..75f7177a0 100644 --- a/api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/api/ports/binance/controller/MarketController.kt +++ b/api/api-ports/api-binance-rest/src/main/kotlin/co/nilin/opex/api/ports/binance/controller/MarketController.kt @@ -141,15 +141,15 @@ class MarketController( @RequestParam("symbols", required = false) symbols: String? ): ExchangeInfoResponse { - val symbolsMap = symbolMapper.getKeyValues() + val symbolsMap = symbolMapper.getAll() val pairConfigs = accountantProxy.getPairConfigs() .map { ExchangeInfoSymbol( symbolsMap[it.pair] ?: it.pair, "TRADING", - it.leftSideWalletSymbol.toUpperCase(), + it.leftSideWalletSymbol.uppercase(), BigDecimal.valueOf(it.leftSideFraction).scale(), - it.rightSideWalletSymbol.toUpperCase(), + it.rightSideWalletSymbol.uppercase(), BigDecimal.valueOf(it.rightSideFraction).scale() ) } @@ -200,4 +200,4 @@ class MarketController( return list } -} \ No newline at end of file +} diff --git a/api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/api/ports/postgres/config/PostgresConfig.kt b/api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/api/ports/postgres/config/PostgresConfig.kt index a2924ed23..47e7038ae 100644 --- a/api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/api/ports/postgres/config/PostgresConfig.kt +++ b/api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/api/ports/postgres/config/PostgresConfig.kt @@ -10,17 +10,13 @@ import org.springframework.r2dbc.core.DatabaseClient @EnableR2dbcRepositories(basePackages = ["co.nilin.opex"]) class PostgresConfig( db: DatabaseClient, - @Value("classpath:schema.sql") private val schemaResource: Resource, - @Value("classpath:data.sql") private val dataResource: Resource? + @Value("classpath:schema.sql") private val schemaResource: Resource ) { init { val schemaReader = schemaResource.inputStream.reader() val schema = schemaReader.readText().trim() schemaReader.close() - val dataReader = dataResource?.inputStream?.reader() - val data = dataReader?.readText()?.trim() ?: "" - dataReader?.close() - val initDb = db.sql { schema.plus(data) } + val initDb = db.sql { schema } initDb // initialize the database .then() .subscribe() // execute diff --git a/api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/api/ports/postgres/dao/SymbolMapRepository.kt b/api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/api/ports/postgres/dao/SymbolMapRepository.kt index 4bc30a34a..0c1824218 100644 --- a/api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/api/ports/postgres/dao/SymbolMapRepository.kt +++ b/api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/api/ports/postgres/dao/SymbolMapRepository.kt @@ -9,10 +9,9 @@ import reactor.core.publisher.Mono @Repository interface SymbolMapRepository : ReactiveCrudRepository { + @Query("select * from symbol_maps where symbol = :symbol and aliasKey = :aliasKey") + fun findByAliasKeyAndSymbol(aliasKey: String, @Param("symbol") symbol: String): Mono - @Query("select * from symbol_maps where symbol = :symbol") - fun findBySymbol(@Param("symbol") symbol: String): Mono - - @Query("select * from symbol_maps where value = :value") - fun findByValue(@Param("value") value: String): Mono -} \ No newline at end of file + @Query("select * from symbol_maps where aliasKey = :aliasKey and alias = :alias") + fun findByAliasKeyAndAlias(aliasKey: String, @Param("alias") alias: String): Mono +} diff --git a/api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/api/ports/postgres/impl/SymbolMapperImpl.kt b/api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/api/ports/postgres/impl/SymbolMapperImpl.kt index d8f462be7..fd7194925 100644 --- a/api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/api/ports/postgres/impl/SymbolMapperImpl.kt +++ b/api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/api/ports/postgres/impl/SymbolMapperImpl.kt @@ -11,20 +11,21 @@ class SymbolMapperImpl(val symbolMapRepository: SymbolMapRepository) : SymbolMap override suspend fun map(symbol: String?): String? { if (symbol == null) return null - return symbolMapRepository.findBySymbol(symbol).awaitFirstOrNull()?.value + return symbolMapRepository.findByAliasKeyAndSymbol("binance", symbol).awaitFirstOrNull()?.alias } - override suspend fun unmap(value: String?): String? { - if (value == null) return null - return symbolMapRepository.findByValue(value).awaitFirstOrNull()?.symbol + override suspend fun unmap(alias: String?): String? { + if (alias == null) return null + return symbolMapRepository.findByAliasKeyAndAlias("binance", alias).awaitFirstOrNull()?.symbol } - override suspend fun getKeyValues(): Map { + override suspend fun getAll(): Map { val map = HashMap() symbolMapRepository.findAll() .collectList() .awaitFirstOrElse { emptyList() } - .forEach { map[it.symbol] = it.value } + .filter { it.aliasKey == "binance" } + .associate { it.symbol to it.alias } return map } -} \ No newline at end of file +} diff --git a/api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/api/ports/postgres/model/SymbolMapModel.kt b/api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/api/ports/postgres/model/SymbolMapModel.kt index 5897724ee..e72ed124a 100644 --- a/api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/api/ports/postgres/model/SymbolMapModel.kt +++ b/api/api-ports/api-persister-postgres/src/main/kotlin/co/nilin/opex/api/ports/postgres/model/SymbolMapModel.kt @@ -2,11 +2,12 @@ package co.nilin.opex.api.ports.postgres.model import org.springframework.data.annotation.Id -import org.springframework.data.relational.core.mapping.Column import org.springframework.data.relational.core.mapping.Table @Table("symbol_maps") class SymbolMapModel( - @Id val symbol: String, - @Column("value") val value: String, -) \ No newline at end of file + @Id var id: Long?, + val symbol: String, + val aliasKey: String, + val alias: String, +) diff --git a/api/api-ports/api-persister-postgres/src/main/resources/data.sql b/api/api-ports/api-persister-postgres/src/main/resources/data.sql deleted file mode 100644 index 803ee830b..000000000 --- a/api/api-ports/api-persister-postgres/src/main/resources/data.sql +++ /dev/null @@ -1,21 +0,0 @@ -INSERT INTO symbol_maps(symbol, value) -VALUES ('btc_usdt', 'BTCUSDT'), - ('eth_usdt', 'ETHUSDT'), - ('bnb_usdt', 'BNBUSDT'), - ('eth_btc', 'ETHBTC'), - ('nln_usdt', 'NLNUSDT'), - ('nln_btc', 'NLNBTC'), - ('btc_irt', 'BTCIRT'), - ('eth_irt', 'ETHIRT'), - ('bnb_irt', 'BNBIRT'), - ('busd_irt', 'BUSDIRT') -ON CONFLICT DO NOTHING; - --- Test symbol mapper -INSERT INTO symbol_maps(symbol, value) -VALUES ('tbtc_tusdt', 'TBTCTUSDT'), - ('teth_tusdt', 'TETHTUSDT'), - ('teth_tbtc', 'TETHTBTC'), - ('nln_tusdt', 'NLNTUSDT'), - ('nln_tbtc', 'NLNTBTC') -ON CONFLICT DO NOTHING; diff --git a/api/api-ports/api-persister-postgres/src/main/resources/schema.sql b/api/api-ports/api-persister-postgres/src/main/resources/schema.sql index 497ccb398..4c59f00ce 100644 --- a/api/api-ports/api-persister-postgres/src/main/resources/schema.sql +++ b/api/api-ports/api-persister-postgres/src/main/resources/schema.sql @@ -56,8 +56,11 @@ CREATE TABLE IF NOT EXISTS trades CREATE TABLE IF NOT EXISTS symbol_maps ( - symbol VARCHAR(72) PRIMARY KEY, - value VARCHAR(72) UNIQUE NOT NULL + id SERIAL PRIMARY KEY, + symbol VARCHAR(72) NOT NULL, + alias_key VARCHAR(72) NOT NULL, + alias VARCHAR(72) NOT NULL, + UNIQUE (symbol, alias_key, alias) ); CREATE OR REPLACE FUNCTION interval_generator( diff --git a/api/pom.xml b/api/pom.xml index 701612a51..ec4485c61 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -77,6 +77,11 @@ interceptors ${project.version} + + co.nilin.opex.utility.preferences + preferences + ${project.version} + org.springframework.cloud spring-cloud-dependencies diff --git a/bc-gateway/bc-gateway-app/pom.xml b/bc-gateway/bc-gateway-app/pom.xml index c8a5fa93b..7c66de544 100644 --- a/bc-gateway/bc-gateway-app/pom.xml +++ b/bc-gateway/bc-gateway-app/pom.xml @@ -81,6 +81,10 @@ co.nilin.opex.bcgateway.ports.kafka.listener bc-gateway-eventlistener-kafka + + co.nilin.opex.utility.preferences + preferences + io.springfox springfox-boot-starter diff --git a/bc-gateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/InitializeService.kt b/bc-gateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/InitializeService.kt new file mode 100644 index 000000000..9e4a1e6ff --- /dev/null +++ b/bc-gateway/bc-gateway-app/src/main/kotlin/co/nilin/opex/bcgateway/app/config/InitializeService.kt @@ -0,0 +1,96 @@ +package co.nilin.opex.bcgateway.app.config + +import co.nilin.opex.bcgateway.ports.postgres.dao.* +import co.nilin.opex.bcgateway.ports.postgres.model.* +import co.nilin.opex.utility.preferences.AddressType +import co.nilin.opex.utility.preferences.Chain +import co.nilin.opex.utility.preferences.Currency +import co.nilin.opex.utility.preferences.Preferences +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.reactor.awaitSingle +import kotlinx.coroutines.reactor.awaitSingleOrNull +import kotlinx.coroutines.runBlocking +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.context.annotation.DependsOn +import org.springframework.stereotype.Component +import java.time.LocalDateTime + +@Component +@DependsOn("postgresConfig") +class InitializeService( + private val addressTypeRepository: AddressTypeRepository, + private val chainRepository: ChainRepository, + private val chainAddressTypeRepository: ChainAddressTypeRepository, + private val chainEndpointRepository: ChainEndpointRepository, + private val currencyRepository: CurrencyRepository, + private val currencyImplementationRepository: CurrencyImplementationRepository, + private val chainSyncScheduleRepository: ChainSyncScheduleRepository, + private val walletSyncScheduleRepository: WalletSyncScheduleRepository +) { + @Autowired + private lateinit var preferences: Preferences + + @Autowired + fun init() = runBlocking { + addAddressTypes(preferences.addressTypes) + addChains(preferences.chains) + addCurrencies(preferences.currencies) + addSchedules(preferences) + } + + private suspend fun addAddressTypes(data: List) = coroutineScope { + val items = data.mapIndexed { i, it -> + if (addressTypeRepository.existsById(i + 1L).awaitSingle()) null + else AddressTypeModel(null, it.addressType, it.addressRegex, null) + }.filterNotNull() + runCatching { addressTypeRepository.saveAll(items).collectList().awaitSingleOrNull() } + } + + private suspend fun addChains(data: List) = coroutineScope { + data.map { chainRepository.insert(it.name).awaitSingleOrNull() } + val items1 = data.map { + val addressTypeId = addressTypeRepository.findByType(it.addressType).awaitSingle().id!! + ChainAddressTypeModel(null, it.name, addressTypeId) + } + runCatching { chainAddressTypeRepository.saveAll(items1).collectList().awaitSingleOrNull() } + val items2 = data.map { ChainEndpointModel(null, it.name, it.endpointUrl, null, null) } + runCatching { chainEndpointRepository.saveAll(items2).collectList().awaitSingleOrNull() } + } + + private suspend fun addCurrencies(data: List) = coroutineScope { + coroutineScope { + data.forEach { + currencyRepository.insert(it.name, it.symbol).awaitSingleOrNull() + } + } + val items = data.flatMap { it.implementations.map { impl -> it to impl } }.map { (currency, impl) -> + CurrencyImplementationModel( + null, + currency.symbol, + impl.chain, + impl.token, + impl.tokenAddress, + impl.tokenName, + impl.withdrawEnabled, + impl.withdrawFee, + impl.withdrawMin, + impl.decimal + ) + } + runCatching { currencyImplementationRepository.saveAll(items).collectList().awaitSingleOrNull() } + } + + private suspend fun addSchedules(data: Preferences) = coroutineScope { + data.chains.map { + chainSyncScheduleRepository.insert(it.name, it.schedule.delay.toInt(), it.schedule.errorDelay.toInt()) + .awaitSingleOrNull() + } + if (walletSyncScheduleRepository.existsById(1).awaitSingle()) null + else { + val item = WalletSyncScheduleModel( + null, LocalDateTime.now(), data.wallet.schedule.delay, data.wallet.schedule.batchSize + ) + runCatching { walletSyncScheduleRepository.save(item).awaitSingleOrNull() } + } + } +} diff --git a/bc-gateway/bc-gateway-ports/bc-gateway-persister-postgres/src/main/kotlin/co/nilin/opex/bcgateway/ports/postgres/config/PostgresConfig.kt b/bc-gateway/bc-gateway-ports/bc-gateway-persister-postgres/src/main/kotlin/co/nilin/opex/bcgateway/ports/postgres/config/PostgresConfig.kt index 0b6367666..c1dae05d6 100644 --- a/bc-gateway/bc-gateway-ports/bc-gateway-persister-postgres/src/main/kotlin/co/nilin/opex/bcgateway/ports/postgres/config/PostgresConfig.kt +++ b/bc-gateway/bc-gateway-ports/bc-gateway-persister-postgres/src/main/kotlin/co/nilin/opex/bcgateway/ports/postgres/config/PostgresConfig.kt @@ -10,17 +10,13 @@ import org.springframework.r2dbc.core.DatabaseClient @EnableR2dbcRepositories(basePackages = ["co.nilin.opex"]) class PostgresConfig( db: DatabaseClient, - @Value("classpath:schema.sql") private val schemaResource: Resource, - @Value("classpath:data.sql") private val dataResource: Resource? + @Value("classpath:schema.sql") private val schemaResource: Resource ) { init { val schemaReader = schemaResource.inputStream.reader() val schema = schemaReader.readText().trim() schemaReader.close() - val dataReader = dataResource?.inputStream?.reader() - val data = dataReader?.readText()?.trim() ?: "" - dataReader?.close() - val initDb = db.sql { schema.plus(data) } + val initDb = db.sql { schema } initDb // initialize the database .then() .subscribe() // execute diff --git a/bc-gateway/bc-gateway-ports/bc-gateway-persister-postgres/src/main/resources/data.sql b/bc-gateway/bc-gateway-ports/bc-gateway-persister-postgres/src/main/resources/data.sql deleted file mode 100644 index acad3fe8b..000000000 --- a/bc-gateway/bc-gateway-ports/bc-gateway-persister-postgres/src/main/resources/data.sql +++ /dev/null @@ -1,112 +0,0 @@ -INSERT INTO currency -VALUES ('BTC', 'Bitcoin'), - ('ETH', 'Ethereum'), - ('USDT', 'Tether'), - ('IRT', 'Toman'), - ('BNB', 'Binance'), - ('BUSD', 'Binance USD') -ON CONFLICT DO NOTHING; - --- Test currency -INSERT INTO currency -VALUES ('TBTC', 'Bitcoin (Test)'), - ('TETH', 'Ethereum (Test)'), - ('TUSDT', 'Tether (Test)') -ON CONFLICT DO NOTHING; - -INSERT INTO chains -VALUES ('bitcoin'), - ('ethereum'), - ('bsc') -ON CONFLICT DO NOTHING; - --- Test chains -INSERT INTO chains -VALUES ('test-bitcoin'), - ('test-ethereum') -ON CONFLICT DO NOTHING; - -INSERT INTO address_types(id, address_type, address_regex) -VALUES (1, 'bitcoin', '.*'), - (2, 'ethereum', '.*'), - (3, 'test-bitcoin', '.*') -ON CONFLICT DO NOTHING; - -SELECT setval(pg_get_serial_sequence('address_types', 'id'), (SELECT MAX(id) FROM address_types)); - -INSERT INTO chain_address_types(chain_name, addr_type_id) -VALUES ('bitcoin', 1), - ('ethereum', 2), - ('bsc', 2) -ON CONFLICT DO NOTHING; - --- Test chain address types -INSERT INTO chain_address_types(chain_name, addr_type_id) -VALUES ('test-bitcoin', 3), - ('test-ethereum', 2) -ON CONFLICT DO NOTHING; - -INSERT INTO currency_implementations(id, - symbol, - chain, - token, - token_address, - token_name, - withdraw_enabled, - withdraw_fee, - withdraw_min, - decimal) -VALUES (1, 'BTC', 'bitcoin', false, null, null, true, 0.0001, 0.0001, 0), - (2, 'ETH', 'ethereum', false, null, null, true, 0.00001, 0.000001, 18), - (3, 'USDT', 'ethereum', true, '0xdac17f958d2ee523a2206206994597c13d831ec7', 'USDT', true, 0.01, 0.01, 6), - (4, 'BNB', 'bsc', false, null, null, true, 0.0001, 0.0001, 0), - (5, 'BUSD', 'bsc', true, '0xe9e7cea3dedca5984780bafc599bd69add087d56', 'BUSD', true, 0.01, 0.01, 6) -ON CONFLICT DO NOTHING; - --- Test currency implementation -INSERT INTO currency_implementations(id, - symbol, - chain, - token, - token_address, - token_name, - withdraw_enabled, - withdraw_fee, - withdraw_min, - decimal) -VALUES (6, 'TBTC', 'test-bitcoin', false, null, null, true, 0.0001, 0.0001, 0), - (7, 'TETH', 'test-ethereum', false, null, null, true, 0.00001, 0.000001, 18), - (8, 'TUSDT', 'test-ethereum', true, '0x110a13fc3efe6a245b50102d2d79b3e76125ae83', 'TUSDT', true, 0.01, 0.01, 6) -ON CONFLICT DO NOTHING; - -SELECT setval(pg_get_serial_sequence('currency_implementations', 'id'), (SELECT MAX(id) FROM currency_implementations)); - -INSERT INTO chain_endpoints(id, chain_name, endpoint_url) -VALUES (1, 'bitcoin', 'lb://chain-scan-gateway/bitcoin/transfers'), - (2, 'ethereum', 'lb://chain-scan-gateway/eth/transfers'), - (3, 'bsc', 'lb://chain-scan-gateway/bsc/transfers') -ON CONFLICT DO NOTHING; - --- Test chain endpoints -INSERT INTO chain_endpoints(id, chain_name, endpoint_url) -VALUES (4, 'test-bitcoin', 'lb://chain-scan-gateway/test-bitcoin/transfers'), - (5, 'test-ethereum', 'lb://chain-scan-gateway/test-eth/transfers') -ON CONFLICT DO NOTHING; - -SELECT setval(pg_get_serial_sequence('chain_endpoints', 'id'), (SELECT MAX(id) FROM chain_endpoints)); - -INSERT INTO chain_sync_schedules -VALUES ('bitcoin', CURRENT_DATE, 600, 60), - ('ethereum', CURRENT_DATE, 90, 60), - ('bsc', CURRENT_DATE, 90, 60) -ON CONFLICT DO NOTHING; - --- Test chain scan schedules -INSERT INTO chain_sync_schedules -VALUES ('test-bitcoin', CURRENT_DATE, 600, 60), - ('test-ethereum', CURRENT_DATE, 90, 60) -ON CONFLICT DO NOTHING; - -INSERT INTO wallet_sync_schedules -VALUES (1, CURRENT_DATE, 10, 10000) -ON CONFLICT DO NOTHING; diff --git a/bc-gateway/bc-gateway-ports/bc-gateway-persister-postgres/src/main/resources/schema.sql b/bc-gateway/bc-gateway-ports/bc-gateway-persister-postgres/src/main/resources/schema.sql index 9ee61fab8..6733d6103 100644 --- a/bc-gateway/bc-gateway-ports/bc-gateway-persister-postgres/src/main/resources/schema.sql +++ b/bc-gateway/bc-gateway-ports/bc-gateway-persister-postgres/src/main/resources/schema.sql @@ -40,7 +40,7 @@ CREATE TABLE IF NOT EXISTS assigned_address_chains CREATE TABLE IF NOT EXISTS chain_address_types ( id SERIAL PRIMARY KEY, - chain_name VARCHAR(72) NOT NULL REFERENCES chains (name), + chain_name VARCHAR(72) UNIQUE NOT NULL REFERENCES chains (name), addr_type_id INTEGER NOT NULL REFERENCES address_types (id) ); diff --git a/bc-gateway/pom.xml b/bc-gateway/pom.xml index 3c408f018..6fc01550a 100644 --- a/bc-gateway/pom.xml +++ b/bc-gateway/pom.xml @@ -72,6 +72,11 @@ interceptors ${project.version} + + co.nilin.opex.utility.preferences + preferences + ${project.version} + diff --git a/docker-compose.override.yml b/docker-compose.override.yml new file mode 100644 index 000000000..647bb9cbc --- /dev/null +++ b/docker-compose.override.yml @@ -0,0 +1,17 @@ +version: '3.8' +services: + accountant: + volumes: + - "./preferences-$PREFERENCES_IDENTIFIER.yml:/preferences.yml" + matching-engine: + volumes: + - "./preferences-$PREFERENCES_IDENTIFIER.yml:/preferences.yml" + wallet: + volumes: + - "./preferences-$PREFERENCES_IDENTIFIER.yml:/preferences.yml" + api: + volumes: + - "./preferences-$PREFERENCES_IDENTIFIER.yml:/preferences.yml" + bc-gateway: + volumes: + - "./preferences-$PREFERENCES_IDENTIFIER.yml:/preferences.yml" diff --git a/docker-compose.yml b/docker-compose.yml index 1a50b6fb9..23226d405 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -256,6 +256,7 @@ services: - BACKEND_USER=${BACKEND_USER} - VAULT_HOST=vault - SWAGGER_AUTH_URL=$KEYCLOAK_FRONTEND_URL + - PREFERENCES=$PREFERENCES networks: - default depends_on: @@ -292,6 +293,7 @@ services: - JAVA_OPTS=-Xmx256m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 - KAFKA_IP_PORT=kafka-1:29092,kafka-2:29092,kafka-3:29092 - REDIS_HOST=redis + - PREFERENCES=$PREFERENCES networks: - default depends_on: @@ -352,6 +354,7 @@ services: - BACKEND_USER=${BACKEND_USER} - VAULT_HOST=vault - SWAGGER_AUTH_URL=$KEYCLOAK_FRONTEND_URL + - PREFERENCES=$PREFERENCES depends_on: - kafka-1 - kafka-2 @@ -376,6 +379,7 @@ services: - BACKEND_USER=${BACKEND_USER} - VAULT_HOST=vault - SWAGGER_AUTH_URL=$KEYCLOAK_FRONTEND_URL + - PREFERENCES=$PREFERENCES depends_on: - kafka-1 - kafka-2 @@ -429,6 +433,7 @@ services: - BACKEND_USER=${BACKEND_USER} - VAULT_HOST=vault - SWAGGER_AUTH_URL=$KEYCLOAK_FRONTEND_URL + - PREFERENCES=$PREFERENCES depends_on: - kafka-1 - kafka-2 diff --git a/matching-engine/matching-engine-app/pom.xml b/matching-engine/matching-engine-app/pom.xml index f35dadf9c..c27f3b7b4 100644 --- a/matching-engine/matching-engine-app/pom.xml +++ b/matching-engine/matching-engine-app/pom.xml @@ -47,6 +47,10 @@ org.springframework.boot spring-boot-starter-actuator + + co.nilin.opex.utility.preferences + preferences + diff --git a/matching-engine/matching-engine-app/src/main/kotlin/co/nilin/opex/matching/engine/app/config/AppConfig.kt b/matching-engine/matching-engine-app/src/main/kotlin/co/nilin/opex/matching/engine/app/config/AppConfig.kt index ef535cba3..246c936ee 100644 --- a/matching-engine/matching-engine-app/src/main/kotlin/co/nilin/opex/matching/engine/app/config/AppConfig.kt +++ b/matching-engine/matching-engine-app/src/main/kotlin/co/nilin/opex/matching/engine/app/config/AppConfig.kt @@ -12,16 +12,14 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.springframework.beans.factory.annotation.Autowired -import org.springframework.beans.factory.annotation.Value import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @Configuration class AppConfig { - - @Value("\${spring.app.symbols}") - private val symbols: String? = null + @Autowired + private lateinit var symbols: List @Bean @ConditionalOnMissingBean(value = [OrderBookPersister::class]) @@ -38,22 +36,21 @@ class AppConfig { @Autowired fun configureOrderBooks(orderBookPersister: OrderBookPersister) { - symbols!!.split(",") - .forEach { symbol -> - CoroutineScope(AppSchedulers.generalExecutor).launch { - val lastOrderBook = orderBookPersister.loadLastState(symbol) - //todo: load db orders from last order in order book and put in order book - //todo: add missing orders to lastOrderBook or create one - if (lastOrderBook != null) { - withContext(coroutineContext) { - OrderBooks.reloadOrderBook(lastOrderBook) - } - } else { - OrderBooks.createOrderBook(symbol) + symbols.forEach { symbol -> + CoroutineScope(AppSchedulers.generalExecutor).launch { + val lastOrderBook = orderBookPersister.loadLastState(symbol) + //todo: load db orders from last order in order book and put in order book + //todo: add missing orders to lastOrderBook or create one + if (lastOrderBook != null) { + withContext(coroutineContext) { + OrderBooks.reloadOrderBook(lastOrderBook) } - + } else { + OrderBooks.createOrderBook(symbol) } + } + } } @Bean @@ -80,5 +77,4 @@ class AppConfig { fun configureMatchingEngineListener(exchangeEventHandler: ExchangeEventHandler) { exchangeEventHandler.register() } - -} \ No newline at end of file +} diff --git a/matching-engine/matching-engine-app/src/main/kotlin/co/nilin/opex/matching/engine/app/config/InitializeService.kt b/matching-engine/matching-engine-app/src/main/kotlin/co/nilin/opex/matching/engine/app/config/InitializeService.kt new file mode 100644 index 000000000..ede9e53a1 --- /dev/null +++ b/matching-engine/matching-engine-app/src/main/kotlin/co/nilin/opex/matching/engine/app/config/InitializeService.kt @@ -0,0 +1,17 @@ +package co.nilin.opex.matching.engine.app.config + +import co.nilin.opex.utility.preferences.Preferences +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +class InitializeService() { + @Autowired + private lateinit var preferences: Preferences + + @Bean("symbols") + fun getSymbols(): List { + return preferences.markets.map { it.pair ?: "${it.leftSide}_${it.rightSide}" }.map { it.lowercase() } + } +} diff --git a/matching-engine/matching-engine-app/src/main/resources/application.yml b/matching-engine/matching-engine-app/src/main/resources/application.yml index 74dec34ed..694e6c8a0 100644 --- a/matching-engine/matching-engine-app/src/main/resources/application.yml +++ b/matching-engine/matching-engine-app/src/main/resources/application.yml @@ -10,5 +10,3 @@ spring: redis: host: ${REDIS_HOST:localhost} port: 6379 - app: - symbols: btc_usdt,eth_usdt,eth_btc,tbtc_tusdt,teth_tusdt,teth_tbtc,btc_irt,eth_irt,bnb_irt,bnb_usdt,busd_irt diff --git a/matching-engine/matching-engine-ports/matching-engine-eventlistener-kafka/src/main/kotlin/co/nilin/opex/matching/engine/ports/kafka/listener/config/OrderKafkaConfig.kt b/matching-engine/matching-engine-ports/matching-engine-eventlistener-kafka/src/main/kotlin/co/nilin/opex/matching/engine/ports/kafka/listener/config/OrderKafkaConfig.kt index 8f2edb314..2817f2c00 100644 --- a/matching-engine/matching-engine-ports/matching-engine-eventlistener-kafka/src/main/kotlin/co/nilin/opex/matching/engine/ports/kafka/listener/config/OrderKafkaConfig.kt +++ b/matching-engine/matching-engine-ports/matching-engine-eventlistener-kafka/src/main/kotlin/co/nilin/opex/matching/engine/ports/kafka/listener/config/OrderKafkaConfig.kt @@ -29,8 +29,8 @@ class OrderKafkaConfig { @Value("\${spring.kafka.consumer.group-id}") private lateinit var groupId: String - @Value("\${spring.app.symbols}") - private lateinit var symbols: String + @Autowired + private lateinit var symbols: List @Bean("consumerConfigs") fun consumerConfigs(): Map { @@ -60,7 +60,7 @@ class OrderKafkaConfig { @Qualifier("orderKafkaTemplate") template: KafkaTemplate, @Qualifier("orderConsumerFactory") consumerFactory: ConsumerFactory ) { - val topics = symbols.split(",").map { s -> "orders_$s" }.toTypedArray() + val topics = symbols.map { s -> "orders_$s" }.toTypedArray() val containerProps = ContainerProperties(*topics) containerProps.messageListener = orderKafkaListener val container = KafkaMessageListenerContainer(consumerFactory, containerProps) @@ -91,4 +91,4 @@ class OrderKafkaConfig { return DefaultErrorHandler(recoverer, FixedBackOff(5_000, 20)) } -} \ No newline at end of file +} diff --git a/matching-engine/matching-engine-ports/matching-engine-submitter-kafka/src/main/kotlin/co/nilin/opex/matching/engine/ports/kafka/submitter/config/KafkaTopicConfig.kt b/matching-engine/matching-engine-ports/matching-engine-submitter-kafka/src/main/kotlin/co/nilin/opex/matching/engine/ports/kafka/submitter/config/KafkaTopicConfig.kt index 5cbc89854..ee8446e29 100644 --- a/matching-engine/matching-engine-ports/matching-engine-submitter-kafka/src/main/kotlin/co/nilin/opex/matching/engine/ports/kafka/submitter/config/KafkaTopicConfig.kt +++ b/matching-engine/matching-engine-ports/matching-engine-submitter-kafka/src/main/kotlin/co/nilin/opex/matching/engine/ports/kafka/submitter/config/KafkaTopicConfig.kt @@ -3,7 +3,6 @@ package co.nilin.opex.matching.engine.ports.kafka.submitter.config import org.apache.kafka.clients.admin.NewTopic import org.apache.kafka.common.config.TopicConfig import org.springframework.beans.factory.annotation.Autowired -import org.springframework.beans.factory.annotation.Value import org.springframework.context.annotation.Configuration import org.springframework.context.support.GenericApplicationContext import org.springframework.kafka.config.TopicBuilder @@ -12,13 +11,12 @@ import java.util.function.Supplier @Configuration class KafkaTopicConfig { - @Value("\${spring.app.symbols}") - private lateinit var symbols: String + @Autowired + private lateinit var symbols: List @Autowired fun createTopics(applicationContext: GenericApplicationContext) { - symbols.split(",") - .map { s -> "orders_$s" } + symbols.map { s -> "orders_$s" } .forEach { topic -> applicationContext.registerBean("topic_${topic}", NewTopic::class.java, Supplier { TopicBuilder.name(topic) @@ -29,8 +27,7 @@ class KafkaTopicConfig { }) } - symbols.split(",") - .map { s -> "events_$s" } + symbols.map { s -> "events_$s" } .forEach { topic -> applicationContext.registerBean("topic_${topic}", NewTopic::class.java, Supplier { TopicBuilder.name(topic) @@ -41,8 +38,7 @@ class KafkaTopicConfig { }) } - symbols.split(",") - .map { s -> "trades_$s" } + symbols.map { s -> "trades_$s" } .forEach { topic -> applicationContext.registerBean("topic_${topic}", NewTopic::class.java, Supplier { TopicBuilder.name(topic) @@ -54,4 +50,4 @@ class KafkaTopicConfig { } } -} \ No newline at end of file +} diff --git a/matching-engine/pom.xml b/matching-engine/pom.xml index da7a0072b..c53a2590f 100644 --- a/matching-engine/pom.xml +++ b/matching-engine/pom.xml @@ -67,6 +67,11 @@ interceptors ${project.version} + + co.nilin.opex.utility.preferences + preferences + ${project.version} + diff --git a/preferences-demo.yml b/preferences-demo.yml new file mode 100644 index 000000000..3cbd08b42 --- /dev/null +++ b/preferences-demo.yml @@ -0,0 +1,203 @@ +addressTypes: + - addressType: bitcoin + addressRegex: "*" + - addressType: ethereum + addressRegex: "*" +chains: + - name: bitcoin + addressType: bitcoin + endpointUrl: lb://chain-scan-gateway/bitcoin/transfers + schedule: + delay: 600 + errorDelay: 60 + - name: ethereum + addressType: ethereum + endpointUrl: lb://chain-scan-gateway/eth/transfers + schedule: + delay: 90 + errorDelay: 60 + - name: bsc + addressType: ethereum + endpointUrl: lb://chain-scan-gateway/bsc/transfers + schedule: + delay: 90 + errorDelay: 60 +currencies: + - symbol: BTC + name: Bitcoin + precision: 0.000001 + mainBalance: 10000 + dailyTotal: 1000 + dailyCount: 100 + monthlyTotal: 30000 + monthlyCount: 3000 + implementations: + - chain: bitcoin + withdrawFee: 0.0001 + withdrawMin: 0.0001 + decimal: 0 + - symbol: ETH + name: Ethereum + precision: 0.000001 + mainBalance: 10000 + dailyTotal: 1000 + dailyCount: 100 + monthlyTotal: 30000 + monthlyCount: 3000 + implementations: + - chain: ethereum + withdrawFee: 0.00001 + withdrawMin: 0.000001 + decimal: 18 + - symbol: BNB + name: Binance + precision: 0.0001 + mainBalance: 10000 + dailyTotal: 1000 + dailyCount: 100 + monthlyTotal: 30000 + monthlyCount: 3000 + implementations: + - chain: bsc + withdrawFee: 0.00001 + withdrawMin: 0.000001 + decimal: 18 + - symbol: BUSD + name: Binance USD + precision: 0.01 + mainBalance: 10000 + dailyTotal: 1000 + dailyCount: 100 + monthlyTotal: 30000 + monthlyCount: 3000 + implementations: + - chain: bsc + token: true + tokenAddress: 0xe9e7cea3dedca5984780bafc599bd69add087d56 + tokenName: BUSD Token + withdrawFee: 0.01 + withdrawMin: 0.01 + decimal: 18 + - symbol: IRT + name: Toman + precision: 0.1 + mainBalance: 10000 + dailyTotal: 1000 + dailyCount: 100 + monthlyTotal: 30000 + monthlyCount: 3000 +markets: + - leftSide: BTC + rightSide: BUSD + aliases: + - key: binance + alias: BTCBUSD + feeConfigs: + - direction: ASK + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 + - direction: BID + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 + - leftSide: ETH + rightSide: BUSD + aliases: + - key: binance + alias: ETHBUSD + feeConfigs: + - direction: ASK + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 + - direction: BID + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 + - leftSide: BNB + rightSide: BUSD + aliases: + - key: binance + alias: BNBBUSD + feeConfigs: + - direction: ASK + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 + - direction: BID + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 + - leftSide: BTC + rightSide: IRT + aliases: + - key: binance + alias: BTCIRT + feeConfigs: + - direction: ASK + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 + - direction: BID + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 + - leftSide: ETH + rightSide: IRT + aliases: + - key: binance + alias: ETHIRT + feeConfigs: + - direction: ASK + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 + - direction: BID + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 + - leftSide: BNB + rightSide: IRT + aliases: + - key: binance + alias: BNBIRT + feeConfigs: + - direction: ASK + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 + - direction: BID + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 + - leftSide: BUSD + rightSide: IRT + aliases: + - key: binance + alias: BUSDIRT + feeConfigs: + - direction: ASK + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 + - direction: BID + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 +wallet: + schedule: + delay: 10 + batchSize: 10000 +userLimits: + - owner: 1 + action: withdraw + walletType: main + withdrawFee: 0.0001 + dailyTotal: 1000 + dailyCount: 100 + monthlyTotal: 30000 + monthlyCount: 3000 +system: + walletTitle: system + walletLevel: basic diff --git a/preferences-dev.yml b/preferences-dev.yml new file mode 100644 index 000000000..1e97292ea --- /dev/null +++ b/preferences-dev.yml @@ -0,0 +1,159 @@ +addressTypes: + - addressType: ethereum + addressRegex: "*" + - addressType: test-bitcoin + addressRegex: "*" +chains: + - name: test-bitcoin + addressType: test-bitcoin + endpointUrl: lb://chain-scan-gateway/test-bitcoin/transfers + schedule: + delay: 600 + errorDelay: 60 + - name: test-ethereum + addressType: ethereum + endpointUrl: lb://chain-scan-gateway/test-eth/transfers + schedule: + delay: 90 + errorDelay: 60 +currencies: + - symbol: IRT + name: Toman + precision: 0.1 + mainBalance: 10000 + dailyTotal: 1000 + dailyCount: 100 + monthlyTotal: 30000 + monthlyCount: 3000 + - symbol: TBTC + name: Bitcoin (Test) + precision: 0.000001 + mainBalance: 10000 + dailyTotal: 1000 + dailyCount: 100 + monthlyTotal: 30000 + monthlyCount: 3000 + implementations: + - chain: test-bitcoin + withdrawFee: 0.0001 + withdrawMin: 0.0001 + decimal: 0 + gift: 21000000 + - symbol: TETH + name: Ethereum (Test) + precision: 0.000001 + mainBalance: 10000 + dailyTotal: 1000 + dailyCount: 100 + monthlyTotal: 30000 + monthlyCount: 3000 + implementations: + - chain: test-ethereum + withdrawFee: 0.00001 + withdrawMin: 0.000001 + decimal: 18 + gift: 2000 + - symbol: TUSDT + name: Tether (Test) + precision: 0.01 + mainBalance: 10000 + dailyTotal: 1000 + dailyCount: 100 + monthlyTotal: 30000 + monthlyCount: 3000 + implementations: + - chain: ethereum + token: true + tokenAddress: 0x110a13fc3efe6a245b50102d2d79b3e76125ae83 + tokenName: Tether USD + withdrawFee: 0.01 + withdrawMin: 0.01 + decimal: 6 + gift: 1000000 +markets: + - leftSide: TBTC + rightSide: TUSDT + aliases: + - key: binance + alias: TBTCTUSDT + feeConfigs: + - direction: ASK + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 + - direction: BID + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 + - leftSide: TETH + rightSide: TUSDT + aliases: + - key: binance + alias: TETHTUSDT + feeConfigs: + - direction: ASK + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 + - direction: BID + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 + - leftSide: TBTC + rightSide: IRT + aliases: + - key: binance + alias: TBTCIRT + feeConfigs: + - direction: ASK + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 + - direction: BID + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 + - leftSide: TETH + rightSide: IRT + aliases: + - key: binance + alias: TETHIRT + feeConfigs: + - direction: ASK + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 + - direction: BID + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 + - leftSide: TUSDT + rightSide: IRT + aliases: + - key: binance + alias: TUSDTIRT + feeConfigs: + - direction: ASK + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 + - direction: BID + userLevel: "*" + makerFee: 0.01 + takerFee: 0.01 +wallet: + schedule: + delay: 10 + batchSize: 10000 +userLimits: + - owner: 1 + action: withdraw + walletType: main + withdrawFee: 0.0001 + dailyTotal: 1000 + dailyCount: 100 + monthlyTotal: 30000 + monthlyCount: 3000 +system: + walletTitle: system + walletLevel: basic diff --git a/utility/pom.xml b/utility/pom.xml index b55db498f..6216eec86 100644 --- a/utility/pom.xml +++ b/utility/pom.xml @@ -20,6 +20,7 @@ error-handler logging-handler interceptors + preferences diff --git a/utility/preferences/pom.xml b/utility/preferences/pom.xml new file mode 100644 index 000000000..6dfc536ef --- /dev/null +++ b/utility/preferences/pom.xml @@ -0,0 +1,26 @@ + + + 4.0.0 + + utility + co.nilin.opex.utility + 1.0-SNAPSHOT + + + co.nilin.opex.utility.preferences + preferences + + + + org.springframework + spring-context + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + 2.13.0 + + + diff --git a/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/AddressType.kt b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/AddressType.kt new file mode 100644 index 000000000..7ef22eba0 --- /dev/null +++ b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/AddressType.kt @@ -0,0 +1,3 @@ +package co.nilin.opex.utility.preferences + +data class AddressType(var addressType: String = "", var addressRegex: String = "*") diff --git a/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/Alias.kt b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/Alias.kt new file mode 100644 index 000000000..4fbd6b4a8 --- /dev/null +++ b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/Alias.kt @@ -0,0 +1,3 @@ +package co.nilin.opex.utility.preferences + +data class Alias(var key: String = "", var alias: String = "") diff --git a/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/Chain.kt b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/Chain.kt new file mode 100644 index 000000000..8cc4abecc --- /dev/null +++ b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/Chain.kt @@ -0,0 +1,8 @@ +package co.nilin.opex.utility.preferences + +data class Chain( + var name: String = "", + var addressType: String = "", + val endpointUrl: String = "", + var schedule: ChainSyncSchedule = ChainSyncSchedule() +) diff --git a/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/ChainSyncSchedule.kt b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/ChainSyncSchedule.kt new file mode 100644 index 000000000..19b8f824a --- /dev/null +++ b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/ChainSyncSchedule.kt @@ -0,0 +1,5 @@ +package co.nilin.opex.utility.preferences + +data class ChainSyncSchedule( + var delay: Long = 600, var errorDelay: Long = 60 +) diff --git a/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/Currency.kt b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/Currency.kt new file mode 100644 index 000000000..1537c7583 --- /dev/null +++ b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/Currency.kt @@ -0,0 +1,16 @@ +package co.nilin.opex.utility.preferences + +import java.math.BigDecimal + +data class Currency( + var symbol: String = "", + var name: String = "", + var precision: BigDecimal = BigDecimal.ONE, + var mainBalance: BigDecimal = BigDecimal.ZERO, + var dailyTotal: BigDecimal = BigDecimal.valueOf(1000), + var dailyCount: Int = 100, + var monthlyTotal: BigDecimal = BigDecimal.valueOf(30000), + var monthlyCount: Int = 3000, + var implementations: List = emptyList(), + var gift: BigDecimal = BigDecimal.ZERO +) diff --git a/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/CurrencyImplementation.kt b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/CurrencyImplementation.kt new file mode 100644 index 000000000..97d778095 --- /dev/null +++ b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/CurrencyImplementation.kt @@ -0,0 +1,14 @@ +package co.nilin.opex.utility.preferences + +import java.math.BigDecimal + +data class CurrencyImplementation( + var chain: String = "", + var withdrawEnabled: Boolean = true, + var token: Boolean = false, + var tokenAddress: String? = null, + var tokenName: String? = null, + var withdrawFee: BigDecimal = BigDecimal.valueOf(0.01), + var withdrawMin: BigDecimal = BigDecimal.valueOf(0.01), + var decimal: Int = 0 +) diff --git a/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/FeeConfig.kt b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/FeeConfig.kt new file mode 100644 index 000000000..44e10c193 --- /dev/null +++ b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/FeeConfig.kt @@ -0,0 +1,10 @@ +package co.nilin.opex.utility.preferences + +import java.math.BigDecimal + +data class FeeConfig( + var userLevel: String = "", + var direction: String = "", + var makerFee: BigDecimal = BigDecimal.valueOf(0.01), + var takerFee: BigDecimal = BigDecimal.valueOf(0.01) +) diff --git a/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/Market.kt b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/Market.kt new file mode 100644 index 000000000..04c738c88 --- /dev/null +++ b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/Market.kt @@ -0,0 +1,13 @@ +package co.nilin.opex.utility.preferences + +import java.math.BigDecimal + +data class Market( + var leftSide: String = "", + var rightSide: String = "", + var pair: String? = null, + var feeConfigs: List = emptyList(), + var aliases: List = emptyList(), + var leftSideFraction: BigDecimal? = null, + var rightSideFraction: BigDecimal? = null +) diff --git a/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/Preferences.kt b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/Preferences.kt new file mode 100644 index 000000000..5649f35d2 --- /dev/null +++ b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/Preferences.kt @@ -0,0 +1,11 @@ +package co.nilin.opex.utility.preferences + +data class Preferences( + var addressTypes: List = emptyList(), + var chains: List = emptyList(), + var currencies: List = emptyList(), + var markets: List = emptyList(), + var userLimits: List = emptyList(), + var wallet: Wallet = Wallet(), + var system: System = System() +) diff --git a/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/System.kt b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/System.kt new file mode 100644 index 000000000..7820c402d --- /dev/null +++ b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/System.kt @@ -0,0 +1,5 @@ +package co.nilin.opex.utility.preferences + +data class System( + var walletTitle: String = "system", var walletLevel: String = "basic" +) diff --git a/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/UserLimit.kt b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/UserLimit.kt new file mode 100644 index 000000000..958374899 --- /dev/null +++ b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/UserLimit.kt @@ -0,0 +1,15 @@ +package co.nilin.opex.utility.preferences + +import java.math.BigDecimal + +data class UserLimit( + var level: String? = null, + var owner: Long = 1, + var action: String = "withdraw", + var walletType: String = "main", + var withdrawFee: BigDecimal = BigDecimal.valueOf(0.0001), + var dailyTotal: BigDecimal = BigDecimal.valueOf(1000), + var dailyCount: Int = 100, + var monthlyTotal: BigDecimal = BigDecimal.valueOf(30000), + var monthlyCount: Int = 3000 +) diff --git a/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/Wallet.kt b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/Wallet.kt new file mode 100644 index 000000000..487130f88 --- /dev/null +++ b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/Wallet.kt @@ -0,0 +1,5 @@ +package co.nilin.opex.utility.preferences + +data class Wallet( + var schedule: WalletSyncSchedule = WalletSyncSchedule() +) diff --git a/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/WalletSyncSchedule.kt b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/WalletSyncSchedule.kt new file mode 100644 index 000000000..d001817e2 --- /dev/null +++ b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/WalletSyncSchedule.kt @@ -0,0 +1,3 @@ +package co.nilin.opex.utility.preferences + +class WalletSyncSchedule(var delay: Long = 10, var batchSize: Long = 10000) diff --git a/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/reader/ReadPreferences.kt b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/reader/ReadPreferences.kt new file mode 100644 index 000000000..cce06f5cd --- /dev/null +++ b/utility/preferences/src/main/kotlin/co/nilin/opex/utility/preferences/reader/ReadPreferences.kt @@ -0,0 +1,23 @@ +package co.nilin.opex.utility.preferences.reader + +import co.nilin.opex.utility.preferences.Preferences +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import java.io.File + +@Configuration +class ReadPreferences() { + private val mapper = ObjectMapper(YAMLFactory()) + + @Value("\${PREFERENCES:classpath:preferences.yml}") + private lateinit var preferencesYmlPath: String + + @Bean + fun preferences(): Preferences = runCatching { + val preferencesYml = File(preferencesYmlPath) + mapper.readValue(preferencesYml, Preferences::class.java) + }.getOrElse { Preferences() } +} diff --git a/wallet/pom.xml b/wallet/pom.xml index 8b7b0dd83..79ffc0cc5 100644 --- a/wallet/pom.xml +++ b/wallet/pom.xml @@ -61,6 +61,11 @@ interceptors ${project.version} + + co.nilin.opex.utility.preferences + preferences + ${project.version} + diff --git a/wallet/wallet-app/pom.xml b/wallet/wallet-app/pom.xml index f38284053..f3fa59979 100644 --- a/wallet/wallet-app/pom.xml +++ b/wallet/wallet-app/pom.xml @@ -107,6 +107,10 @@ org.springframework.cloud spring-cloud-starter-vault-config + + co.nilin.opex.utility.preferences + preferences + diff --git a/wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/InitializeService.kt b/wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/InitializeService.kt new file mode 100644 index 000000000..f98c87207 --- /dev/null +++ b/wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/config/InitializeService.kt @@ -0,0 +1,83 @@ +package co.nilin.opex.wallet.app.config + +import co.nilin.opex.utility.preferences.Currency +import co.nilin.opex.utility.preferences.Preferences +import co.nilin.opex.utility.preferences.UserLimit +import co.nilin.opex.wallet.ports.postgres.dao.CurrencyRepository +import co.nilin.opex.wallet.ports.postgres.dao.UserLimitsRepository +import co.nilin.opex.wallet.ports.postgres.dao.WalletOwnerRepository +import co.nilin.opex.wallet.ports.postgres.dao.WalletRepository +import co.nilin.opex.wallet.ports.postgres.model.UserLimitsModel +import co.nilin.opex.wallet.ports.postgres.model.WalletModel +import co.nilin.opex.wallet.ports.postgres.model.WalletOwnerModel +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.reactor.awaitSingle +import kotlinx.coroutines.reactor.awaitSingleOrNull +import kotlinx.coroutines.runBlocking +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.DependsOn +import org.springframework.stereotype.Component +import java.math.BigDecimal + +@Component +@DependsOn("postgresConfig") +class InitializeService( + @Value("\${app.system.uuid}") val systemUuid: String, + private val currencyRepository: CurrencyRepository, + private val walletOwnerRepository: WalletOwnerRepository, + private val walletRepository: WalletRepository, + private val userLimitsRepository: UserLimitsRepository +) { + @Autowired + private lateinit var preferences: Preferences + + @Autowired + fun init() = runBlocking { + addCurrencies(preferences.currencies) + addSystemWallet(preferences) + addUserLimits(preferences.userLimits) + } + + private suspend fun addUserLimits(data: List) = coroutineScope { + data.forEachIndexed { i, it -> + if (!userLimitsRepository.existsById(i + 1L).awaitSingle()) { + runCatching { + userLimitsRepository.save( + UserLimitsModel( + null, + it.level, + it.owner, + it.action, + it.walletType, + it.dailyTotal, + it.dailyCount, + it.monthlyTotal, + it.monthlyCount + ) + ).awaitSingleOrNull() + } + } + } + } + + private suspend fun addSystemWallet(p: Preferences) = coroutineScope { + if (!walletOwnerRepository.existsById(1).awaitSingle()) { + walletOwnerRepository.save(WalletOwnerModel(null, systemUuid, p.system.walletTitle, p.system.walletLevel)) + .awaitSingleOrNull() + } + val items = p.currencies.flatMap { + listOf( + WalletModel(null, 1, "main", it.symbol, it.mainBalance), + WalletModel(null, 1, "exchange", it.symbol, BigDecimal.ZERO) + ) + } + runCatching { walletRepository.saveAll(items).collectList().awaitSingleOrNull() } + } + + private suspend fun addCurrencies(data: List) = coroutineScope { + data.forEach { + currencyRepository.insert(it.name, it.symbol, it.precision.toDouble()).awaitSingleOrNull() + } + } +} diff --git a/wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/service/UserRegistrationService.kt b/wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/service/UserRegistrationService.kt index 4ee4c420e..f2c679c81 100644 --- a/wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/service/UserRegistrationService.kt +++ b/wallet/wallet-app/src/main/kotlin/co/nilin/opex/wallet/app/service/UserRegistrationService.kt @@ -2,47 +2,31 @@ package co.nilin.opex.wallet.app.service import co.nilin.opex.utility.error.data.OpexError import co.nilin.opex.utility.error.data.OpexException +import co.nilin.opex.utility.preferences.Preferences import co.nilin.opex.wallet.core.model.Amount -import co.nilin.opex.wallet.core.model.Wallet import co.nilin.opex.wallet.core.spi.CurrencyService import co.nilin.opex.wallet.core.spi.WalletManager import co.nilin.opex.wallet.core.spi.WalletOwnerManager import co.nilin.opex.wallet.ports.kafka.listener.model.UserCreatedEvent -import org.springframework.beans.factory.annotation.Value +import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component import org.springframework.transaction.annotation.Transactional import java.math.BigDecimal @Component class UserRegistrationService( - val walletOwnerManager: WalletOwnerManager, - val walletManager: WalletManager, - val currencyService: CurrencyService, - @Value("\${app.gift.symbol}") - val symbol: String, - @Value("\${app.gift.amount}") - val amount: BigDecimal + val walletOwnerManager: WalletOwnerManager, val walletManager: WalletManager, val currencyService: CurrencyService ) { - @Transactional - suspend fun registerNewUser(event: UserCreatedEvent): Wallet { - val owner = - walletOwnerManager.createWalletOwner(event.uuid, "${event.firstName} ${event.lastName}", "1") + @Autowired + private lateinit var preferences: Preferences - val btcSymbol = currencyService.getCurrency("tbtc") ?: throw OpexException(OpexError.CurrencyNotFound) - //TODO REMOVE LATER - walletManager.createWallet( - owner, - Amount(btcSymbol, BigDecimal.ONE), - btcSymbol, - "main" - ) + @Transactional + suspend fun registerNewUser(event: UserCreatedEvent) { + val owner = walletOwnerManager.createWalletOwner(event.uuid, "${event.firstName} ${event.lastName}", "1") - val giftSymbol = currencyService.getCurrency(symbol) ?: throw OpexException(OpexError.CurrencyNotFound) - return walletManager.createWallet( - owner, - Amount(giftSymbol, amount), - giftSymbol, - "main" - ) + preferences.currencies.filter { it.gift > BigDecimal.ZERO }.forEach { + val currency = currencyService.getCurrency(it.symbol) ?: throw OpexException(OpexError.CurrencyNotFound) + walletManager.createWallet(owner, Amount(currency, it.gift), currency, "main") + } } } diff --git a/wallet/wallet-app/src/main/resources/application.yml b/wallet/wallet-app/src/main/resources/application.yml index 6874669f9..2f3f0f75e 100644 --- a/wallet/wallet-app/src/main/resources/application.yml +++ b/wallet/wallet-app/src/main/resources/application.yml @@ -44,9 +44,6 @@ spring: config: import: vault://secret/${spring.application.name} app: - gift: - symbol: tusdt - amount: 1000 auth: cert-url: lb://opex-auth/auth/realms/opex/protocol/openid-connect/certs system: diff --git a/wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/wallet/ports/postgres/config/PostgresConfig.kt b/wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/wallet/ports/postgres/config/PostgresConfig.kt index 793b4a09a..d06b8686d 100644 --- a/wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/wallet/ports/postgres/config/PostgresConfig.kt +++ b/wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/wallet/ports/postgres/config/PostgresConfig.kt @@ -10,17 +10,13 @@ import org.springframework.r2dbc.core.DatabaseClient @EnableR2dbcRepositories(basePackages = ["co.nilin.opex"]) class PostgresConfig( db: DatabaseClient, - @Value("classpath:schema.sql") private val schemaResource: Resource, - @Value("classpath:data.sql") private val dataResource: Resource? + @Value("classpath:schema.sql") private val schemaResource: Resource ) { init { val schemaReader = schemaResource.inputStream.reader() val schema = schemaReader.readText().trim() schemaReader.close() - val dataReader = dataResource?.inputStream?.reader() - val data = dataReader?.readText()?.trim() ?: "" - dataReader?.close() - val initDb = db.sql { schema.plus(data) } + val initDb = db.sql { schema } initDb // initialize the database .then() .subscribe() // execute diff --git a/wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/wallet/ports/postgres/dao/CurrencyRepository.kt b/wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/wallet/ports/postgres/dao/CurrencyRepository.kt index dcc8981a2..53700ce0b 100644 --- a/wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/wallet/ports/postgres/dao/CurrencyRepository.kt +++ b/wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/wallet/ports/postgres/dao/CurrencyRepository.kt @@ -12,10 +12,10 @@ interface CurrencyRepository : ReactiveCrudRepository { @Query("select * from currency where symbol = :symbol") fun findBySymbol(symbol: String): Mono - @Query("insert into currency values (:name, :symbol, :precision) on conflict do nothing") + @Query("insert into currency values (:symbol, :name, :precision) on conflict do nothing") fun insert(name: String, symbol: String, precision: Double): Mono @Query("delete from currency where name = :name") fun deleteByName(name: String): Mono -} \ No newline at end of file +} diff --git a/wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/wallet/ports/postgres/model/UserLimitsModel.kt b/wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/wallet/ports/postgres/model/UserLimitsModel.kt index edc298c78..cb70e3379 100644 --- a/wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/wallet/ports/postgres/model/UserLimitsModel.kt +++ b/wallet/wallet-ports/wallet-persister-postgres/src/main/kotlin/co/nilin/opex/wallet/ports/postgres/model/UserLimitsModel.kt @@ -7,7 +7,7 @@ import java.math.BigDecimal @Table("user_limits") class UserLimitsModel( - @Id val id: Long?, + @Id var id: Long?, val level: String?, val owner: Long?, val action: String, //withdraw or deposit @@ -16,4 +16,4 @@ class UserLimitsModel( @Column("daily_count") val dailyCount: Int?, @Column("monthly_total") val monthlyTotal: BigDecimal?, @Column("monthly_count") val monthlyCount: Int? -) \ No newline at end of file +) diff --git a/wallet/wallet-ports/wallet-persister-postgres/src/main/resources/data.sql b/wallet/wallet-ports/wallet-persister-postgres/src/main/resources/data.sql deleted file mode 100644 index edc167ca4..000000000 --- a/wallet/wallet-ports/wallet-persister-postgres/src/main/resources/data.sql +++ /dev/null @@ -1,87 +0,0 @@ -INSERT INTO wallet_owner(id, uuid, title, level) -VALUES (1, '1', 'system', 'basic') -ON CONFLICT DO NOTHING; - -SELECT setval(pg_get_serial_sequence('wallet_owner', 'id'), (SELECT MAX(id) FROM wallet_owner)); - -INSERT INTO currency(name, symbol, precision) -VALUES ('btc', 'btc', 0.000001), - ('eth', 'eth', 0.00001), - ('usdt', 'usdt', 0.01), - ('bnb', 'bnb', 0.01), - ('busd', 'busd', 0.01), - ('nln', 'nln', 1), - ('irt', 'irt', 1) -ON CONFLICT DO NOTHING; - --- Test currency -INSERT INTO currency(name, symbol, precision) -VALUES ('tbtc', 'tbtc', 0.000001), - ('teth', 'teth', 0.00001), - ('tusdt', 'tusdt', 0.01) -ON CONFLICT DO NOTHING; - -INSERT INTO currency_rate(id, source_currency, dest_currency, rate) -VALUES (1, 'btc', 'nln', 5500000), - (2, 'usdt', 'nln', 100), - (3, 'btc', 'usdt', 55000), - (4, 'eth', 'usdt', 3800), - (5, 'btc', 'irt', 3800), - (6, 'eth', 'irt', 3800), - (7, 'bnb', 'irt', 3800), - (8, 'bnb', 'usdt', 3800), - (9, 'busd', 'usdt', 3800) -ON CONFLICT DO NOTHING; - --- Test currency rate -INSERT INTO currency_rate(id, source_currency, dest_currency, rate) -VALUES (10, 'tbtc', 'nln', 5500000), - (11, 'tusdt', 'nln', 100), - (12, 'tbtc', 'tusdt', 55000), - (13, 'teth', 'tusdt', 3800) -ON CONFLICT DO NOTHING; - -SELECT setval(pg_get_serial_sequence('currency_rate', 'id'), (SELECT MAX(id) FROM currency_rate)); - -INSERT INTO wallet(id, owner, wallet_type, currency, balance) -VALUES (1, 1, 'main', 'btc', 10), - (2, 1, 'exchange', 'btc', 0), - (3, 1, 'main', 'usdt', 550000), - (4, 1, 'exchange', 'usdt', 0), - (5, 1, 'main', 'nln', 100000000), - (6, 1, 'exchange', 'nln', 0), - (7, 1, 'main', 'eth', 10000), - (8, 1, 'exchange', 'eth', 0), - (9, 1, 'main', 'irt', 100000000), - (10, 1, 'exchange', 'irt', 0), - (11, 1, 'main', 'bnb', 100000000), - (12, 1, 'exchange', 'bnb', 0), - (13, 1, 'main', 'busd', 100000000), - (14, 1, 'exchange', 'busd', 0) -ON CONFLICT DO NOTHING; - --- Test wallet -INSERT INTO wallet(id, owner, wallet_type, currency, balance) -VALUES (15, 1, 'main', 'tbtc', 10), - (16, 1, 'exchange', 'tbtc', 0), - (17, 1, 'main', 'tusdt', 550000), - (18, 1, 'exchange', 'tusdt', 0), - (19, 1, 'main', 'teth', 10000), - (20, 1, 'exchange', 'teth', 0) -ON CONFLICT DO NOTHING; - -SELECT setval(pg_get_serial_sequence('wallet', 'id'), (SELECT MAX(id) FROM wallet)); - -INSERT INTO user_limits(id, - level, - owner, - action, - wallet_type, - daily_total, - daily_count, - monthly_total, - monthly_count) -VALUES (1, null, 1, 'withdraw', 'main', 1000, 100, 10000, 1000) -ON CONFLICT DO NOTHING; - -SELECT setval(pg_get_serial_sequence('user_limits', 'id'), (SELECT MAX(id) FROM user_limits)); diff --git a/wallet/wallet-ports/wallet-persister-postgres/src/main/resources/schema.sql b/wallet/wallet-ports/wallet-persister-postgres/src/main/resources/schema.sql index fd9462799..e37ea43c1 100644 --- a/wallet/wallet-ports/wallet-persister-postgres/src/main/resources/schema.sql +++ b/wallet/wallet-ports/wallet-persister-postgres/src/main/resources/schema.sql @@ -31,7 +31,8 @@ CREATE TABLE IF NOT EXISTS wallet owner INTEGER NOT NULL REFERENCES wallet_owner (id), wallet_type VARCHAR(10) NOT NULL, currency VARCHAR(25) NOT NULL REFERENCES currency (symbol), - balance DECIMAL NOT NULL + balance DECIMAL NOT NULL, + UNIQUE (owner, wallet_type, currency) ); CREATE TABLE IF NOT EXISTS transaction