From b933241dee59c748bc350706c7f7dbb3029e61a1 Mon Sep 17 00:00:00 2001 From: min-96 Date: Mon, 2 Sep 2024 20:48:03 +0900 Subject: [PATCH 1/2] refactor --- .../com/api/admin/config/RabbitMQConfig.kt | 6 +- .../api/admin/controller/AdminController.kt | 4 +- .../domain/transferFailLog/TransferFailLog.kt | 14 ++++ .../TransferFailLogRepository.kt | 6 ++ src/main/kotlin/com/api/admin/enums/Enum.kt | 4 +- .../rabbitMQ/event/AdminEventListener.kt | 2 +- .../admin/service/TransferFailLogService.kt | 24 ++++++ .../com/api/admin/service/TransferService.kt | 1 - .../com/api/admin/service/Web3jService.kt | 82 +++++++++---------- src/main/kotlin/com/api/admin/util/Util.kt | 14 ++++ .../db/migration/V1__Initial_schema.sql | 8 ++ 11 files changed, 112 insertions(+), 53 deletions(-) create mode 100644 src/main/kotlin/com/api/admin/domain/transferFailLog/TransferFailLog.kt create mode 100644 src/main/kotlin/com/api/admin/domain/transferFailLog/TransferFailLogRepository.kt create mode 100644 src/main/kotlin/com/api/admin/service/TransferFailLogService.kt diff --git a/src/main/kotlin/com/api/admin/config/RabbitMQConfig.kt b/src/main/kotlin/com/api/admin/config/RabbitMQConfig.kt index 7ae9e58..6737dda 100644 --- a/src/main/kotlin/com/api/admin/config/RabbitMQConfig.kt +++ b/src/main/kotlin/com/api/admin/config/RabbitMQConfig.kt @@ -1,10 +1,7 @@ package com.api.admin.config -import org.springframework.amqp.core.Binding -import org.springframework.amqp.core.BindingBuilder -import org.springframework.amqp.core.DirectExchange + import org.springframework.amqp.core.FanoutExchange -import org.springframework.amqp.core.Queue import org.springframework.amqp.rabbit.connection.ConnectionFactory import org.springframework.amqp.rabbit.core.RabbitTemplate import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter @@ -33,5 +30,4 @@ class RabbitMQConfig { @Bean fun transferExchange() = createExchange("transferExchange") - } \ No newline at end of file diff --git a/src/main/kotlin/com/api/admin/controller/AdminController.kt b/src/main/kotlin/com/api/admin/controller/AdminController.kt index 2c96ae1..1c97607 100644 --- a/src/main/kotlin/com/api/admin/controller/AdminController.kt +++ b/src/main/kotlin/com/api/admin/controller/AdminController.kt @@ -37,7 +37,7 @@ class AdminController( @RequestBody request: WithdrawERC20Request, ): Mono> { println("Received withdraw request for address: $address with amount: ${request.amount}") - return web3jService.createTransactionERC20(address, request.amount, request.chainType) + return web3jService.processTransactionERC20(address, request.amount, request.chainType) .doOnSuccess { println("Transaction successful") } .then(Mono.just(ResponseEntity.ok().build())) .doOnError { e -> @@ -53,7 +53,7 @@ class AdminController( @RequestParam address: String, @RequestBody request: WithdrawERC721Request, ): Mono> { - return web3jService.createTransactionERC721(address, request.nftId) + return web3jService.processTransactionERC721(address, request.nftId) .then(Mono.just(ResponseEntity.ok().build())) .onErrorResume { Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build()) diff --git a/src/main/kotlin/com/api/admin/domain/transferFailLog/TransferFailLog.kt b/src/main/kotlin/com/api/admin/domain/transferFailLog/TransferFailLog.kt new file mode 100644 index 0000000..af6894f --- /dev/null +++ b/src/main/kotlin/com/api/admin/domain/transferFailLog/TransferFailLog.kt @@ -0,0 +1,14 @@ +package com.api.admin.domain.transferFailLog + +import org.springframework.data.annotation.Id +import org.springframework.data.relational.core.mapping.Table +import java.sql.Timestamp + +@Table("transfer_fail_log") +data class TransferFailLog( + @Id val id: Long? = null, + val wallet: String, + val timestamp: Long? = System.currentTimeMillis(), + val transactionHash: String?, + val errorMessage: String, + ) diff --git a/src/main/kotlin/com/api/admin/domain/transferFailLog/TransferFailLogRepository.kt b/src/main/kotlin/com/api/admin/domain/transferFailLog/TransferFailLogRepository.kt new file mode 100644 index 0000000..a3ce0b4 --- /dev/null +++ b/src/main/kotlin/com/api/admin/domain/transferFailLog/TransferFailLogRepository.kt @@ -0,0 +1,6 @@ +package com.api.admin.domain.transferFailLog + +import org.springframework.data.repository.reactive.ReactiveCrudRepository + +interface TransferFailLogRepository: ReactiveCrudRepository { +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/admin/enums/Enum.kt b/src/main/kotlin/com/api/admin/enums/Enum.kt index efa0589..09711dd 100644 --- a/src/main/kotlin/com/api/admin/enums/Enum.kt +++ b/src/main/kotlin/com/api/admin/enums/Enum.kt @@ -24,5 +24,7 @@ enum class TokenType { MATIC, ETH } - +enum class TransactionStatus { + SUCCESS, FAILURE +} diff --git a/src/main/kotlin/com/api/admin/rabbitMQ/event/AdminEventListener.kt b/src/main/kotlin/com/api/admin/rabbitMQ/event/AdminEventListener.kt index ce2a555..cd8405e 100644 --- a/src/main/kotlin/com/api/admin/rabbitMQ/event/AdminEventListener.kt +++ b/src/main/kotlin/com/api/admin/rabbitMQ/event/AdminEventListener.kt @@ -11,7 +11,7 @@ class AdminEventListener( ) { @EventListener - fun onDepositSend(event: AdminTransferCreatedEvent) { + fun onTransferSend(event: AdminTransferCreatedEvent) { provider.transferSend(event.transfer) } } \ No newline at end of file diff --git a/src/main/kotlin/com/api/admin/service/TransferFailLogService.kt b/src/main/kotlin/com/api/admin/service/TransferFailLogService.kt new file mode 100644 index 0000000..d4fbf37 --- /dev/null +++ b/src/main/kotlin/com/api/admin/service/TransferFailLogService.kt @@ -0,0 +1,24 @@ +package com.api.admin.service + +import com.api.admin.domain.transferFailLog.TransferFailLog +import com.api.admin.domain.transferFailLog.TransferFailLogRepository +import org.springframework.stereotype.Service +import reactor.core.publisher.Mono + +@Service +class TransferFailLogService( + private val transferFailLogRepository: TransferFailLogRepository, +) { + + fun save(wallet: String, transactionHash: String?, message: String): Mono { + return transferFailLogRepository.save( + TransferFailLog( + wallet = wallet, + transactionHash = transactionHash, + errorMessage = message + ) + ).doOnSuccess { + + }.then() + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/admin/service/TransferService.kt b/src/main/kotlin/com/api/admin/service/TransferService.kt index 62cf074..6666091 100644 --- a/src/main/kotlin/com/api/admin/service/TransferService.kt +++ b/src/main/kotlin/com/api/admin/service/TransferService.kt @@ -36,7 +36,6 @@ class TransferService( transactionHash: String, accountType: AccountType, ): Mono { - println("transactionHash : $transactionHash") return transferRepository.existsByTransactionHash(transactionHash) .flatMap { if (it) { diff --git a/src/main/kotlin/com/api/admin/service/Web3jService.kt b/src/main/kotlin/com/api/admin/service/Web3jService.kt index 940b3fa..e1b3b64 100644 --- a/src/main/kotlin/com/api/admin/service/Web3jService.kt +++ b/src/main/kotlin/com/api/admin/service/Web3jService.kt @@ -4,6 +4,7 @@ import com.api.admin.domain.nft.NftRepository import com.api.admin.enums.AccountType import com.api.admin.enums.ChainType import com.api.admin.properties.AdminInfoProperties +import com.api.admin.util.Util.getChainId import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper import org.springframework.stereotype.Service @@ -16,6 +17,7 @@ import org.web3j.crypto.RawTransaction import org.web3j.crypto.TransactionEncoder import org.web3j.utils.Numeric import reactor.core.publisher.Mono +import reactor.core.scheduler.Schedulers import java.math.BigDecimal import java.math.BigInteger import java.time.Duration @@ -28,48 +30,38 @@ class Web3jService( private val adminInfoProperties: AdminInfoProperties ) { - // private val privateKey = "4ec9e64419547100af4f38d7ec57ba1de2d5c36a7dfb03f1a349b2c5b62ac0a9" - - private fun getChainId(chain: ChainType): Long { - val chain = when (chain) { - ChainType.ETHEREUM_MAINNET -> 1L - ChainType.POLYGON_MAINNET -> 137L - ChainType.LINEA_MAINNET -> 59144L - ChainType.LINEA_SEPOLIA -> 59140L - ChainType.ETHEREUM_HOLESKY -> 1L - ChainType.ETHEREUM_SEPOLIA -> 11155111L - ChainType.POLYGON_AMOY -> 80002L - } - return chain - } - - fun createTransactionERC721( - toAddress: String, - nftId: Long, - ): Mono { - return nftRepository.findById(nftId).flatMap { nft-> - val credentials = Credentials.create(adminInfoProperties.privatekey) - createERC721TransactionData(credentials, nft.tokenAddress, toAddress, BigInteger(nft.tokenId), nft.chainType) - .flatMap { transactionHash -> - waitForTransactionReceipt(transactionHash, nft.chainType) - .flatMap { - transferService.getTransferData( - wallet = toAddress, - chainType = nft.chainType, - transactionHash = transactionHash, - accountType = AccountType.WITHDRAW - ) - } - } - .doOnError { e -> - println("Error in createTransactionERC20: ${e.message}") - e.printStackTrace() - } - .then() - } +// fun createTransactionERC721(toAddress: String, nftId: Long): Mono { +// return Mono.defer { processTransactionERC721(toAddress, nftId) } +// .subscribeOn(Schedulers.boundedElastic()) +// .onErrorResume { error -> +// transferService.getTransferData(toAddress, nft.chainType, "failed_hash", AccountType.WITHDRAW, TransactionStatus.FAILURE, error.message ?: "Unknown error") +// } +// .then(Mono.empty()) +// } + + fun processTransactionERC721(toAddress: String, nftId: Long): Mono { + return nftRepository.findById(nftId) + .flatMap { nft -> + val credentials = Credentials.create(adminInfoProperties.privatekey) + createERC721TransactionData(credentials, nft.tokenAddress, toAddress, BigInteger(nft.tokenId), nft.chainType) + .flatMap { transactionHash -> + waitForTransactionReceipt(transactionHash, nft.chainType) + .flatMap { + transferService.getTransferData( + wallet = toAddress, + chainType = nft.chainType, + transactionHash = transactionHash, + accountType = AccountType.WITHDRAW + ) + } + } + } + .doOnError { e -> + println("Error in createTransactionERC721: ${e.message}") + e.printStackTrace() + } } - fun createERC721TransactionData( credentials: Credentials, contractAddress: String, @@ -108,8 +100,14 @@ class Web3jService( } } +// fun createTransactionERC20(recipientAddress: String, amount: BigDecimal, chainType: ChainType): Mono { +// Mono.defer { processTransactionERC20(recipientAddress, amount, chainType) } +// .subscribeOn(Schedulers.boundedElastic()) +// .subscribe() +// return Mono.empty() +// } - fun createTransactionERC20( + fun processTransactionERC20( recipientAddress: String, amount: BigDecimal, chainType: ChainType @@ -165,8 +163,6 @@ class Web3jService( fun waitForTransactionReceipt(transactionHash: String, chainType: ChainType, maxAttempts: Int = 5, attempt: Int = 1): Mono { - - val objectMapper = ObjectMapper() println("Attempt $attempt for transaction $transactionHash") return infuraApiService.getTransactionReceipt(chainType, transactionHash) diff --git a/src/main/kotlin/com/api/admin/util/Util.kt b/src/main/kotlin/com/api/admin/util/Util.kt index c39ed4e..1f34eb3 100644 --- a/src/main/kotlin/com/api/admin/util/Util.kt +++ b/src/main/kotlin/com/api/admin/util/Util.kt @@ -1,5 +1,19 @@ package com.api.admin.util +import com.api.admin.enums.ChainType + object Util { + fun getChainId(chain: ChainType): Long { + val chain = when (chain) { + ChainType.ETHEREUM_MAINNET -> 1L + ChainType.POLYGON_MAINNET -> 137L + ChainType.LINEA_MAINNET -> 59144L + ChainType.LINEA_SEPOLIA -> 59140L + ChainType.ETHEREUM_HOLESKY -> 1L + ChainType.ETHEREUM_SEPOLIA -> 11155111L + ChainType.POLYGON_AMOY -> 80002L + } + return chain + } } \ No newline at end of file diff --git a/src/main/resources/db/migration/V1__Initial_schema.sql b/src/main/resources/db/migration/V1__Initial_schema.sql index 863a35f..33d2dd6 100644 --- a/src/main/resources/db/migration/V1__Initial_schema.sql +++ b/src/main/resources/db/migration/V1__Initial_schema.sql @@ -36,3 +36,11 @@ CREATE TABLE IF NOT EXISTS transfer ( transaction_hash VARCHAR(255) NOT NULL, chain_type chain_type NOT NULL ); + +CREATE TABLE IF NOT EXISTS transfer_fail_log ( + id SERIAL PRIMARY KEY, + wallet VARCHAR(255) NOT NULL, + timestamp bigint not null, + transaction_hash VARCHAR(255), + error_message varchar(1000) NOT NULL +); From 36ed4f41671b5e8687fb8e96345fcd53e070db3d Mon Sep 17 00:00:00 2001 From: min-96 Date: Tue, 3 Sep 2024 20:50:09 +0900 Subject: [PATCH 2/2] refactor --- .../api/admin/controller/AdminController.kt | 19 +-- .../admin/controller/dto/DepositRequest.kt | 1 + .../controller/dto/WithdrawERC20Request.kt | 1 + .../controller/dto/WithdrawERC721Request.kt | 1 + .../domain/transferFailLog/TransferFailLog.kt | 2 + src/main/kotlin/com/api/admin/enums/Enum.kt | 2 +- .../event/dto/AdminTransferDetailResponse.kt | 10 ++ .../event/dto/AdminTransferResponse.kt | 39 ++++- .../admin/service/TransferFailLogService.kt | 24 --- .../api/admin/service/TransferFailService.kt | 38 +++++ .../com/api/admin/service/TransferService.kt | 56 ++++--- .../com/api/admin/service/Web3jService.kt | 67 +++++--- .../service/dto/InfuraTransferResponse.kt | 3 - src/main/kotlin/com/api/admin/util/Util.kt | 1 - .../db/migration/V1__Initial_schema.sql | 1 + .../kotlin/com/api/admin/AdminServiceTest.kt | 143 +++++++++--------- 16 files changed, 238 insertions(+), 170 deletions(-) create mode 100644 src/main/kotlin/com/api/admin/rabbitMQ/event/dto/AdminTransferDetailResponse.kt delete mode 100644 src/main/kotlin/com/api/admin/service/TransferFailLogService.kt create mode 100644 src/main/kotlin/com/api/admin/service/TransferFailService.kt diff --git a/src/main/kotlin/com/api/admin/controller/AdminController.kt b/src/main/kotlin/com/api/admin/controller/AdminController.kt index 1c97607..f4e2600 100644 --- a/src/main/kotlin/com/api/admin/controller/AdminController.kt +++ b/src/main/kotlin/com/api/admin/controller/AdminController.kt @@ -23,10 +23,10 @@ class AdminController( @RequestParam address: String, @RequestBody request: DepositRequest, ): Mono> { - return transferService.getTransferData(address, request.chainType, request.transactionHash, AccountType.DEPOSIT) + return transferService.getTransferData(address, request.chainType, request.transactionHash, AccountType.DEPOSIT, request.accountLogId) .then(Mono.just(ResponseEntity.ok().build())) .onErrorResume { - Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build()) + Mono.just(ResponseEntity.status(HttpStatus.BAD_REQUEST).build()) } } @@ -37,13 +37,9 @@ class AdminController( @RequestBody request: WithdrawERC20Request, ): Mono> { println("Received withdraw request for address: $address with amount: ${request.amount}") - return web3jService.processTransactionERC20(address, request.amount, request.chainType) + return web3jService.createTransactionERC20(address, request.amount, request.chainType,request.accountLogId) .doOnSuccess { println("Transaction successful") } - .then(Mono.just(ResponseEntity.ok().build())) - .doOnError { e -> - println("Error in withdrawERC20: ${e.message}") - e.printStackTrace() - } + .then(Mono.just(ResponseEntity.ok().build())) } @@ -53,11 +49,8 @@ class AdminController( @RequestParam address: String, @RequestBody request: WithdrawERC721Request, ): Mono> { - return web3jService.processTransactionERC721(address, request.nftId) - .then(Mono.just(ResponseEntity.ok().build())) - .onErrorResume { - Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build()) - } + return web3jService.createTransactionERC721(address, request.nftId,request.accountLogId) + .then(Mono.just(ResponseEntity.ok().build())) } diff --git a/src/main/kotlin/com/api/admin/controller/dto/DepositRequest.kt b/src/main/kotlin/com/api/admin/controller/dto/DepositRequest.kt index 9ffe758..5096e72 100644 --- a/src/main/kotlin/com/api/admin/controller/dto/DepositRequest.kt +++ b/src/main/kotlin/com/api/admin/controller/dto/DepositRequest.kt @@ -5,5 +5,6 @@ import com.api.admin.enums.ChainType data class DepositRequest( val chainType: ChainType, val transactionHash: String, + val accountLogId: Long, ) diff --git a/src/main/kotlin/com/api/admin/controller/dto/WithdrawERC20Request.kt b/src/main/kotlin/com/api/admin/controller/dto/WithdrawERC20Request.kt index 9b26e87..7775ebd 100644 --- a/src/main/kotlin/com/api/admin/controller/dto/WithdrawERC20Request.kt +++ b/src/main/kotlin/com/api/admin/controller/dto/WithdrawERC20Request.kt @@ -7,4 +7,5 @@ import java.math.BigInteger data class WithdrawERC20Request( val chainType: ChainType, val amount: BigDecimal, + val accountLogId: Long, ) diff --git a/src/main/kotlin/com/api/admin/controller/dto/WithdrawERC721Request.kt b/src/main/kotlin/com/api/admin/controller/dto/WithdrawERC721Request.kt index a8879f0..ec7f254 100644 --- a/src/main/kotlin/com/api/admin/controller/dto/WithdrawERC721Request.kt +++ b/src/main/kotlin/com/api/admin/controller/dto/WithdrawERC721Request.kt @@ -2,4 +2,5 @@ package com.api.admin.controller.dto data class WithdrawERC721Request( val nftId: Long, + val accountLogId: Long, ) diff --git a/src/main/kotlin/com/api/admin/domain/transferFailLog/TransferFailLog.kt b/src/main/kotlin/com/api/admin/domain/transferFailLog/TransferFailLog.kt index af6894f..fabcb0a 100644 --- a/src/main/kotlin/com/api/admin/domain/transferFailLog/TransferFailLog.kt +++ b/src/main/kotlin/com/api/admin/domain/transferFailLog/TransferFailLog.kt @@ -1,5 +1,6 @@ package com.api.admin.domain.transferFailLog +import com.api.admin.enums.TransferType import org.springframework.data.annotation.Id import org.springframework.data.relational.core.mapping.Table import java.sql.Timestamp @@ -11,4 +12,5 @@ data class TransferFailLog( val timestamp: Long? = System.currentTimeMillis(), val transactionHash: String?, val errorMessage: String, + val transferType: TransferType, ) diff --git a/src/main/kotlin/com/api/admin/enums/Enum.kt b/src/main/kotlin/com/api/admin/enums/Enum.kt index 09711dd..678d475 100644 --- a/src/main/kotlin/com/api/admin/enums/Enum.kt +++ b/src/main/kotlin/com/api/admin/enums/Enum.kt @@ -24,7 +24,7 @@ enum class TokenType { MATIC, ETH } -enum class TransactionStatus { +enum class TransactionStatusType { SUCCESS, FAILURE } diff --git a/src/main/kotlin/com/api/admin/rabbitMQ/event/dto/AdminTransferDetailResponse.kt b/src/main/kotlin/com/api/admin/rabbitMQ/event/dto/AdminTransferDetailResponse.kt new file mode 100644 index 0000000..5d612b3 --- /dev/null +++ b/src/main/kotlin/com/api/admin/rabbitMQ/event/dto/AdminTransferDetailResponse.kt @@ -0,0 +1,10 @@ +package com.api.admin.rabbitMQ.event.dto + +import com.api.admin.enums.TransferType +import java.math.BigDecimal + +data class AdminTransferDetailResponse( + val nftId: Long?, + val transferType: TransferType, + val balance: BigDecimal?, +) diff --git a/src/main/kotlin/com/api/admin/rabbitMQ/event/dto/AdminTransferResponse.kt b/src/main/kotlin/com/api/admin/rabbitMQ/event/dto/AdminTransferResponse.kt index 5dc9251..f6fa81d 100644 --- a/src/main/kotlin/com/api/admin/rabbitMQ/event/dto/AdminTransferResponse.kt +++ b/src/main/kotlin/com/api/admin/rabbitMQ/event/dto/AdminTransferResponse.kt @@ -1,18 +1,43 @@ package com.api.admin.rabbitMQ.event.dto +import com.api.admin.domain.transfer.Transfer +import com.api.admin.domain.transferFailLog.TransferFailLog import com.api.admin.enums.AccountType import com.api.admin.enums.ChainType +import com.api.admin.enums.TransactionStatusType import com.api.admin.enums.TransferType +import com.api.admin.rabbitMQ.event.dto.AdminTransferResponse.Companion.toResponse import java.math.BigDecimal data class AdminTransferResponse( - val id: Long, - val walletAddress: String, - val nftId: Long?, - val timestamp: Long, + val accountLogId: Long, val accountType: AccountType, val transferType: TransferType, - val balance: BigDecimal?, - val chainType: ChainType, -) + val transactionStatusType: TransactionStatusType, + val adminTransferDetailResponse: AdminTransferDetailResponse? +) { + companion object { + fun Transfer.toResponse(accountId: Long) = AdminTransferResponse( + accountLogId = accountId, + accountType = this.accountType, + transferType = this.transferType, + transactionStatusType = TransactionStatusType.SUCCESS, + adminTransferDetailResponse = AdminTransferDetailResponse( + nftId = this.nftId, + transferType =this.transferType, + balance = this.balance + ) + ) + + fun TransferFailLog.toResponse(accountId: Long,accountType: AccountType) = AdminTransferResponse( + accountLogId = accountId, + accountType = accountType, + transferType = this.transferType, + transactionStatusType = TransactionStatusType.FAILURE, + adminTransferDetailResponse = null + + ) + + } +} diff --git a/src/main/kotlin/com/api/admin/service/TransferFailLogService.kt b/src/main/kotlin/com/api/admin/service/TransferFailLogService.kt deleted file mode 100644 index d4fbf37..0000000 --- a/src/main/kotlin/com/api/admin/service/TransferFailLogService.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.api.admin.service - -import com.api.admin.domain.transferFailLog.TransferFailLog -import com.api.admin.domain.transferFailLog.TransferFailLogRepository -import org.springframework.stereotype.Service -import reactor.core.publisher.Mono - -@Service -class TransferFailLogService( - private val transferFailLogRepository: TransferFailLogRepository, -) { - - fun save(wallet: String, transactionHash: String?, message: String): Mono { - return transferFailLogRepository.save( - TransferFailLog( - wallet = wallet, - transactionHash = transactionHash, - errorMessage = message - ) - ).doOnSuccess { - - }.then() - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/api/admin/service/TransferFailService.kt b/src/main/kotlin/com/api/admin/service/TransferFailService.kt new file mode 100644 index 0000000..ddbe101 --- /dev/null +++ b/src/main/kotlin/com/api/admin/service/TransferFailService.kt @@ -0,0 +1,38 @@ +package com.api.admin.service + +import com.api.admin.domain.transferFailLog.TransferFailLog +import com.api.admin.domain.transferFailLog.TransferFailLogRepository +import com.api.admin.enums.AccountType +import com.api.admin.enums.TransferType +import com.api.admin.rabbitMQ.event.dto.AdminTransferCreatedEvent +import com.api.admin.rabbitMQ.event.dto.AdminTransferResponse.Companion.toResponse +import org.springframework.context.ApplicationEventPublisher +import org.springframework.stereotype.Service +import reactor.core.publisher.Mono + +@Service +class TransferFailService( + private val transferFailLogRepository: TransferFailLogRepository, + private val eventPublisher: ApplicationEventPublisher, +) { + + fun save(accountId: Long, + address: String, + transactionHash: String?, + message: String, + transferType: TransferType, + accountType: AccountType + ): Mono { + return transferFailLogRepository.save( + TransferFailLog( + wallet = address, + transactionHash = transactionHash, + errorMessage = message, + transferType = transferType + ) + ).doOnSuccess { + eventPublisher.publishEvent(AdminTransferCreatedEvent(this, it.toResponse(accountId,accountType))) + }.then() + + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/api/admin/service/TransferService.kt b/src/main/kotlin/com/api/admin/service/TransferService.kt index 6666091..e313e24 100644 --- a/src/main/kotlin/com/api/admin/service/TransferService.kt +++ b/src/main/kotlin/com/api/admin/service/TransferService.kt @@ -7,12 +7,13 @@ import com.api.admin.enums.ChainType import com.api.admin.enums.TransferType import com.api.admin.properties.AdminInfoProperties import com.api.admin.rabbitMQ.event.dto.AdminTransferCreatedEvent -import com.api.admin.rabbitMQ.event.dto.AdminTransferResponse +import com.api.admin.rabbitMQ.event.dto.AdminTransferResponse.Companion.toResponse import com.api.admin.service.dto.InfuraTransferDetail import org.springframework.context.ApplicationEventPublisher import org.springframework.stereotype.Service import reactor.core.publisher.Flux import reactor.core.publisher.Mono +import reactor.core.scheduler.Schedulers import java.math.BigDecimal import java.math.BigInteger import java.time.Instant @@ -25,32 +26,36 @@ class TransferService( private val nftService: NftService, private val adminInfoProperties: AdminInfoProperties, ) { - - private val transferEventSignature = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" - private val nativeTransferEventSignature = "0xe6497e3ee548a3372136af2fcb0696db31fc6cf20260707645068bd3fe97f3c4" - + companion object{ + const val transferEventSignature = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + const val nativeTransferEventSignature = "0xe6497e3ee548a3372136af2fcb0696db31fc6cf20260707645068bd3fe97f3c4" + } fun getTransferData( wallet: String, chainType: ChainType, transactionHash: String, accountType: AccountType, + accountLogId: Long ): Mono { return transferRepository.existsByTransactionHash(transactionHash) - .flatMap { - if (it) { + .flatMap { exists -> + if (exists) { Mono.error(IllegalStateException("Transaction already exists")) } else { - saveTransfer(wallet, chainType, transactionHash, accountType) - .doOnNext { transfer -> - eventPublisher.publishEvent(AdminTransferCreatedEvent(this, transfer.toResponse())) - } - .then() + Mono.defer { saveTransfer(wallet, chainType, transactionHash, accountType,accountLogId).then() } + .subscribeOn(Schedulers.boundedElastic()) + .then(Mono.empty()) } } } - fun saveTransfer(wallet: String, chainType: ChainType, transactionHash: String, accountType: AccountType): Flux { + fun saveTransfer(wallet: String, + chainType: ChainType, + transactionHash: String, + accountType: AccountType, + accountId: Long + ): Flux { return infuraApiService.getTransferLog(chainType, transactionHash) .flatMapMany { response -> val result = response.result @@ -61,21 +66,12 @@ class TransferService( Flux.error(IllegalStateException("Transaction logs not found for transaction hash: $transactionHash")) } } - .flatMap { transfer -> transferRepository.save(transfer) } + .flatMap { transfer -> transferRepository.save(transfer).doOnNext { println("having ?") } + .doOnSuccess {transfer -> + eventPublisher.publishEvent(AdminTransferCreatedEvent(this, transfer.toResponse(accountId))) + }.then() + } } - - - private fun Transfer.toResponse() = AdminTransferResponse( - id = this.id!!, - walletAddress = this.wallet, - nftId = this.nftId, - timestamp = this.timestamp, - accountType = this.accountType, - transferType = this.transferType, - balance = this.balance, - chainType = this.chainType, - ) - fun InfuraTransferDetail.toEntity(wallet: String, accountType: AccountType, chainType: ChainType): Mono { return Mono.just(this) .flatMap { log -> @@ -83,8 +79,8 @@ class TransferService( when { log.topics[0] == nativeTransferEventSignature -> handleERC20Transfer(log, wallet, accountType, chainType,TransferType.NATIVE) -// log.topics[0] == transferEventSignature && log.topics.size == 3 -> -// handleERC20Transfer(log, wallet, accountType, chainType, TransferType.ERC20) + // log.topics[0] == transferEventSignature && log.topics.size == 3 -> + // handleERC20Transfer(log, wallet, accountType, chainType, TransferType.ERC20) log.topics[0] == transferEventSignature && log.topics.size == 4 -> handleERC721Transfer(log, wallet, accountType, chainType) else -> Mono.empty() @@ -97,7 +93,7 @@ class TransferService( val to = parseAddress(log.topics[3]) val amount = when (transferType) { TransferType.NATIVE -> parseNativeTransferAmount(log.data) -// TransferType.ERC20 -> toBigDecimal(log.data) + // TransferType.ERC20 -> toBigDecimal(log.data) else -> BigDecimal.ZERO } diff --git a/src/main/kotlin/com/api/admin/service/Web3jService.kt b/src/main/kotlin/com/api/admin/service/Web3jService.kt index e1b3b64..a241a90 100644 --- a/src/main/kotlin/com/api/admin/service/Web3jService.kt +++ b/src/main/kotlin/com/api/admin/service/Web3jService.kt @@ -3,11 +3,13 @@ package com.api.admin.service import com.api.admin.domain.nft.NftRepository import com.api.admin.enums.AccountType import com.api.admin.enums.ChainType +import com.api.admin.enums.TransferType import com.api.admin.properties.AdminInfoProperties import com.api.admin.util.Util.getChainId import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional import org.web3j.abi.FunctionEncoder import org.web3j.abi.datatypes.Address import org.web3j.abi.datatypes.Function @@ -27,20 +29,20 @@ class Web3jService( private val infuraApiService: InfuraApiService, private val transferService: TransferService, private val nftRepository: NftRepository, - private val adminInfoProperties: AdminInfoProperties + private val adminInfoProperties: AdminInfoProperties, + private val transferFailService: TransferFailService, ) { -// fun createTransactionERC721(toAddress: String, nftId: Long): Mono { -// return Mono.defer { processTransactionERC721(toAddress, nftId) } -// .subscribeOn(Schedulers.boundedElastic()) -// .onErrorResume { error -> -// transferService.getTransferData(toAddress, nft.chainType, "failed_hash", AccountType.WITHDRAW, TransactionStatus.FAILURE, error.message ?: "Unknown error") -// } -// .then(Mono.empty()) -// } + fun createTransactionERC721(toAddress: String, nftId: Long,accountId: Long): Mono { + return Mono.defer { processTransactionERC721(toAddress, nftId, accountId) } + .subscribeOn(Schedulers.boundedElastic()) + .then(Mono.empty()) + } - fun processTransactionERC721(toAddress: String, nftId: Long): Mono { + @Transactional + fun processTransactionERC721(toAddress: String, nftId: Long,accountId: Long): Mono { + var transactionHash: String? = null return nftRepository.findById(nftId) .flatMap { nft -> val credentials = Credentials.create(adminInfoProperties.privatekey) @@ -52,12 +54,23 @@ class Web3jService( wallet = toAddress, chainType = nft.chainType, transactionHash = transactionHash, - accountType = AccountType.WITHDRAW + accountType = AccountType.WITHDRAW, + accountLogId = accountId, ) } } } .doOnError { e -> + e.message?.let { + transferFailService.save( + accountId, + toAddress, + transactionHash = transactionHash, + message = it, + transferType = TransferType.ERC721, + accountType = AccountType.WITHDRAW + ) + } println("Error in createTransactionERC721: ${e.message}") e.printStackTrace() } @@ -100,29 +113,41 @@ class Web3jService( } } -// fun createTransactionERC20(recipientAddress: String, amount: BigDecimal, chainType: ChainType): Mono { -// Mono.defer { processTransactionERC20(recipientAddress, amount, chainType) } -// .subscribeOn(Schedulers.boundedElastic()) -// .subscribe() -// return Mono.empty() -// } + fun createTransactionERC20(recipientAddress: String, amount: BigDecimal, chainType: ChainType,accountId: Long): Mono { + Mono.defer { processTransactionERC20(recipientAddress, amount, chainType,accountId) } + .subscribeOn(Schedulers.boundedElastic()) + .subscribe() + return Mono.empty() + } fun processTransactionERC20( - recipientAddress: String, + toAddress: String, amount: BigDecimal, - chainType: ChainType + chainType: ChainType, + accountId: Long, ): Mono { + var transactionHash: String? = null val credentials = Credentials.create(adminInfoProperties.privatekey) val weiAmount = amountToWei(amount) - return createERC20TransactionData(credentials, recipientAddress, weiAmount, chainType) + return createERC20TransactionData(credentials, toAddress, weiAmount, chainType) .flatMap { transactionHash -> println("transactionLog: $transactionHash") waitForTransactionReceipt(transactionHash, chainType) .flatMap { - transferService.getTransferData(recipientAddress, chainType, transactionHash, AccountType.WITHDRAW) + transferService.getTransferData(toAddress, chainType, transactionHash, AccountType.WITHDRAW,accountId) } } .doOnError { e -> + e.message?.let { + transferFailService.save( + accountId, + toAddress, + transactionHash = transactionHash, + message = it, + transferType = TransferType.ERC721, + accountType = AccountType.WITHDRAW + ) + } println("Error in createTransactionERC20: ${e.message}") e.printStackTrace() } diff --git a/src/main/kotlin/com/api/admin/service/dto/InfuraTransferResponse.kt b/src/main/kotlin/com/api/admin/service/dto/InfuraTransferResponse.kt index 9cec097..8016a04 100644 --- a/src/main/kotlin/com/api/admin/service/dto/InfuraTransferResponse.kt +++ b/src/main/kotlin/com/api/admin/service/dto/InfuraTransferResponse.kt @@ -1,8 +1,5 @@ package com.api.admin.service.dto -import java.math.BigDecimal -import java.math.BigInteger - data class InfuraTransferResponse( val jsonrpc: String, val id: String, diff --git a/src/main/kotlin/com/api/admin/util/Util.kt b/src/main/kotlin/com/api/admin/util/Util.kt index 1f34eb3..eec1cf5 100644 --- a/src/main/kotlin/com/api/admin/util/Util.kt +++ b/src/main/kotlin/com/api/admin/util/Util.kt @@ -15,5 +15,4 @@ object Util { } return chain } - } \ No newline at end of file diff --git a/src/main/resources/db/migration/V1__Initial_schema.sql b/src/main/resources/db/migration/V1__Initial_schema.sql index 33d2dd6..f9db82e 100644 --- a/src/main/resources/db/migration/V1__Initial_schema.sql +++ b/src/main/resources/db/migration/V1__Initial_schema.sql @@ -42,5 +42,6 @@ CREATE TABLE IF NOT EXISTS transfer_fail_log ( wallet VARCHAR(255) NOT NULL, timestamp bigint not null, transaction_hash VARCHAR(255), + transfer_type transfer_type NOT NULL, error_message varchar(1000) NOT NULL ); diff --git a/src/test/kotlin/com/api/admin/AdminServiceTest.kt b/src/test/kotlin/com/api/admin/AdminServiceTest.kt index 24bf486..06d065f 100644 --- a/src/test/kotlin/com/api/admin/AdminServiceTest.kt +++ b/src/test/kotlin/com/api/admin/AdminServiceTest.kt @@ -11,11 +11,13 @@ import com.api.admin.service.Web3jService import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.ActiveProfiles import java.math.BigDecimal import java.math.BigInteger import java.time.Instant @SpringBootTest +@ActiveProfiles("local") class AdminServiceTest( @Autowired private val transferService: TransferService, @Autowired private val rabbitMQSender: RabbitMQSender, @@ -36,10 +38,10 @@ class AdminServiceTest( println(res.toString()) } - @Test - fun saveTransfer() { - transferService.saveTransfer("0x01b72b4aa3f66f213d62d53e829bc172a6a72867",ChainType.POLYGON_MAINNET,"0x55fa4495f983e9f162b39b3df4dec8ebcff9aa05daee7b051c680ccfb49422a6",AccountType.DEPOSIT).next().block() - } + // @Test + // fun saveTransfer() { + // transferService.saveTransfer("0x01b72b4aa3f66f213d62d53e829bc172a6a72867",ChainType.POLYGON_MAINNET,"0x55fa4495f983e9f162b39b3df4dec8ebcff9aa05daee7b051c680ccfb49422a6",AccountType.DEPOSIT).next().block() + // } @Test fun test1() { @@ -50,52 +52,52 @@ class AdminServiceTest( } - @Test - fun deposit() { - val res = transferService.getTransferData("0x01b72b4aa3f66f213d62d53e829bc172a6a72867",ChainType.POLYGON_MAINNET,"0xed96d307d81fd04a752d222b0cf06397dff8fc87245e8764b6641d97bc36081c",AccountType.DEPOSIT) - .block() - - - println("res : " + res.toString()) - - } + // @Test + // fun deposit() { + // val res = transferService.getTransferData("0x01b72b4aa3f66f213d62d53e829bc172a6a72867",ChainType.POLYGON_MAINNET,"0xed96d307d81fd04a752d222b0cf06397dff8fc87245e8764b6641d97bc36081c",AccountType.DEPOSIT) + // .block() + // + // + // println("res : " + res.toString()) + // + // } private fun parseAddress(address: String): String { return "0x" + address.substring(26).padStart(40, '0') } - @Test - fun sendMessage() { - val response = AdminTransferResponse( - id= 1L, - walletAddress = "0x01b72b4aa3f66f213d62d53e829bc172a6a72867", - nftId = 3L, - timestamp = Instant.now().toEpochMilli(), - accountType = AccountType.DEPOSIT, - transferType = TransferType.ERC721, - balance = null, - chainType = ChainType.POLYGON_MAINNET - ) - rabbitMQSender.transferSend(response) - Thread.sleep(10000) - } - - @Test - fun sendMatic() { -// web3jService.createTransaction( -// "e9769d3c00032a83d703e03630edbfc3cb634b40b92e38ab2890d5e37f21bb15", -// "0x9bDeF468ae33b09b12a057B4c9211240D63BaE65", -// BigInteger("1000000000000000000") -// ) - - val transactionData = web3jService.createTransactionERC20( - "0x9bDeF468ae33b09b12a057B4c9211240D63BaE65", - BigDecimal("1000000000000000000"), - ChainType.POLYGON_MAINNET - ) -// val response = infuraApiService.getSend(ChainType.POLYGON_MAINNET,transactionData).block() -// println(response) - } + // @Test + // fun sendMessage() { + // val response = AdminTransferResponse( + // id= 1L, + // walletAddress = "0x01b72b4aa3f66f213d62d53e829bc172a6a72867", + // nftId = 3L, + // timestamp = Instant.now().toEpochMilli(), + // accountType = AccountType.DEPOSIT, + // transferType = TransferType.ERC721, + // balance = null, + // chainType = ChainType.POLYGON_MAINNET + // ) + // rabbitMQSender.transferSend(response) + // Thread.sleep(10000) + // } + +// @Test +// fun sendMatic() { +// // web3jService.createTransaction( +// // "e9769d3c00032a83d703e03630edbfc3cb634b40b92e38ab2890d5e37f21bb15", +// // "0x9bDeF468ae33b09b12a057B4c9211240D63BaE65", +// // BigInteger("1000000000000000000") +// // ) +// +// val transactionData = web3jService.createTransactionERC20( +// "0x9bDeF468ae33b09b12a057B4c9211240D63BaE65", +// BigDecimal("1000000000000000000"), +// ChainType.POLYGON_MAINNET +// ) +// // val response = infuraApiService.getSend(ChainType.POLYGON_MAINNET,transactionData).block() +// // println(response) +// } @Test fun infuraTest() { @@ -104,36 +106,37 @@ class AdminServiceTest( } - @Test - fun createTransactionERC721() { - // val res = web3jService.createTransactionERC721("0xbc0c96c8d12a149cac4f7688f740ef21b2c8fd23","0x01b72b4aa3f66f213d62d53e829bc172a6a72867", - // BigInteger("0"),ChainType.POLYGON_MAINNET).block() - // - // println(res.toString()) - - // 0x1b0f6c70528addc34a5f17d0c7df59d932c27e85d29af14a957567fff29ef267 - val res1 = transferService.getTransferData( - wallet = "0x01b72b4aa3f66f213d62d53e829bc172a6a72867", - transactionHash = "0x1b0f6c70528addc34a5f17d0c7df59d932c27e85d29af14a957567fff29ef267", - chainType = ChainType.POLYGON_MAINNET, - accountType = AccountType.WITHDRAW - ).block() - - } - - @Test - fun createTransactionERC20() { - val res = web3jService.createTransactionERC20("0x01b72b4aa3f66f213d62d53e829bc172a6a72867", amount = BigDecimal("1000000000000"),ChainType.POLYGON_AMOY).block() - println(res.toString()) - } + // @Test + // fun createTransactionERC721() { + // // val res = web3jService.createTransactionERC721("0xbc0c96c8d12a149cac4f7688f740ef21b2c8fd23","0x01b72b4aa3f66f213d62d53e829bc172a6a72867", + // // BigInteger("0"),ChainType.POLYGON_MAINNET).block() + // // + // // println(res.toString()) + // + // // 0x1b0f6c70528addc34a5f17d0c7df59d932c27e85d29af14a957567fff29ef267 + // val res1 = transferService.getTransferData( + // wallet = "0x01b72b4aa3f66f213d62d53e829bc172a6a72867", + // transactionHash = "0x1b0f6c70528addc34a5f17d0c7df59d932c27e85d29af14a957567fff29ef267", + // chainType = ChainType.POLYGON_MAINNET, + // accountType = AccountType.WITHDRAW + // ).block() + // + // } + + // @Test + // fun createTransactionERC20() { + // val res = web3jService.createTransactionERC20("0x01b72b4aa3f66f213d62d53e829bc172a6a72867", amount = BigDecimal("1000000000000"),ChainType.POLYGON_AMOY).block() + // println(res.toString()) + // } @Test fun test1sd() { - val res = transferService.getTransferData( + transferService.getTransferData( wallet = "0x01b72b4aa3f66f213d62d53e829bc172a6a72867", chainType = ChainType.POLYGON_AMOY, - transactionHash = "0xe9d42662999109de038dc6701acf297ff7e693eaf241664323a44f71d626890e", - accountType = AccountType.WITHDRAW + transactionHash = "0x21b77e341d43a1b58f5a40c648e4878e532ec1011efcec3fe82a7e23d35c5b2f", + accountType = AccountType.WITHDRAW, + accountLogId = 13 ).block() Thread.sleep(50000)