<commit_before>package example.ark_smartbridge_listener.eth_contract_deploy;

import example.ark_smartbridge_listener.EthBalanceScriptExecutor;
import example.ark_smartbridge_listener.GetBalanceResult;
import example.ark_smartbridge_listener.ScriptExecutorService;
import example.ark_smartbridge_listener.ServiceInfoView;
import example.ark_smartbridge_listener.ark_listener.CreateMessageRequest;
import example.ark_smartbridge_listener.exchange_rate_service.ExchangeRateService;
import io.ark.ark_client.ArkClient;
import io.ark.ark_client.Transaction;
import lib.IOUtilsWrapper;
import example.ark_smartbridge_listener.NotFoundException;
import example.ark_smartbridge_listener.ark_listener.TransactionMatch;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.UUID;

@CrossOrigin
@RestController
@Transactional
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Slf4j
public class EthContractDeployController {

    private final BigDecimal arkFlatFee = new BigDecimal("2.0000000");
    private final BigDecimal arkFeePercent = new BigDecimal("2.25");

    // todo: get the current eth per gas rate from the network http://ethgasstation.info/
    private final BigDecimal ethPerGas = new BigDecimal("0.00000002");

    private final BigDecimal arkTransactionFee = new BigDecimal("0.10000000");

    private final BigInteger satoshisPerArk = new BigInteger("100000000");

    private final EthContractDeployContractRepository ethContractDeployContractRepository;
    private final EthContractDeployContractViewMapper ethContractDeployContractViewMapper;
    private final String serviceArkAddress;
    private final ScriptExecutorService scriptExecutorService;
    private final ExchangeRateService exchangeRateService;
    private final ArkClient arkClient;
    private final String serviceArkPassphrase;
    private final RetryTemplate arkClientRetryTemplate;
    private final EthBalanceScriptExecutor ethBalanceScriptExecutor;

    private final RestTemplate listenerRestTemplate = new RestTemplateBuilder()
        .rootUri("http://localhost:8080/")
        .build();

    @GetMapping("/eth-contract-deploy-service-info")
    public ServiceInfoView getServiceInfo() {
        GetBalanceResult getBalanceResult = ethBalanceScriptExecutor.execute();

        ServiceInfoView serviceInfoView = new ServiceInfoView();
        serviceInfoView.setCapacity(getBalanceResult.getBalance().toPlainString() + " Eth");
        serviceInfoView.setFlatFeeArk(arkFlatFee.toPlainString());
        serviceInfoView.setPercentFee(arkFeePercent.toPlainString());
        serviceInfoView.setStatus(ServiceInfoView.STATUS_UP);

        return serviceInfoView;
    }

