Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/native-contracts-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ When calling a native contract method by transaction script, there are several t
|--------|---------|------------|--------------|---------|-------------|------------|----------|
| getFeePerByte | Gets the network fee per transaction byte. | -- | Int64 | 1<<15 | 0 | ReadStates | -- |
| getExecFeeFactor | Gets the execution fee factor. This is a multiplier that can be adjusted by the committee to adjust the system fees for transactions. | -- | UInt32 | 1<<15 | 0 | ReadStates | -- |
| getExecPicoFeeFactor | Gets the execution fee factor. This is a multiplier that can be adjusted by the committee to adjust the system fees for transactions. | -- | BigInteger | 1<<15 | 0 | ReadStates | HF_Faun |
| getStoragePrice | Gets the storage price. | -- | UInt32 | 1<<15 | 0 | ReadStates | -- |
| getMillisecondsPerBlock | Gets the block generation time in milliseconds. | -- | UInt32 | 1<<15 | 0 | ReadStates | HF_Echidna |
| getMaxValidUntilBlockIncrement | Gets the upper increment size of blockchain height (in blocks) exceeding that a transaction should fail validation. | -- | UInt32 | 1<<15 | 0 | ReadStates | HF_Echidna |
Expand All @@ -161,7 +162,7 @@ When calling a native contract method by transaction script, there are several t
| setAttributeFee | Sets the fee for attribute before Echidna hardfork. NotaryAssisted attribute type not supported. | Byte(*attributeType*), UInt32(*value*) | Void | 1<<15 | 0 | States | Deprecated in HF_Echidna |
| setAttributeFee | Sets the fee for attribute after Echidna hardfork. NotaryAssisted attribute type supported. | Byte(*attributeType*), UInt32(*value*) | Void | 1<<15 | 0 | States | HF_Echidna |
| setFeePerByte | -- | Int64(*value*) | Void | 1<<15 | 0 | States | -- |
| setExecFeeFactor | -- | UInt32(*value*) | Void | 1<<15 | 0 | States | -- |
| setExecFeeFactor | -- | UInt64(*value*) | Void | 1<<15 | 0 | States | -- |
| setStoragePrice | -- | UInt32(*value*) | Void | 1<<15 | 0 | States | -- |
| setMaxValidUntilBlockIncrement | -- | UInt32(*value*) | Void | 1<<15 | 0 | States | HF_Echidna |
| setMaxTraceableBlocks | Sets the length of the chain accessible to smart contracts. | UInt32(*value*) | Void | 1<<15 | 0 | States | HF_Echidna |
Expand Down
4 changes: 2 additions & 2 deletions src/Neo/Network/P2P/Payloads/Transaction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -387,11 +387,11 @@ public virtual VerifyResult VerifyStateDependent(ProtocolSettings settings, Data
return VerifyResult.InvalidAttribute;
attributesFee += attribute.CalculateNetworkFee(snapshot, this);
}
long netFeeDatoshi = NetworkFee - (Size * NativeContract.Policy.GetFeePerByte(snapshot)) - attributesFee;
var netFeeDatoshi = NetworkFee - (Size * NativeContract.Policy.GetFeePerByte(snapshot)) - attributesFee;
if (netFeeDatoshi < 0) return VerifyResult.InsufficientFunds;

if (netFeeDatoshi > MaxVerificationGas) netFeeDatoshi = MaxVerificationGas;
uint execFeeFactor = NativeContract.Policy.GetExecFeeFactor(snapshot);
var execFeeFactor = NativeContract.Policy.GetExecFeeFactor(settings, snapshot, height);
for (int i = 0; i < hashes.Length; i++)
{
if (IsSignatureContract(witnesses[i].VerificationScript.Span) && IsSingleSignatureInvocationScript(witnesses[i].InvocationScript, out var _))
Expand Down
4 changes: 2 additions & 2 deletions src/Neo/SmartContract/ApplicationEngine.Contract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ internal protected UInt160 CreateStandardAccount(ECPoint pubKey)
long fee = IsHardforkEnabled(Hardfork.HF_Aspidochelone)
? CheckSigPrice
: 1 << 8;
AddFee(fee * ExecFeeFactor);
AddFee(fee * _execFeeFactor);
return Contract.CreateSignatureRedeemScript(pubKey).ToScriptHash();
}

