Nethereum.Unity庫是一個特定于Nethereum的Unity庫和API,它支持UnityWebRequest使用RPCoverHttp與Ethereum交互。Nethereum.Unity庫是唯一支持在Unity中使用協程時使
Nethereum.Unity 庫是一個特定于 Nethereum 的 Unity 庫和 API,它支持 UnityWebRequest 使用 RPC over Http 與 Ethereum 交互。Nethereum.Unity 庫是唯一支持在 Unity 中使用協程時使用 IEnumerator 和 yield 的庫。

用自己熟悉的語言學習 以太坊開發 : Java | Php | Python | .Net / C# | Golang | Node.JS | Flutter / Dart
1、Nethereum.Unity簡介
如果想在 net461 / netstandard 中使用 async / await 和 Tasks,只要你的環境不需要使用 UnityWebRequest 而不是 HttpRequest,也可以使用與“vanilla” Nethereum 相同的方式。 (Webgl 需要使用 UnityWebRequest)
Nethereum 還為 net351 和 net461 框架構建提供了 AoT 庫。 所有“dll”都可以從 Nethereum github 下載。
flappy eth 是 Unity3d 的“flappy”示例,使用 Nethereum 作為 webgl dapp 游戲轉換為 與 Ethereum、Infura 和 metamask 交互。主要集成組件的源代碼可以在這里找到, 你也可以在這里嘗試游戲。
2、Unity3dSimpleSample示例簡介
Unity3dSimpleSample是使用 Net472(現在從 Net461 升級)進行 Unity3d 開發的簡單示例,使用的 Unity 版本是 2020.3.15f LTS。 示例中包含資產文件夾中的所有 DLL,你可能不需要其中一些代碼,直接刪除它們(如 Besu、Geth、HdWallet、NBitcoin 等)就可以了,具體 取決于你的需要。
示例代碼演示了如何使用Nethereum.Unity實現以下功能:
- 在異步和協程中使用 Unity.UI 將當前 BlockNumber 輸出到日志
- 使用 Unity.UI 和協程進行以太傳輸
- ETH轉賬時使用 1559 Suggestion 策略或 Legacy 模式
- 智能合約部署(ERC20)、交易(Transfer)和查詢(Balance)
注意:
WebGL 僅支持協程 UnityWebRequest。如果構建 WebGL版本發生問題,請取消選中 Development Build。為了支持 WebGL 和 AOT,此示例使用 Net472AOT dll 和自定義 Json.Net Unity請記住刪除 Nethereum 發布包的 System.HttpClient 和 UnityEngine
下面是桌面版的截圖:
下面是WebGL版的截圖:
要運行本地區塊鏈,可以使用預配置的測試鏈。
3、異步查詢區塊號 — 非協程方式
下面的代碼展示了如何用Nethereum.Unity異步查詢區塊號:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| using System; using System.Collections; using System.Collections.Generic; using System.Net; using System.Net.Security; using System.Security.Cryptography.X509Certificates; using Nethereum.ABI.FunctionEncoding.Attributes; using UnityEngine; using UnityEngine.UI; using Nethereum.Contracts; using Nethereum.Web3; public class GetLatestBlockVanillaNethereum : MonoBehaviour { private static bool TrustCertificate(object sender, X509Certificate x509Certificate, X509Chain x509Chain, SslPolicyErrors sslPolicyErrors) { // all certificates are accepted return true; } public string Url = "https://mainnet.infura.io"; public InputField ResultBlockNumber; public InputField InputUrl; // Use this for initialization void Start() { InputUrl.text = Url; } public async void GetBlockNumber() { Url = InputUrl.text; //This is to workaround issue with certificates https://forum.unity.com/threads/how-to-allow-self-signed-certificate.522183/ //Uncomment if needed // ServicePointManager.ServerCertificatevalidationCallback = TrustCertificate; var web3 = new Web3(Url); var blockNumber = await web3.Eth.Blocks.GetBlockNumber.SendRequestAsync(); ResultBlockNumber.text = blockNumber.Value.ToString(); } }
|
4、異步查詢區塊號 — 協程方式
下面的代碼展示了如何用Nethereum.Unity以協程方式異步查詢區塊號:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using Nethereum.JsonRpc.UnityClient; using Nethereum.RPC.Eth.DTOs; using Nethereum.Util; public class GetLatestBlockCoroutine : MonoBehaviour { public string Url = "https://mainnet.infura.io"; public InputField ResultBlockNumber; public InputField InputUrl; // Use this for initialization void Start() { InputUrl.text = Url; } public void GetBlockNumberRequest() { StartCoroutine(GetBlockNumber()); } public IEnumerator GetBlockNumber() { Url = InputUrl.text; var blockNumberRequest = new EthBlockNumberUnityRequest(Url); yield return blockNumberRequest.SendRequest(); ResultBlockNumber.text = blockNumberRequest.Result.Value.ToString(); } }
|
5、ETH轉賬的簡單實現
為了支持ETH轉賬, Nethereum 提供了一個特定的 Unity 請求,EthTransferUnityRequest.
EthTransferUnityRequest 使用我們的以太坊客戶端的“url”、能夠簽署交易的私鑰和我們的帳戶 地址(與私鑰相同)進行實例化。
1 2 3
| var url = " http://localhost:8545 " ; var privateKey = " 0xb5b1870957d373ef0eeffecc6e4812c0fd08f554b37b233526acc331bf1544f7 " ; var ethTransfer = new EthTransferUnityRequest ( url , privateKey , " YOURCHAINID " );
|
一旦我們的請求被實例化,就可以使用傳統模式啟動傳輸,下面的代碼設置 2 Gwei 作為gas價格:
1 2
| var receivingAddress = "0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe"; yield return ethTransfer.TransferEther(receivingAddress, 1.1m, 2);
|
在這里,我們指定了接收地址、發送量和可選的 gas 價格。該請求將自動將 gas 價格轉換為 Wei。
我們可以在之后驗證是否有任何異常,如下所示:
1 2 3 4 5
| if (ethTransfer.Exception != null) { Debug.Log(ethTransfer.Exception.Message); yield break; }
|
如果沒有發生錯誤,我們可以每 2 秒從請求和輪詢中檢索交易哈希以等待交易確認:
1 2 3 4 5
| var transactionHash = ethTransfer.Result; //create a poll to get the receipt when mined var transactionReceiptPolling = new TransactionReceiptPollingRequest(url); //checking every 2 seconds for the receipt yield return transactionReceiptPolling.PollForReceipt(transactionHash, 2);
|
最后,我們可以使用EthGetBalanceUnityRequest檢查收款賬戶的余額。請注意,我們在執行 請求時聲明想要最新塊記錄的余額。
1 2
| var balanceRequest = new EthGetBalanceUnityRequest(url); yield return balanceRequest.SendRequest(receivingAddress, BlockParameter.CreateLatest());
|
可以使用默認的 Wei UnitConvertor 將 Wei 中的結果轉換為 Eth:
1
| Debug.Log("Balance of account:" + UnitConversion.Convert.FromWei(balanceRequest.Result.Value));
|
6、EIP 1559 使用建議
以下是使用協程、TimePreference、MedianFeeHistory 或 LegacyMode 提供的費用建議策略以使用 舊模式或與其他鏈一起使用的一些示例。
要使用 LegacyMode,你必須提供 GasPrice,或者可以通過設置UseLegacyAsDefault為 true 來強制 LegacyMode:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| if (feeStrategy == FeeStrategy.TimePreference) { Debug.Log("Time Preference"); var timePreferenceFeeSuggestion = new TimePreferenceFeeSuggestionUnityRequestStrategy(Url); yield return timePreferenceFeeSuggestion.SuggestFees(); if (timePreferenceFeeSuggestion.Exception != null) { Debug.Log(timePreferenceFeeSuggestion.Exception.Message); yield break; } //lets get the first one so it is higher priority Debug.Log(timePreferenceFeeSuggestion.Result.Length); if (timePreferenceFeeSuggestion.Result.Length > 0) { Debug.Log(timePreferenceFeeSuggestion.Result[0].MaxFeePerGas); Debug.Log(timePreferenceFeeSuggestion.Result[0].MaxPriorityFeePerGas); } var fee = timePreferenceFeeSuggestion.Result[0]; yield return ethTransfer.TransferEther(receivingAddress, Amount, fee.MaxPriorityFeePerGas.Value, fee.MaxFeePerGas.Value); if (ethTransfer.Exception != null) { Debug.Log(ethTransfer.Exception.Message); yield break; } } if(feeStrategy == FeeStrategy.MedianFeeHistory) { Debug.Log("MedianFeeHistory mode"); var medianPriorityFeeStrategy = new MedianPriorityFeeHistorySuggestionUnityRequestStrategy(Url); yield return medianPriorityFeeStrategy.SuggestFee(); if (medianPriorityFeeStrategy.Exception != null) { Debug.Log(medianPriorityFeeStrategy.Exception.Message); yield break; } Debug.Log(medianPriorityFeeStrategy.Result.MaxFeePerGas); Debug.Log(medianPriorityFeeStrategy.Result.MaxPriorityFeePerGas); var fee = medianPriorityFeeStrategy.Result; yield return ethTransfer.TransferEther(receivingAddress, Amount, fee.MaxPriorityFeePerGas.Value, fee.MaxFeePerGas.Value); if (ethTransfer.Exception != null) { Debug.Log(ethTransfer.Exception.Message); yield break; } } if (feeStrategy == FeeStrategy.Legacy) { Debug.Log("Legacy mode"); //I am forcing the legacy mode but also I am including the gas price ethTransfer.UseLegacyAsDefault = true; yield return ethTransfer.TransferEther(receivingAddress, Amount, GasPriceGwei); if (ethTransfer.Exception != null) { Debug.Log(ethTransfer.Exception.Message); yield break; } }
|
7、智能合約定義聲明
在游戲中實現智能合約支持的第一步是包含我們的智能合約定義,這可以是使用 vscode solidity 擴展 或控制臺代碼生成工具生成的代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| //Deployment contract object definition public partial class EIP20Deployment : EIP20Deploymentbase { public EIP20Deployment() : base(BYTECODE) { } public EIP20Deployment(string byteCode) : base(byteCode) { } } public class EIP20Deploymentbase : ContractDeploymentMessage { public static string BYTECODE = "608060405234801561001057600080fd5b506040516107843803806107848339810160409081528151602080840151838501516060860151336000908152808552959095208490556002849055908501805193959094919391019161006991600391860190610096565b506004805460ff191660ff8416179055805161008c906005906020840190610096565b5050505050610131565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100d757805160ff1916838001178555610104565b82800160010185558215610104579182015b828111156101045782518255916020019190600101906100e9565b50610110929150610114565b5090565b61012e91905b80821115610110576000815560010161011a565b90565b610644806101406000396000f3006080604052600436106100ae5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde0381146100b3578063095ea7b31461013d57806318160ddd1461017557806323b872dd1461019c57806327e235e3146101c6578063313ce567146101e75780635c6581651461021257806370a082311461023957806395d89b411461025a578063a9059cbb1461026f578063dd62ed3e14610293575b600080fd5b3480156100bf57600080fd5b506100c86102ba565b6040805160208082528351818301528351919283929083019185019080838360005b838110156101025781810151838201526020016100ea565b50505050905090810190601f16801561012f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561014957600080fd5b50610161600160a060020a0360043516602435610348565b604080519115158252519081900360200190f35b34801561018157600080fd5b5061018a6103ae565b60408051918252519081900360200190f35b3480156101a857600080fd5b50610161600160a060020a03600435811690602435166044356103b4565b3480156101d257600080fd5b5061018a600160a060020a03600435166104b7565b3480156101f357600080fd5b506101fc6104c9565b6040805160ff9092168252519081900360200190f35b34801561021e57600080fd5b5061018a600160a060020a03600435811690602435166104d2565b34801561024557600080fd5b5061018a600160a060020a03600435166104ef565b34801561026657600080fd5b506100c861050a565b34801561027b57600080fd5b50610161600160a060020a0360043516602435610565565b34801561029f57600080fd5b5061018a600160a060020a03600435811690602435166105ed565b6003805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156103405780601f1061031557610100808354040283529160200191610340565b820191906000526020600020905b81548152906001019060200180831161032357829003601f168201915b505050505081565b336000818152600160209081526040808320600160a060020a038716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a350600192915050565b60025481565b600160a060020a03831660008181526001602090815260408083203384528252808320549383529082905281205490919083118015906103f45750828110155b15156103ff57600080fd5b600160a060020a038085166000908152602081905260408082208054870190559187168152208054849003905560001981101561046157600160a060020a03851660009081526001602090815260408083203384529091529020805484900390555b83600160a060020a031685600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a3506001949350505050565b60006020819052908152604090205481565b60045460ff1681565b600160209081526000928352604080842090915290825290205481565b600160a060020a031660009081526020819052604090205490565b6005805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156103405780601f1061031557610100808354040283529160200191610340565b3360009081526020819052604081205482111561058157600080fd5b3360008181526020818152604080832080548790039055600160a060020a03871680845292819020805487019055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a350600192915050565b600160a060020a039182166000908152600160209081526040808320939094168252919091522054905600a165627a7a7230582084c618322109054a21a57e27075384a6172ab854e4b2c2d35062a964a6bf593f0029"; public EIP20Deploymentbase() : base(BYTECODE) { } public EIP20Deploymentbase(string byteCode) : base(byteCode) { } [Parameter("uint256", "_initialAmount", 1)] public BigInteger InitialAmount { get; set; } [Parameter("string", "_tokenName", 2)] public string TokenName { get; set; } [Parameter("uint8", "_decimalUnits", 3)] public byte DecimalUnits { get; set; } [Parameter("string", "_tokenSymbol", 4)] public string TokenSymbol { get; set; } } [Function("transfer", "bool")] public class TransferFunctionbase : FunctionMessage { [Parameter("address", "_to", 1)] public string To { get; set; } [Parameter("uint256", "_value", 2)] public BigInteger Value { get; set; } } public partial class TransferFunction : TransferFunctionbase { } [Function("balanceOf", "uint256")] public class BalanceOfFunction : FunctionMessage { [Parameter("address", "_owner", 1)] public string Owner { get; set; } } [FunctionOutput] public class BalanceOfFunctionOutput : IFunctionOutputDTO { [Parameter("uint256", 1)] public int Balance { get; set; } } [Event("Transfer")] public class TransferEventDTObase : IEventDTO { [Parameter("address", "_from", 1, true)] public virtual string From { get; set; } [Parameter("address", "_to", 2, true)] public virtual string To { get; set; } [Parameter("uint256", "_value", 3, false)] public virtual BigInteger Value { get; set; } } public partial class TransferEventDTO : TransferEventDTObase { public static EventABI GetEventABI() { return EventExtensions.GetEventABI(); } }
|
8、智能合約部署
為了部署智能合約,我們使用節點 url 和簽名信息創建一個 TransactionSignedUnityRequest。 創建一個新的 EIP20Deployment 合約定義,我們設置構造函數參數并發送交易。最后,我們創建 TransactionReceiptPollingRequest 來輪詢交易收據并從交易收據中檢索新部署的合約地址:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| var url = "http://localhost:8545"; var privateKey = "0xb5b1870957d373ef0eeffecc6e4812c0fd08f554b37b233526acc331bf1544f7"; var account = "0x12890d2cce102216644c59daE5baed380d84830c"; //initialising the transaction request sender var transactionRequest = new TransactionSignedUnityRequest(url, privateKey, "YOURCHAINID"); var deployContract = new EIP20Deployment() { InitialAmount = 10000, FromAddress = account, TokenName = "TST", TokenSymbol = "TST" }; //deploy the contract yield return transactionRequest.SignAndSendDeploymentContractTransactionse>(deployContract); if (transactionRequest.Exception != null) { Debug.Log(transactionRequest.Exception.Message); yield break; } var transactionHash = transactionRequest.Result; Debug.Log("Deployment transaction hash:" + transactionHash); //create a poll to get the receipt when mined var transactionReceiptPolling = new TransactionReceiptPollingRequest(url); //checking every 2 seconds for the receipt yield return transactionReceiptPolling.PollForReceipt(transactionHash, 2); var deploymentReceipt = transactionReceiptPolling.Result; Debug.Log("Deployment contract address:" + deploymentReceipt.ContractAddress);
|
9、查詢智能合約
要查詢智能合約,我們需要創建一個提供 FunctionType 和 ReturnType 的新 QueryUnityRequest。 然后我們將執行查詢,查詢結果 Result 對象將為我們提供已經解碼的合約的輸出:
1 2 3 4 5 6 7
| //Query request using our acccount and the contracts address (no parameters needed and default values) var queryRequest = new QueryUnityRequest(url, account); yield return queryRequest.Query(new BalanceOfFunction(){Owner = account}, deploymentReceipt.ContractAddress); //Getting the dto response already decoded var dtoResult = queryRequest.Result; Debug.Log(dtoResult.Balance);
|
10、轉賬交易
發送交易以便與與智能合約進行交互的步驟類似于部署。我們首先創建一個 TransactionSignedUnityRequest 和包含任何參數的函數,一旦發送交易,我們會輪詢確認交易成功的交易收據。
使用交易收據,我們可以解碼該交易的任何日志/事件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| var transactionTransferRequest = new TransactionSignedUnityRequest(url, privateKey, "YOURCHAINID"); var newAddress = "0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe"; var transactionMessage = new TransferFunction { FromAddress = account, To = newAddress, Value = 1000, }; yield return transactionTransferRequest.SignAndSendTransaction(transactionMessage, deploymentReceipt.ContractAddress); var transactionTransferHash = transactionTransferRequest.Result; Debug.Log("Transfer txn hash:" + transactionHash); transactionReceiptPolling = new TransactionReceiptPollingRequest(url); yield return transactionReceiptPolling.PollForReceipt(transactionTransferHash, 2); var transferReceipt = transactionReceiptPolling.Result; var transferEvent = transferReceipt.DecodeAllEvents(); Debug.Log("Transferd amount from event: " + transferEvent[0].Event.Value);
|
11、日志和事件
為了檢索智能合約的日志/事件,我們使用 EthGetLogsUnityRequest 和特定于我們事件的 FilterInput。 可以使用 EventDTO 擴展 GetEventABI() 創建 FilterInputs。一旦我們生成了請求,我們就可以使用 Result.DecodeAllEvents 擴展方法解碼所有匹配的事件。
1 2 3 4 5
| var getLogsRequest = new EthGetLogsUnityRequest(url); var eventTransfer = TransferEventDTO.GetEventABI(); yield return getLogsRequest.SendRequest(eventTransfer.CreateFilterInput(deploymentReceipt.ContractAddress, account)); var eventDecoded = getLogsRequest.Result.DecodeAllEvents(); Debug.Log("Transferd amount from get logs event: " + eventDecoded[0].Event.Value);
|
原文鏈接:http://blog.hubwiz.com/2022/01/28/nethereum-unity-tutorial/