Esempio n. 1
0
        public async void GetRedeemedEventTest()
        {
            var api = new EtherScanApi(Common.EthTestNet);

            var eventSignatureHash = EventSignatureExtractor.GetSignatureHash <RedeemedEventDTO>();

            var eventsAsyncResult = await api.GetContractEventsAsync(
                address : "0x527d1049837edf5f99c287a41a87702686082bf8",
                fromBlock : Common.EthTestNet.SwapContractBlockNumber,
                toBlock : ulong.MaxValue,
                topic0 : eventSignatureHash,
                topic1 : "0x7ca4344b5d8e624917b6b0cee015bab65397349062ec2fcdbaebc25d5e1cbb4d")
                                    .ConfigureAwait(false);

            Assert.False(eventsAsyncResult.HasError);

            var events = eventsAsyncResult.Value?.ToList();

            Assert.NotNull(events);
            Assert.Single(events);
            Assert.True(events.First().IsRedeemedEvent());

            var redeemedEvent = events.First().ParseRedeemedEvent();

            Assert.NotNull(redeemedEvent);
            Assert.Equal("7ca4344b5d8e624917b6b0cee015bab65397349062ec2fcdbaebc25d5e1cbb4d", redeemedEvent.HashedSecret.ToHexString());
            Assert.Equal("f53c77f812c16243d3cdffc48fc4fd5a1f36541db9409b112a0e56436fc7fa35", redeemedEvent.Secret.ToHexString());
        }
Esempio n. 2
0
        public static ERC20InitiatedEventDTO ParseERC20InitiatedEvent(
            this EtherScanApi.ContractEvent contractEvent)
        {
            var eventSignatureHash = EventSignatureExtractor.GetSignatureHash <ERC20InitiatedEventDTO>();

            if (contractEvent.Topics == null ||
                contractEvent.Topics.Count != 4 ||
                contractEvent.EventSignatureHash() != eventSignatureHash)
            {
                throw new Exception("Invalid contract event");
            }

            var initiatorHex  = contractEvent.HexData.Substring(PrefixOffset, TopicSizeInHex);
            var refundTimeHex = contractEvent.HexData.Substring(PrefixOffset + TopicSizeInHex, TopicSizeInHex);
            var countdownHex  = contractEvent.HexData.Substring(PrefixOffset + 2 * TopicSizeInHex, TopicSizeInHex);
            var valueHex      = contractEvent.HexData.Substring(PrefixOffset + 3 * TopicSizeInHex, TopicSizeInHex);
            var redeemFeeHex  = contractEvent.HexData.Substring(PrefixOffset + 4 * TopicSizeInHex, TopicSizeInHex);
            var active        = contractEvent.HexData.Substring(PrefixOffset + 5 * TopicSizeInHex, TopicSizeInHex);

            return(new ERC20InitiatedEventDTO
            {
                HashedSecret = Hex.FromString(contractEvent.Topics[1], true),
                ERC20Contract =
                    $"0x{contractEvent.Topics[2].Substring(contractEvent.Topics[2].Length - AddressLengthInHex)}",
                Participant =
                    $"0x{contractEvent.Topics[3].Substring(contractEvent.Topics[3].Length - AddressLengthInHex)}",
                Initiator =
                    $"0x{initiatorHex.Substring(initiatorHex.Length - AddressLengthInHex)}",
                RefundTimestamp = new HexBigInteger(refundTimeHex).Value,
                Countdown = new HexBigInteger(countdownHex).Value,
                Value = new HexBigInteger(valueHex).Value,
                RedeemFee = new HexBigInteger(redeemFeeHex).Value,
                Active = new HexBigInteger(active).Value != 0
            });
        }
Esempio n. 3
0
        public async void GetAddedEventTest()
        {
            var api = new EtherScanApi(Common.EthTestNet);

            var eventSignatureHash = EventSignatureExtractor.GetSignatureHash <AddedEventDTO>();

            var eventsAsyncResult = await api.GetContractEventsAsync(
                address : "0x527d1049837edf5f99c287a41a87702686082bf8",
                fromBlock : Common.EthTestNet.SwapContractBlockNumber,
                toBlock : ulong.MaxValue,
                topic0 : eventSignatureHash,
                topic1 : "0xbe51acca480dba043159355d597e39744ad7140d325f6cb3c1554db6b33947d6")
                                    .ConfigureAwait(false);

            Assert.False(eventsAsyncResult.HasError);

            var events = eventsAsyncResult.Value?.ToList();

            Assert.NotNull(events);
            Assert.Single(events);
            Assert.True(events.First().IsAddedEvent());

            var addedEvent = events.First().ParseAddedEvent();

            Assert.NotNull(addedEvent);
            Assert.Equal("be51acca480dba043159355d597e39744ad7140d325f6cb3c1554db6b33947d6", addedEvent.HashedSecret.ToHexString());
            Assert.Equal("0xe4aec93f3c0807b66b3fd043623e21dbbb0a3a82", addedEvent.Initiator);
            Assert.Equal(289637150000000000, (long)addedEvent.Value);
        }
