Skip to content

Commit 4400c69

Browse files
authored
Unify Transaction Flows (#172)
1 parent 207f78e commit 4400c69

File tree

6 files changed

+104
-178
lines changed

6 files changed

+104
-178
lines changed

Assets/Thirdweb/Core/Scripts/ThirdwebSession.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,6 @@ internal async Task EnsureCorrectNetwork(BigInteger newChainId)
194194
CurrentChainData = newChainData;
195195
RPC = CurrentChainData.rpcUrls[0];
196196
Web3 = await ActiveWallet.GetWeb3();
197-
Web3.TransactionManager.UseLegacyAsDefault = !Utils.Supports1559(newChainId.ToString());
198197
Web3.Client.OverridingRequestInterceptor = new ThirdwebInterceptor(ActiveWallet);
199198
}
200199

Assets/Thirdweb/Core/Scripts/Transaction.cs

Lines changed: 90 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,9 @@ public struct GasCosts
4141
/// </summary>
4242
public class Transaction
4343
{
44-
private readonly Contract contract;
45-
private readonly string fnName;
46-
private object[] fnArgs;
47-
48-
/// <summary>
49-
/// Gets the transaction input.
50-
/// </summary>
44+
public Contract Contract { get; private set; }
45+
public string FunctionName { get; private set; }
46+
public object[] FunctionArgs { get; private set; }
5147
public TransactionInput Input { get; private set; }
5248

5349
/// <summary>
@@ -57,10 +53,15 @@ public class Transaction
5753
/// <param name="txInput">The transaction input.</param>
5854
public Transaction(Contract contract, TransactionInput txInput, string fnName, object[] fnArgs)
5955
{
60-
this.contract = contract;
56+
this.Contract = contract;
57+
this.Input = txInput;
58+
this.FunctionName = fnName;
59+
this.FunctionArgs = fnArgs;
60+
}
61+
62+
public Transaction(TransactionInput txInput)
63+
{
6164
this.Input = txInput;
62-
this.fnName = fnName;
63-
this.fnArgs = fnArgs;
6465
}
6566

6667
/// <summary>
@@ -202,12 +203,12 @@ public Transaction SetArgs(params object[] args)
202203
{
203204
if (Utils.IsWebGLBuild())
204205
{
205-
this.fnArgs = args;
206+
this.FunctionArgs = args;
206207
}
207208
else
208209
{
209210
var web3 = Utils.GetWeb3();
210-
var function = web3.Eth.GetContract(contract.ABI, contract.Address).GetFunction(Input.To);
211+
var function = web3.Eth.GetContract(Contract.ABI, Contract.Address).GetFunction(Input.To);
211212
Input.Data = function.GetData(args);
212213
}
213214
return this;
@@ -221,17 +222,12 @@ public async Task<BigInteger> GetGasPrice()
221222
{
222223
if (Utils.IsWebGLBuild())
223224
{
224-
var val = await Bridge.InvokeRoute<string>(GetTxBuilderRoute("getGasPrice"), Utils.ToJsonStringArray(Input, fnName, fnArgs));
225+
var val = await Bridge.InvokeRoute<string>(GetTxBuilderRoute("getGasPrice"), Utils.ToJsonStringArray(Input, FunctionName, FunctionArgs));
225226
return BigInteger.Parse(val);
226227
}
227228
else
228229
{
229-
var web3 = Utils.GetWeb3();
230-
var gasPrice = await web3.Eth.GasPrice.SendRequestAsync();
231-
var maxGasPrice = BigInteger.Parse("300000000000"); // 300 Gwei in Wei
232-
var extraTip = gasPrice.Value / 10; // +10%
233-
var txGasPrice = gasPrice.Value + extraTip;
234-
return txGasPrice > maxGasPrice ? maxGasPrice : txGasPrice;
230+
return await Utils.GetLegacyGasPriceAsync(ThirdwebManager.Instance.SDK.Session.ChainId);
235231
}
236232
}
237233

@@ -243,7 +239,7 @@ public async Task<BigInteger> EstimateGasLimit()
243239
{
244240
if (Utils.IsWebGLBuild())
245241
{
246-
var val = await Bridge.InvokeRoute<string>(GetTxBuilderRoute("estimateGasLimit"), Utils.ToJsonStringArray(Input, fnName, fnArgs));
242+
var val = await Bridge.InvokeRoute<string>(GetTxBuilderRoute("estimateGasLimit"), Utils.ToJsonStringArray(Input, FunctionName, FunctionArgs));
247243
return BigInteger.Parse(val);
248244
}
249245
else
@@ -262,7 +258,7 @@ public async Task<GasCosts> EstimateGasCosts()
262258
{
263259
if (Utils.IsWebGLBuild())
264260
{
265-
return await Bridge.InvokeRoute<GasCosts>(GetTxBuilderRoute("estimateGasCosts"), Utils.ToJsonStringArray(Input, fnName, fnArgs));
261+
return await Bridge.InvokeRoute<GasCosts>(GetTxBuilderRoute("estimateGasCosts"), Utils.ToJsonStringArray(Input, FunctionName, FunctionArgs));
266262
}
267263
else
268264
{
@@ -294,7 +290,7 @@ public async Task<string> Simulate()
294290
{
295291
if (Utils.IsWebGLBuild())
296292
{
297-
return JsonConvert.SerializeObject(await Bridge.InvokeRoute<object>(GetTxBuilderRoute("simulate"), Utils.ToJsonStringArray(Input, fnName, fnArgs)));
293+
return JsonConvert.SerializeObject(await Bridge.InvokeRoute<object>(GetTxBuilderRoute("simulate"), Utils.ToJsonStringArray(Input, FunctionName, FunctionArgs)));
298294
}
299295
else
300296
{
@@ -314,7 +310,7 @@ public async Task<string> Sign()
314310

315311
if (Utils.IsWebGLBuild())
316312
{
317-
return await Bridge.InvokeRoute<string>(GetTxBuilderRoute("sign"), Utils.ToJsonStringArray(Input, fnName, fnArgs));
313+
return await Bridge.InvokeRoute<string>(GetTxBuilderRoute("sign"), Utils.ToJsonStringArray(Input, FunctionName, FunctionArgs));
318314
}
319315
else
320316
{
@@ -371,7 +367,7 @@ public async Task<TransactionResult> SendAndWaitForTransactionResult(bool? gasle
371367
else
372368
action = "executeGasless";
373369

374-
return await Bridge.InvokeRoute<TransactionResult>(GetTxBuilderRoute(action), Utils.ToJsonStringArray(Input, fnName, fnArgs));
370+
return await Bridge.InvokeRoute<TransactionResult>(GetTxBuilderRoute(action), Utils.ToJsonStringArray(Input, FunctionName, FunctionArgs));
375371
}
376372
else
377373
{
@@ -437,30 +433,54 @@ private async Task<string> Send()
437433
{
438434
if (Utils.IsWebGLBuild())
439435
{
440-
return await Bridge.InvokeRoute<string>(GetTxBuilderRoute("send"), Utils.ToJsonStringArray(Input, fnName, fnArgs));
436+
return await Bridge.InvokeRoute<string>(GetTxBuilderRoute("send"), Utils.ToJsonStringArray(Input, FunctionName, FunctionArgs));
441437
}
442438
else
443439
{
440+
var supports1559 = Utils.Supports1559(ThirdwebManager.Instance.SDK.Session.ChainId.ToString());
441+
if (supports1559)
442+
{
443+
if (Input.GasPrice == null)
444+
{
445+
var fees = await Utils.GetGasPriceAsync(ThirdwebManager.Instance.SDK.Session.ChainId);
446+
if (Input.MaxFeePerGas == null)
447+
Input.MaxFeePerGas = new HexBigInteger(fees.MaxFeePerGas);
448+
if (Input.MaxPriorityFeePerGas == null)
449+
Input.MaxPriorityFeePerGas = new HexBigInteger(fees.MaxPriorityFeePerGas);
450+
}
451+
}
452+
else
453+
{
454+
if (Input.MaxFeePerGas == null && Input.MaxPriorityFeePerGas == null)
455+
{
456+
ThirdwebDebug.Log("Using Legacy Gas Pricing");
457+
Input.GasPrice = new HexBigInteger(await Utils.GetLegacyGasPriceAsync(ThirdwebManager.Instance.SDK.Session.ChainId));
458+
}
459+
}
460+
461+
string hash;
444462
if (
445463
ThirdwebManager.Instance.SDK.Session.ActiveWallet.GetSignerProvider() == WalletProvider.LocalWallet
446464
&& ThirdwebManager.Instance.SDK.Session.ActiveWallet.GetProvider() != WalletProvider.SmartWallet
447465
)
448466
{
449-
return await ThirdwebManager.Instance.SDK.Session.Web3.Eth.TransactionManager.SendTransactionAsync(Input);
467+
hash = await ThirdwebManager.Instance.SDK.Session.Web3.Eth.TransactionManager.SendTransactionAsync(Input);
450468
}
451469
else
452470
{
453471
var ethSendTx = new EthSendTransaction(ThirdwebManager.Instance.SDK.Session.Web3.Client);
454-
return await ethSendTx.SendRequestAsync(Input);
472+
hash = await ethSendTx.SendRequestAsync(Input);
455473
}
474+
ThirdwebDebug.Log($"Transaction hash: {hash}");
475+
return hash;
456476
}
457477
}
458478

459479
private async Task<string> SendGasless()
460480
{
461481
if (Utils.IsWebGLBuild())
462482
{
463-
return await Bridge.InvokeRoute<string>(GetTxBuilderRoute("sendGasless"), Utils.ToJsonStringArray(Input, fnName, fnArgs));
483+
return await Bridge.InvokeRoute<string>(GetTxBuilderRoute("sendGasless"), Utils.ToJsonStringArray(Input, FunctionName, FunctionArgs));
464484
}
465485
else
466486
{
@@ -525,8 +545,49 @@ private async Task<string> SendGasless()
525545

526546
private string GetTxBuilderRoute(string action)
527547
{
528-
string route = contract.ABI != null ? $"{contract.Address}{Routable.subSeparator}{contract.ABI}" : contract.Address;
548+
string route = Contract.ABI != null ? $"{Contract.Address}{Routable.subSeparator}{Contract.ABI}" : Contract.Address;
529549
return $"{route}{Routable.separator}tx{Routable.separator}{action}";
530550
}
531551
}
552+
553+
[System.Serializable]
554+
public struct RelayerResponse
555+
{
556+
[JsonProperty("status")]
557+
public string status;
558+
559+
[JsonProperty("result")]
560+
public string result;
561+
}
562+
563+
[System.Serializable]
564+
public struct RelayerResult
565+
{
566+
[JsonProperty("txHash")]
567+
public string txHash;
568+
}
569+
570+
[System.Serializable]
571+
public struct RelayerRequest
572+
{
573+
[JsonProperty("request")]
574+
public MinimalForwarder.ForwardRequest request;
575+
576+
[JsonProperty("signature")]
577+
public string signature;
578+
579+
[JsonProperty("forwarderAddress")]
580+
public string forwarderAddress;
581+
582+
[JsonProperty("type")]
583+
public string type;
584+
585+
public RelayerRequest(MinimalForwarder.ForwardRequest request, string signature, string forwarderAddress)
586+
{
587+
this.request = request;
588+
this.signature = signature;
589+
this.forwarderAddress = forwarderAddress;
590+
this.type = "forward";
591+
}
592+
}
532593
}
Lines changed: 4 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
using System.Threading.Tasks;
22
using System.Numerics;
33
using Nethereum.Contracts;
4-
using UnityEngine;
54
using Nethereum.RPC.Eth.DTOs;
65
using MinimalForwarder = Thirdweb.Contracts.Forwarder.ContractDefinition;
7-
using UnityEngine.Networking;
86
using Newtonsoft.Json;
97
using Thirdweb.Contracts.Forwarder.ContractDefinition;
10-
using Nethereum.RPC.Eth.Transactions;
11-
using Thirdweb.Redcode.Awaiting;
128
using Nethereum.ABI.FunctionEncoding.Attributes;
139
using Nethereum.Contracts.QueryHandlers.MultiCall;
1410
using System.Collections.Generic;
@@ -66,8 +62,6 @@ public static async Task<TransactionResult> ThirdwebWrite<TWFunction>(string con
6662
public static async Task<TransactionReceipt> ThirdwebWriteRawResult<TWFunction>(string contractAddress, TWFunction functionMessage, BigInteger? weiValue = null, BigInteger? gasOverride = null)
6763
where TWFunction : FunctionMessage, new()
6864
{
69-
string txHash = null;
70-
7165
functionMessage.FromAddress = await ThirdwebManager.Instance.SDK.Wallet.GetAddress();
7266
functionMessage.AmountToSend = weiValue ?? 0;
7367

@@ -89,130 +83,10 @@ public static async Task<TransactionReceipt> ThirdwebWriteRawResult<TWFunction>(
8983
functionMessage.Gas = 100000;
9084
}
9185
}
92-
93-
bool isGasless = ThirdwebManager.Instance.SDK.Session.Options.gasless.HasValue && ThirdwebManager.Instance.SDK.Session.Options.gasless.Value.openzeppelin.HasValue;
94-
95-
if (!isGasless)
96-
{
97-
if (functionMessage.GasPrice == null && functionMessage.MaxFeePerGas == null && functionMessage.MaxPriorityFeePerGas == null)
98-
{
99-
var gasPrice = await Utils.GetGasPriceAsync(await ThirdwebManager.Instance.SDK.Wallet.GetChainId());
100-
functionMessage.MaxFeePerGas = gasPrice.MaxFeePerGas;
101-
functionMessage.MaxPriorityFeePerGas = gasPrice.MaxPriorityFeePerGas;
102-
}
103-
104-
if (
105-
ThirdwebManager.Instance.SDK.Session.ActiveWallet.GetSignerProvider() == WalletProvider.LocalWallet
106-
&& ThirdwebManager.Instance.SDK.Session.ActiveWallet.GetProvider() != WalletProvider.SmartWallet
107-
)
108-
{
109-
var web3 = await ThirdwebManager.Instance.SDK.Session.ActiveWallet.GetSignerWeb3();
110-
var transactionHandler = web3.Eth.GetContractTransactionHandler<TWFunction>();
111-
txHash = await transactionHandler.SendRequestAsync(contractAddress, functionMessage);
112-
}
113-
else
114-
{
115-
var transaction = new EthSendTransaction(ThirdwebManager.Instance.SDK.Session.Web3.Client);
116-
var transactionInput = functionMessage.CreateTransactionInput(contractAddress);
117-
txHash = await transaction.SendRequestAsync(transactionInput);
118-
}
119-
}
120-
else
121-
{
122-
string relayerUrl = ThirdwebManager.Instance.SDK.Session.Options.gasless.Value.openzeppelin?.relayerUrl;
123-
string forwarderAddress = ThirdwebManager.Instance.SDK.Session.Options.gasless.Value.openzeppelin?.relayerForwarderAddress;
124-
string forwarderDomain = ThirdwebManager.Instance.SDK.Session.Options.gasless.Value.openzeppelin?.domainName;
125-
string forwarderVersion = ThirdwebManager.Instance.SDK.Session.Options.gasless.Value.openzeppelin?.domainVersion;
126-
127-
functionMessage.Nonce = (
128-
await ThirdwebRead<MinimalForwarder.GetNonceFunction, MinimalForwarder.GetNonceOutputDTO>(
129-
forwarderAddress,
130-
new MinimalForwarder.GetNonceFunction() { From = functionMessage.FromAddress }
131-
)
132-
).ReturnValue1;
133-
134-
var request = new MinimalForwarder.ForwardRequest()
135-
{
136-
From = functionMessage.FromAddress,
137-
To = contractAddress,
138-
Value = functionMessage.AmountToSend,
139-
Gas = functionMessage.Gas.Value,
140-
Nonce = functionMessage.Nonce.Value,
141-
Data = functionMessage.GetCallData().ByteArrayToHexString()
142-
};
143-
144-
var signature = await EIP712.GenerateSignature_MinimalForwarder(forwarderDomain, forwarderVersion, ThirdwebManager.Instance.SDK.Session.ChainId, forwarderAddress, request);
145-
146-
var postData = new RelayerRequest(request, signature, forwarderAddress);
147-
148-
using UnityWebRequest req = UnityWebRequest.Post(relayerUrl, "");
149-
byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(postData));
150-
req.uploadHandler = (UploadHandler)new UploadHandlerRaw(bodyRaw);
151-
req.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
152-
req.SetRequestHeader("Content-Type", "application/json");
153-
await req.SendWebRequest();
154-
if (req.result != UnityWebRequest.Result.Success)
155-
{
156-
throw new UnityException(
157-
$"Forward Request Failed!\nError: {req.downloadHandler.text}\nRelayer URL: {relayerUrl}\nRelayer Forwarder Address: {forwarderAddress}\nRequest: {request}\nSignature: {signature}\nPost Data: {postData}"
158-
);
159-
}
160-
else
161-
{
162-
var response = JsonConvert.DeserializeObject<RelayerResponse>(req.downloadHandler.text);
163-
if (response.status != "success")
164-
{
165-
throw new UnityException(
166-
$"Forward Request Failed!\nError: {req.downloadHandler.text}\nRelayer URL: {relayerUrl}\nRelayer Forwarder Address: {forwarderAddress}\nRequest: {request}\nSignature: {signature}\nPost Data: {postData}"
167-
);
168-
}
169-
var result = JsonConvert.DeserializeObject<RelayerResult>(response.result);
170-
txHash = result.txHash;
171-
}
172-
}
173-
ThirdwebDebug.Log("txHash: " + txHash);
174-
return await Transaction.WaitForTransactionResultRaw(txHash);
175-
}
176-
}
177-
178-
[System.Serializable]
179-
public struct RelayerResponse
180-
{
181-
[JsonProperty("status")]
182-
public string status;
183-
184-
[JsonProperty("result")]
185-
public string result;
186-
}
187-
188-
[System.Serializable]
189-
public struct RelayerResult
190-
{
191-
[JsonProperty("txHash")]
192-
public string txHash;
193-
}
194-
195-
[System.Serializable]
196-
public struct RelayerRequest
197-
{
198-
[JsonProperty("request")]
199-
public MinimalForwarder.ForwardRequest request;
200-
201-
[JsonProperty("signature")]
202-
public string signature;
203-
204-
[JsonProperty("forwarderAddress")]
205-
public string forwarderAddress;
206-
207-
[JsonProperty("type")]
208-
public string type;
209-
210-
public RelayerRequest(ForwardRequest request, string signature, string forwarderAddress)
211-
{
212-
this.request = request;
213-
this.signature = signature;
214-
this.forwarderAddress = forwarderAddress;
215-
this.type = "forward";
86+
var transactionInput = functionMessage.CreateTransactionInput(contractAddress);
87+
var tx = new Transaction(transactionInput);
88+
var hash = await tx.Send();
89+
return await Transaction.WaitForTransactionResultRaw(hash);
21690
}
21791
}
21892
}

0 commit comments

Comments
 (0)