Beispiel #1
0
        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);
        }
Beispiel #2
0
        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
     }));
 }
Beispiel #4
0
 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);
        }
Beispiel #6
0
 /// <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));
 }
Beispiel #7
0
 private static EthereumAmount MinSourceNativeCurrencyAccountBalance(EthereumAmount amountToGive)
 {
     return(new(amountToGive.Value * 2));
 }
Beispiel #8
0
 private EthereumAmount CalculateAmountOfNativeCurrencyToIssueFromFaucet(EthereumAmount recipientEthBalance)
 {
     return(this._nativeCurrencyLimits.AmountToIssue - recipientEthBalance);
 }
Beispiel #9
0
 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));
 }
Beispiel #13
0
 /// <summary>
 ///     Constructor.
 /// </summary>
 /// <param name="minimumAllowedXdaiBalance">Minimum XDAI balance.</param>
 public HouseBalanceConfiguration(EthereumAmount minimumAllowedXdaiBalance)
 {
     this.MinimumAllowedXdaiBalance = minimumAllowedXdaiBalance;
 }
Beispiel #14
0
 /// <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;
 }
Beispiel #15
0
 /// <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));
 }