private bool ChargeSizeFee(ChargeTransactionFeesInput input, ref TransactionFeeBill bill) { string symbolChargedForBaseFee = null; var amountChargedForBaseFee = 0L; var symbolToPayTxFee = input.PrimaryTokenSymbol; if (bill.FeesMap.Any()) { symbolChargedForBaseFee = bill.FeesMap.First().Key; amountChargedForBaseFee = bill.FeesMap.First().Value; } var availableBalance = symbolChargedForBaseFee == symbolToPayTxFee // Available balance need to deduct amountChargedForBaseFee ? GetBalance(Context.Sender, symbolToPayTxFee).Sub(amountChargedForBaseFee) : GetBalance(Context.Sender, symbolToPayTxFee); var txSizeFeeAmount = input.TransactionSizeFee; if (input.SymbolsToPayTxSizeFee.Any()) { var allSymbolToTxFee = input.SymbolsToPayTxSizeFee; var availableSymbol = allSymbolToTxFee.FirstOrDefault(x => GetBalanceCalculatedBaseOnPrimaryToken(x, symbolChargedForBaseFee, amountChargedForBaseFee) >= txSizeFeeAmount) ?? allSymbolToTxFee.FirstOrDefault(x => GetBalanceCalculatedBaseOnPrimaryToken(x, symbolChargedForBaseFee, amountChargedForBaseFee) > 0); if (availableSymbol != null && availableSymbol.TokenSymbol != symbolToPayTxFee) { symbolToPayTxFee = availableSymbol.TokenSymbol; txSizeFeeAmount = txSizeFeeAmount.Mul(availableSymbol.AddedTokenWeight) .Div(availableSymbol.BaseTokenWeight); availableBalance = symbolChargedForBaseFee == symbolToPayTxFee ? GetBalance(Context.Sender, symbolToPayTxFee).Sub(amountChargedForBaseFee) : GetBalance(Context.Sender, symbolToPayTxFee); } } var chargeAmount = availableBalance > txSizeFeeAmount ? txSizeFeeAmount : availableBalance; if (symbolToPayTxFee == null) { return(availableBalance >= txSizeFeeAmount); } if (symbolChargedForBaseFee == symbolToPayTxFee) { bill.FeesMap[symbolToPayTxFee] = bill.FeesMap[symbolToPayTxFee].Add(chargeAmount); } else { bill.FeesMap.Add(symbolToPayTxFee, chargeAmount); } return(availableBalance >= txSizeFeeAmount); }
/// <summary> /// Related transactions will be generated by acs1 pre-plugin service, /// and will be executed before the origin transaction. /// </summary> /// <param name="input"></param> /// <returns></returns> public override BoolValue ChargeTransactionFees(ChargeTransactionFeesInput input) { Assert(input.MethodName != null && input.ContractAddress != null, "Invalid charge transaction fees input."); // Primary token not created yet. if (string.IsNullOrEmpty(input.PrimaryTokenSymbol)) { return(new BoolValue { Value = true }); } // Record tx fee bill during current charging process. var bill = new TransactionFeeBill(); var fromAddress = Context.Sender; var methodFees = Context.Call <MethodFees>(input.ContractAddress, nameof(GetMethodFee), new StringValue { Value = input.MethodName }); var successToChargeBaseFee = true; if (methodFees != null && methodFees.Fees.Any()) { successToChargeBaseFee = ChargeBaseFee(GetBaseFeeDictionary(methodFees), ref bill); } var successToChargeSizeFee = true; if (!IsMethodFeeSetToZero(methodFees)) { // Then also do not charge size fee. successToChargeSizeFee = ChargeSizeFee(input, ref bill); } // Update balances. foreach (var tokenToAmount in bill.FeesMap) { ModifyBalance(fromAddress, tokenToAmount.Key, -tokenToAmount.Value); Context.Fire(new TransactionFeeCharged { Symbol = tokenToAmount.Key, Amount = tokenToAmount.Value }); if (tokenToAmount.Value == 0) { Context.LogDebug(() => $"Maybe incorrect charged tx fee of {tokenToAmount.Key}: it's 0."); } } return(new BoolValue { Value = successToChargeBaseFee && successToChargeSizeFee }); }
public override ConsumedResourceTokens ChargeResourceToken(ChargeResourceTokenInput input) { var consumedResourceTokens = new ConsumedResourceTokens(); Context.LogDebug(() => $"Start executing ChargeResourceToken.{input}"); if (input.Equals(new ChargeResourceTokenInput())) { return(consumedResourceTokens); } var symbolToAmount = new Dictionary <string, long> { { "READ", input.ReadCost }, { "TRAFFIC", input.TrafficCost }, { "STORAGE", input.StorageCost }, { "WRITE", input.WriteCost } }; var bill = new TransactionFeeBill(); foreach (var pair in symbolToAmount) { Context.LogDebug(() => $"Charging {pair.Value} {pair.Key} tokens."); var existingBalance = State.Balances[Context.Sender][pair.Key]; if (existingBalance < pair.Value) { bill.TokenToAmount.Add(pair.Key, existingBalance); var owningBalance = State.OwningResourceToken[Context.Sender][pair.Key] .Add(pair.Value.Sub(existingBalance)); State.OwningResourceToken[Context.Sender][pair.Key] = owningBalance; consumedResourceTokens.IsFailedToCharge = true; consumedResourceTokens.Owning.Add(pair.Key, owningBalance); Context.LogDebug(() => $"Insufficient resource. {pair.Key}: {existingBalance} / {pair.Value}"); } else { bill.TokenToAmount.Add(pair.Key, pair.Value); } } foreach (var pair in bill.TokenToAmount) { State.ChargedResourceTokens[input.Caller][Context.Sender][pair.Key] = State.ChargedResourceTokens[input.Caller][Context.Sender][pair.Key].Add(pair.Value); consumedResourceTokens.Value.Add(pair.Key, pair.Value); } Context.LogDebug(() => $"Finished executing ChargeResourceToken.{consumedResourceTokens}"); return(consumedResourceTokens); }
public static TransactionFeeBill operator +(TransactionFeeBill bill1, TransactionFeeBill bill2) { var result = new TransactionFeeBill(); foreach (var symbol in bill1.FeesMap.Keys.Union(bill2.FeesMap.Keys)) { var amountInBill1 = bill1.FeesMap.ContainsKey(symbol) ? bill1.FeesMap[symbol] : 0; var amountInBill2 = bill2.FeesMap.ContainsKey(symbol) ? bill2.FeesMap[symbol] : 0; result.FeesMap.Add(symbol, amountInBill1.Add(amountInBill2)); } return(result); }
public override Empty ChargeResourceToken(ChargeResourceTokenInput input) { Context.LogDebug(() => $"Start executing ChargeResourceToken.{input}"); if (input.Equals(new ChargeResourceTokenInput())) { return(new Empty()); } var bill = new TransactionFeeBill(); foreach (var pair in input.CostDic) { Context.LogDebug(() => $"Charging {pair.Value} {pair.Key} tokens."); var existingBalance = GetBalance(Context.Sender, pair.Key); if (existingBalance < pair.Value) { bill.FeesMap.Add(pair.Key, existingBalance); var owningBalance = State.OwningResourceToken[Context.Sender][pair.Key] .Add(pair.Value.Sub(existingBalance)); State.OwningResourceToken[Context.Sender][pair.Key] = owningBalance; Context.LogDebug(() => $"Insufficient resource. {pair.Key}: {existingBalance} / {pair.Value}"); Context.Fire(new ResourceTokenOwned { Symbol = pair.Key, Amount = owningBalance }); } else { bill.FeesMap.Add(pair.Key, pair.Value); } } foreach (var pair in bill.FeesMap) { Context.Fire(new ResourceTokenCharged { Symbol = pair.Key, Amount = pair.Value, ContractAddress = Context.Sender }); if (pair.Value == 0) { Context.LogDebug(() => $"Maybe incorrect charged resource fee of {pair.Key}: it's 0."); } } return(new Empty()); }
public override Empty ClaimTransactionFees(Empty input) { Context.LogDebug(() => "Claim transaction fee."); if (State.TreasuryContract.Value == null) { var treasuryContractAddress = Context.GetContractAddressByName(SmartContractConstants.TreasuryContractSystemName); if (treasuryContractAddress == null) { // Which means Treasury Contract didn't deployed yet. Ignore this method. return(new Empty()); } State.TreasuryContract.Value = treasuryContractAddress; } var transactions = Context.GetPreviousBlockTransactions(); Context.LogDebug(() => $"Got {transactions.Count} transaction(s) from previous block."); var senders = transactions.Select(t => t.From).ToList(); var totalBill = new TransactionFeeBill(); foreach (var sender in senders) { var oldBill = State.ChargedFees[sender]; if (oldBill != null) { totalBill += oldBill; } // Clear State.ChargedFees[sender] = new TransactionFeeBill(); } foreach (var bill in totalBill.TokenToAmount) { var symbol = bill.Key; var amount = bill.Value; State.Balances[Context.Self][symbol] = State.Balances[Context.Self][symbol].Add(amount); TransferTransactionFeesToFeeReceiver(symbol, amount); } Context.LogDebug(() => "Finish claim transaction fee."); return(new Empty()); }
/// <summary> /// Related transactions will be generated by acs1 pre-plugin service, /// and will be executed before the origin transaction. /// </summary> /// <param name="input"></param> /// <returns></returns> public override TransactionFee ChargeTransactionFees(ChargeTransactionFeesInput input) { Assert(input.MethodName != null && input.ContractAddress != null, "Invalid charge transaction fees input."); var transactionFee = new TransactionFee(); // Primary token not created yet. if (string.IsNullOrEmpty(input.PrimaryTokenSymbol)) { return(transactionFee); } // Record tx fee bill during current charging process. var bill = new TransactionFeeBill(); var fromAddress = Context.Sender; var methodFees = Context.Call <MethodFees>(input.ContractAddress, nameof(GetMethodFee), new StringValue { Value = input.MethodName }); var successToChargeBaseFee = true; if (methodFees != null && methodFees.Fees.Any()) { successToChargeBaseFee = ChargeBaseFee(GetBaseFeeDictionary(methodFees), ref bill); } var successToChargeSizeFee = ChargeSizeFee(input, ref bill); // Update the bill. var oldBill = State.ChargedFees[fromAddress]; State.ChargedFees[fromAddress] = oldBill == null ? bill : oldBill + bill; // Update balances. foreach (var tokenToAmount in bill.TokenToAmount) { State.Balances[fromAddress][tokenToAmount.Key] = State.Balances[fromAddress][tokenToAmount.Key].Sub(tokenToAmount.Value); transactionFee.Value[tokenToAmount.Key] = tokenToAmount.Value; } transactionFee.IsFailedToCharge = !successToChargeBaseFee || !successToChargeSizeFee; return(transactionFee); }
private bool ChargeBaseFee(Dictionary <string, long> methodFeeMap, ref TransactionFeeBill bill) { if (!ChargeFirstSufficientToken(methodFeeMap, out var symbolToChargeBaseFee, out var amountToChargeBaseFee, out var existingBalance)) { Context.LogDebug(() => "Failed to charge first sufficient token."); if (symbolToChargeBaseFee != null) { bill.FeesMap.Add(symbolToChargeBaseFee, existingBalance); } // If symbol == null, then charge nothing in base fee part. return(false); } bill.FeesMap.Add(symbolToChargeBaseFee, amountToChargeBaseFee); return(true); }
private bool ChargeSizeFee(ChargeTransactionFeesInput input, ref TransactionFeeBill bill) { string symbolChargedForBaseFee = null; var amountChargedForBaseFee = 0L; if (bill.TokenToAmount.Any()) { symbolChargedForBaseFee = bill.TokenToAmount.First().Key; amountChargedForBaseFee = bill.TokenToAmount.First().Value; } var availableBalance = symbolChargedForBaseFee == input.PrimaryTokenSymbol // Available balance need to deduct amountChargedForBaseFee ? State.Balances[Context.Sender][input.PrimaryTokenSymbol].Sub(amountChargedForBaseFee) : State.Balances[Context.Sender][input.PrimaryTokenSymbol]; var txSizeFeeAmount = input.TransactionSizeFee; var chargeAmount = availableBalance > txSizeFeeAmount // Is available balance enough to pay tx size fee? ? txSizeFeeAmount : availableBalance; if (input.PrimaryTokenSymbol == null) { return(availableBalance >= txSizeFeeAmount); } if (symbolChargedForBaseFee == input.PrimaryTokenSymbol) { bill.TokenToAmount[input.PrimaryTokenSymbol] = bill.TokenToAmount[input.PrimaryTokenSymbol].Add(chargeAmount); } else { bill.TokenToAmount.Add(input.PrimaryTokenSymbol, chargeAmount); } return(availableBalance >= txSizeFeeAmount); }
public override Empty ChargeResourceToken(ChargeResourceTokenInput input) { AssertTransactionGeneratedByPlugin(); Context.LogDebug(() => $"Start executing ChargeResourceToken.{input}"); if (input.Equals(new ChargeResourceTokenInput())) { return(new Empty()); } var bill = new TransactionFeeBill(); foreach (var pair in input.CostDic) { Context.LogDebug(() => $"Charging {pair.Value} {pair.Key} tokens."); var existingBalance = GetBalance(Context.Sender, pair.Key); Assert(existingBalance >= pair.Value, $"Insufficient resource of {pair.Key}. Need balance: {pair.Value}; Current balance: {existingBalance}."); bill.FeesMap.Add(pair.Key, pair.Value); } foreach (var pair in bill.FeesMap) { Context.Fire(new ResourceTokenCharged { Symbol = pair.Key, Amount = pair.Value, ContractAddress = Context.Sender }); if (pair.Value == 0) { Context.LogDebug(() => $"Maybe incorrect charged resource fee of {pair.Key}: it's 0."); } } return(new Empty()); }
/// <summary> /// Related transactions will be generated by acs1 pre-plugin service, /// and will be executed before the origin transaction. /// </summary> /// <param name="input"></param> /// <returns></returns> public override TransactionFee ChargeTransactionFees(ChargeTransactionFeesInput input) { Assert(input.MethodName != null && input.ContractAddress != null, "Invalid charge transaction fees input."); var result = new TransactionFee(); if (input.PrimaryTokenSymbol == string.Empty) { // Primary token not created yet. return(result); } var fee = Context.Call <MethodFees>(input.ContractAddress, nameof(GetMethodFee), new StringValue { Value = input.MethodName }); var bill = new TransactionFeeBill(); var fromAddress = Context.Sender; if (fee != null && fee.Fees.Any()) { if (!ChargeFirstSufficientToken(fee.Fees.ToDictionary(f => f.Symbol, f => f.BasicFee), out var symbol, out var amount, out var existingBalance)) { Assert(false, "Failed to charge first sufficient token."); } bill.TokenToAmount.Add(symbol, amount); // Charge base fee. State.Balances[fromAddress][symbol] = existingBalance.Sub(amount); result.Value.Add(symbol, amount); } var balanceAfterChargingBaseFee = State.Balances[fromAddress][input.PrimaryTokenSymbol]; var txSizeFeeAmount = input.TransactionSizeFee; txSizeFeeAmount = balanceAfterChargingBaseFee > txSizeFeeAmount // Enough to pay tx size fee. ? txSizeFeeAmount // It's safe to convert from long to int here because // balanceAfterChargingBaseFee <= txSizeFeeAmount and // typeof(txSizeFeeAmount) == int : (int)balanceAfterChargingBaseFee; bill += new TransactionFeeBill { TokenToAmount = { { input.PrimaryTokenSymbol, txSizeFeeAmount } } }; // Charge tx size fee. var finalBalanceOfNativeSymbol = balanceAfterChargingBaseFee.Sub(txSizeFeeAmount); State.Balances[fromAddress][input.PrimaryTokenSymbol] = finalBalanceOfNativeSymbol; // Record the bill finally. var oldBill = State.ChargedFees[fromAddress]; State.ChargedFees[fromAddress] = oldBill == null ? bill : oldBill + bill; // If balanceAfterChargingBaseFee < txSizeFeeAmount, make sender's balance of native symbol to 0 and make current execution failed. Assert( balanceAfterChargingBaseFee >= input.TransactionSizeFee, $"Insufficient balance to pay tx size fee: {balanceAfterChargingBaseFee} < {input.TransactionSizeFee}.\n " + $"Primary token: {input.PrimaryTokenSymbol}"); if (result.Value.ContainsKey(input.PrimaryTokenSymbol)) { result.Value[input.PrimaryTokenSymbol] = result.Value[input.PrimaryTokenSymbol].Add(txSizeFeeAmount); } else { result.Value.Add(input.PrimaryTokenSymbol, txSizeFeeAmount); } return(result); }
/// <summary> /// Related transactions will be generated by acs1 pre-plugin service, /// and will be executed before the origin transaction. /// </summary> /// <param name="input"></param> /// <returns></returns> public override ChargeTransactionFeesOutput ChargeTransactionFees(ChargeTransactionFeesInput input) { AssertTransactionGeneratedByPlugin(); Assert(input.MethodName != null && input.ContractAddress != null, "Invalid charge transaction fees input."); // Primary token not created yet. if (State.ChainPrimaryTokenSymbol.Value == null) { return(new ChargeTransactionFeesOutput { Success = true }); } // Record tx fee bill during current charging process. var bill = new TransactionFeeBill(); var fromAddress = Context.Sender; var methodFees = Context.Call <MethodFees>(input.ContractAddress, nameof(GetMethodFee), new StringValue { Value = input.MethodName }); var successToChargeBaseFee = true; if (methodFees != null && methodFees.Fees.Any()) { successToChargeBaseFee = ChargeBaseFee(GetBaseFeeDictionary(methodFees), ref bill); } var successToChargeSizeFee = true; if (!IsMethodFeeSetToZero(methodFees)) { // Then also do not charge size fee. successToChargeSizeFee = ChargeSizeFee(input, ref bill); } // Update balances. foreach (var tokenToAmount in bill.FeesMap) { ModifyBalance(fromAddress, tokenToAmount.Key, -tokenToAmount.Value); Context.Fire(new TransactionFeeCharged { Symbol = tokenToAmount.Key, Amount = tokenToAmount.Value }); if (tokenToAmount.Value == 0) { Context.LogDebug(() => $"Maybe incorrect charged tx fee of {tokenToAmount.Key}: it's 0."); } } var chargingResult = successToChargeBaseFee && successToChargeSizeFee; var chargingOutput = new ChargeTransactionFeesOutput { Success = chargingResult }; if (!chargingResult) { chargingOutput.ChargingInformation = "Transaction fee not enough."; } return(chargingOutput); }