private static Transaction[] GenerateTxsForRefunds(IGrouping <Address, DepositDetails> depositGroup, DevKeyStoreWallet wallet) { Console.WriteLine(); Console.Write($"Provide nonce for {depositGroup.Key}: "); int nonce = int.Parse(Console.ReadLine()); Console.Write($"Provide address to send the refund to: "); string hexAddress = Console.ReadLine(); Address refundTo = new Address(hexAddress); SecureString securedPassword = ConsoleUtils.ReadSecret("Provide password: "******"Password has been accepted."); Console.WriteLine(); Console.WriteLine($"Great, will generate refund transactions for deposits of {depositGroup.Key} starting with nonce {nonce}. ETH/DAI will be sent to {refundTo}."); List <Transaction> transactions = new List <Transaction>(depositGroup.Count()); foreach (DepositDetails depositDetails in depositGroup) { Deposit deposit = depositDetails.Deposit; RefundClaim refundClaim = new RefundClaim(deposit.Id, depositDetails.DataAsset.Id, deposit.Units, deposit.Value, deposit.ExpiryTime, depositDetails.Pepper, depositDetails.DataAsset.Provider.Address, refundTo); UInt256 gasPrice = 20.GWei(); AbiEncoder abiEncoder = new AbiEncoder(); byte[] txData = abiEncoder.Encode(AbiEncodingStyle.IncludeSignature, ContractData.ClaimRefundSig, depositDetails.DataAsset.Id, refundClaim.Units, refundClaim.Value, refundClaim.ExpiryTime, refundClaim.Pepper, refundClaim.Provider, depositDetails.Consumer); Transaction transaction = new Transaction(); transaction.Value = 0; transaction.Data = txData; transaction.To = new Address("0xb1AD03b75bD9E5AB89968D7a37d99F9dd220796D"); transaction.SenderAddress = depositDetails.Consumer; transaction.GasLimit = 100000; transaction.GasPrice = gasPrice; transaction.Nonce = (UInt256)nonce++; wallet.Sign(transaction, ChainId.Mainnet); EthereumEcdsa ecdsa = new EthereumEcdsa(ChainId.Mainnet, LimboLogs.Instance); Address recoveredAddress = ecdsa.RecoverAddress(transaction); if (recoveredAddress != transaction.SenderAddress) { Console.WriteLine("Signature failure"); return(new Transaction[0]); } transactions.Add(transaction); } return(transactions.ToArray()); } Console.WriteLine("Incorrect password."); return(new Transaction[0]); }
public async Task TryClaimRefundAsync(DepositDetails deposit, Address refundTo) { var now = (ulong)_blockchainBridge.Head.Timestamp; if (!deposit.CanClaimRefund(now)) { return; } var depositId = deposit.Deposit.Id; var transactionHash = deposit.ClaimedRefundTransactionHash; if (transactionHash is null) { var provider = deposit.DataAsset.Provider.Address; var refundClaim = new RefundClaim(depositId, deposit.DataAsset.Id, deposit.Deposit.Units, deposit.Deposit.Value, deposit.Deposit.ExpiryTime, deposit.Pepper, provider, refundTo); transactionHash = _refundService.ClaimRefund(refundTo, refundClaim); deposit.SetClaimedRefundTransactionHash(transactionHash); await _depositRepository.UpdateAsync(deposit); if (_logger.IsInfo) { _logger.Info($"Claimed a refund for deposit: '{depositId}', transaction hash: '{transactionHash}' (awaits a confirmation)."); } } await TryConfirmClaimAsync(deposit, string.Empty); }
public async Task <RefundClaimStatus> TryClaimRefundAsync(DepositDetails deposit, Address refundTo) { ulong now = _timestamper.EpochSeconds; if (!deposit.CanClaimRefund(now)) { return(RefundClaimStatus.Empty); } Block?latestBlock = await _blockchainBridge.GetLatestBlockAsync(); if (latestBlock == null) { return(RefundClaimStatus.Empty); } now = (ulong)latestBlock.Timestamp; if (!deposit.CanClaimRefund(now)) { return(RefundClaimStatus.Empty); } Keccak depositId = deposit.Deposit.Id; Keccak?transactionHash = deposit.ClaimedRefundTransaction?.Hash; if (transactionHash is null) { Address provider = deposit.DataAsset.Provider.Address; RefundClaim refundClaim = new RefundClaim(depositId, deposit.DataAsset.Id, deposit.Deposit.Units, deposit.Deposit.Value, deposit.Deposit.ExpiryTime, deposit.Pepper, provider, refundTo); UInt256 gasPrice = await _gasPriceService.GetCurrentAsync(); transactionHash = await _refundService.ClaimRefundAsync(refundTo, refundClaim, gasPrice); if (transactionHash is null) { if (_logger.IsError) { _logger.Error("There was an error when trying to claim refund (no transaction hash returned)."); } return(RefundClaimStatus.Empty); } deposit.AddClaimedRefundTransaction(TransactionInfo.Default(transactionHash, 0, gasPrice, _refundService.GasLimit, _timestamper.EpochSeconds)); await _depositRepository.UpdateAsync(deposit); if (_logger.IsInfo) { _logger.Info($"Claimed a refund for deposit: '{depositId}', gas price: {gasPrice} wei, transaction hash: '{transactionHash}' (awaits a confirmation)."); } } bool confirmed = await TryConfirmClaimAsync(deposit, string.Empty); return(confirmed ? RefundClaimStatus.Confirmed(transactionHash) : RefundClaimStatus.Unconfirmed(transactionHash)); }
public async Task can_claim_refund() { uint timestamp = 1546871954; _bridge.NextBlockPlease(timestamp); DepositService depositService = new DepositService(_ndmBridge, _abiEncoder, _wallet, _contractAddress); Keccak assetId = Keccak.Compute("data asset"); uint expiryTime = timestamp + 4; UInt256 value = 1.Ether(); uint units = 10U; byte[] salt = new byte[16]; AbiSignature depositAbiDef = new AbiSignature("deposit", new AbiBytes(32), new AbiUInt(32), new AbiUInt(96), new AbiUInt(32), new AbiBytes(16), AbiType.Address, AbiType.Address); byte[] depositData = _abiEncoder.Encode(AbiEncodingStyle.Packed, depositAbiDef, assetId.Bytes, units, value, expiryTime, salt, _providerAccount, _consumerAccount); Keccak depositId = Keccak.Compute(depositData); Deposit deposit = new Deposit(depositId, units, expiryTime, value); Keccak depositTxHash = await depositService.MakeDepositAsync(_consumerAccount, deposit, 20.GWei()); _bridge.IncrementNonce(_consumerAccount); TxReceipt depositTxReceipt = _bridge.GetReceipt(depositTxHash); TestContext.WriteLine("GAS USED FOR DEPOSIT: {0}", depositTxReceipt.GasUsed); Assert.AreEqual(StatusCode.Success, depositTxReceipt.StatusCode, $"deposit made {depositTxReceipt.Error} {Encoding.UTF8.GetString(depositTxReceipt.ReturnValue ?? new byte[0])}"); // calls revert and cannot reuse the same state - use only for manual debugging // Assert.True(depositService.VerifyDeposit(deposit.Id), "deposit verified"); RefundService refundService = new RefundService(_ndmBridge, _abiEncoder, _depositRepository, _contractAddress, LimboLogs.Instance, _wallet); // it will not work so far as we do everything within the same block and timestamp is wrong _bridge.NextBlockPlease(expiryTime + 1); RefundClaim refundClaim = new RefundClaim(depositId, assetId, units, value, expiryTime, salt, _providerAccount, _consumerAccount); UInt256 balanceBefore = _state.GetBalance(_consumerAccount); Keccak refundTxHash = await refundService.ClaimRefundAsync(_consumerAccount, refundClaim, 20.GWei()); TxReceipt refundReceipt = _bridge.GetReceipt(refundTxHash); TestContext.WriteLine("GAS USED FOR REFUND CLAIM: {0}", refundReceipt.GasUsed); Assert.AreEqual(StatusCode.Success, refundReceipt.StatusCode, $"refund claim {refundReceipt.Error} {Encoding.UTF8.GetString(refundReceipt.ReturnValue ?? new byte[0])}"); UInt256 balanceAfter = _state.GetBalance(_consumerAccount); Assert.Greater(balanceAfter, balanceBefore); }
public async Task <Keccak?> ClaimRefundAsync(Address onBehalfOf, RefundClaim refundClaim, UInt256 gasPrice) { byte[] txData = _abiEncoder.Encode(AbiEncodingStyle.IncludeSignature, ContractData.ClaimRefundSig, refundClaim.AssetId.Bytes, refundClaim.Units, refundClaim.Value, refundClaim.ExpiryTime, refundClaim.Pepper, refundClaim.Provider, onBehalfOf); Transaction transaction = new Transaction(); transaction.Value = 0; transaction.Data = txData; transaction.To = _contractAddress; transaction.SenderAddress = onBehalfOf; transaction.GasLimit = (long)GasLimit; transaction.GasPrice = gasPrice; if (_logger.IsInfo) { _logger.Info($"Sending a refund claim transaction for {refundClaim.DepositId} to be refunded to {refundClaim.RefundTo}"); } return(await _blockchainBridge.SendOwnTransactionAsync(transaction)); }
public Keccak ClaimRefund(Address onBehalfOf, RefundClaim refundClaim) { byte[] txData = _abiEncoder.Encode(AbiEncodingStyle.IncludeSignature, ContractData.ClaimRefundSig, refundClaim.AssetId.Bytes, refundClaim.Units, refundClaim.Value, refundClaim.ExpiryTime, refundClaim.Pepper, refundClaim.Provider, onBehalfOf); Transaction transaction = new Transaction(); transaction.Value = 0; transaction.Data = txData; transaction.To = _contractAddress; transaction.SenderAddress = onBehalfOf; transaction.GasLimit = 90000; // check transaction.GasPrice = 20.GWei(); transaction.Nonce = _txPool.ReserveOwnTransactionNonce(onBehalfOf); _wallet.Sign(transaction, _blockchainBridge.GetNetworkId()); if (_logger.IsInfo) { _logger.Info($"Sending a refund claim transaction for {refundClaim.DepositId} to be refunded to {refundClaim.RefundTo}"); } return(_blockchainBridge.SendTransaction(transaction, true)); }