using System.Numerics;
using Neo.Sol.Runtime;
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Services;
using NeoLedger = Neo.SmartContract.Framework.Native.Ledger;
using NeoToken = Neo.SmartContract.Framework.Native.NEO;
using NeoFrameworkRuntime = Neo.SmartContract.Framework.Services.Runtime;
namespace Neo.Sol.Runtime.Context;
public sealed class ExecutionContext
{
private static ExecutionContext? _current;
private readonly Lazy<MsgContext> _msg;
private readonly Lazy<TxContext> _tx;
private readonly Lazy<BlockContext> _block;
private ExecutionContext()
{
_msg = new Lazy<MsgContext>(() => new MsgContext());
_tx = new Lazy<TxContext>(() => new TxContext());
_block = new Lazy<BlockContext>(() => new BlockContext());
}
public static ExecutionContext Current => _current ??= new ExecutionContext();
public MsgContext Msg => _msg.Value;
public TxContext Tx => _tx.Value;
public BlockContext Block => _block.Value;
internal static void Reset()
{
_current = null;
}
}
public sealed class MsgContext
{
private UInt160? _sender;
private BigInteger? _value;
private byte[]? _data;
private uint? _gasLimit;
public UInt160 Sender
{
get
{
if (_sender == null)
{
try
{
_sender = NeoTypeConversions.ToCoreUInt160(NeoFrameworkRuntime.CallingScriptHash);
}
catch
{
try
{
_sender = NeoTypeConversions.ToCoreUInt160(NeoFrameworkRuntime.Transaction.Sender);
}
catch
{
_sender = UInt160.Zero;
}
}
}
return _sender;
}
internal set => _sender = value;
}
public BigInteger Value
{
get
{
if (_value == null)
{
_value = 0; }
return _value.Value;
}
internal set => _value = value;
}
public byte[] Data
{
get
{
if (_data == null)
{
_data = Array.Empty<byte>();
}
return _data;
}
internal set => _data = value;
}
public uint Gas
{
get
{
if (_gasLimit == null)
{
try
{
_gasLimit = (uint)NeoFrameworkRuntime.GasLeft;
}
catch
{
_gasLimit = 1000000; }
}
return _gasLimit.Value;
}
internal set => _gasLimit = value;
}
public byte[] Sig => Data.Length >= 4 ? Data[..4] : Array.Empty<byte>();
}
public sealed class TxContext
{
private UInt160? _origin;
private BigInteger? _gasPrice;
public UInt160 Origin
{
get
{
if (_origin == null)
{
try
{
_origin = NeoTypeConversions.ToCoreUInt160(NeoFrameworkRuntime.Transaction.Sender);
}
catch
{
_origin = UInt160.Zero;
}
}
return _origin;
}
internal set => _origin = value;
}
public BigInteger GasPrice
{
get
{
if (_gasPrice == null)
{
try
{
var tx = NeoFrameworkRuntime.Transaction;
var networkFee = tx.NetworkFee;
var txSize = tx.Script?.Length ?? 0;
_gasPrice = txSize > 0 ? networkFee / txSize : 0;
}
catch
{
_gasPrice = 1000000; }
}
return _gasPrice.Value;
}
internal set => _gasPrice = value;
}
public UInt256 Hash
{
get
{
try
{
return NeoTypeConversions.ToCoreUInt256(NeoFrameworkRuntime.Transaction.Hash);
}
catch
{
return UInt256.Zero;
}
}
}
public BigInteger Nonce
{
get
{
try
{
return NeoFrameworkRuntime.Transaction.Nonce;
}
catch
{
return BigInteger.Zero;
}
}
}
}
public sealed class BlockContext
{
private UInt160? _coinbase;
private BigInteger? _difficulty;
private BigInteger? _gasLimit;
private BigInteger? _baseFee;
public UInt160 Coinbase
{
get
{
if (_coinbase == null)
{
try
{
_coinbase = NeoTypeConversions.ToCoreUInt160(NeoToken.GetCommitteeAddress());
}
catch
{
_coinbase = UInt160.Zero; }
}
return _coinbase;
}
internal set => _coinbase = value;
}
public BigInteger Difficulty
{
get
{
if (_difficulty == null)
{
_difficulty = 1;
}
return _difficulty.Value;
}
internal set => _difficulty = value;
}
public BigInteger GasLimit
{
get
{
if (_gasLimit == null)
{
_gasLimit = 100000000; }
return _gasLimit.Value;
}
internal set => _gasLimit = value;
}
public uint Number
{
get
{
try
{
return NeoLedger.CurrentIndex;
}
catch
{
return 0;
}
}
}
public ulong Timestamp
{
get
{
try
{
return NeoFrameworkRuntime.Time;
}
catch
{
return 0;
}
}
}
public UInt256 Hash
{
get
{
try
{
return NeoTypeConversions.ToCoreUInt256(NeoLedger.CurrentHash);
}
catch
{
return UInt256.Zero;
}
}
}
public BigInteger BaseFee
{
get
{
if (_baseFee == null)
{
_baseFee = 1000; }
return _baseFee.Value;
}
internal set => _baseFee = value;
}
public UInt256 GetBlockHash(uint blockNumber)
{
if (blockNumber >= Number)
return UInt256.Zero;
try
{
var block = NeoLedger.GetBlock(blockNumber);
return block != null ? NeoTypeConversions.ToCoreUInt256(block.Hash) : UInt256.Zero;
}
catch
{
return UInt256.Zero;
}
}
public bool IsBlockHashAvailable(uint blockNumber)
{
return blockNumber < Number && (Number - blockNumber) <= 256;
}
}
public sealed class GasContext
{
public static long GetGasLeft()
{
try
{
return NeoFrameworkRuntime.GasLeft;
}
catch
{
return 0;
}
}
public static bool ConsumeGas(long amount)
{
var remaining = GetGasLeft();
return remaining >= amount;
}
public static ulong CalculateMemoryCost(uint currentSize, uint newSize)
{
if (newSize <= currentSize) return 0;
var currentWords = (currentSize + 31) / 32;
var newWords = (newSize + 31) / 32;
var currentCost = currentWords * 3 + (currentWords * currentWords) / 512;
var newCost = newWords * 3 + (newWords * newWords) / 512;
return newCost - currentCost;
}
}