    @PostMapping("/eth-contract-deploy-contracts")
    public EthContractDeployContractView postContract(
        @RequestParam("abiJson") MultipartFile abiJsonFile,
        @RequestParam("code") MultipartFile codeFile,
        @RequestParam("params") MultipartFile paramsFile,
        @RequestParam("gasLimit") Long gasLimit,
        @RequestParam("returnArkAddress") String returnArkAddress
    ) {
        String code = "0x" + IOUtilsWrapper.read(codeFile);
        String abiJson = IOUtilsWrapper.read(abiJsonFile);
        String paramsJson = IOUtilsWrapper.read(paramsFile);

        Long estimatedGasCost = scriptExecutorService.executeEstimateGas(code).getGasEstimate();
        if (gasLimit < estimatedGasCost) {
            // todo: return an error to the user and do not create contract message
            // it doesn't really matter if we continue on since the contract will get rejected
            // when ark transaction is sent, but we should be nice and return an error early to
            // avoid transaction fees for the client
        }

        BigDecimal estimatedEthCost = ethPerGas.multiply(new BigDecimal(estimatedGasCost));
        // todo: we should sanity check the exchange rate to prevent a bad rate being used
        BigDecimal arkPerEthExchangeRate = exchangeRateService.getRate("ETH", "ARK");
        BigDecimal estimatedArkCost = estimatedEthCost.multiply(arkPerEthExchangeRate);
        BigDecimal baseArkCost = estimatedArkCost.multiply(new BigDecimal("2.0")) // require a 2x buffer
            .add(arkTransactionFee);
        BigDecimal arkFeeTotal = baseArkCost.multiply(arkFeePercent.divide(new BigDecimal("100"), BigDecimal.ROUND_UP))
            .add(arkFlatFee);
        BigDecimal requiredArkCost = baseArkCost.add(arkFeeTotal);

        EthContractDeployContractEntity ethContractDeployContractEntity = new EthContractDeployContractEntity();
        ethContractDeployContractEntity.setStatus(EthContractDeployContractEntity.STATUS_PENDING);
        ethContractDeployContractEntity.setToken(UUID.randomUUID().toString());
        ethContractDeployContractEntity.setContractCode(code);
        ethContractDeployContractEntity.setContractAbiJson(abiJson);
        ethContractDeployContractEntity.setContractParamsJson(paramsJson);
        ethContractDeployContractEntity.setGasLimit(gasLimit);
        ethContractDeployContractEntity.setArkPerEthExchangeRate(arkPerEthExchangeRate.setScale(8, BigDecimal.ROUND_UP));
        ethContractDeployContractEntity.setEstimatedGasCost(estimatedGasCost);
        ethContractDeployContractEntity.setEstimatedEthCost(estimatedEthCost.setScale(8, BigDecimal.ROUND_UP));
        ethContractDeployContractEntity.setArkFlatFee(arkFlatFee);
        ethContractDeployContractEntity.setArkFeePercent(arkFeePercent);
        ethContractDeployContractEntity.setArkFeeTotal(arkFeeTotal);
        ethContractDeployContractEntity.setRequiredArkCost(requiredArkCost.setScale(8, BigDecimal.ROUND_UP));
        ethContractDeployContractEntity.setCreatedAt(ZonedDateTime.from(Instant.now().atOffset(ZoneOffset.UTC)));
        ethContractDeployContractEntity.setServiceArkAddress(serviceArkAddress);
        ethContractDeployContractEntity.setReturnArkAddress(returnArkAddress);
        ethContractDeployContractRepository.save(ethContractDeployContractEntity);

        // Register contract message with listener so we get a callback when a matching ark transaction is found
        CreateMessageRequest createMessageRequest = new CreateMessageRequest();
        createMessageRequest.setCallbackUrl("http://localhost:8080/eth-contract-deploy-contracts/ark-transactions");
        createMessageRequest.setToken(ethContractDeployContractEntity.getToken());
        listenerRestTemplate.postForObject("/messages", createMessageRequest, Void.class);

        return ethContractDeployContractViewMapper.map(ethContractDeployContractEntity);
    }

    @GetMapping("/eth-contract-deploy-contracts/{token}")
    public EthContractDeployContractView getContract(@PathVariable String token) {
        EthContractDeployContractEntity ethContractDeployContractEntity = getContractMessageOrThrowNotFound(token);

        return ethContractDeployContractViewMapper.map(ethContractDeployContractEntity);
    }

