Skip to content

Commit

Permalink
feat: safe accounts working
Browse files Browse the repository at this point in the history
  • Loading branch information
code-z2 committed Apr 4, 2024
1 parent 61fbab0 commit 686de74
Show file tree
Hide file tree
Showing 29 changed files with 574 additions and 267 deletions.
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
## 0.1.0-r2

* Fix safe transaction encoding
* Remove _checkDeployment function in counterfactual creation
* Add getBlockInformation in JsonRPCProvider

## 0.1.0-r1

* Mainnet Pre-release
* refactor sdk to use the factory method for creating smart-accounts
* add safe smart accounts via safe plugin
* reduce external dependencies to 3
* implement custom errors and add logger for debugging
* update contract abis, adding more erc20/erc721 abi snippets
* fix paymaster plugin context incompatibility
* add utility for packing and unpacking uint256 values
* update chain configuration to surpport minimal modest chains
* update example to a real flutter example
* rename library name from variance to variance_dart for consistency
* update API refs and README to reflect new changes

## 0.0.9

* Add support for entrypoint v0.7 in parrallel.
Expand Down
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,19 @@ Variance is a Dart SDK designed to simplify interaction with Ethereum-based bloc
open your terminal and run the following command:

```sh
flutter pub get variance_dart
flutter pub get web3_signers
flutter pub add variance_dart
flutter pub add web3_signers

# optionally
flutter pub add web3dart
```

### Usage

