diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index e5e33af..a8bdf92 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -217,7 +217,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 331C8080294A63A400263BE5 = { diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 87131a0..8e3ca5d 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ _errorMessage; final EthereumAddress nft = - EthereumAddress.fromHex("0x4B509a7e891Dc8fd45491811d67a8B9e7ef547B9"); + EthereumAddress.fromHex("0x3661b40C520a273214d281bd84730BA68604d874"); final EthereumAddress erc20 = - EthereumAddress.fromHex("0xAEaF19097D8a8da728438D6B57edd9Bc5DAc4795"); + EthereumAddress.fromHex("0x69583ED4AA579fdc83FB6CCF13A5Ffd9B39F62aF"); final EthereumAddress deployer = EthereumAddress.fromHex("0xf5bb7f874d8e3f41821175c0aa9910d30d10e193"); + final EthereumAddress sharedSigner = + EthereumAddress.fromHex("0x94a4F6affBd8975951142c3999aEAB7ecee555c2"); + final EthereumAddress p256Verifier = + EthereumAddress.fromHex("0xc2b78104907F722DABAc4C69f826a522B2754De4"); final salt = Uint256.zero; - static const rpc = "https://api.pimlico.io/v2/84532/rpc?apikey=API_KEY"; + static const rpc = + "https://api.pimlico.io/v2/123/rpc?apikey=${"PIMLICO_API_KEY"}"; WalletProvider() - : _chain = Chains.getChain(Network.baseTestnet) - ..accountFactory = Constants.lightAccountFactoryAddressv07 - ..bundlerUrl = rpc - ..paymasterUrl = rpc; + : _chain = Chain( + bundlerUrl: rpc, + // paymasterUrl: rpc, // for fuse network, not really working. + testnet: true, + chainId: 123, + jsonRpcUrl: "https://rpc.fusespark.io", + accountFactory: Constants.safeProxyFactoryAddress, + explorer: "https://explorer.fusespark.io/", + entrypoint: EntryPointAddress.v07); Future registerWithPassKey(String name, {bool? requiresUserVerification}) async { - _chain.accountFactory = Constants.safeProxyFactoryAddress; - final options = PassKeysOptions( - name: "variance", - namespace: "variance.space", - sharedWebauthnSigner: EthereumAddress.fromHex( - "0xfD90FAd33ee8b58f32c00aceEad1358e4AFC23f9")); + name: "variance", + namespace: "variance.space", + sharedWebauthnSigner: sharedSigner, + ); final pkpSigner = PassKeySigner(options: options); try { @@ -48,8 +56,8 @@ class WalletProvider extends ChangeNotifier { "${DateTime.timestamp().millisecondsSinceEpoch}@variance.space", name); _wallet = await walletFactory.createSafeAccountWithPasskey( - keypair, salt, options.sharedWebauthnSigner); - + keypair, salt, options.sharedWebauthnSigner, p256Verifier); + overrideGas(); log("wallet created ${_wallet?.address.hex} "); } catch (e) { _errorMessage = e.toString(); @@ -60,6 +68,8 @@ class WalletProvider extends ChangeNotifier { } Future createEOAWallet() async { + _chain.accountFactory = Constants.lightAccountFactoryAddressv07; + final signer = EOAWallet.createWallet( WordLength.word_12, const SignatureOptions(prefix: [0])); final SmartWalletFactory walletFactory = SmartWalletFactory(_chain, signer); @@ -75,6 +85,8 @@ class WalletProvider extends ChangeNotifier { } Future createPrivateKeyWallet() async { + _chain.accountFactory = Constants.lightAccountFactoryAddressv07; + final signer = PrivateKeySigner.createRandom( "password", const SignatureOptions(prefix: [0])); final SmartWalletFactory walletFactory = SmartWalletFactory(_chain, signer); @@ -90,13 +102,12 @@ class WalletProvider extends ChangeNotifier { } Future createSafeWallet() async { - _chain.accountFactory = Constants.safeProxyFactoryAddress; - final signer = EOAWallet.createWallet(); final SmartWalletFactory walletFactory = SmartWalletFactory(_chain, signer); try { _wallet = await walletFactory.createSafeAccount(salt); + overrideGas(); log("wallet created ${_wallet?.address.hex} "); } catch (e) { log("something happened: $e"); @@ -132,11 +143,21 @@ class WalletProvider extends ChangeNotifier { final etherAmount = EtherAmount.fromBigInt( EtherUnit.wei, BigInt.from(double.parse(amount) * math.pow(10, 18))); - final response = await _wallet?.send( - EthereumAddress.fromHex("0xF5bB7F874D8e3f41821175c0Aa9910d30d10e193"), - etherAmount); + final response = await _wallet?.send(deployer, etherAmount); final receipt = await response?.wait(); log("Transaction receipt Hash: ${receipt?.userOpHash}"); } + + overrideGas() { + //@dev use only when using contract verifier, + // do not use this function with precompiles. + // for the safe deployment transaction do not use the multiplier + // multiply verification gas until it exceeds 400k gas + _wallet?.gasSettings = GasSettings( + verificationGasMultiplierPercentage: + 650, //7.5x higher than base - about 410k. adjust if needed + userDefinedMaxFeePerGas: BigInt.from(24500000000), + userDefinedMaxPriorityFeePerGas: BigInt.from(12300000000)); + } } diff --git a/lib/src/4337/wallet.dart b/lib/src/4337/wallet.dart index b29536b..9a838ff 100644 --- a/lib/src/4337/wallet.dart +++ b/lib/src/4337/wallet.dart @@ -144,6 +144,7 @@ class SmartWallet with _PluginManager, _GasSettings implements SmartWalletBase { @override Future sendUserOperation(UserOperation op) => prepareUserOperation(op) + .then(applyCustomGasSettings) .then(signUserOperation) .then(sendSignedUserOperation); @@ -152,16 +153,12 @@ class SmartWallet with _PluginManager, _GasSettings implements SmartWalletBase { {bool update = true}) async { // Update the user operation with the latest nonce and gas prices if needed if (update) op = await _updateUserOperation(op); - // If the 'paymaster' plugin is enabled, intercept the user operation if (hasPlugin('paymaster')) { op = await plugin('paymaster').intercept(op); } - - op = applyCustomGasSettings(op); // Validate the user operation op.validate(op.nonce > BigInt.zero, initCode); - return op; } @@ -171,7 +168,7 @@ class SmartWallet with _PluginManager, _GasSettings implements SmartWalletBase { final blockInfo = await plugin('jsonRpc').getBlockInformation(); - calculateOperationHash(UserOperation op, BlockInformation blockInfo) async { + calculateOperationHash(UserOperation op) async { if (isSafe) { return plugin<_SafePlugin>('safe').getSafeOperationHash(op, blockInfo); } else { @@ -190,7 +187,7 @@ class SmartWallet with _PluginManager, _GasSettings implements SmartWalletBase { return signatureHex; } - final opHash = await calculateOperationHash(op, blockInfo); + final opHash = await calculateOperationHash(op); op.signature = await signOperationHash(opHash, index); return op; } diff --git a/lib/src/common/mixins.dart b/lib/src/common/mixins.dart index 04fea23..64069a0 100644 --- a/lib/src/common/mixins.dart +++ b/lib/src/common/mixins.dart @@ -7,7 +7,9 @@ class GasSettings { /// The percentage by which the gas limits should be multiplied. /// /// This value should be between 0 and 100. - Percent gasMultiplierPercentage; + Percent callGasMultiplierPercentage; + Percent verificationGasMultiplierPercentage; + Percent preVerificationGasMultiplierPercentage; /// The user-defined maximum fee per gas for the transaction. BigInt? userDefinedMaxFeePerGas; @@ -26,10 +28,16 @@ class GasSettings { /// /// An assertion is made to ensure that [gasMultiplierPercentage] is between 0 and 100. GasSettings({ - this.gasMultiplierPercentage = 0, + this.callGasMultiplierPercentage = 0, + this.verificationGasMultiplierPercentage = 0, + this.preVerificationGasMultiplierPercentage = 0, this.userDefinedMaxFeePerGas, this.userDefinedMaxPriorityFeePerGas, - }) : assert(gasMultiplierPercentage >= 0, + }) : assert( + callGasMultiplierPercentage >= 0 && + callGasMultiplierPercentage <= 100 && + verificationGasMultiplierPercentage >= 0 && + preVerificationGasMultiplierPercentage >= 0, RangeOutOfBounds("Wrong Gas multiplier percentage", 0, 100)); } @@ -49,16 +57,23 @@ mixin _GasSettings { /// /// Returns a new [UserOperation] object with the updated gas settings. UserOperation applyCustomGasSettings(UserOperation op) { - final multiplier = _gasParams.gasMultiplierPercentage / 100 + 1; + final cglMultiplier = _gasParams.callGasMultiplierPercentage / 100 + 1; + final vglMultiplier = + _gasParams.verificationGasMultiplierPercentage / 100 + 1; + final preVglMultiplier = + _gasParams.preVerificationGasMultiplierPercentage / 100 + 1; + final multiplier = cglMultiplier * vglMultiplier * preVglMultiplier; - if (multiplier == 1) return op; + if (multiplier == 1 && + _gasParams.userDefinedMaxFeePerGas == null && + _gasParams.userDefinedMaxPriorityFeePerGas == null) return op; return op.copyWith( - callGasLimit: BigInt.from(op.callGasLimit.toDouble() * multiplier), + callGasLimit: BigInt.from(op.callGasLimit.toDouble() * cglMultiplier), verificationGasLimit: - BigInt.from(op.verificationGasLimit.toDouble() * multiplier), + BigInt.from(op.verificationGasLimit.toDouble() * vglMultiplier), preVerificationGas: - BigInt.from(op.preVerificationGas.toDouble() * multiplier), + BigInt.from(op.preVerificationGas.toDouble() * preVglMultiplier), maxFeePerGas: _gasParams.userDefinedMaxFeePerGas, maxPriorityFeePerGas: _gasParams.userDefinedMaxPriorityFeePerGas); }