Skip to content

Commit 67d8d64

Browse files
authored
[WebGL] Support contract.Prepare and tx builder (#111)
1 parent c2e87b9 commit 67d8d64

File tree

4 files changed

+43209
-41858
lines changed

4 files changed

+43209
-41858
lines changed

Assets/Thirdweb/Core/Scripts/Contract.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,16 @@ public async Task<Transaction> Prepare(string functionName, params object[] args
111111
/// <returns>A <see cref="Transaction"/> object representing the prepared transaction.</returns>
112112
public async Task<Transaction> Prepare(string functionName, string from = null, params object[] args)
113113
{
114-
var contract = new Web3(ThirdwebManager.Instance.SDK.session.RPC).Eth.GetContract(this.abi, this.address);
115-
var function = contract.GetFunction(functionName);
116-
var fromAddress = from ?? await ThirdwebManager.Instance.SDK.wallet.GetAddress();
117-
var txInput = function.CreateTransactionInput(fromAddress, args);
118-
return new Transaction(this, txInput);
114+
var initialInput = new TransactionInput();
115+
if (!Utils.IsWebGLBuild())
116+
{
117+
var contract = new Web3(ThirdwebManager.Instance.SDK.session.RPC).Eth.GetContract(this.abi, this.address);
118+
var function = contract.GetFunction(functionName);
119+
var fromAddress = from ?? await ThirdwebManager.Instance.SDK.wallet.GetAddress();
120+
initialInput = function.CreateTransactionInput(fromAddress, args);
121+
}
122+
123+
return new Transaction(this, initialInput, functionName, args);
119124
}
120125

121126
/// <summary>

Assets/Thirdweb/Core/Scripts/Transaction.cs

Lines changed: 105 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ public struct GasCosts
3838
public class Transaction
3939
{
4040
private readonly Contract contract;
41+
private readonly string fnName;
42+
private object[] fnArgs;
4143

4244
/// <summary>
4345
/// Gets the transaction input.
@@ -49,10 +51,12 @@ public class Transaction
4951
/// </summary>
5052
/// <param name="contract">The contract associated with the transaction.</param>
5153
/// <param name="txInput">The transaction input.</param>
52-
public Transaction(Contract contract, TransactionInput txInput)
54+
public Transaction(Contract contract, TransactionInput txInput, string fnName, object[] fnArgs)
5355
{
5456
this.contract = contract;
5557
this.Input = txInput;
58+
this.fnName = fnName;
59+
this.fnArgs = fnArgs;
5660
}
5761

5862
/// <summary>
@@ -192,9 +196,16 @@ public Transaction SetNonce(string nonce)
192196
/// <returns>The modified <see cref="Transaction"/> object.</returns>
193197
public Transaction SetArgs(params object[] args)
194198
{
195-
var web3 = new Web3(ThirdwebManager.Instance.SDK.session.RPC);
196-
var function = web3.Eth.GetContract(contract.abi, contract.address).GetFunction(Input.To);
197-
Input.Data = function.GetData(args);
199+
if (Utils.IsWebGLBuild())
200+
{
201+
this.fnArgs = args;
202+
}
203+
else
204+
{
205+
var web3 = new Web3(ThirdwebManager.Instance.SDK.session.RPC);
206+
var function = web3.Eth.GetContract(contract.abi, contract.address).GetFunction(Input.To);
207+
Input.Data = function.GetData(args);
208+
}
198209
return this;
199210
}
200211

@@ -204,12 +215,19 @@ public Transaction SetArgs(params object[] args)
204215
/// <returns>The gas price for the transaction as a <see cref="BigInteger"/>.</returns>
205216
public async Task<BigInteger> GetGasPrice()
206217
{
207-
var web3 = new Web3(ThirdwebManager.Instance.SDK.session.RPC);
208-
var gasPrice = await web3.Eth.GasPrice.SendRequestAsync();
209-
var maxGasPrice = BigInteger.Parse("300000000000"); // 300 Gwei in Wei
210-
var extraTip = gasPrice.Value / 10; // +10%
211-
var txGasPrice = gasPrice.Value + extraTip;
212-
return txGasPrice > maxGasPrice ? maxGasPrice : txGasPrice;
218+
if (Utils.IsWebGLBuild())
219+
{
220+
return await Bridge.InvokeRoute<BigInteger>(GetTxBuilderRoute("getGasPrice"), Utils.ToJsonStringArray(Input, fnName, fnArgs));
221+
}
222+
else
223+
{
224+
var web3 = new Web3(ThirdwebManager.Instance.SDK.session.RPC);
225+
var gasPrice = await web3.Eth.GasPrice.SendRequestAsync();
226+
var maxGasPrice = BigInteger.Parse("300000000000"); // 300 Gwei in Wei
227+
var extraTip = gasPrice.Value / 10; // +10%
228+
var txGasPrice = gasPrice.Value + extraTip;
229+
return txGasPrice > maxGasPrice ? maxGasPrice : txGasPrice;
230+
}
213231
}
214232

215233
/// <summary>
@@ -218,9 +236,16 @@ public async Task<BigInteger> GetGasPrice()
218236
/// <returns>The estimated gas limit for the transaction as a <see cref="BigInteger"/>.</returns>
219237
public async Task<BigInteger> EstimateGasLimit()
220238
{
221-
var gasEstimator = new Web3(ThirdwebManager.Instance.SDK.session.RPC);
222-
var gas = await gasEstimator.Eth.Transactions.EstimateGas.SendRequestAsync(Input);
223-
return gas.Value;
239+
if (Utils.IsWebGLBuild())
240+
{
241+
return await Bridge.InvokeRoute<BigInteger>(GetTxBuilderRoute("estimateGasLimit"), Utils.ToJsonStringArray(Input, fnName, fnArgs));
242+
}
243+
else
244+
{
245+
var gasEstimator = new Web3(ThirdwebManager.Instance.SDK.session.RPC);
246+
var gas = await gasEstimator.Eth.Transactions.EstimateGas.SendRequestAsync(Input);
247+
return gas.Value;
248+
}
224249
}
225250

226251
/// <summary>
@@ -229,11 +254,17 @@ public async Task<BigInteger> EstimateGasLimit()
229254
/// <returns>The estimated gas costs for the transaction as a <see cref="GasCosts"/> struct.</returns>
230255
public async Task<GasCosts> EstimateGasCosts()
231256
{
232-
var gasLimit = await EstimateGasLimit();
233-
var gasPrice = await GetGasPrice();
234-
var gasCost = gasLimit * gasPrice;
235-
236-
return new GasCosts { ether = gasPrice.ToString().ToEth(18, false), wei = gasCost };
257+
if (Utils.IsWebGLBuild())
258+
{
259+
return await Bridge.InvokeRoute<GasCosts>(GetTxBuilderRoute("estimateGasCosts"), Utils.ToJsonStringArray(Input, fnName, fnArgs));
260+
}
261+
else
262+
{
263+
var gasLimit = await EstimateGasLimit();
264+
var gasPrice = await GetGasPrice();
265+
var gasCost = gasLimit * gasPrice;
266+
return new GasCosts { ether = gasCost.ToString().ToEth(18, false), wei = gasCost };
267+
}
237268
}
238269

239270
/// <summary>
@@ -255,8 +286,15 @@ public async Task<Transaction> EstimateAndSetGasLimitAsync(string minimumGas = "
255286
/// <returns>The result of the transaction simulation as a string.</returns>
256287
public async Task<string> Simulate()
257288
{
258-
var web3 = new Web3(ThirdwebManager.Instance.SDK.session.RPC);
259-
return await web3.Eth.Transactions.Call.SendRequestAsync(Input);
289+
if (Utils.IsWebGLBuild())
290+
{
291+
return JsonConvert.SerializeObject(await Bridge.InvokeRoute<object>(GetTxBuilderRoute("simulate"), Utils.ToJsonStringArray(Input, fnName, fnArgs)));
292+
}
293+
else
294+
{
295+
var web3 = new Web3(ThirdwebManager.Instance.SDK.session.RPC);
296+
return await web3.Eth.Transactions.Call.SendRequestAsync(Input);
297+
}
260298
}
261299

262300
/// <summary>
@@ -266,21 +304,28 @@ public async Task<string> Simulate()
266304
/// <returns>The transaction hash as a string.</returns>
267305
public async Task<string> Send(bool? gasless = null)
268306
{
269-
if (Input.Gas == null)
270-
await EstimateAndSetGasLimitAsync();
271-
272-
if (Input.Value == null)
273-
Input.Value = new HexBigInteger(0);
274-
275-
bool isGaslessSetup = ThirdwebManager.Instance.SDK.session.Options.gasless.HasValue && ThirdwebManager.Instance.SDK.session.Options.gasless.Value.openzeppelin.HasValue;
276-
if (gasless != null && gasless.Value && !isGaslessSetup)
277-
throw new UnityException("Gasless transactions are not enabled. Please enable them in the SDK options.");
278-
279-
bool sendGaslessly = gasless == null ? isGaslessSetup : gasless.Value;
280-
if (sendGaslessly)
281-
return await SendGasless();
307+
if (Utils.IsWebGLBuild())
308+
{
309+
if (gasless == null || gasless == false)
310+
return await Send();
311+
else
312+
return await SendGasless();
313+
}
282314
else
283-
return await Send();
315+
{
316+
if (Input.Gas == null)
317+
await EstimateAndSetGasLimitAsync();
318+
if (Input.Value == null)
319+
Input.Value = new HexBigInteger(0);
320+
bool isGaslessSetup = ThirdwebManager.Instance.SDK.session.Options.gasless.HasValue && ThirdwebManager.Instance.SDK.session.Options.gasless.Value.openzeppelin.HasValue;
321+
if (gasless != null && gasless.Value && !isGaslessSetup)
322+
throw new UnityException("Gasless transactions are not enabled. Please enable them in the SDK options.");
323+
bool sendGaslessly = gasless == null ? isGaslessSetup : gasless.Value;
324+
if (sendGaslessly)
325+
return await SendGasless();
326+
else
327+
return await Send();
328+
}
284329
}
285330

286331
/// <summary>
@@ -290,8 +335,21 @@ public async Task<string> Send(bool? gasless = null)
290335
/// <returns>The transaction result as a <see cref="TransactionResult"/> object.</returns>
291336
public async Task<TransactionResult> SendAndWaitForTransactionResult(bool? gasless = null)
292337
{
293-
var txHash = await Send(gasless);
294-
return await WaitForTransactionResult(txHash);
338+
if (Utils.IsWebGLBuild())
339+
{
340+
string action;
341+
if (gasless == null || gasless == false)
342+
action = "execute";
343+
else
344+
action = "executeGasless";
345+
346+
return await Bridge.InvokeRoute<TransactionResult>(GetTxBuilderRoute(action), Utils.ToJsonStringArray(Input, fnName, fnArgs));
347+
}
348+
else
349+
{
350+
var txHash = await Send(gasless);
351+
return await WaitForTransactionResult(txHash);
352+
}
295353
}
296354

297355
/// <summary>
@@ -301,6 +359,9 @@ public async Task<TransactionResult> SendAndWaitForTransactionResult(bool? gasle
301359
/// <returns>The transaction result as a <see cref="TransactionResult"/> object.</returns>
302360
public static async Task<TransactionResult> WaitForTransactionResult(string txHash)
303361
{
362+
if (Utils.IsWebGLBuild())
363+
throw new UnityException("WaitForTransactionResult is not supported in WebGL builds.");
364+
304365
var receiptPoller = new Web3(ThirdwebManager.Instance.SDK.session.RPC);
305366
var receipt = await receiptPoller.TransactionReceiptPolling.PollForReceiptAsync(txHash);
306367
return receipt.ToTransactionResult();
@@ -310,10 +371,7 @@ private async Task<string> Send()
310371
{
311372
if (Utils.IsWebGLBuild())
312373
{
313-
throw new UnityException("This functionality is not yet available on your current platform.");
314-
// string route = contract.abi != null ? $"{contract.address}{Routable.subSeparator}{contract.abi}" : contract.address;
315-
// string sendRoute = $"{route}{Routable.separator}send";
316-
// return await Bridge.InvokeRoute<string>(sendRoute, new string[] { JsonConvert.SerializeObject(Input) });
374+
return await Bridge.InvokeRoute<string>(GetTxBuilderRoute("send"), Utils.ToJsonStringArray(Input, fnName, fnArgs));
317375
}
318376
else
319377
{
@@ -336,7 +394,7 @@ private async Task<string> SendGasless()
336394
{
337395
if (Utils.IsWebGLBuild())
338396
{
339-
throw new UnityException("This functionality is not yet available on your current platform.");
397+
return await Bridge.InvokeRoute<string>(GetTxBuilderRoute("sendGasless"), Utils.ToJsonStringArray(Input, fnName, fnArgs));
340398
}
341399
else
342400
{
@@ -398,5 +456,11 @@ private async Task<string> SendGasless()
398456
}
399457
}
400458
}
459+
460+
private string GetTxBuilderRoute(string action)
461+
{
462+
string route = contract.abi != null ? $"{contract.address}{Routable.subSeparator}{contract.abi}" : contract.address;
463+
return $"{route}{Routable.separator}tx{Routable.separator}{action}";
464+
}
401465
}
402466
}

Assets/Thirdweb/Examples/Scripts/Prefabs/Prefab_Miscellaneous.cs

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -23,36 +23,9 @@ public async void CustomCall()
2323
{
2424
Contract contract = ThirdwebManager.Instance.SDK.GetContract("0x62Cf5485B6C24b707E47C5E0FB2EAe7EbE18EC4c", MY_CUSTOM_CONTRACT_ABI);
2525

26-
// Contract.Prepare
27-
// string connectedAddress = await ThirdwebManager.Instance.SDK.wallet.GetAddress();
28-
// Transaction transaction = await contract.Prepare(functionName: "claim", from: connectedAddress, connectedAddress, 0, 1);
29-
// Debugger.Instance.Log("[Custom Call] Prepare Successful", transaction.ToString());
30-
// // transaction.SetValue("0.00000000001");
31-
// // transaction.SetGasLimit("100000");
32-
// try
33-
// {
34-
// var data = await transaction.Simulate();
35-
// Debugger.Instance.Log("[Custom Call] Simulate Successful", "Data: " + data.ToString() + " \nSending Transaction...");
36-
// }
37-
// catch (System.Exception e)
38-
// {
39-
// Debugger.Instance.Log("[Custom Call] Simulate Error", e.Message);
40-
// return;
41-
// }
42-
43-
// try
44-
// {
45-
// TransactionResult transactionResult = await transaction.SendAndWaitForTransactionResult();
46-
// Debugger.Instance.Log("[Custom Call] Send Successful", transactionResult.ToString());
47-
// }
48-
// catch (System.Exception e)
49-
// {
50-
// Debugger.Instance.Log("[Custom Call] Send Error", e.ToString());
51-
// }
52-
5326
// Contract.Read
54-
string uri = await contract.Read<string>("uri", 0);
55-
Debugger.Instance.Log("[Custom Call] Read Custom URI Successful", uri);
27+
// string uri = await contract.Read<string>("uri", 0);
28+
// Debugger.Instance.Log("[Custom Call] Read Custom URI Successful", uri);
5629

5730
// Contract.Write
5831
// TransactionResult transactionResult = await contract.Write("claimKitten");
@@ -62,6 +35,43 @@ public async void CustomCall()
6235
// {
6336
// value = "0.05".ToWei() // 0.05 ETH
6437
// }, "0xE79ee09bD47F4F5381dbbACaCff2040f2FbC5803", 0, 1);
38+
39+
// Contract.Prepare
40+
string connectedAddress = await ThirdwebManager.Instance.SDK.wallet.GetAddress();
41+
Transaction transaction = await contract.Prepare(functionName: "claim", from: connectedAddress, connectedAddress, 0, 1);
42+
// transaction.SetValue("0.00000000001");
43+
// transaction.SetGasLimit("100000");
44+
45+
try
46+
{
47+
var data = await transaction.Simulate();
48+
Debugger.Instance.Log("[Custom Call] Simulate Successful", "Data: " + data.ToString() + " \nSending Transaction...");
49+
}
50+
catch (System.Exception e)
51+
{
52+
Debugger.Instance.Log("[Custom Call] Simulate Error", e.Message);
53+
return;
54+
}
55+
56+
await transaction.EstimateAndSetGasLimitAsync();
57+
58+
var gasPrice = await transaction.GetGasPrice();
59+
Debug.Log($"Gas Price: {gasPrice}");
60+
61+
var gasCosts = await transaction.EstimateGasCosts();
62+
Debug.Log($"Gas Cost: {gasCosts.wei} WEI");
63+
64+
Debugger.Instance.Log("[Custom Call] Transaction Preview", transaction.ToString());
65+
66+
try
67+
{
68+
string transactionResult = await transaction.Send(gasless: false);
69+
Debugger.Instance.Log("[Custom Call] Send Successful", "Tx Hash: " + transactionResult);
70+
}
71+
catch (System.Exception e)
72+
{
73+
Debugger.Instance.Log("[Custom Call] Send Error", e.ToString());
74+
}
6575
}
6676
catch (System.Exception e)
6777
{

0 commit comments

Comments
 (0)