Expand All @@ -141,7 +141,7 @@ internal protected UInt160 CreateMultisigAccount(int m, ECPoint[] pubKeys)
long fee = IsHardforkEnabled(Hardfork.HF_Aspidochelone)
? CheckSigPrice * pubKeys.Length
: 1 << 8;
AddFee(fee * ExecFeeFactor);
AddFee(fee * _execFeeFactor);
return Contract.CreateMultiSigRedeemScript(m, pubKeys).ToScriptHash();
}

Expand Down
2 changes: 1 addition & 1 deletion src/Neo/SmartContract/ApplicationEngine.Crypto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ protected internal bool CheckMultisig(byte[][] pubkeys, byte[][] signatures)
if (n == 0) throw new ArgumentException("pubkeys array cannot be empty.");
if (m == 0) throw new ArgumentException("signatures array cannot be empty.");
if (m > n) throw new ArgumentException($"signatures count ({m}) cannot be greater than pubkeys count ({n}).");
AddFee(CheckSigPrice * n * ExecFeeFactor);
AddFee(CheckSigPrice * n * _execFeeFactor);
try
{
for (int i = 0, j = 0; i < m && j < n;)
Expand Down
4 changes: 2 additions & 2 deletions src/Neo/SmartContract/ApplicationEngine.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ protected internal BigInteger GetRandom()
buffer = nonceData = Cryptography.Helper.Murmur128(nonceData, ProtocolSettings.Network);
price = 1 << 4;
}
AddFee(price * ExecFeeFactor);
AddFee(price * _execFeeFactor);
return new BigInteger(buffer, isUnsigned: true);
}

Expand Down Expand Up @@ -447,7 +447,7 @@ protected internal void BurnGas(long datoshi)
{
if (datoshi <= 0)
throw new InvalidOperationException("GAS must be positive.");
AddFee(datoshi);
AddFee(datoshi * FeeFactor);
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Neo/SmartContract/ApplicationEngine.Storage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ protected internal void Put(StorageContext context, byte[] key, byte[] value)
else
newDataSize = (item.Value.Length - 1) / 4 + 1 + value.Length - item.Value.Length;
}
AddFee(newDataSize * StoragePrice);
AddFee(newDataSize * StoragePrice * FeeFactor);

item.Value = value;
}
Expand Down
63 changes: 45 additions & 18 deletions src/Neo/SmartContract/ApplicationEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,19 @@ public partial class ApplicationEngine : ExecutionEngine

private static Dictionary<uint, InteropDescriptor> services;
// Total amount of GAS spent to execute.
// In the unit of datoshi, 1 datoshi = 1e-8 GAS, 1 GAS = 1e8 datoshi
private readonly long _feeAmount;
// In the unit of picoGAS, 1 picoGAS = 1e-12 GAS
private readonly BigInteger _feeAmount;
private BigInteger _feeConsumed;
// Decimals for fee calculation
public const uint FeeFactor = 10000;
private Dictionary<Type, object> states;
private readonly DataCache originalSnapshotCache;
private List<NotifyEventArgs> notifications;
private List<IDisposable> disposables;
private readonly Dictionary<UInt160, int> invocationCounter = new();
private readonly Dictionary<ExecutionContext, ContractTaskAwaiter> contractTasks = new();
internal readonly uint ExecFeeFactor;
// In the unit of picoGAS, 1 picoGAS = 1e-12 GAS
private readonly BigInteger _execFeeFactor;
// In the unit of datoshi, 1 datoshi = 1e-8 GAS
internal readonly uint StoragePrice;
private byte[] nonceData;
Expand Down Expand Up @@ -131,19 +135,29 @@ public partial class ApplicationEngine : ExecutionEngine
/// In the unit of datoshi, 1 datoshi = 1e-8 GAS, 1 GAS = 1e8 datoshi
/// </summary>
[Obsolete("This property is deprecated. Use FeeConsumed instead.")]
public long GasConsumed { get; protected set; } = 0;
public long GasConsumed => FeeConsumed;