Esempio n. 4
0
        public async void GetInitiatedEventTest()
        {
            var api = new EtherScanApi(Common.EthTestNet);

            var eventSignatureHash = EventSignatureExtractor.GetSignatureHash <InitiatedEventDTO>();

            var eventsAsyncResult = await api.GetContractEventsAsync(
                address : "0x527d1049837edf5f99c287a41a87702686082bf8",
                fromBlock : Common.EthTestNet.SwapContractBlockNumber,
                toBlock : ulong.MaxValue,
                topic0 : eventSignatureHash,
                topic1 : "0x87639bcb4d5e61e52398acb13181ddec825744f8fd90a3f8efa68c129a968d0f")
                                    .ConfigureAwait(false);

            Assert.False(eventsAsyncResult.HasError);

            var events = eventsAsyncResult.Value?.ToList();

            Assert.NotNull(events);
            Assert.Single(events);
            Assert.True(events.First().IsInitiatedEvent());

            var initiatedEvent = events.First().ParseInitiatedEvent();

            Assert.NotNull(initiatedEvent);
            Assert.Equal("87639bcb4d5e61e52398acb13181ddec825744f8fd90a3f8efa68c129a968d0f", initiatedEvent.HashedSecret.ToHexString());
            Assert.Equal("0xcd38a31acf4db224a20052acb1934c993680b3e2", initiatedEvent.Participant);
            Assert.Equal("0x981c7251a11a1614d4c70c0f3507bbda54808065", initiatedEvent.Initiator);
            Assert.Equal(1569198635, (long)initiatedEvent.RefundTimestamp);
            Assert.Equal(27645001080510000, (long)initiatedEvent.Value);
            Assert.Equal(0, (long)initiatedEvent.RedeemFee);
        }
Esempio n. 5
0
        public static async Task <BigInteger> GetTransferValue(
            Currency currency,
            string from,
            string to,
            string blockNumber,
            CancellationToken cancellationToken = default)
        {
            try
            {
                Log.Debug("Ethereum ERC20: check transfer event");

                var erc20 = (EthereumTokens.ERC20)currency;

                var api = new EtherScanApi(erc20);

                var transferEventsResult = await api
                                           .GetContractEventsAsync(
                    address : erc20.ERC20ContractAddress,
                    fromBlock : (ulong)new HexBigInteger(blockNumber).Value,
                    toBlock : ulong.MaxValue,
                    topic0 : EventSignatureExtractor.GetSignatureHash <ERC20TransferEventDTO>(),
                    topic1 : "0x000000000000000000000000" + from,
                    topic2 : "0x000000000000000000000000" + to,
                    cancellationToken : cancellationToken)
                                           .ConfigureAwait(false);

                if (transferEventsResult == null)
                {
                    return(0);
                }

                if (transferEventsResult.HasError)
                {
                    return(0);
                }

                var events = transferEventsResult.Value?.ToList();

                if (events == null || !events.Any())
                {
                    return(0);
                }

                var transferEvent = events.Last().ParseERC20TransferEvent(); //todo: get last

                return(transferEvent.Value);
            }
            catch (Exception e)
            {
                Log.Error(e, "Ethereum ERC20 get transfer value task error");

                return(0);
            }
        }
