diff --git a/Assets/Thirdweb/Core/Scripts/AccountAbstraction/Core/SmartWallet.cs b/Assets/Thirdweb/Core/Scripts/AccountAbstraction/Core/SmartWallet.cs index 42220ec5..47f3e001 100644 --- a/Assets/Thirdweb/Core/Scripts/AccountAbstraction/Core/SmartWallet.cs +++ b/Assets/Thirdweb/Core/Scripts/AccountAbstraction/Core/SmartWallet.cs @@ -124,7 +124,6 @@ internal async Task Request(RpcRequestMessage requestMessage private async Task CreateUserOpAndSend(RpcRequestMessage requestMessage) { // Deserialize the transaction input from the request message - Debug.Log("Creating UserOp..."); var paramList = JsonConvert.DeserializeObject>(JsonConvert.SerializeObject(requestMessage.RawParameters)); var transactionInput = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(paramList[0])); @@ -146,7 +145,6 @@ private async Task CreateUserOpAndSend(RpcRequestMessage req var executeInput = executeFn.CreateTransactionInput(Accounts[0]); // Create the user operation and its safe (hexified) version - Debug.Log("Create the user operation and its safe (hexified) version..."); var partialUserOp = new EntryPointContract.UserOperation() { @@ -155,43 +153,26 @@ private async Task CreateUserOpAndSend(RpcRequestMessage req InitCode = initData.initCode, CallData = executeInput.Data.HexStringToByteArray(), CallGasLimit = transactionInput.Gas.Value, - VerificationGasLimit = 150000 + initData.gas, - PreVerificationGas = 50000, - MaxFeePerGas = latestBlock.BaseFeePerGas.Value + 2, - MaxPriorityFeePerGas = 2, + VerificationGasLimit = 100000 + initData.gas, + PreVerificationGas = 21000, + MaxFeePerGas = latestBlock.BaseFeePerGas.Value * 2 + BigInteger.Parse("1500000000"), + MaxPriorityFeePerGas = BigInteger.Parse("1500000000"), PaymasterAndData = Constants.DUMMY_PAYMASTER_AND_DATA_HEX.HexStringToByteArray(), Signature = dummySig, }; + partialUserOp.PreVerificationGas = partialUserOp.CalcPreVerificationGas(); var partialUserOpHexified = partialUserOp.EncodeUserOperation(); // Update paymaster data if any - Debug.Log("Update paymaster data if any..."); partialUserOp.PaymasterAndData = await GetPaymasterAndData(requestMessage.Id, partialUserOpHexified); - partialUserOp.Signature = await partialUserOp.HashAndSignUserOp(Config.entryPointAddress); - partialUserOpHexified = partialUserOp.EncodeUserOperation(); - - // Estimate gas with updated paymaster data - Debug.Log("Estimate gas with updated paymaster data..."); + // Hash, sign and encode the user operation - var gasEstimates = await BundlerClient.EthEstimateUserOperationGas(Config.bundlerUrl, Config.thirdwebApiKey, requestMessage.Id, partialUserOpHexified, Config.entryPointAddress); - partialUserOp.CallGasLimit = new HexBigInteger(gasEstimates.CallGasLimit).Value; - partialUserOp.VerificationGasLimit = new HexBigInteger(gasEstimates.VerificationGas).Value; - partialUserOp.PreVerificationGas = new HexBigInteger(gasEstimates.PreVerificationGas).Value; - - partialUserOp.Signature = await partialUserOp.HashAndSignUserOp(Config.entryPointAddress); - partialUserOpHexified = partialUserOp.EncodeUserOperation(); - - // Update paymaster data post estimates again - Debug.Log("Update paymaster data post estimates again..."); - - partialUserOp.PaymasterAndData = await GetPaymasterAndData(requestMessage.Id, partialUserOpHexified); partialUserOp.Signature = await partialUserOp.HashAndSignUserOp(Config.entryPointAddress); partialUserOpHexified = partialUserOp.EncodeUserOperation(); // Send the user operation - Debug.Log("Send the user operation..."); Debug.Log("Valid UserOp: " + JsonConvert.SerializeObject(partialUserOp)); Debug.Log("Valid Encoded UserOp: " + JsonConvert.SerializeObject(partialUserOpHexified)); @@ -199,7 +180,6 @@ private async Task CreateUserOpAndSend(RpcRequestMessage req Debug.Log("UserOp Hash: " + userOpHash); // Wait for the transaction to be mined - Debug.Log("Wait for the transaction to be mined..."); string txHash = null; while (txHash == null && Application.isPlaying) @@ -211,7 +191,6 @@ private async Task CreateUserOpAndSend(RpcRequestMessage req Debug.Log("Tx Hash: " + txHash); // Check if successful - Debug.Log("Check if successful..."); var receipt = await new Web3(ThirdwebManager.Instance.SDK.session.RPC).Eth.Transactions.GetTransactionReceipt.SendRequestAsync(txHash); var decodedEvents = receipt.DecodeAllEvents(); diff --git a/Assets/Thirdweb/Core/Scripts/AccountAbstraction/Core/UserOpUtils.cs b/Assets/Thirdweb/Core/Scripts/AccountAbstraction/Core/UserOpUtils.cs index 50a02751..231ef716 100644 --- a/Assets/Thirdweb/Core/Scripts/AccountAbstraction/Core/UserOpUtils.cs +++ b/Assets/Thirdweb/Core/Scripts/AccountAbstraction/Core/UserOpUtils.cs @@ -44,5 +44,75 @@ public static UserOperationHexified EncodeUserOperation(this EntryPointContract. signature = userOperation.Signature.ByteArrayToHexString() }; } + + public static int CalcPreVerificationGas(this EntryPointContract.UserOperation userOp, GasOverheads gasOverheads = null) + { + GasOverheads ov = gasOverheads ?? new GasOverheads(); + + userOp.Signature = new byte[ov.sigSize]; + userOp.PreVerificationGas = 21000; + + byte[] packed = userOp.PackUserOp(false); + int lengthInWord = (packed.Length + 31) / 32; + + int callDataCost = 0; + foreach (byte b in packed) + { + callDataCost += (b == 0) ? ov.zeroByte : ov.nonZeroByte; + } + + int totalGas = callDataCost + ov.fixedGas / ov.bundleSize + ov.perUserOp + ov.perUserOpWord * lengthInWord; + + return totalGas; + } + + public static byte[] PackUserOp(this EntryPointContract.UserOperation op, bool forSignature = true) + { + var abiEncode = new Nethereum.ABI.ABIEncode(); + var sha3Keccak = new Nethereum.Util.Sha3Keccack(); + + if (forSignature) + { + return abiEncode.GetABIEncoded( + op.Sender, + op.Nonce, + sha3Keccak.CalculateHash(op.InitCode), + sha3Keccak.CalculateHash(op.CallData), + op.CallGasLimit, + op.VerificationGasLimit, + op.PreVerificationGas, + op.MaxFeePerGas, + op.MaxPriorityFeePerGas, + sha3Keccak.CalculateHash(op.PaymasterAndData) + ); + } + else + { + return abiEncode.GetABIEncodedPacked( + op.Sender, + op.Nonce, + op.InitCode, + op.CallData, + op.CallGasLimit, + op.VerificationGasLimit, + op.PreVerificationGas, + op.MaxFeePerGas, + op.MaxPriorityFeePerGas, + op.PaymasterAndData, + op.Signature + ); + } + } + } + + public class GasOverheads + { + public int fixedGas = 21000; + public int perUserOp = 18300; + public int perUserOpWord = 4; + public int zeroByte = 4; + public int nonZeroByte = 16; + public int bundleSize = 1; + public int sigSize = Constants.DUMMY_SIG_LENGTH; } }