logo

Token Fee

ACS8 - Transaction Resource Token Fee Standard#

ACS8 is a transaction fee standard similar to ACS1, but it charges the called contract rather than the user. The fee charged includes four specified tokens: WRITE, READ, NET, and TRAFFIC.

When a contract inherits from ACS8, each transaction within this contract incurs charges in these four resource tokens.

Interface#

The acs8.proto file defines the following method:

Methods#

Method NameRequest TypeResponse TypeDescription
BuyResourceTokenacs8.BuyResourceTokenInputgoogle.protobuf.EmptyBuys one of the four resource tokens. Consumes ELF balance in the contract account.

Types#

acs8.BuyResourceTokenInput

FieldTypeDescriptionLabel
symbolstringThe symbol of the token to buy.
amountint64The amount of token to buy.
pay_limitint64Limit of cost; buying is abandoned if exceeded. 0 means no limit.

Usage#

Contracts inheriting ACS1 use a pre-plugin transaction called ChargeTransactionFees for transaction fee charging. ACS8 introduces a similar post-plugin transaction called ChargeResourceToken, which charges resource tokens based on actual transaction consumption.

The ChargeResourceToken implementation involves calculating token amounts using polynomial coefficients stored in CalculateFeeCoefficients defined in token_contract.proto. Each resource token has a polynomial for fee calculation, which determines the cost based on transaction consumption.

1
public override Empty ChargeResourceToken(ChargeResourceTokenInput input)
2
{
3
Context.LogDebug(() => string.Format("Start executing ChargeResourceToken.{0}", input));
4
if (input.Equals(new ChargeResourceTokenInput()))
5
{
6
return new Empty();
7
}
8
var bill = new TransactionFeeBill();
9
foreach (var pair in input.CostDic)
10
{
11
Context.LogDebug(() => string.Format("Charging {0} {1} tokens.", pair.Value, pair.Key));
12
var existingBalance = GetBalance(Context.Sender, pair.Key);
13
Assert(existingBalance >= pair.Value,
14
string.Format("Insufficient resource of {0}. Need balance: {1}; Current balance: {2}.", pair.Key, pair.Value, existingBalance));
15
bill.FeesMap.Add(pair.Key, pair.Value);
16
}
17
foreach (var pair in bill.FeesMap)
18
{
19
Context.Fire(new ResourceTokenCharged
20
{
21
Symbol = pair.Key,
22
Amount = pair.Value,
23
ContractAddress = Context.Sender
24
});
25
if (pair.Value == 0)
26
{
27
Context.LogDebug(() => string.Format("Maybe incorrect charged resource fee of {0}: it's 0.", pair.Key));
28
}
29
}
30
return new Empty();
31
}

Additionally, contracts cannot execute methods if they lack sufficient resource token balance. To enforce this, a pre-plugin transaction CheckResourceToken, similar to ACS5, verifies the contract's resource token balance before method execution.

1
public override Empty CheckResourceToken(Empty input)
2
{
3
foreach (var symbol in Context.Variables.GetStringArray(TokenContractConstants.PayTxFeeSymbolListName))
4
{
5
var balance = GetBalance(Context.Sender, symbol);
6
var owningBalance = State.OwningResourceToken[Context.Sender][symbol];
7
Assert(balance > owningBalance,
8
string.Format("Contract balance of {0} token is not enough. Owning {1}.", symbol, owningBalance));
9
}
10
return new Empty();
11
}

Edited on: 15 July 2024 05:52:51 GMT+0