Releases: Nethereum/Nethereum
4.21.4
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.
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
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
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
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);
...
4.20.0
-
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
- 712 Bytes json serialisation encoding support, Newtonsoft internally defaults to base64 da958c6
- GetBlockReceipts new RPC method, returns all the block receipts for a block, this method also supports batching. 5e38191
- Validation for Zero Length arrays when Abi decoding Arrays (Dynamic and Static) acb6bfd
- Extra constant chains 5f7e788
- Extended chain features so in the future we can add many known chains, this also allows the mapping to switchchain rpc method in hosted wallets 9bf29d8
- WalletConnect upgrade and SignClient as an interface so it can interop with both unity and dekstop / wasm 8b008b4
- RPCClient connectivity settings, more specific d3a3afc
- Batch exceptions, per request to enable decoding 44db2e2
- added polygon and arbitrum explorers api @ridicoulous in #1001
- added log processing parallelization @ridicoulous in #1002
- Nethereum.Unity https://github.com/Nethereum/Nethereum.Unity and Nethereum.Unity.WalletConnect https://github.com/Nethereum/Nethereum.Unity.WalletConnect (Experimental) published as openupm https://openupm.com/packages/?sort=downloads&q=nethereum (thanks @skibitsky for his help)
Full Changelog: 4.18.0...4.19.0
4.18.0
.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 βοΈβοΈππ
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)
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:
- Add the package Nethereum.WalletConnect
- Check one of these examples:
- Blazor https://github.com/Nethereum/Nethereum/blob/master/consoletests/NethereumWCBlazor/Pages/Index.razor
- Console https://github.com/Nethereum/Nethereum/blob/master/consoletests/WCNethereum/SimpleExample.cs
- Avalonia https://github.com/Nethereum/Nethereum/tree/master/consoletests/NethereumWCAvalonia
- GoDot with Avalonia https://github.com/Nethereum/Nethereum/tree/master/consoletests/NethereumGodotWCAvalonia
Commits: 27e5c93, 7153532, b3e617d, 8fa81e1, 6324189
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 βοΈβοΈππ
See 4.17.1
Full Changelog: 4.16.0...4.17.0
4.16.0 βοΈβοΈππ
Nethereum.DataServices
New DataServices package, adding support to external (or local) data services starting with Etherscan, 4ByteDirectory and Sourcify.
Usage:
- Add the package Nethereum.DataServices
- 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