Esempio n. 6
0
        public static async Task <Result <byte[]> > IsRedeemedAsync(
            Swap swap,
            Currency currency,
            CancellationToken cancellationToken = default)
        {
            try
            {
                Log.Debug("Ethereum: check redeem event");

                var ethereum = (Atomex.Ethereum)currency;

                var api = new EtherScanApi(ethereum);

                var redeemEventsResult = await api
                                         .GetContractEventsAsync(
                    address : ethereum.SwapContractAddress,
                    fromBlock : ethereum.SwapContractBlockNumber,
                    toBlock : ulong.MaxValue,
                    topic0 : EventSignatureExtractor.GetSignatureHash <RedeemedEventDTO>(),
                    topic1 : "0x" + swap.SecretHash.ToHexString(),
                    cancellationToken : cancellationToken)
                                         .ConfigureAwait(false);

                if (redeemEventsResult == null)
                {
                    return(new Error(Errors.RequestError, $"Connection error while getting contract {ethereum.SwapContractAddress} redeem events"));
                }

                if (redeemEventsResult.HasError)
                {
                    return(redeemEventsResult.Error);
                }

                var events = redeemEventsResult.Value?.ToList();

                if (events == null || !events.Any())
                {
                    return((byte[])null);
                }

                var secret = events.First().ParseRedeemedEvent().Secret;

                Log.Debug("Redeem event received with secret {@secret}", Convert.ToBase64String(secret));

                return(secret);
            }
            catch (Exception e)
            {
                Log.Error(e, "Ethereum redeem control task error");

                return(new Error(Errors.InternalError, e.Message));
            }
        }
        public static async Task <Result <bool> > IsRefundedAsync(
            Swap swap,
            CurrencyConfig currency,
            CancellationToken cancellationToken = default)
        {
            try
            {
                Log.Debug("Ethereum: check refund event");

                var ethereum = (Atomex.EthereumConfig)currency;

                var api = new EtherScanApi(ethereum);

                var refundEventsResult = await api
                                         .GetContractEventsAsync(
                    address : ethereum.SwapContractAddress,
                    fromBlock : ethereum.SwapContractBlockNumber,
                    toBlock : ulong.MaxValue,
                    topic0 : EventSignatureExtractor.GetSignatureHash <RefundedEventDTO>(),
                    topic1 : "0x" + swap.SecretHash.ToHexString(),
                    cancellationToken : cancellationToken)
                                         .ConfigureAwait(false);

                if (refundEventsResult == null)
                {
                    return(new Error(Errors.RequestError, $"Connection error while getting contract {ethereum.SwapContractAddress} refund event"));
                }

                if (refundEventsResult.HasError)
                {
                    return(refundEventsResult.Error);
                }

                var events = refundEventsResult.Value?.ToList();

                if (events == null || !events.Any())
                {
                    return(false);
                }

                Log.Debug("Refund event received for swap {@swap}", swap.Id);

                return(true);
            }
            catch (Exception e)
            {
                Log.Error(e, "Ethereum refund control task error");

                return(new Error(Errors.InternalError, e.Message));
            }
        }
Esempio n. 8
0
        public static ERC20RefundedEventDTO ParseRefundedEvent(
            this EtherScanApi.ContractEvent contractEvent)
        {
            var eventSignatureHash = EventSignatureExtractor.GetSignatureHash <ERC20RefundedEventDTO>();

            if (contractEvent.Topics == null ||
                contractEvent.Topics.Count != 2 ||
                contractEvent.EventSignatureHash() != eventSignatureHash)
            {
                throw new Exception("Invalid contract event");
            }

            return(new ERC20RefundedEventDTO
            {
                HashedSecret = Hex.FromString(contractEvent.Topics[1], true),
            });
        }
Esempio n. 9
0
        public static ERC20AddedEventDTO ParseERC20AddedEvent(
            this EtherScanApi.ContractEvent contractEvent)
        {
            var eventSignatureHash = EventSignatureExtractor.GetSignatureHash <ERC20AddedEventDTO>();

            if (contractEvent.Topics == null ||
                contractEvent.Topics.Count != 2 ||
                contractEvent.EventSignatureHash() != eventSignatureHash)
            {
                throw new Exception("Invalid contract event");
            }

            var initiatorHex = contractEvent.HexData.Substring(PrefixOffset, TopicSizeInHex);
            var valueHex     = contractEvent.HexData.Substring(PrefixOffset + TopicSizeInHex, TopicSizeInHex);

            return(new ERC20AddedEventDTO
            {
                HashedSecret = Hex.FromString(contractEvent.Topics[1], true),
                Initiator = $"0x{initiatorHex.Substring(initiatorHex.Length - AddressLengthInHex)}",
                Value = new HexBigInteger(valueHex).Value
            });
        }