/// <summary>
/// Exec Fee Factor. In the unit of datoshi, 1 datoshi = 1e-8 GAS
/// </summary>
internal long ExecFeeFactor => (long)_execFeeFactor.DivideCeiling(FeeFactor);

/// <summary>
/// GAS spent to execute.
/// In the unit of datoshi, 1 datoshi = 1e-8 GAS, 1 GAS = 1e8 datoshi
/// </summary>
public long FeeConsumed { get; protected set; } = 0;
public long FeeConsumed => (long)_feeConsumed.DivideCeiling(FeeFactor);

/// <summary>
/// Exec Fee Factor. In the unit of picoGAS, 1 picoGAS = 1e-12 GAS
/// </summary>
internal BigInteger ExecFeePicoFactor => _execFeeFactor;

/// <summary>
/// The remaining GAS that can be spent in order to complete the execution.
/// In the unit of datoshi, 1 datoshi = 1e-8 GAS, 1 GAS = 1e8 datoshi
/// </summary>
public long GasLeft => _feeAmount - FeeConsumed;
public long GasLeft => (long)(_feeAmount - _feeConsumed).DivideCeiling(FeeFactor);
Copy link
Member

@AnnaShaleva AnnaShaleva Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now both GasConsumed and GasLeft are rounded to the upper integer. But this behaviour violates GasConsumed+GasLeft=(_feeAmount / FeeFactor) condition (we know that _feeAmount is divisible by FeeFactor by definition).

For GasConsumed rounding to the upper integer is correct since GasConsumed is used for transaction's fee calculation and e.g. transaction's SystemFee should cover sub-Datoshi execution cost.