```dart
// Import the package
import 'package:web3_signers/web3_signers.dart';
import 'package:variance_dart/variance.dart';
import 'package:variance_dart/variance_dart.dart';
// optionally
import 'package:web3dart/web3dart.dart';
Expand Down Expand Up @@ -56,11 +59,11 @@ chain.entrypoint = entrypointAddress;
Also if wish to use paymasters with your smart wallet you can do so by specifying the endpoint of the paymaster. By default the paymaster is set to null. This would add a paymaster Plugin to the smart wallet.

```dart
final String paymasterUrl = 'https://pimlico.io/...';
final String paymasterUrl = 'https://api.pimlico.io/v2/84532/rpc?apikey=...';
chain.paymasterUrl = paymasterUrl;
```

If you have additional context for the paymaster, you will be able to add it to the smart wallet.
If you have additional context for the paymaster, you will be able to add it to the smart wallet after creation or before initiating a transaction.

```dart
wallet.plugin<Paymaster>('paymaster').context = {'key': 'value'};
Expand Down Expand Up @@ -153,7 +156,7 @@ await wallet.send(
);
```

For detailed usage and examples, refer to the [documentation](https://docs.variance.space). Additional refer to the [demo](https://github.com/vaariance/variancedemo) for use in a flutter app.
For detailed usage and examples, refer to the [documentation](https://docs.variance.space). Additional refer to the [example](./example/) for use in a flutter app.

## API Reference

Expand Down
2 changes: 1 addition & 1 deletion example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1510;
LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C8080294A63A400263BE5 = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1510"
LastUpgradeVersion = "1430"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
111 changes: 102 additions & 9 deletions example/lib/providers/wallet_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import 'dart:io';
import 'dart:math' as math;
import 'package:flutter/foundation.dart';
import 'package:web3_signers/web3_signers.dart';
import 'package:variance_dart/variance.dart';
import 'package:variance_dart/variance_dart.dart';
import 'package:web3dart/credentials.dart';
import 'package:web3dart/web3dart.dart' as w3d;
import 'package:web3dart/crypto.dart' as w3d;
Expand All @@ -18,34 +18,40 @@ class WalletProvider extends ChangeNotifier {
String _errorMessage = "";
String get errorMessage => _errorMessage;

final EthereumAddress nft =
EthereumAddress.fromHex("0x4B509a7e891Dc8fd45491811d67a8B9e7ef547B9");
final EthereumAddress erc20 =
EthereumAddress.fromHex("0xAEaF19097D8a8da728438D6B57edd9Bc5DAc4795");
final EthereumAddress deployer =
EthereumAddress.fromHex("0x218F6Bbc32Ef28F547A67c70AbCF8c2ea3b468BA");

WalletProvider()
: _chain = Chains.getChain(Network.baseTestnet)
..accountFactory = EthereumAddress.fromHex(
"0x402A266e92993EbF04a5B3fd6F0e2b21bFC83070")
..bundlerUrl =
"https://base-sepolia.g.alchemy.com/v2/RWbMhXe00ZY-SjGQF72kyCVQJ_nQopba"
..jsonRpcUrl =
"https://base-sepolia.g.alchemy.com/v2/RWbMhXe00ZY-SjGQF72kyCVQJ_nQopba";
..bundlerUrl = "https://api.pimlico.io/v2/84532/rpc?apikey="
..paymasterUrl = "https://paymaster.optimism.io/v1/84532/rpc";

Future<void> registerWithPassKey(String name,
{bool? requiresUserVerification}) async {
final pkpSigner =
PassKeySigner("webauthn.io", "webauthn", "https://webauthn.io");
final hwdSigner = HardwareSigner.withTag(name);

final SmartWalletFactory walletFactory =
SmartWalletFactory(_chain, pkpSigner);

final salt = Uint256.fromHex(
hexlify(w3d.keccak256(Uint8List.fromList(utf8.encode(name)))));

try {
// uses passkeys on android, secure enclave on iOS
if (Platform.isAndroid) {
final SmartWalletFactory walletFactory =
SmartWalletFactory(_chain, pkpSigner);
final keypair = await pkpSigner.register(name, name);
_wallet =
await walletFactory.createP256Account<PassKeyPair>(keypair, salt);
} else if (Platform.isIOS) {
final SmartWalletFactory walletFactory =
SmartWalletFactory(_chain, hwdSigner);
final keypair = await hwdSigner.generateKeyPair();
_wallet = await walletFactory.createP256Account<P256Credential>(
keypair, salt);
Expand All @@ -59,6 +65,49 @@ class WalletProvider extends ChangeNotifier {
}
}

Future<void> createEOAWallet() async {
_chain.accountFactory = Constants.simpleAccountFactoryAddress;

final signer = EOAWallet.createWallet();
log("signer: ${signer.getAddress()}");

final SmartWalletFactory walletFactory = SmartWalletFactory(_chain, signer);
final salt = Uint256.fromHex(hexlify(w3d
.keccak256(EthereumAddress.fromHex(signer.getAddress()).addressBytes)));

try {
_wallet = await walletFactory.createSimpleAccount(salt);
log("wallet created ${_wallet?.address.hex} ");
} catch (e) {
_errorMessage = e.toString();
notifyListeners();
log("something happened: $e");
}
}

Future<void> createPrivateKeyWallet() async {
_chain.accountFactory = Constants.simpleAccountFactoryAddress;

final signer = PrivateKeySigner.createRandom("123456");
log("signer: ${signer.getAddress()}");

final SmartWalletFactory walletFactory = SmartWalletFactory(_chain, signer);

final salt = Uint256.fromHex(hexlify(w3d
.keccak256(EthereumAddress.fromHex(signer.getAddress()).addressBytes)));

log("salt: ${salt.toHex()}");

try {
_wallet = await walletFactory.createSimpleAccount(salt);
log("wallet created ${_wallet?.address.hex} ");
} catch (e) {
_errorMessage = e.toString();
notifyListeners();
log("something happened: $e");
}
}

Future<void> createSafeWallet() async {
_chain.accountFactory = Constants.safeProxyFactoryAddress;

Expand All @@ -80,12 +129,56 @@ class WalletProvider extends ChangeNotifier {
}
}

Future<void> mintNFt() async {}
Future<void> mintNFt() async {
// mints nft
final tx1 = await _wallet?.sendTransaction(
nft,
Contract.encodeFunctionCall("safeMint", nft,
ContractAbis.get("ERC721_SafeMint"), [_wallet?.address]));
await tx1?.wait();

// mints erc20 tokens
final tx2 = await _wallet?.sendTransaction(
erc20,
Contract.encodeFunctionCall(
"mint", erc20, ContractAbis.get("ERC20_Mint"), [
_wallet?.address,
w3d.EtherAmount.fromInt(w3d.EtherUnit.ether, 20).getInWei
]));
await tx2?.wait();

// transfers the tokens
final tx3 = await _wallet?.sendTransaction(
erc20,
Contract.encodeERC20TransferCall(
erc20, deployer, w3d.EtherAmount.fromInt(w3d.EtherUnit.ether, 18)));
await tx3?.wait();
log("trying batched transaction");
await sendBatchedTransaction();
}

Future<void> sendBatchedTransaction() async {
final tx = await _wallet?.sendBatchedTransaction([
erc20,
erc20
], [
Contract.encodeFunctionCall(
"mint", erc20, ContractAbis.get("ERC20_Mint"), [
_wallet?.address,
w3d.EtherAmount.fromInt(w3d.EtherUnit.ether, 20).getInWei
]),
Contract.encodeERC20TransferCall(
erc20, deployer, w3d.EtherAmount.fromInt(w3d.EtherUnit.ether, 20))
]);

await tx?.wait();
}

Future<void> sendTransaction(String recipient, String amount) async {
if (_wallet != null) {
final etherAmount = w3d.EtherAmount.fromBigInt(w3d.EtherUnit.wei,
BigInt.from(double.parse(amount) * math.pow(10, 18)));

final response =
await _wallet?.send(EthereumAddress.fromHex(recipient), etherAmount);
final receipt = await response?.wait();
Expand Down
4 changes: 3 additions & 1 deletion example/lib/screens/home/home_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ class _WalletHomeState extends State<WalletHome> {
label: const Text(' Receive')),
50.horizontalSpace,
TextButton.icon(
onPressed: () {},
onPressed: () {
context.read<WalletProvider>().mintNFt();
},
style: TextButton.styleFrom(
backgroundColor: const Color(0xffE1FF01)),
icon: const Icon(
Expand Down
22 changes: 7 additions & 15 deletions example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -364,18 +364,18 @@ packages:
dependency: transitive
description:
name: passkeys
sha256: "79f07498b44d8372a569904d5e3e1de238d83abcf83b79d583a06394b98d2789"
sha256: "59e50b21746aff90cbc56145174caa3b99523f449e42f7d8aa2199ec09c511cd"
url: "https://pub.dev"
source: hosted
version: "2.0.7"
version: "2.0.8"
passkeys_android:
dependency: transitive
description:
name: passkeys_android
sha256: ab245d18d88040409d3aa93c3359a4c10c569894361c56929cb367d4b892bbaf
sha256: "9dc0b84dad03329ff2f3be18bedecf1b8de9309c8e9cda6ef821dc88556a126d"
url: "https://pub.dev"
source: hosted
version: "2.0.3"
version: "2.0.4"
passkeys_ios:
dependency: transitive
description:
Expand Down Expand Up @@ -629,14 +629,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.0"
string_validator:
dependency: transitive
description:
name: string_validator
sha256: "54d4f42cd6878ae72793a58a529d9a18ebfdfbfebd9793bbe55c9b28935e8543"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
term_glyph:
dependency: transitive
description:
Expand Down Expand Up @@ -691,7 +683,7 @@ packages:
path: ".."
relative: true
source: path
version: "0.0.9"
version: "0.1.0-r1"
vector_math:
dependency: transitive
description:
Expand Down Expand Up @@ -720,10 +712,10 @@ packages:
dependency: "direct main"
description:
name: web3_signers
sha256: "7dc7f83d2eba5e78eb0310fcdf0cb4c0b167c27abafc86c8027383913dd64488"
sha256: fd2a4ee394537f2140c08a395eadd8611aa713e04699c29b6aaba75e11264faf
url: "https://pub.dev"
source: hosted
version: "0.0.6-r2"
version: "0.0.6"
web3dart:
dependency: "direct main"
description:
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ dependencies:
web3dart: ^2.7.2
shared_preferences: ^2.2.2
path_provider: ^2.1.1
web3_signers: ^0.0.6-r2
fluttertoast: ^8.2.4
variance_dart:
path: ../
web3_signers: ^0.0.6

dev_dependencies:
flutter_test:
Expand Down
2 changes: 1 addition & 1 deletion lib/src/4337/chains.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
part of '../../variance.dart';
part of '../../variance_dart.dart';

/// Represents an Ethereum-based blockchain chain.
class Chain {
Expand Down
24 changes: 10 additions & 14 deletions lib/src/4337/factory.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
part of '../../variance.dart';
part of '../../variance_dart.dart';

/// A factory class for creating various types of Ethereum smart wallets.
/// {@inheritDoc SmartWalletFactoryBase}
Expand Down Expand Up @@ -31,6 +31,13 @@ class SmartWalletFactory implements SmartWalletFactoryBase {
chainId: _chain.chainId,
rpc: _jsonRpc.rpc);

/// A getter for the SafePlugin instance.
_SafePlugin get _safePlugin => _SafePlugin(
address:
Safe4337ModuleAddress.fromVersion(_chain.entrypoint.version).address,
chainId: _chain.chainId,
client: _safeProxyFactory.client);

/// A getter for the SafeProxyFactory contract instance.
_SafeProxyFactory get _safeProxyFactory => _SafeProxyFactory(
address: _chain.accountFactory!,
Expand All @@ -43,13 +50,6 @@ class SmartWalletFactory implements SmartWalletFactoryBase {
chainId: _chain.chainId,
rpc: _jsonRpc.rpc);

/// A getter for the SafePlugin instance.
_SafePlugin get _safePlugin => _SafePlugin(
address:
Safe4337ModuleAddress.fromVersion(_chain.entrypoint.version).address,
chainId: _chain.chainId,
client: _safeProxyFactory.client);

@override
Future<SmartWallet> createP256Account<T>(T keyPair, Uint256 salt,
[EthereumAddress? recoveryAddress]) {
Expand All @@ -67,15 +67,11 @@ class SmartWalletFactory implements SmartWalletFactoryBase {
}

@override
Future<SmartWallet> createSafeAccount(Uint256 salt,
[List<EthereumAddress>? owners, int? threshold]) async {
Future<SmartWallet> createSafeAccount(Uint256 salt) async {
final signer = EthereumAddress.fromHex(_signer.getAddress());
final ownerSet = owners != null ? {signer, ...owners} : [signer];

// Get the initializer data for the Safe account
final initializer = _safeProxyFactory.getInitializer(
ownerSet,
threshold ?? 1,
final initializer = _safeProxyFactory.getInitializer([signer], 1,
Safe4337ModuleAddress.fromVersion(_chain.entrypoint.version));

// Get the proxy creation code for the Safe account
Expand Down
Loading

0 comments on commit 686de74

Please sign in to comment.