Esempio n. 10
0
        public static ERC20TransferEventDTO ParseERC20TransferEvent(
            this EtherScanApi.ContractEvent contractEvent)
        {
            var eventSignatureHash = EventSignatureExtractor.GetSignatureHash <ERC20TransferEventDTO>();

            if (contractEvent.Topics == null ||
                contractEvent.Topics.Count != 3 ||
                contractEvent.EventSignatureHash() != eventSignatureHash)
            {
                throw new Exception("Invalid ERC20 token contract event");
            }

            var valueHex = contractEvent.HexData.Substring(PrefixOffset, TopicSizeInHex);

            return(new ERC20TransferEventDTO
            {
                From =
                    $"0x{contractEvent.Topics[1].Substring(contractEvent.Topics[1].Length - AddressLengthInHex)}",
                To =
                    $"0x{contractEvent.Topics[2].Substring(contractEvent.Topics[2].Length - AddressLengthInHex)}",
                Value = new HexBigInteger(valueHex).Value
            });
        }
Esempio n. 11
0
        public static async Task <Result <bool> > IsInitiatedAsync(
            Swap swap,
            Currency currency,
            long lockTimeInSec,
            CancellationToken cancellationToken = default)
        {
            try
            {
                Log.Debug("Ethereum ERC20: check initiated event");

                var erc20 = (EthereumTokens.ERC20)currency;

                var side = swap.Symbol
                           .OrderSideForBuyCurrency(swap.PurchasedCurrency)
                           .Opposite();

                var refundTimeStamp                   = new DateTimeOffset(swap.TimeStamp.ToUniversalTime().AddSeconds(lockTimeInSec)).ToUnixTimeSeconds();
                var requiredAmountInERC20             = AmountHelper.QtyToAmount(side, swap.Qty, swap.Price, erc20.DigitsMultiplier);
                var requiredAmountInDecimals          = erc20.TokensToTokenDigits(requiredAmountInERC20);
                var receivedAmountInDecimals          = new BigInteger(0);
                var requiredRewardForRedeemInDecimals = swap.IsAcceptor
                    ? erc20.TokensToTokenDigits(swap.RewardForRedeem)
                    : 0;

                var api = new EtherScanApi(erc20);

                var initiateEventsResult = await api
                                           .GetContractEventsAsync(
                    address : erc20.SwapContractAddress,
                    fromBlock : erc20.SwapContractBlockNumber,
                    toBlock : ulong.MaxValue,
                    topic0 : EventSignatureExtractor.GetSignatureHash <ERC20InitiatedEventDTO>(),
                    topic1 : "0x" + swap.SecretHash.ToHexString(),
                    topic2 : "0x000000000000000000000000" + erc20.ERC20ContractAddress.Substring(2),     //??
                    topic3 : "0x000000000000000000000000" + swap.ToAddress.Substring(2),
                    cancellationToken : cancellationToken)
                                           .ConfigureAwait(false);

                if (initiateEventsResult == null)
                {
                    return(new Error(Errors.RequestError, $"Connection error while trying to get contract {erc20.SwapContractAddress} initiate event"));
                }

                if (initiateEventsResult.HasError)
                {
                    return(initiateEventsResult.Error);
                }

                var events = initiateEventsResult.Value?.ToList();

                if (events == null || !events.Any())
                {
                    return(false);
                }

                var contractInitEvent = events.Last();

                var initiatedEvent = contractInitEvent.ParseERC20InitiatedEvent();

                if (initiatedEvent.RefundTimestamp != refundTimeStamp)
                {
                    Log.Debug(
                        "Invalid refund time in initiated event. Expected value is {@expected}, actual is {@actual}",
                        refundTimeStamp,
                        (long)initiatedEvent.RefundTimestamp);

                    return(new Error(
                               code: Errors.InvalidRefundLockTime,
                               description: $"Invalid refund time in initiated event. Expected value is {refundTimeStamp}, actual is {(long)initiatedEvent.RefundTimestamp}"));
                }

                if (initiatedEvent.Countdown != lockTimeInSec)  //todo: use it
                {
                    Log.Debug(
                        "Invalid countdown in initiated event. Expected value is {@expected}, actual is {@actual}",
                        lockTimeInSec,
                        (long)initiatedEvent.Countdown);

                    return(new Error(
                               code: Errors.InvalidRewardForRedeem,
                               description: $"Invalid countdown in initiated event. Expected value is {lockTimeInSec}, actual is {(long)initiatedEvent.Countdown}"));
                }

                if (initiatedEvent.RedeemFee != requiredRewardForRedeemInDecimals)
                {
                    Log.Debug(
                        "Invalid redeem fee in initiated event. Expected value is {@expected}, actual is {@actual}",
                        requiredRewardForRedeemInDecimals,
                        (long)initiatedEvent.RedeemFee);

                    return(new Error(
                               code: Errors.InvalidRewardForRedeem,
                               description: $"Invalid redeem fee in initiated event. Expected value is {requiredRewardForRedeemInDecimals}, actual is {(long)initiatedEvent.RedeemFee}"));
                }

                if (!initiatedEvent.Active)
                {
                    Log.Debug(
                        "Invalid active value in initiated event. Expected value is {@expected}, actual is {@actual}",
                        true,
                        initiatedEvent.Active);

                    return(new Error(
                               code: Errors.InvalidRewardForRedeem,
                               description: $"Invalid active value in initiated event. Expected value is {true}, actual is {initiatedEvent.Active}"));
                }

                var erc20TransferValue = await GetTransferValue(
                    currency : currency,
                    from : initiatedEvent.Initiator.Substring(2),
                    to : erc20.SwapContractAddress.Substring(2),
                    blockNumber : contractInitEvent.HexBlockNumber,
                    cancellationToken : cancellationToken)
                                         .ConfigureAwait(false);

                if (erc20TransferValue != initiatedEvent.Value + initiatedEvent.RedeemFee)
                {
                    Log.Debug(
                        "Invalid transfer value in erc20 initiated event. Expected value is {@expected}, actual is {@actual}",
                        initiatedEvent.Value,
                        erc20TransferValue);

                    return(new Error(
                               code: Errors.InvalidSwapPaymentTx,
                               description: $"Invalid transfer value in erc20 initiated event. Expected value is {initiatedEvent.Value}, actual is {initiatedEvent.Active}"));
                }

                receivedAmountInDecimals = initiatedEvent.Value;

                if (receivedAmountInDecimals >= requiredAmountInDecimals - requiredRewardForRedeemInDecimals)
                {
                    return(true);
                }

                Log.Debug(
                    "Ethereum ERC20 value is not enough. Expected value is {@expected}. Actual value is {@actual}",
                    (decimal)(requiredAmountInDecimals - requiredRewardForRedeemInDecimals),
                    (decimal)initiatedEvent.Value);

                var addEventsResult = await api
                                      .GetContractEventsAsync(
                    address : erc20.SwapContractAddress,
                    fromBlock : erc20.SwapContractBlockNumber,
                    toBlock : ulong.MaxValue,
                    topic0 : EventSignatureExtractor.GetSignatureHash <ERC20AddedEventDTO>(),
                    topic1 : "0x" + swap.SecretHash.ToHexString(),
                    cancellationToken : cancellationToken)
                                      .ConfigureAwait(false);

                if (addEventsResult == null)
                {
                    return(new Error(Errors.RequestError, $"Connection error while trying to get contract {erc20.SwapContractAddress} add event"));
                }

                if (addEventsResult.HasError)
                {
                    return(addEventsResult.Error);
                }

                events = addEventsResult.Value?.ToList();

                if (events == null || !events.Any())
                {
                    return(false);
                }

                foreach (var @event in events.Select(e => e.ParseERC20AddedEvent()))
                {
                    erc20TransferValue = await GetTransferValue(
                        currency : currency,
                        from : @event.Initiator.Substring(2),
                        to : erc20.SwapContractAddress.Substring(2),
                        blockNumber : contractInitEvent.HexBlockNumber,
                        cancellationToken : cancellationToken)
                                         .ConfigureAwait(false);

                    if (erc20TransferValue != @event.Value - receivedAmountInDecimals)
                    {
                        Log.Debug(
                            "Invalid transfer value in added event. Expected value is {@expected}, actual is {@actual}",
                            @event.Value - receivedAmountInDecimals,
                            erc20TransferValue);

                        return(new Error(
                                   code: Errors.InvalidSwapPaymentTx,
                                   description: $"Invalid transfer value in initiated event. Expected value is {@event.Value - receivedAmountInDecimals}, actual is {erc20TransferValue}"));
                    }

                    receivedAmountInDecimals = @event.Value;

                    if (receivedAmountInDecimals >= requiredAmountInDecimals - requiredRewardForRedeemInDecimals)
                    {
                        return(true);
                    }

                    Log.Debug(
                        "Ethereum ERC20 value is not enough. Expected value is {@expected}. Actual value is {@actual}",
                        requiredAmountInDecimals - requiredRewardForRedeemInDecimals,
                        (long)@event.Value);
                }
            }
            catch (Exception e)
            {
                Log.Error(e, "Ethereum ERC20 swap initiated control task error");

                return(new Error(Errors.InternalError, e.Message));
            }

            return(false);
        }
        private async Task <List <ContractEvent> > GetERC20EventsAsync(
            string address,
            CancellationToken cancellationToken = default)
        {
            var currency = Currency;
            var api      = new EtherScanApi(currency);

            var approveEventsResult = await api
                                      .GetContractEventsAsync(
                address : currency.ERC20ContractAddress,
                fromBlock : currency.SwapContractBlockNumber,
                toBlock : ulong.MaxValue,
                topic0 : EventSignatureExtractor.GetSignatureHash <ERC20ApprovalEventDTO>(),
                topic1 : "0x000000000000000000000000" + address.Substring(2),
                topic2 : "0x000000000000000000000000" + currency.SwapContractAddress.Substring(2),
                cancellationToken : cancellationToken)
                                      .ConfigureAwait(false);

            if (approveEventsResult == null)
            {
                Log.Error("Connection error while get approve events");
                return(null);
            }

            if (approveEventsResult.HasError)
            {
                Log.Error(
                    "Error while scan address transactions for {@address} with code {@code} and description {@description}",
                    address,
                    approveEventsResult.Error.Code,
                    approveEventsResult.Error.Description);

                return(null);
            }

            var outEventsResult = await api
                                  .GetContractEventsAsync(
                address : currency.ERC20ContractAddress,
                fromBlock : currency.SwapContractBlockNumber,
                toBlock : ulong.MaxValue,
                topic0 : EventSignatureExtractor.GetSignatureHash <ERC20TransferEventDTO>(),
                topic1 : "0x000000000000000000000000" + address.Substring(2),
                topic2 : null,
                cancellationToken : cancellationToken)
                                  .ConfigureAwait(false);

            if (outEventsResult == null)
            {
                Log.Error("Connection error while get output events");
                return(null);
            }

            if (outEventsResult.HasError)
            {
                Log.Error(
                    "Error while scan address transactions for {@address} with code {@code} and description {@description}",
                    address,
                    outEventsResult.Error.Code,
                    outEventsResult.Error.Description);

                return(null);
            }

            var inEventsResult = await api
                                 .GetContractEventsAsync(
                address : currency.ERC20ContractAddress,
                fromBlock : currency.SwapContractBlockNumber,
                toBlock : ulong.MaxValue,
                topic0 : EventSignatureExtractor.GetSignatureHash <ERC20TransferEventDTO>(),
                topic1 : null,
                topic2 : "0x000000000000000000000000" + address.Substring(2),
                cancellationToken : cancellationToken)
                                 .ConfigureAwait(false);

            if (inEventsResult == null)
            {
                Log.Error("Connection error while get input events");
                return(null);
            }

            if (inEventsResult.HasError)
            {
                Log.Error(
                    "Error while scan address transactions for {@address} with code {@code} and description {@description}",
                    address,
                    inEventsResult.Error.Code,
                    inEventsResult.Error.Description);

                return(null);
            }

            var events = approveEventsResult.Value?
                         .Concat(outEventsResult.Value?.Concat(inEventsResult.Value))
                         .ToList();

            if (events == null || !events.Any()) // address without activity
            {
                return(null);
            }

            return(events);
        }