    @PostMapping("/eth-contract-deploy-contracts/ark-transactions")
    public void postArkTransactions(@RequestBody TransactionMatch transactionMatch) {
        String token = transactionMatch.getToken();
        EthContractDeployContractEntity ethContractDeployContractEntity = getContractMessageOrThrowNotFound(token);

        // Skip already processed transactions
        if (! ethContractDeployContractEntity.getStatus().equals(EthContractDeployContractEntity.STATUS_PENDING)) {
            return;
        }

        String code = ethContractDeployContractEntity.getContractCode();

        Long estimatedGasCost = scriptExecutorService.executeEstimateGas(code).getGasEstimate();
        BigDecimal estimatedEthCost = ethPerGas.multiply(new BigDecimal(estimatedGasCost));
        // todo: we should sanity check the exchange rate to prevent a bad rate being used
        BigDecimal arkPerEthExchangeRate = exchangeRateService.getRate("ETH", "ARK");
        BigDecimal estimatedArkCost = estimatedEthCost.multiply(arkPerEthExchangeRate);

        BigDecimal baseArkCost = estimatedArkCost.multiply(new BigDecimal("2.0")) // require a 2x buffer
            .add(arkTransactionFee);
        BigDecimal arkFeeTotal = baseArkCost
            .add(baseArkCost.multiply(arkFeePercent.divide(new BigDecimal("100"), BigDecimal.ROUND_UP)))
            .add(arkFlatFee);
        BigDecimal requiredArkCost = baseArkCost.add(arkFeeTotal);

        Transaction transaction = arkClientRetryTemplate.execute(retryContext ->
            arkClient.getTransaction(transactionMatch.getArkTransactionId()));<commit_msg>fix maths<commit_after>package example.ark_smartbridge_listener.eth_contract_deploy;

import example.ark_smartbridge_listener.EthBalanceScriptExecutor;
import example.ark_smartbridge_listener.GetBalanceResult;
import example.ark_smartbridge_listener.ScriptExecutorService;
import example.ark_smartbridge_listener.ServiceInfoView;
import example.ark_smartbridge_listener.ark_listener.CreateMessageRequest;
import example.ark_smartbridge_listener.exchange_rate_service.ExchangeRateService;
import io.ark.ark_client.ArkClient;
import io.ark.ark_client.Transaction;
import lib.IOUtilsWrapper;
import example.ark_smartbridge_listener.NotFoundException;
import example.ark_smartbridge_listener.ark_listener.TransactionMatch;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.UUID;

@CrossOrigin
@RestController
@Transactional
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Slf4j
public class EthContractDeployController {

    private final BigDecimal arkFlatFee = new BigDecimal("2.0000000");
    private final BigDecimal arkFeePercent = new BigDecimal("2.25");

    // todo: get the current eth per gas rate from the network http://ethgasstation.info/
    private final BigDecimal ethPerGas = new BigDecimal("0.00000002");

    private final BigDecimal arkTransactionFee = new BigDecimal("0.10000000");

    private final BigInteger satoshisPerArk = new BigInteger("100000000");

    private final EthContractDeployContractRepository ethContractDeployContractRepository;
    private final EthContractDeployContractViewMapper ethContractDeployContractViewMapper;
    private final String serviceArkAddress;
    private final ScriptExecutorService scriptExecutorService;
    private final ExchangeRateService exchangeRateService;
    private final ArkClient arkClient;
    private final String serviceArkPassphrase;
    private final RetryTemplate arkClientRetryTemplate;
    private final EthBalanceScriptExecutor ethBalanceScriptExecutor;

    private final RestTemplate listenerRestTemplate = new RestTemplateBuilder()
        .rootUri("http://localhost:8080/")
        .build();

    @GetMapping("/eth-contract-deploy-service-info")
    public ServiceInfoView getServiceInfo() {
        GetBalanceResult getBalanceResult = ethBalanceScriptExecutor.execute();

        ServiceInfoView serviceInfoView = new ServiceInfoView();
        serviceInfoView.setCapacity(getBalanceResult.getBalance().toPlainString() + " Eth");
        serviceInfoView.setFlatFeeArk(arkFlatFee.toPlainString());
        serviceInfoView.setPercentFee(arkFeePercent.toPlainString());
        serviceInfoView.setStatus(ServiceInfoView.STATUS_UP);

        return serviceInfoView;
    }

    @PostMapping("/eth-contract-deploy-contracts")
    public EthContractDeployContractView postContract(
        @RequestParam("abiJson") MultipartFile abiJsonFile,
        @RequestParam("code") MultipartFile codeFile,
        @RequestParam("params") MultipartFile paramsFile,
        @RequestParam("gasLimit") Long gasLimit,
        @RequestParam("returnArkAddress") String returnArkAddress
    ) {
        String code = "0x" + IOUtilsWrapper.read(codeFile);
        String abiJson = IOUtilsWrapper.read(abiJsonFile);
        String paramsJson = IOUtilsWrapper.read(paramsFile);

        Long estimatedGasCost = scriptExecutorService.executeEstimateGas(code).getGasEstimate();
        if (gasLimit < estimatedGasCost) {
            // todo: return an error to the user and do not create contract message
            // it doesn't really matter if we continue on since the contract will get rejected
            // when ark transaction is sent, but we should be nice and return an error early to
            // avoid transaction fees for the client
        }

        BigDecimal estimatedEthCost = ethPerGas.multiply(new BigDecimal(estimatedGasCost));
        // todo: we should sanity check the exchange rate to prevent a bad rate being used
        BigDecimal arkPerEthExchangeRate = exchangeRateService.getRate("ETH", "ARK");
        BigDecimal estimatedArkCost = estimatedEthCost.multiply(arkPerEthExchangeRate);
        BigDecimal baseArkCost = estimatedArkCost.multiply(new BigDecimal("2.0")) // require a 2x buffer
            .add(arkTransactionFee);
        BigDecimal arkFeeTotal = baseArkCost.multiply(arkFeePercent.divide(new BigDecimal("100"), BigDecimal.ROUND_UP))
            .add(arkFlatFee);
        BigDecimal requiredArkCost = baseArkCost.add(arkFeeTotal);

        EthContractDeployContractEntity ethContractDeployContractEntity = new EthContractDeployContractEntity();
        ethContractDeployContractEntity.setStatus(EthContractDeployContractEntity.STATUS_PENDING);
        ethContractDeployContractEntity.setToken(UUID.randomUUID().toString());
        ethContractDeployContractEntity.setContractCode(code);
        ethContractDeployContractEntity.setContractAbiJson(abiJson);
        ethContractDeployContractEntity.setContractParamsJson(paramsJson);
        ethContractDeployContractEntity.setGasLimit(gasLimit);
        ethContractDeployContractEntity.setArkPerEthExchangeRate(arkPerEthExchangeRate.setScale(8, BigDecimal.ROUND_UP));
        ethContractDeployContractEntity.setEstimatedGasCost(estimatedGasCost);
        ethContractDeployContractEntity.setEstimatedEthCost(estimatedEthCost.setScale(8, BigDecimal.ROUND_UP));
        ethContractDeployContractEntity.setArkFlatFee(arkFlatFee);
        ethContractDeployContractEntity.setArkFeePercent(arkFeePercent);
        ethContractDeployContractEntity.setArkFeeTotal(arkFeeTotal);
        ethContractDeployContractEntity.setRequiredArkCost(requiredArkCost.setScale(8, BigDecimal.ROUND_UP));
        ethContractDeployContractEntity.setCreatedAt(ZonedDateTime.from(Instant.now().atOffset(ZoneOffset.UTC)));
        ethContractDeployContractEntity.setServiceArkAddress(serviceArkAddress);
        ethContractDeployContractEntity.setReturnArkAddress(returnArkAddress);
        ethContractDeployContractRepository.save(ethContractDeployContractEntity);

        // Register contract message with listener so we get a callback when a matching ark transaction is found
        CreateMessageRequest createMessageRequest = new CreateMessageRequest();
        createMessageRequest.setCallbackUrl("http://localhost:8080/eth-contract-deploy-contracts/ark-transactions");
        createMessageRequest.setToken(ethContractDeployContractEntity.getToken());
        listenerRestTemplate.postForObject("/messages", createMessageRequest, Void.class);

        return ethContractDeployContractViewMapper.map(ethContractDeployContractEntity);
    }

    @GetMapping("/eth-contract-deploy-contracts/{token}")
    public EthContractDeployContractView getContract(@PathVariable String token) {
        EthContractDeployContractEntity ethContractDeployContractEntity = getContractMessageOrThrowNotFound(token);

        return ethContractDeployContractViewMapper.map(ethContractDeployContractEntity);
    }

    @PostMapping("/eth-contract-deploy-contracts/ark-transactions")
    public void postArkTransactions(@RequestBody TransactionMatch transactionMatch) {
        String token = transactionMatch.getToken();
        EthContractDeployContractEntity ethContractDeployContractEntity = getContractMessageOrThrowNotFound(token);

        // Skip already processed transactions
        if (! ethContractDeployContractEntity.getStatus().equals(EthContractDeployContractEntity.STATUS_PENDING)) {
            return;
        }

        String code = ethContractDeployContractEntity.getContractCode();

        Long estimatedGasCost = scriptExecutorService.executeEstimateGas(code).getGasEstimate();
        BigDecimal estimatedEthCost = ethPerGas.multiply(new BigDecimal(estimatedGasCost));
        // todo: we should sanity check the exchange rate to prevent a bad rate being used
        BigDecimal arkPerEthExchangeRate = exchangeRateService.getRate("ETH", "ARK");
        BigDecimal estimatedArkCost = estimatedEthCost.multiply(arkPerEthExchangeRate);

        BigDecimal baseArkCost = estimatedArkCost.multiply(new BigDecimal("2.0")) // require a 2x buffer
            .add(arkTransactionFee);
        BigDecimal arkFeeTotal = baseArkCost.multiply(arkFeePercent.divide(new BigDecimal("100"), BigDecimal.ROUND_UP))
            .add(arkFlatFee);
        BigDecimal requiredArkCost = baseArkCost.add(arkFeeTotal);

        Transaction transaction = arkClientRetryTemplate.execute(retryContext ->
            arkClient.getTransaction(transactionMatch.getArkTransactionId()));
{'fraction_non_alphanumeric': 0.0764240506329114, 'fraction_numerical': 0.006856540084388186, 'mean_word_length': 5.042383683875079, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 8, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5716096', 'n_tokens_mistral': 5034, 'n_tokens_neox': 4875, 'n_words': 887}