private (EthereumAmount nativeCurrencyAmount, Token tokenAmount) CalculateFundsToIssue(INetworkAccount recipient, EthereumAmount recipientNativeCurrencyBalance, Token recipientTokenBalance, EthereumAmount faucetNativeCurrencyBalance, Token faucetTokenBalance) { bool giveNativeCurrency = recipientNativeCurrencyBalance < this._nativeCurrencyLimits.MaximumRecipientBalance; bool giveToken = recipientTokenBalance < this._tokenCurrencyLimits.MaximumRecipientBalance; this._logger.LogInformation( $"{recipient.Network.Name}: Source: {faucetNativeCurrencyBalance.ToFormattedUnitWithSymbol()} {faucetTokenBalance.ToFormattedUnitWithSymbol()} Recipient: {recipientNativeCurrencyBalance.ToFormattedUnitWithSymbol()} {recipientTokenBalance.ToFormattedUnitWithSymbol()} Issue: ETH: {giveNativeCurrency} FUN: {giveToken} Max Eth: {this._nativeCurrencyLimits.MaximumRecipientBalance.ToFormattedUnitWithSymbol()} Max FUN: {this._tokenCurrencyLimits.MaximumRecipientBalance.ToFormattedUnitWithSymbol()}"); if (!giveNativeCurrency && !giveToken) { if (this._executionEnvironment.IsDevelopmentOrTest()) { this._logger.LogWarning( $"{recipient.Network.Name}: Could not issue {recipient.Network.NativeCurrency} or {this._tokenContract.Symbol} to {recipient.Address} - Recipient balance > max"); } throw new TooMuchTokenException(); } EthereumAmount nativeCurrencyAmount = giveNativeCurrency ? this.CalculateAmountOfNativeCurrencyToIssueFromFaucet(recipientNativeCurrencyBalance) : EthereumAmount.Zero; Token tokenAmount = giveToken ? this.CalculateAmountOfTokenToIssueFromFaucet(recipientTokenBalance) : Token.Zero; return(nativeCurrencyAmount, tokenAmount); }
private Task <PendingTransaction> IssueFundsAsync(INetworkSigningAccount networkSigningAccount, Issuance issuance, TransactionContext context) { this._logger.LogDebug($"Faucet Contract Balances: {issuance.SourceAccountBalance.ToFormattedUnitWithSymbol()}, {issuance.SourceTokenBalance.ToFormattedUnitWithSymbol()}"); EthereumAmount minEth = MinSourceNativeCurrencyAccountBalance(issuance.EthToIssue); Token minFun = MinSourceTokenAccountBalance(issuance.TokenToIssue); // don't allow the source account balance to go too low if (issuance.SourceAccountBalance < minEth) { string message = $"{issuance.Recipient.Network.Name}: Cannot issue {issuance.Recipient.Network.NativeCurrency} from {issuance.SourceName}. Faucet Contract {issuance.FundsSourceContract.Address} is low on ETH (Has {issuance.SourceAccountBalance.ToFormattedUnitWithSymbol()}. Minimum {minEth.ToFormattedUnitWithSymbol()})"; this._logger.LogCritical(message); throw new InsufficientTokenException(message); } if (issuance.SourceTokenBalance < minFun) { string message = $"{issuance.Recipient.Network.Name}: Cannot issue {this._tokenContract.Symbol} from {issuance.SourceName}. Faucet Contract {issuance.FundsSourceContract.Address} is low on FUN (Has {issuance.SourceTokenBalance.ToFormattedUnitWithSymbol()}. Minimum {minFun.ToFormattedUnitWithSymbol()})"; this._logger.LogCritical(message); throw new InsufficientTokenException(message); } return(issuance.SendFundsAsync(networkSigningAccount: networkSigningAccount, context: context, transactionExecutorFactory: this._transactionExecutorFactory)); }
/// <inheritdoc /> public Task RecordFundsIssuedAsync(INetworkAccount recipient, EthereumAmount nativeCurrencyAmount, Token tokenAmount, IPAddress ipAddress) { return(this._database.ExecuteAsync(storedProcedure: @"Faucet.Minting_Success", new { Network = recipient.Network.Name, recipient.Address, IPAddress = ipAddress, NativeCurrencyIssued = nativeCurrencyAmount, TokenIssued = tokenAmount })); }
public Issuance(INetworkAccount recipient, EthereumAmount ethToIssue, Token tokenToIssue, EthereumAmount sourceAccountBalance, Token sourceTokenBalance, string sourceName, NetworkContract fundsSourceContract, IContractInfo contractInfo) { this.Recipient = recipient ?? throw new ArgumentNullException(nameof(recipient)); this.EthToIssue = ethToIssue ?? throw new ArgumentNullException(nameof(ethToIssue)); this.TokenToIssue = tokenToIssue ?? throw new ArgumentNullException(nameof(tokenToIssue)); this.SourceAccountBalance = sourceAccountBalance ?? throw new ArgumentNullException(nameof(sourceAccountBalance)); this.SourceTokenBalance = sourceTokenBalance ?? throw new ArgumentNullException(nameof(sourceTokenBalance)); this.SourceName = sourceName ?? throw new ArgumentNullException(nameof(sourceName)); this.FundsSourceContract = fundsSourceContract ?? throw new ArgumentNullException(nameof(fundsSourceContract)); this._contractInfo = contractInfo ?? throw new ArgumentNullException(nameof(contractInfo)); }
/// <summary> /// Constructor. /// </summary> /// <param name="transactionExecutorFactory">Transaction execution</param> /// <param name="faucetDataManager">Data manager for faucet.</param> /// <param name="ethereumAccountBalanceSource">Balance source for Ethereum/ERC20 tokens.</param> /// <param name="contractInfoRegistry">Registry of contracts.</param> /// <param name="houseFundingIpWhiteList">IP Whitelist for faucet/funding operations.</param> /// <param name="executionEnvironment">The Execution Environment.</param> /// <param name="faucetConfiguration">Faucet configuration.</param> /// <param name="ethereumAccountManager">Ethereum account manager</param> /// <param name="logger">Logging.</param> public FaucetManager(ITransactionExecutorFactory transactionExecutorFactory, IFaucetDataManager faucetDataManager, IEthereumAccountBalanceSource ethereumAccountBalanceSource, IContractInfoRegistry contractInfoRegistry, IWhiteListedIpAddressIdentifier houseFundingIpWhiteList, ExecutionEnvironment executionEnvironment, IFaucetConfiguration faucetConfiguration, IEthereumAccountManager ethereumAccountManager, ILogger <FaucetManager> logger) { if (contractInfoRegistry == null) { throw new ArgumentNullException(nameof(contractInfoRegistry)); } if (faucetConfiguration == null) { throw new ArgumentNullException(nameof(faucetConfiguration)); } this._faucetDataManager = faucetDataManager ?? throw new ArgumentNullException(nameof(faucetDataManager)); this._ethereumAccountBalanceSource = ethereumAccountBalanceSource ?? throw new ArgumentNullException(nameof(ethereumAccountBalanceSource)); this._transactionExecutorFactory = transactionExecutorFactory ?? throw new ArgumentNullException(nameof(transactionExecutorFactory)); this._fundingWhiteList = houseFundingIpWhiteList ?? throw new ArgumentNullException(nameof(houseFundingIpWhiteList)); this._executionEnvironment = executionEnvironment; this._ethereumAccountManager = ethereumAccountManager; this._ethToGive = faucetConfiguration.EthToGive; this._tokenToGiven = faucetConfiguration.TokenToGive; this._maximumRecipientEthBalance = this._ethToGive / 2; this._maximumRecipientTokenBalance = this._tokenToGiven / 2; this._logger = logger ?? throw new ArgumentNullException(nameof(logger)); this._tokenContract = (Erc20TokenContractInfo)contractInfoRegistry.FindContractInfo(WellKnownContracts.Token); this._faucetContract = contractInfoRegistry.FindContractInfo(WellKnownContracts.Faucet); }
/// <summary> /// Constructor /// </summary> /// <param name="ethAmount">The Eth amount to distribute.</param> /// <param name="recipient">The recipient of the payments.</param> public DistributeEthInput(EthereumAmount ethAmount, AccountAddress recipient) { this.Amount = ethAmount ?? throw new ArgumentNullException(nameof(ethAmount)); this.Recipient = recipient ?? throw new ArgumentNullException(nameof(recipient)); }
private static EthereumAmount MinSourceNativeCurrencyAccountBalance(EthereumAmount amountToGive) { return(new(amountToGive.Value * 2)); }
private EthereumAmount CalculateAmountOfNativeCurrencyToIssueFromFaucet(EthereumAmount recipientEthBalance) { return(this._nativeCurrencyLimits.AmountToIssue - recipientEthBalance); }
private Task RecordSuccessfulFaucetDripAsync(INetworkAccount recipient, EthereumAmount nativeCurrencyAmount, Token tokenAmount, IPAddress ipAddress) { return(this._faucetDataManager.RecordFundsIssuedAsync(recipient: recipient, nativeCurrencyAmount: nativeCurrencyAmount, tokenAmount: tokenAmount, ipAddress: ipAddress)); }
private EthereumAmount CalculateAmountOfEthToIssueFromFaucet(EthereumAmount recipientEthBalance) { return(this._ethToGive - recipientEthBalance); }
/// <inheritdoc /> public async Task <FaucetDrip> OpenAsync(IPAddress ipAddress, INetworkAccount recipient, INetworkBlockHeader networkBlockHeader, CancellationToken cancellationToken) { const string sourceName = @"Test Network Faucet"; // ETH & FUN source (Faucet account - no need to be a signing account) if (!this._faucetContract.Addresses.TryGetValue(key: recipient.Network, out ContractAddress? contractAddress)) { this._logger.LogCritical($"{recipient.Network.Name}: Cannot issue ETH from faucet. Faucet not available on network"); throw new InsufficientTokenException(); } INetworkSigningAccount networkSigningAccount = this._ethereumAccountManager.GetAccount(network: recipient.Network); NetworkContract fundsSourceContract = new(network : recipient.Network, contractAddress : contractAddress); IReadOnlyList <EthereumAddress> networkAccounts = new EthereumAddress[] { contractAddress, recipient.Address }; IReadOnlyDictionary <EthereumAddress, EthereumAmount> accountBalances = await this._ethereumAccountBalanceSource.GetAccountBalancesAsync(networkAccounts : networkAccounts, networkBlockHeader : networkBlockHeader, cancellationToken : CancellationToken.None); // get the SOURCE account's balance if (!accountBalances.TryGetValue(key: contractAddress, out EthereumAmount? sourceAccountBalance)) { this._logger.LogCritical($"{recipient.Network.Name}: Could not retrieve balance for {networkSigningAccount.Address}"); throw new InsufficientTokenException(); } // get the account's current ETH balance if (!accountBalances.TryGetValue(key: recipient.Address, out EthereumAmount? recipientEthBalance)) { this._logger.LogCritical($"{recipient.Network.Name}: Could not retrieve balance for {recipient.Address}"); throw new InsufficientTokenException(); } IReadOnlyDictionary <EthereumAddress, Erc20TokenBalance> balances = await this._ethereumAccountBalanceSource.GetErc20TokenBalancesAsync( addresses : networkAccounts, networkBlockHeader : networkBlockHeader, tokenContract : this._tokenContract, cancellationToken : cancellationToken); // get the SOURCE account's current FUN balance if (!balances.TryGetValue(key: fundsSourceContract.Address, out Erc20TokenBalance sourceTokenBalance)) { this._logger.LogCritical($"{recipient.Network.Name}: Could not retrieve balance for {fundsSourceContract.Address}"); throw new InsufficientTokenException(); } Token sourceBalanceForToken = new(sourceTokenBalance); // get the recipient account's current FUN balance if (!balances.TryGetValue(key: recipient.Address, out Erc20TokenBalance recipientTokenBalance)) { this._logger.LogCritical($"{recipient.Network.Name}: Could not retrieve balance for {recipient.Address}"); throw new InsufficientTokenException(); } Token recipientBalanceForToken = new(recipientTokenBalance); bool giveToken = recipientBalanceForToken < this._maximumRecipientTokenBalance; bool giveEth = recipientEthBalance < this._maximumRecipientEthBalance; this._logger.LogInformation( $"{recipient.Network.Name}: Source: {sourceAccountBalance.ToFormattedUnitWithSymbol()} {sourceBalanceForToken.ToFormattedUnitWithSymbol()} Recipient: {recipientEthBalance.ToFormattedUnitWithSymbol()} {recipientBalanceForToken.ToFormattedUnitWithSymbol()} Issue: ETH: {giveEth} FUN: {giveToken} Max Eth: {this._maximumRecipientEthBalance.ToFormattedUnitWithSymbol()} Max FUN: {this._maximumRecipientTokenBalance.ToFormattedUnitWithSymbol()}"); if (!giveToken && !giveEth) { if (this._executionEnvironment.IsDevelopmentOrTest()) { this._logger.LogWarning($"{recipient.Network.Name}: Could not issue eth to {recipient.Address} - Recipient balance > max"); } throw new TooMuchTokenException(); } if (!await this.IsAllowedToIssueFromFaucetAsync(ipAddress: ipAddress, recipientAddress: recipient.Address)) { throw new TooFrequentTokenException(); } EthereumAmount ethAmount = giveEth ? this.CalculateAmountOfEthToIssueFromFaucet(recipientEthBalance) : EthereumAmount.Zero; Token tokenAmount = giveToken ? this.CalculateAmountOfFunToIssueFromFaucet(recipientBalanceForToken) : Token.Zero; Issuance issuance = new(recipient : recipient, ethToIssue : ethAmount, tokenToIssue : tokenAmount, sourceAccountBalance : sourceAccountBalance, sourceTokenBalance : sourceBalanceForToken, sourceName : sourceName, fundsSourceContract : fundsSourceContract, contractInfo : this._faucetContract); try { PendingTransaction tx = await this.IssueFundsAsync(networkSigningAccount : networkSigningAccount, issuance : issuance, new TransactionContext(contextType : WellKnownContracts.Faucet, recipient.Address.ToString())); return(new FaucetDrip(transaction: tx, ethAmount: ethAmount, tokenAmount: tokenAmount)); } catch (TransactionWillAlwaysFailException exception) { this._logger.LogCritical(new EventId(exception.HResult), exception: exception, $"{issuance.Recipient.Network.Name}: Cannot issue FUN from faucet: {exception.Message}"); throw new InsufficientTokenException(message: "Could not request fun from faucet", innerException: exception); } }
/// <summary> /// Constructor. /// </summary> /// <param name="nativeCurrencyToGive">The amount of the native currency to issue.</param> /// <param name="tokenToGive">The amount of the token to issue.</param> public FaucetConfiguration(EthereumAmount nativeCurrencyToGive, Token tokenToGive) { this.NativeCurrencyToGive = nativeCurrencyToGive ?? throw new ArgumentNullException(nameof(nativeCurrencyToGive)); this.TokenToGive = tokenToGive ?? throw new ArgumentNullException(nameof(tokenToGive)); }
/// <summary> /// Constructor. /// </summary> /// <param name="minimumAllowedXdaiBalance">Minimum XDAI balance.</param> public HouseBalanceConfiguration(EthereumAmount minimumAllowedXdaiBalance) { this.MinimumAllowedXdaiBalance = minimumAllowedXdaiBalance; }
/// <summary> /// Constructor. /// </summary> /// <param name="ethToGive">The amount of ETH to issue.</param> /// <param name="tokenToGive">The amount of BAN to issue.</param> public FaucetConfiguration(EthereumAmount ethToGive, Token tokenToGive) { this.EthToGive = ethToGive; this.TokenToGive = tokenToGive; }
/// <summary> /// Constructor /// </summary> /// <param name="tokenAmount">The token amount to distribute.</param> /// <param name="ethAmount">The Eth amount to distribute.</param> /// <param name="recipient">The recipient of the payments.</param> public DistributeTokenAndEthInput(DataTypes.Primitives.Token tokenAmount, EthereumAmount ethAmount, AccountAddress recipient) { this.TokenAmount = tokenAmount ?? throw new ArgumentNullException(nameof(tokenAmount)); this.EthAmount = ethAmount ?? throw new ArgumentNullException(nameof(ethAmount)); this.Recipient = recipient ?? throw new ArgumentNullException(nameof(recipient)); }