Esempio n. 13
0
 public static bool IsERC20RefundedEvent(this EtherScanApi.ContractEvent contractEvent)
 {
     return(contractEvent.EventSignatureHash() == EventSignatureExtractor.GetSignatureHash <ERC20RefundedEventDTO>());
 }
        public static async Task <Result <bool> > IsInitiatedAsync(
            Swap swap,
            Currency currency,
            long refundTimeStamp,
            CancellationToken cancellationToken = default)
        {
            try
            {
                Log.Debug("Ethereum: check initiated event");

                var ethereum = (Atomex.Ethereum)currency;

                var sideOpposite = swap.Symbol
                                   .OrderSideForBuyCurrency(swap.PurchasedCurrency)
                                   .Opposite();

                var requiredAmountInEth          = AmountHelper.QtyToAmount(sideOpposite, swap.Qty, swap.Price, ethereum.DigitsMultiplier);
                var requiredAmountInWei          = Atomex.Ethereum.EthToWei(requiredAmountInEth);
                var requiredRewardForRedeemInWei = Atomex.Ethereum.EthToWei(swap.RewardForRedeem);


                var api = new EtherScanApi(ethereum);

                var initiateEventsResult = await api
                                           .GetContractEventsAsync(
                    address : ethereum.SwapContractAddress,
                    fromBlock : ethereum.SwapContractBlockNumber,
                    toBlock : ulong.MaxValue,
                    topic0 : EventSignatureExtractor.GetSignatureHash <InitiatedEventDTO>(),
                    topic1 : "0x" + swap.SecretHash.ToHexString(),
                    topic2 : "0x000000000000000000000000" + swap.ToAddress.Substring(2),
                    cancellationToken : cancellationToken)
                                           .ConfigureAwait(false);

                if (initiateEventsResult == null)
                {
                    return(new Error(Errors.RequestError, $"Connection error while getting contract {ethereum.SwapContractAddress} initiate event"));
                }

                if (initiateEventsResult.HasError)
                {
                    return(initiateEventsResult.Error);
                }

                var events = initiateEventsResult.Value?.ToList();

                if (events == null || !events.Any())
                {
                    return(false);
                }

                var initiatedEvent = events.First().ParseInitiatedEvent();

                if (initiatedEvent.Value >= requiredAmountInWei - requiredRewardForRedeemInWei)
                {
                    if (initiatedEvent.RefundTimestamp != refundTimeStamp)
                    {
                        Log.Debug(
                            "Invalid refund time in initiated event. Expected value is {@expected}, actual is {@actual}",
                            refundTimeStamp,
                            (long)initiatedEvent.RefundTimestamp);

                        return(new Error(
                                   code: Errors.InvalidRefundLockTime,
                                   description: $"Invalid refund time in initiated event. Expected value is {refundTimeStamp}, actual is {(long)initiatedEvent.RefundTimestamp}"));
                    }

                    if (swap.IsAcceptor)
                    {
                        if (initiatedEvent.RedeemFee != requiredRewardForRedeemInWei)
                        {
                            Log.Debug(
                                "Invalid redeem fee in initiated event. Expected value is {@expected}, actual is {@actual}",
                                requiredRewardForRedeemInWei,
                                (long)initiatedEvent.RedeemFee);

                            return(new Error(
                                       code: Errors.InvalidRewardForRedeem,
                                       description: $"Invalid redeem fee in initiated event. Expected value is {requiredRewardForRedeemInWei}, actual is {(long)initiatedEvent.RedeemFee}"));
                        }
                    }

                    return(true);
                }

                Log.Debug(
                    "Eth value is not enough. Expected value is {@expected}. Actual value is {@actual}",
                    (decimal)(requiredAmountInWei - requiredRewardForRedeemInWei),
                    (decimal)initiatedEvent.Value);

                var addEventsResult = await api
                                      .GetContractEventsAsync(
                    address : ethereum.SwapContractAddress,
                    fromBlock : ethereum.SwapContractBlockNumber,
                    toBlock : ulong.MaxValue,
                    topic0 : EventSignatureExtractor.GetSignatureHash <AddedEventDTO>(),
                    topic1 : "0x" + swap.SecretHash.ToHexString(),
                    cancellationToken : cancellationToken)
                                      .ConfigureAwait(false);

                if (addEventsResult == null)
                {
                    return(new Error(Errors.RequestError, $"Connection error while getting contract {ethereum.SwapContractAddress} add event"));
                }

                if (addEventsResult.HasError)
                {
                    return(addEventsResult.Error);
                }

                events = addEventsResult.Value?.ToList();

                if (events == null || !events.Any())
                {
                    return(false);
                }

                foreach (var @event in events.Select(e => e.ParseAddedEvent()))
                {
                    if (@event.Value >= requiredAmountInWei - requiredRewardForRedeemInWei)
                    {
                        return(true);
                    }

                    Log.Debug(
                        "Eth value is not enough. Expected value is {@expected}. Actual value is {@actual}",
                        requiredAmountInWei - requiredRewardForRedeemInWei,
                        (long)@event.Value);
                }
            }
            catch (Exception e)
            {
                Log.Error(e, "Ethereum swap initiated control task error");

                return(new Error(Errors.InternalError, e.Message));
            }

            return(false);
        }