For GasLeft I'm not sure what's the correct behaviour. Right now GasLeft is used only by System.Runtime.GasLeft interop, so rounding it to the upper value may be misleading for the smart contract (since in fact there's less gas remaining in the VM than System.Runtime.GasLeft tells). From another hand, rounding GasLeft to the lower integer may lead to the situation when System.Runtime.GasLeft returns 0 whereas some instructions still may be executed if we have some 0.999 GAS left in fact.

Copy link
Member

@AnnaShaleva AnnaShaleva Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the statistics of System.Runtime.GasLeft interop usage on Mainnet up to 8179793 height:

Here's the full log with additional data on usage: link.

The most active contract is FlamingoBroker contract. Its source code is not public, but here's the usage scenario taken from the bytecode (this pattern is used multiple times in the contract):

System.Runtime.GasLeft usage in `FlamingoBroker` contract
31039    LDLOC0          
31040    SYSCALL         System.Runtime.GasLeft (1488d8ce)
31045    SUB             
31046    DUP             
31047    PUSHINT64       -9223372036854775808 (0000000000000080)
31056    JMPGE           31060 (4/04)
31058    JMP             31072 (14/0e)
31060    DUP             
31061    PUSHINT64       9223372036854775807 (ffffffffffffff7f)
31070    JMPLE           31120 (50/32)
31072    PUSHINT128      18446744073709551615 (ffffffffffffffff0000000000000000)
31089    AND             
31090    DUP             
31091    PUSHINT64       9223372036854775807 (ffffffffffffff7f)
31100    JMPLE           31120 (20/14)
31102    PUSHINT128      18446744073709551616 (00000000000000000100000000000000)
31119    SUB             
31120    STLOC           10 (0a)
31122    LDLOC           9 (09)
31124    LDLOC           10 (0a)
31126    SUB             
31127    STLOC           11 (0b)
31129    LDLOC           11 (0b)
31131    PUSH0           
31132    LE              
31133    JMPIFNOT        31137 (4/04)
31135    JMP             31175 (40/28)
31137    LDLOC           11 (0b)
31139    DUP             
31140    PUSHINT64       -9223372036854775808 (0000000000000080)
31149    PUSHINT128      9223372036854775808 (00000000000000800000000000000000)
31166    WITHIN          
31167    JMPIF           31170 (3/03)
31169    THROW           
31170    SYSCALL         System.Runtime.BurnGas (c35a8cbc)
31175    LDSFLD          75 (4b)
31177    CALL_L          1235 (-29942/0a8bffff)
31182    RET             

So it looks like the overall usage pattern is "Burn some GAS until a specific amount of free GAS remains" (although we may ask @adrian-fjellberg or @odd86 for details). So @shargon, given this fact and the fact that it's related to trading, we assume that it's more safe to round GasLeft to the lower integer instead of rounding to the upper integer. Because smart contract relays on fact that the specified amount of GAS is 100% available to spent it to some invocation.

The second contract is generator contract deployed by CoZ, it has a little bit different pattern of usage:

System.Runtime.GasLeft usage in `generator` contract
6738     SYSCALL        System.Runtime.GasLeft (1488d8ce)
6743     STLOC          9 (09)
6745     LDLOC1         
6746     LDLOC          9 (09)
6748     SUB            
6749     STLOC          10 (0a)
6751     LDARG1         
6752     CALL_L         2137 (-4615/f9edffff)
6757     LDLOC          10 (0a)
6759     SUB            
6760     STLOC          11 (0b)
6762     LDLOC          11 (0b)
6764     SYSCALL        System.Runtime.BurnGas (c35a8cbc)
6769     LDLOC0         
6770     RET           

And it looks like rounding GasLeft to the lower integer doesn't hurt this pattern of usage as far.

The third contract is puppet by CoZ, it also has a very similar pattern of usage which looks to me like: "burn the amount of GAS left after execution of some contract bytecode (with an adjustment)"

System.Runtime.GasLeft usage in `puppet` contract
894      INITSLOT       10 local, 2 arg
897      SYSCALL        System.Runtime.GasLeft (1488d8ce) 
902      STLOC0         

... // execution of some side contract code

1114     SYSCALL        System.Runtime.GasLeft (1488d8ce) 
1119     STLOC6         
1120     LDLOC0         
1121     LDLOC6         
1122     SUB             
1123     STLOC          7 (07)
1125     LDLOC1         
1126     CALL_L         3231 (2105/39080000)
1131     STLOC          8 (08)
1133     LDLOC          8 (08)
1135     LDLOC          7 (07)
1137     SUB            
1138     STLOC          9 (09)
1140     LDLOC          9 (09)
1142     SYSCALL        System.Runtime.BurnGas (c35a8cbc)

And for this pattern rounding GasLeft to the lower integer also doesn't hurt.

I didn't check other contracts, but if needed, then I may analyze the usage pattern for the rest of the contracts and attach the results. @shargon, @roman-khimov, please let me know your thoughts on this.


/// <summary>
/// The exception that caused the execution to terminate abnormally. This field could be <see langword="null"/> if no exception is thrown.
Expand Down Expand Up @@ -205,17 +219,31 @@ protected ApplicationEngine(
originalSnapshotCache = snapshotCache;
PersistingBlock = persistingBlock;
ProtocolSettings = settings;
_feeAmount = gas;
_feeAmount = gas * FeeFactor; // PicoGAS
Diagnostic = diagnostic;
nonceData = container is Transaction tx ? tx.Hash.ToArray()[..16] : new byte[16];
if (snapshotCache is null || persistingBlock?.Index == 0)
{
ExecFeeFactor = PolicyContract.DefaultExecFeeFactor;
_execFeeFactor = PolicyContract.DefaultExecFeeFactor * FeeFactor; // Add fee decimals
StoragePrice = PolicyContract.DefaultStoragePrice;
}
else
{
ExecFeeFactor = NativeContract.Policy.GetExecFeeFactor(snapshotCache);
var persistingIndex = persistingBlock?.Index ?? NativeContract.Ledger.CurrentIndex(snapshotCache);

if (settings == null || !settings.IsHardforkEnabled(Hardfork.HF_Faun, persistingIndex))
{
// The values doesn't have the decimals stored
_execFeeFactor = NativeContract.Policy.GetExecFeeFactor(this) * FeeFactor;
}
else
{
// The values have the decimals stored starting from OnPersist of Faun's block.
_execFeeFactor = NativeContract.Policy.GetExecPicoFeeFactor(this);
if (trigger == TriggerType.OnPersist && persistingIndex > 0 && !settings.IsHardforkEnabled(Hardfork.HF_Faun, persistingIndex - 1))
_execFeeFactor *= FeeFactor;
}

StoragePrice = NativeContract.Policy.GetStoragePrice(snapshotCache);
}

Expand Down Expand Up @@ -296,13 +324,11 @@ protected static void OnSysCall(ExecutionEngine engine, Instruction instruction)
/// <summary>
/// Adds GAS to <see cref="FeeConsumed"/> and checks if it has exceeded the maximum limit.
/// </summary>
/// <param name="datoshi">The amount of GAS, in the unit of datoshi, 1 datoshi = 1e-8 GAS, to be added.</param>
protected internal void AddFee(long datoshi)
/// <param name="picoGas">The amount of GAS, in the unit of picoGAS, 1 picoGAS = 1e-12 GAS, to be added.</param>
protected internal void AddFee(BigInteger picoGas)
{
#pragma warning disable CS0618 // Type or member is obsolete
FeeConsumed = GasConsumed = checked(FeeConsumed + datoshi);
#pragma warning restore CS0618 // Type or member is obsolete
if (FeeConsumed > _feeAmount)
_feeConsumed = _feeConsumed + picoGas;
if (_feeConsumed > _feeAmount)
throw new InvalidOperationException("Insufficient GAS.");
}

Expand Down Expand Up @@ -448,11 +474,12 @@ protected override void ContextUnloaded(ExecutionContext context)
/// <returns>The engine instance created.</returns>
public static ApplicationEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock = null, ProtocolSettings settings = null, long gas = TestModeGas, IDiagnostic diagnostic = null)
{
settings ??= ProtocolSettings.Default;
var index = persistingBlock?.Index ?? (snapshot == null ? 0 : NativeContract.Ledger.CurrentIndex(snapshot));

// Adjust jump table according persistingBlock

var jumpTable = settings == null || settings.IsHardforkEnabled(Hardfork.HF_Echidna, index) ? DefaultJumpTable : NotEchidnaJumpTable;
var jumpTable = settings.IsHardforkEnabled(Hardfork.HF_Echidna, index) ? DefaultJumpTable : NotEchidnaJumpTable;
var engine = Provider?.Create(trigger, container, snapshot, persistingBlock, settings, gas, diagnostic, jumpTable)
?? new ApplicationEngine(trigger, container, snapshot, persistingBlock, settings, gas, diagnostic, jumpTable);

Expand Down Expand Up @@ -657,7 +684,7 @@ internal protected void ValidateCallFlags(CallFlags requiredCallFlags)
protected virtual void OnSysCall(InteropDescriptor descriptor)
{
ValidateCallFlags(descriptor.RequiredCallFlags);
AddFee(descriptor.FixedPrice * ExecFeeFactor);
AddFee(descriptor.FixedPrice * _execFeeFactor);

object[] parameters = new object[descriptor.Parameters.Count];
for (int i = 0; i < parameters.Length; i++)
Expand All @@ -671,7 +698,7 @@ protected virtual void OnSysCall(InteropDescriptor descriptor)
protected override void PreExecuteInstruction(Instruction instruction)
{
Diagnostic?.PreExecuteInstruction(instruction);
AddFee(ExecFeeFactor * OpCodePriceTable[(byte)instruction.OpCode]);
AddFee(_execFeeFactor * OpCodePriceTable[(byte)instruction.OpCode]);
}

protected override void PostExecuteInstruction(Instruction instruction)
Expand Down
10 changes: 5 additions & 5 deletions src/Neo/SmartContract/Native/ContractManagement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -267,10 +267,10 @@ private async ContractTask<ContractState> Deploy(ApplicationEngine engine, byte[
if (manifest.Length == 0)
throw new ArgumentException($"Manifest length cannot be zero.");

engine.AddFee(Math.Max(
engine.StoragePrice * (nefFile.Length + manifest.Length),
GetMinimumDeploymentFee(engine.SnapshotCache)
));
// In the unit of picoGAS, 1 picoGAS = 1e-12 GAS
engine.AddFee(BigInteger.Max(engine.StoragePrice * (nefFile.Length + manifest.Length),
GetMinimumDeploymentFee(engine.SnapshotCache))
* ApplicationEngine.FeeFactor);

NefFile nef = nefFile.AsSerializable<NefFile>();
ContractManifest parsedManifest = ContractManifest.Parse(manifest);
Expand Down Expand Up @@ -336,7 +336,7 @@ private ContractTask Update(ApplicationEngine engine, byte[] nefFile, byte[] man
if (nefFile is null && manifest is null)
throw new ArgumentException("NEF file and manifest cannot both be null.");

engine.AddFee(engine.StoragePrice * ((nefFile?.Length ?? 0) + (manifest?.Length ?? 0)));
engine.AddFee(engine.StoragePrice * ApplicationEngine.FeeFactor * ((nefFile?.Length ?? 0) + (manifest?.Length ?? 0)));

var contractState = engine.SnapshotCache.GetAndChange(CreateStorageKey(Prefix_Contract, engine.CallingScriptHash))
?? throw new InvalidOperationException($"Updating Contract Does Not Exist: {engine.CallingScriptHash}");
Expand Down
6 changes: 4 additions & 2 deletions src/Neo/SmartContract/Native/NativeContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -435,8 +435,10 @@ internal async void Invoke(ApplicationEngine engine, byte version)
var state = context.GetState<ExecutionContextState>();
if (!state.CallFlags.HasFlag(method.RequiredCallFlags))
throw new InvalidOperationException($"Cannot call this method with the flag {state.CallFlags}.");
// In the unit of datoshi, 1 datoshi = 1e-8 GAS
engine.AddFee(method.CpuFee * engine.ExecFeeFactor + method.StorageFee * engine.StoragePrice);
// In the unit of picoGAS, 1 picoGAS = 1e-12 GAS
engine.AddFee(
(method.CpuFee * engine.ExecFeePicoFactor) +
(method.StorageFee * engine.StoragePrice * ApplicationEngine.FeeFactor));
List<object> parameters = new();
if (method.NeedApplicationEngine) parameters.Add(engine);
if (method.NeedSnapshot) parameters.Add(engine.SnapshotCache);
Expand Down
4 changes: 2 additions & 2 deletions src/Neo/SmartContract/Native/NeoToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -394,8 +394,8 @@ private bool RegisterCandidate(ApplicationEngine engine, ECPoint pubkey)
if (!engine.IsHardforkEnabled(Hardfork.HF_Echidna) &&
!engine.CheckWitnessInternal(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash()))
return false;
// In the unit of datoshi, 1 datoshi = 1e-8 GAS
engine.AddFee(GetRegisterPrice(engine.SnapshotCache));
// In the unit of picoGAS, 1 picoGAS = 1e-12 GAS
engine.AddFee(GetRegisterPrice(engine.SnapshotCache) * ApplicationEngine.FeeFactor);
return RegisterInternal(engine, pubkey);
}

Expand Down
4 changes: 2 additions & 2 deletions src/Neo/SmartContract/Native/OracleContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,10 @@ private async ContractTask Request(ApplicationEngine engine, string url, string
if (gasForResponse < 0_10000000)
throw new ArgumentException($"gasForResponse {gasForResponse} must be at least 0.1 datoshi.");

engine.AddFee(GetPrice(engine.SnapshotCache));
engine.AddFee(GetPrice(engine.SnapshotCache) * ApplicationEngine.FeeFactor);

//Mint gas for the response
engine.AddFee(gasForResponse);
engine.AddFee(gasForResponse * ApplicationEngine.FeeFactor);
await GAS.Mint(engine, Hash, gasForResponse, false);

//Increase the request id
Expand Down
Loading