Skip to content

Releases: Nethereum/Nethereum

4.21.4

04 Sep 16:38
Compare
Choose a tag to compare

Contract Handler

The Contract Handler can now be overridden and changed on ContractServices, this allows for specialisation of ContractHandlers as the one used in Mud to to create a CallFrom Message when delegating a call from the given signer.

This means that Nethereum now can have 2 levels of interception, one at the rpc level, for example when using Metamask, WalletConnect etc to redirect messages to another provider, and this one at ContractHandler level when we want to sign as usual but embed the contract call into another function message.

Commits: aa137a7 aa137a7

Code generator changes

Code generator updated (JavaScript) to support multiple configurations, this allow you to define multiple code generation settings for your smart contracts, enabling the generation of client-side code for various platforms (e.g., C#, Unity) based on Solidity ABI files.

Example of usage in vscode solidity https://github.com/juanfranblanco/vscode-solidity?tab=readme-ov-file#code-generation-with-nethereum-genmultisettings

An example of the file is:

[
    {
        "paths": ["out/ERC20.sol/Standard_Token.json"],
        "generatorConfigs": [
            {
                "baseNamespace": "MyProject.Contracts",
                "basePath": "codeGenNodeTest/GeneratorSets/Example2/MyProject.Contracts",
                "codeGenLang": 0,  // Code generation for C#
                "generatorType": "ContractDefinition"  // Generates contract definitions in C#
            },
            {
                "baseNamespace": "MyProject.Contracts",
                "basePath": "codeGenNodeTest/GeneratorSets/Example2/MyProject.Contracts",
                "codeGenLang": 0,  // Code generation for C#
                "generatorType": "UnityRequest"  // Generates Unity client request code
            }
        ]
    },
    {
        "paths": ["out/IncrementSystem.sol/IncrementSystem.json"],
        "generatorConfigs": [
            {
                "baseNamespace": "MyProject.Contracts.MyWorld1.Systems",
                "basePath": "codeGenNodeTest/GeneratorSets/Example2/MyProject.Contracts.MyWorld1.Systems",
                "codeGenLang": 0,  // Code generation for C#
                "generatorType": "ContractDefinition"
            },
            {
                "baseNamespace": "MyProject.Contracts.MyWorld1.Systems",
                "basePath": "codeGenNodeTest/GeneratorSets/Example2/MyProject.Contracts.MyWorld1.Systems",
                "codeGenLang": 0,
                "generatorType": "MudExtendedService",  // Generates Mud services
                "mudNamespace": "myworld1"
            }
        ]
    },
    {
        "paths": ["mudMultipleNamespace/mud.config.ts"],
        "generatorConfigs": [
            {
                "baseNamespace": "MyProject.Contracts.MyWorld1.Tables",
                "basePath": "codeGenNodeTest/GeneratorSets/Example2/MyProject.Contracts.MyWorld1.Tables",
                "generatorType": "MudTables",  // Generates Mud tables
                "mudNamespace": "myworld1"
            }
        ]
    },
    {
        "paths": ["mudMultipleNamespace/mud.config.ts"],
        "generatorConfigs": [
            {
                "baseNamespace": "MyProject.Contracts.MyWorld2.Tables",
                "basePath": "codeGenNodeTest/GeneratorSets/Example2/MyProject.Contracts.MyWorld2.Tables",
                "generatorType": "MudTables",
                "mudNamespace": "myworld2"
            }
        ]
    }
]

Commits: 3b9dabf

Full Changelog: 4.21.3...4.21.4

4.21.3

22 Jul 13:35
Compare
Choose a tag to compare

Mud
Multiple Table registration (not using bulk)

Contract Standards
Supports interface standard

Full Changelog: 4.21.2...4.21.3

Unity release in openupm.com https://github.com/Nethereum/Nethereum.Unity

4.21.2

26 Jun 13:44
Compare
Choose a tag to compare

ERC6492 support

Example and more info: https://eips.ethereum.org/EIPS/eip-6492

 [Fact]
 public async Task ShouldValidateOfflineAsync()
 {
     var create2FactoryAddress = "0xBf07a0Df119Ca234634588fbDb5625594E2a5BCA";
     var factoryCallData = "0x49c81579000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000004836a472ab1dd406ecb8d0f933a985541ee3921f0000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000007a7f00000000000000000000000000000000000000000000000000000000000000017f7f0f292b79d9ce101861526459da50f62368077ae24affe97b792bf4bdd2e171553d602d80604d3d3981f3363d3d373d3d3d363d732a2b85eb1054d6f0c6c2e37da05ed3e5fea684ef5af43d82803e903d91602b57fd5bf300000000000000000000000000000000000000000000000000000000000000000000000002246171d1c9000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000004836a472ab1dd406ecb8d0f933a985541ee3921f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000942f9ce5d9a33a82f88d233aeb3292e6802303480000000000000000000000000000000000000000000000000014c3c6ef1cdc01000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042f2eaaebf45fc0340eb55f11c52a30e2ca7f48539d0a1f1cdc240482210326494545def903e8ed4441bd5438109abe950f1f79baf032f184728ba2d4161dea32e1b0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
     var originalSignature = "0xc0f8db6019888d87a0afc1299e81ef45d3abce64f63072c8d7a6ef00f5f82c1522958ff110afa98b8c0d23b558376db1d2fbab4944e708f8bf6dc7b977ee07201b00";


     var web3 = new Web3.Web3("https://polygon-mainnet.public.blastapi.io");
     var signatureValidationERC6492 = web3.Eth.SignatureValidationPredeployContractERC6492;
   
     var signatureERC6492 = signatureValidationERC6492.BuildSignature(create2FactoryAddress, factoryCallData.HexToByteArray(), originalSignature.HexToByteArray());

     Assert.True(signatureValidationERC6492.IsERC6492Signature(signatureERC6492));

     var message = "0x787177";
     var address = "0x4836a472ab1dd406ecb8d0f933a985541ee3921f";

     var isValid = await signatureValidationERC6492.IsValidSignatureMessageAsync(address, message.HexToByteArray(), signatureERC6492);
     Assert.True(isValid);

 }

Fixes and extras

  • Fix to make AWSKeyManagementExternalSigner signed messages compatible with EIP-2 by @hasandogu in #1036
  • Request batching for EthSendRawTransaction by @teledu in #1032
  • Mud adding multiquery rpc support to retrieve multiple records of a table, other extra functionality on table services to simplify querying, set and delete records.

Full Changelog: 4.21.0...4.21.2

Unity package can be found in Nethereum.Unity

4.21.0

18 Jun 14:56
Compare
Choose a tag to compare

Mud Support
New packages Nethereum.Mud and Nethereum.Mud.Contracts, also initial code generation support.

More info on Mud can be found here: https://mud.dev/

Full example in Nethereum: https://github.com/Nethereum/Nethereum/tree/master/tests/Nethereum.Mud.IntegrationTests and project structure: https://github.com/Nethereum/Nethereum/tree/master/tests/Nethereum.Mud.IntegrationTests/MudTest

Usage:

Creating a namespace resource and namespace

This example is a root namespace, it includes both a Table and Systems Service

 public class MudTestNamespaceResource : NamespaceResource
    {
        public MudTestNamespaceResource() : base(String.Empty)
        {
        }
    }
    public class MudTestNamespace: NamespaceBase<MudTestNamespaceResource, MudTestSystemServices, MudTestTableServices>
    {
        public MudTestNamespace(IWeb3 web3, string contractAddress) : base(web3, contractAddress)
        {
            Tables = new MudTestTableServices(web3, contractAddress);
            Systems = new MudTestSystemServices(web3, contractAddress);
        }
    }

Tables and System Services
Tables and systems services provide a container for all the tables and services of a namespace, simplifying the registration of both.

 public class MudTestSystemServices : SystemsServices
    {
        public IncrementSystemService IncrementSystemService { get; protected set; }
        public MudTestSystemServices(IWeb3 web3, string contractAddress) : base(web3, contractAddress)
        {
            IncrementSystemService = new IncrementSystemService(web3, contractAddress);
            SystemServices = new List<ISystemService> { IncrementSystemService };
        }
    }
 public class MudTestTableServices : TablesServices
    {
        public CounterTableService CounterTableService { get; protected set; }
        public ItemTableService ItemTableService { get; protected set; }
        public ConfigTableService ConfigTableService { get; protected set; }

        public MudTestTableServices(IWeb3 web3, string contractAddress) : base(web3, contractAddress)
        {
            CounterTableService = new CounterTableService(web3, contractAddress);
            ItemTableService = new ItemTableService(web3, contractAddress);
            ConfigTableService = new ConfigTableService(web3, contractAddress);

            TableServices = new List<ITableServiceBase> { CounterTableService, ItemTableService, ConfigTableService };
        }
    }

Systems
Systems are Nethereum contract services (that are commonly generated) but also extended with extra partial class and ResourceId to identify them. This will be generated in the same way as in the vscode solidity extension (WIP)

public class IncrementSystemServiceResource : SystemResource
    {
        public IncrementSystemServiceResource() : base("IncrementSystem") { }
    }

    public partial class IncrementSystemService : ISystemService<IncrementSystemServiceResource>
    {
        public IResource Resource => this.GetResource();

        public ISystemServiceResourceRegistration SystemServiceResourceRegistrator
        {
            get
            {
                return this.GetSystemServiceResourceRegistration<IncrementSystemServiceResource, IncrementSystemService>();
            }
        }

        public List<FunctionABI> GetSystemFunctionABIs()
        {
            return GetAllFunctionABIs();
        }

        public string CalculateCreate2Address(string deployerAddress, string salt, params ByteCodeLibrary[] byteCodeLibraries)
        {
            return new IncrementSystemDeployment().CalculateCreate2Address(deployerAddress, salt, byteCodeLibraries);
        }

        public Task<Create2ContractDeploymentTransactionResult> DeployCreate2ContractAsync(string deployerAddress, string salt, params ByteCodeLibrary[] byteCodeLibraries)
        {
            var create2ProxyDeployerService = Web3.Eth.Create2DeterministicDeploymentProxyService;
            var accessManagementSystemDeployment = new IncrementSystemDeployment();
            return create2ProxyDeployerService.DeployContractRequestAsync(accessManagementSystemDeployment, deployerAddress, salt, byteCodeLibraries);
        }

        public Task<Create2ContractDeploymentTransactionReceiptResult> DeployCreate2ContractAndWaitForReceiptAsync(string deployerAddress, string salt, ByteCodeLibrary[] byteCodeLibraries, CancellationToken cancellationToken = default)
        {
            var create2ProxyDeployerService = Web3.Eth.Create2DeterministicDeploymentProxyService;
            var accessManagementSystemDeployment = new IncrementSystemDeployment();
            return create2ProxyDeployerService.DeployContractRequestAndWaitForReceiptAsync(accessManagementSystemDeployment, deployerAddress, salt, byteCodeLibraries, cancellationToken);
        }
    }

Tables
Tables are created using their name and namespace, and separated internally into keys and values. A table service is used as a helper to retrieve or set the data. This can also be generated using a json representation of the Mud.config WIP to be integrated in vscode solidity

  public class ItemTableRecord : TableRecord<ItemKey, ItemValue>
    {
        public ItemTableRecord() : base("Item")
        {

        }

        public class ItemKey
        {
            [Parameter("uint32", "id", 1)]
            public int Id { get; set; }
        }

        public class ItemValue
        {
            [Parameter("uint32", "price", 1)]
            public int Price { get; set; }
            [Parameter("string", "name", 2)]
            public string Name { get; set; }
            [Parameter("string", "description", 3)]
            public string Description { get; set; }
            [Parameter("string", "owner", 4)]
            public string Owner { get; set; }
        }
    }

  public partial class ItemTableService : TableService<ItemTableRecord, ItemTableRecord.ItemKey, ItemTableRecord.ItemValue>
    {
       
        public ItemTableService(IWeb3 web3, string contractAddress) : base(web3, contractAddress)
        {
        }

        public virtual Task<ItemTableRecord> GetTableRecordAsync(int id, BlockParameter blockParameter = null)
        {
            var key = new ItemTableRecord.ItemKey();
            key.Id = id;
            return GetTableRecordAsync(key, blockParameter);
        }

        public virtual Task<string> SetRecordRequestAsync(int id, int price, string name, string description, string owner)
        {
            var key = new ItemTableRecord.ItemKey();
            key.Id = id;
            var values = new ItemTableRecord.ItemValue();
            values.Price = price;
            values.Name = name;
            values.Description = description;
            values.Owner = owner;
            return SetRecordRequestAsync(key, values);
        }

        public virtual Task<TransactionReceipt> SetRecordRequestAndWaitForReceipt(int id, int price, string name, string description, string owner)
        {
            var key = new ItemTableRecord.ItemKey();
            key.Id = id;
            var values = new ItemTableRecord.ItemValue();
            values.Price = price;
            values.Name = name;
            values.Description = description;
            values.Owner = owner;
            return SetRecordRequestAndWaitForReceiptAsync(key, values);
        }
    }

Store event changes
To retrieve all the store event changes we can use the StoreEventsLogProcessingService, this wraps the Nethereum log processing service to process all changes, all data will be stored in a TableRepository, in here we provide an InMemoryTableRepository implementation to use internally.

[Fact]
        public async Task ShouldGetAllChanges()
        {
            var web3 = GetWeb3();
            var storeLogProcessingService = new StoreEventsLogProcessingService(web3, WorldAddress);
            var inMemoryStore = new InMemoryTableRepository();
            var tableId = new CounterTableRecord().ResourceIdEncoded;
            await storeLogProcessingService.ProcessAllStoreChangesAsync(inMemoryStore, null, null, CancellationToken.None);
            var results = await inMemoryStore.GetTableRecordsAsync<CounterTableRecord>(tableId);

            Assert.True(results.ToList()[0].Values.Value> 0);

            var resultsSystems = await inMemoryStore.GetTableRecordsAsync<SystemsTableRecord>(new SystemsTableRecord().ResourceIdEncoded);
            Assert.True(resultsSystems.ToList().Count > 0);
            foreach (var result in resultsSystems)
            {
                Debug.WriteLine(ResourceEncoder.Decode(result.Keys.SystemId).Name);
                Debug.WriteLine(ResourceEncoder.Decode(result.Keys.SystemId).Namespace);
            }

            var resultsAccess = await inMemoryStore.GetTableRecordsAsync<ResourceAccessTableRecord>(new ResourceAccessTableRecord().ResourceIdEncoded);
            Assert.True(resultsAccess.ToList().Count > 0);
            foreach (var result in resultsAccess)
            {
                Debug.WriteLine(ResourceEncoder.Decode(result.Keys.ResourceId).Name);
                Debug.WriteLine(result.Keys.Caller);
                Debug.WriteLine(result.Values.Access);
            }


            //the world factory is the owner of the store and world namespaces
            var namespaceOwner = await inMemoryStore.GetTableRecordsAsync<NamespaceOwnerTableRecord>(new NamespaceOwnerTableRecord().ResourceIdEncoded);
            Assert.True(namespaceOwner.ToList().Count > 0);
            foreach (var result in namespaceOwner)
            {
                Debug.WriteLine(ResourceEncoder.Decode(result.Keys.NamespaceId).Name);
                ...
Read more

4.20.0

28 Mar 17:33
a3d2dab
Compare
Choose a tag to compare
  • Diamonds support as a Standard contract, including factory extensions to create Facets from service contracts.

  • New ServiceContractBase and ServiceContractWeb3Base these should be complemented normally by the new code generation changes, this include helpers to get all the contract signatures, abis or types.

  • Service code generation changes, to support the new ServiceContractWeb3Base and implement supporting functions to get all abi FunctionTypes, EventTypes and ErrorTypes these can be complimented with the Diamonds to create the Facet cuts, or handle errors specific to the that contract, etc.

  • WalletConnect host provider, adding switchchain, addchain to the interceptor, support the current account and changes to selected chain and selected account. Examples upgraded for Avalonia and Blazor.

  • Use IWeb3 instead of Web3 to deploy contracts by @Magehernan in #1000

  • Fixes to signing in the NethereumWalletConnectInterceptor by @skibitsky in #1017

  • CI workflow to call the nuget.bat and build the "development" nuget packages by @webwarrior-ws in #1018

Full Changelog: 4.19.0...4.20.0

4.19.0

16 Feb 18:46
Compare
Choose a tag to compare

Full Changelog: 4.18.0...4.19.0

4.18.0

21 Nov 11:54
f0e48bd
Compare
Choose a tag to compare

.Net 8 Target

All projects target .net8 where possible/

Other changes

  • OpenZeppelinMerkleTree using a new AbiStructSha3KeccackMerkleTree to match implementation that hashes the data struct Item when encoding by @dejx in #975
  • Updated text records enum according to ENS docs by @ridicoulous in #980
  • Enhancement: Adding new constructor that accepts aws kms client interface parameter. by @kjmoraji #979
  • Error handling when building .NET Standard projects by @aarani in #983
  • Support Logging Abstractions 8.0 as reference by @raymens in #986

4.17.1 β˜€οΈβ˜€οΈπŸžπŸž

28 Sep 10:54
Compare
Choose a tag to compare

2930 Transaction Support

Legacy2930 Transaction support to Sign, Recover and reconstruct RLP (As it is legacy this is not part of the normal TransactionManager flow)

Commits: 7c7d86b. a812ef2

Batch rpc Items (Create Batch Item for common methods)

This simplifies the creation of batches as per:

            var web3 = _ethereumClientIntegrationFixture.GetWeb3();

            var batchRequest = new RpcRequestResponseBatch();
            var batchItem1 = web3.Eth.GetBalance.CreateBatchItem(EthereumClientIntegrationFixture.AccountAddress, BlockParameter.CreateLatest(), 1);
            var batchItem2 = web3.Eth.GetBalance.CreateBatchItem(EthereumClientIntegrationFixture.AccountAddress, BlockParameter.CreateLatest(), 2);
            batchRequest.BatchItems.Add(batchItem1);
            batchRequest.BatchItems.Add(batchItem2);
            var response = await web3.Client.SendBatchRequestAsync(batchRequest);
            Assert.Equal(batchItem1.Response.Value, batchItem2.Response.Value);

Commit: 3332901

Wallet Connect Integration

Integration of Wallet connect libraries with Nethereum

Usage:

  1. Add the package Nethereum.WalletConnect
  2. Check one of these examples:

Commits: 27e5c93, 7153532, b3e617d, 8fa81e1, 6324189

nethereum-walletconnect-avalonia
nethereum-walletconnect-blazor-wasm

712 Encoding

Enhancements and fix on tuples (New methods HashStruct, GetEncodedType, GetEncodedTypeDomainSeparator, HashDomainSeparator)
See unit test for example: https://github.com/Nethereum/Nethereum/blob/894aaf6421106465a56ea6dbc12a030c155f8e25/tests/Nethereum.Signer.IntegrationTests/EIP712IntegrationTestRaribleExample.cs
Commits: 894aaf6, 0d35d8c

Event decoding

Added support to indexed values, when decoding default values to json, object dictionary, string dynamic dictionary support for indexed values that are hashed

Commit: 3ea051a

Full Changelog: 4.16.0...4.17.1

4.17.0 β˜€οΈβ˜€οΈπŸžπŸž

27 Sep 17:51
Compare
Choose a tag to compare

4.16.0 β˜€οΈβ˜€οΈπŸžπŸž

14 Aug 08:38
Compare
Choose a tag to compare

Nethereum.DataServices

New DataServices package, adding support to external (or local) data services starting with Etherscan, 4ByteDirectory and Sourcify.

Usage:

  1. Add the package Nethereum.DataServices
  2. Add the namespaces from Nethereum.DataServices

Etherscan example

var etherscanService = new EtherscanApiService();
var result = await etherscanService.Contracts.GetSourceCodeAsync("0xC36442b4a4522E871399CD717aBDD847Ab11FE88");
Assert.Equal("1", result.Status);
var contract = result.Result.First();
Assert.True(contract.ContainsSourceCodeCompilationMetadata());
var compilationMetadata = contract.DeserialiseCompilationMetadata();
var contractSource = compilationMetadata.GetLocalSourceCode(contract.ContractName);

Sourcify example

 var sourcifyApiService = new Sourcify.SourcifyApiService();
            var compilationMetadata = await sourcifyApiService.GetCompilationMetadataAsync(1, "0xC36442b4a4522E871399CD717aBDD847Ab11FE88");

4Bytes Example

 var fourByteDirectoryService = new FourByteDirectoryService();
var signature = await fourByteDirectoryService.GetFunctionSignatureByHexSignatureAsync("0x722713f7");

Commits:
226371c, 96290cf, 0fbcfa0, 4868317,

Nethereum.ABI

  • Fix Decoding, multidimentional array of structs of non dynamic structs will only decode first element as it needed to be initialise by iterating all the types. 929c336
  • Compilation metadata parsing and objects
  • Extensions to ABI Model (FunctionABI, EventABI, ErrorABI) to simplify decoding, selection and finding
  • AbiInfo, AbiInfoStorage and AbiInfoInMemoryStorage to maintain in memory or externally abi information.
  • Extensions to Transactions, Logs, RpcException to find FunctionAbis, EventAbis and ErrorAbis and facilitate decoding
    Commits:
  • 6847eb7, 657390b, 04e9494, 8464988, b5a82f4, 4a92caf

Nethereum.EVM

Adding revert message to Evm simulator
Commit: 9d3ce60

 var transactionHash = "0xf3d2a323110370a4dc72c04c738bf9b45d14b03603ed70372128a3966c54fca6";

            var web3 = _ethereumClientIntegrationFixture.GetInfuraWeb3(InfuraNetwork.Mainnet);
            var txn = await web3.Eth.Transactions.GetTransactionByHash.SendRequestAsync(transactionHash);
            var txnReceipt = await web3.Eth.Transactions.GetTransactionReceipt.SendRequestAsync(transactionHash);
            var block = await web3.Eth.Blocks.GetBlockWithTransactionsHashesByNumber.SendRequestAsync(txn.BlockNumber);
            var code = await web3.Eth.GetCode.SendRequestAsync(txn.To); // runtime code;
            Program program = await ExecuteProgramAsync(web3, txn, block, code, null);
            Assert.NotNull(program.ProgramResult.GetRevertMessage());

Nethereum.RPC

Bath requests added to common rpc methods
EthGetBlockWithTransactionsHashesByHash, EthGetBlockWithTransactionsByNumber, EthGetBlockWithTransactionsHashesByNumber, EthGetBalance, EthCall, EthGetTransactionByHash, EthGetTransactionReceipt, have now SendBatchRequestAsync included with an array of parameters

Commit: b031a3b

Nethereum.Contracts

MultiQueryBatchRpcHandler creates a multi query handler, to enable execute a single request combining multiple queries to multiple contracts using rpc batches

This uses the same IMulticallInputOutput as per the contract multiquery enabling to easily switch between them (although the contract version can handle errors in the calls)

//Connecting to Ethereum mainnet using Infura
            var web3 = _ethereumClientIntegrationFixture.GetInfuraWeb3(InfuraNetwork.Mainnet);

            //Setting the owner https://etherscan.io/tokenholdings?a=0x8ee7d9235e01e6b42345120b5d270bdb763624c7
            var balanceOfMessage1 = new BalanceOfFunction()
            { Owner = "0x5d3a536e4d6dbd6114cc1ead35777bab948e3643" }; //compound
            var call1 = new MulticallInputOutput<BalanceOfFunction, BalanceOfOutputDTO>(balanceOfMessage1,
                "0x6b175474e89094c44da98b954eedeac495271d0f"); //dai

            var balanceOfMessage2 = new BalanceOfFunction()
            { Owner = "0x6c6bc977e13df9b0de53b251522280bb72383700" }; //uni
            var call2 = new MulticallInputOutput<BalanceOfFunction, BalanceOfOutputDTO>(balanceOfMessage2,
                "0x6b175474e89094c44da98b954eedeac495271d0f"); //dai

            await web3.Eth.GetMultiQueryBatchRpcHandler().MultiCallAsync(call1, call2);
            Assert.True(call1.Output.Balance > 0);
            Assert.True(call2.Output.Balance > 0);

Full Changelog: 4.15.0...4.16.0

Unity Package (.net472 AOT)

You can now use the latest release directly from a package in this repository: https://github.com/Nethereum/Nethereum.Unity

Unity DLLs (attached)

net472UnityCommonAOT Includes all the Common used net472 AOT dlls, if you need extra libraries just copy them from net472dllsAOT.
net472dllsAOT: Includes all the net472 AOT dlls, this includes libraries like Nethereum.Geth, Nethereum.Besu etc that are not Common

netStandardMinimalWebglUnityAOT Includes a minimal set of netStandard dlls to work with webgl, it excludes libraries like Nethereum.Web3 and Nethereum.JsonRpc.RpcClient which cannot be used in webgl but instead is required to use Nethereum.Unity with coroutines.
netStandardCommonUnityAOT Includes all the Common dlls / libraries used in netstandard AOT, if you need extra libraries just copy them from netStandardUnityCommonAOT.
netStandardUnityCommonAOT Includes all the netstandard AOT dlls, this includes libraries like Nethereum.Geth, Nethereum.Besu etc that are not Common, this may include duplicated libraries like UnityEngine or Newtonsoft.Json.dll

net461dllsAOT Includes all the net461 AOT dlls, this includes libraries like Nethereum.Geth, Nethereum.Besu etc that are not Common, this may include duplicated libraries like UnityEngine or Newtonsoft.Json.dll