Skip to content

Commit

Permalink
Merge pull request #14 from vaariance/fix/useroperations
Browse files Browse the repository at this point in the history
Fix/useroperations
  • Loading branch information
code-z2 authored Jan 13, 2024
2 parents 0c00dbd + f1a783d commit 17ebfae
Show file tree
Hide file tree
Showing 52 changed files with 2,173 additions and 877 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
get_dependencies:
name: "Get dependencies"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: dart-lang/setup-dart@v1
- name: "Print Dart SDK Version"
run: dart --version
- uses: actions/cache@v2
with:
path: .dart_tool
key: dart-dependencies-${{ hashFiles('pubspec.yaml') }}
- name: "Get dependencies"
env:
PUB_CACHE: ".dart_tool/pub_cache"
run: dart pub upgrade

analyze:
name: "Analysis"
needs: get_dependencies
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: .dart_tool
key: dart-dependencies-${{ hashFiles('pubspec.yaml') }}
- uses: dart-lang/setup-dart@v1
- run: "dart format --output=none --set-exit-if-changed ."
- run: dart analyze --fatal-infos
12 changes: 12 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: Publish to pub.dev

on:
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+*"

jobs:
publish:
permissions:
id-token: write # Required for authentication using OIDC
uses: dart-lang/setup-dart/.github/workflows/publish.yml@v1
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

* Biometric Auth layer over the secure storage repository

* Deprecate the ChainbaseAPI helpers for ENS resolution to use the new ensdart_v3
* Retrieve account nonce from Entrypoint instead of account contract using zeroth key.

* fixed userOp hashing and added compulsory dummySignature field to account signer interface.

* Bug Fixes

Expand Down
187 changes: 178 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,181 @@
# 4337 Dart
# Variance SDK

Account Abstraction SDK written in dart. Build Cross Platform Native Ethereum dApps easily.
Variance is a Dart SDK designed to simplify interaction with Ethereum-based blockchains and enables flutter developers to implement account abstraction with minimal efforts. It provides functionalities such as encoding and decoding ABI data, handling Ethereum transactions, working with ERC20 and ERC721 tokens, and managing Ethereum smart accounts.

supports:
## Features

- [x] multi-signatures
- - [x] passkeys (secp256r1)
- - [x] default (secp256k1)
- - [x] DIY (custom schemes)
- [] safe core protocol
- [x] SDK plugins
- **ABI Encoding/Decoding:** Easily encode and decode ABI data for Ethereum smart contract and Entrypoint interactions.
- **Transaction Handling:** Simplify the process of creating and sending UserOperations.
- **Token Operations:** Work with ERC20 and ERC721 tokens, including transfer and approval functionalities.
- **Secure Storage:** Securely store and manage sensitive data such as private keys and credentials.
- **Web3 Functionality:** Interact with Ethereum nodes and bundlers using web3 functions like `eth_call`, `eth_sendTransaction`, `eth_sendUserOperation`, etc.
- **PassKeyPair and HDWalletSigner:** Manage smart accounts signers using Passkeys or Seed Phrases.

## Getting Started

### Installation

```yml
// Add this line to your pubspec.yaml file

dependencies:
variance_dart: ^0.0.4
```
Then run:
```sh
flutter pub get
```

### Usage

```dart
// Import the package
import 'package:variance_dart/utils.dart';
import 'package:variance_dart/variance.dart';
// optionally
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:web3dart/web3dart.dart';
```

configure your chains: there are 2 ways to get the chain configuration. either manually or using the already supported configurations.

```dart
Chain chain;
// manually
const String rpcUrl = 'http://localhost:8545';
const String bundlerUrl = 'http://localhost:3000/rpc';
chain = Chain(
ethRpcUrl: rpcUrl,
bundlerUrl: bundlerUrl,
entrypoint: Constants.entrypoint,
accountFactory: Constants.accountFactory,
chainId: 1337,
explorer: "");
// using pre configured chain
chain = Chains.getChain(Network.localhost)
..ethRpcUrl = rpcUrl
..bundlerUrl = bundlerUrl;
```

In order to create a smart wallet client you need to set up a signer, which will sign useroperation hashes to be verified onchain.
there are 3 available signers:

- passkeys
- hd wallet
- simple credential (privatekey)

> Variance SDK can be used to create both EOA and Smart Wallets. the `HD wallet signer` itself is a fully featured EOA wallet that can be used to build any EOA wallet like metamask. it can also be used as an account signer for a smart wallet.
```dart
// create smart wallet signer based of seed phrase
final HDWalletSigner hd = HDWalletSigner.createWallet();
print("mnemonic: ${hd.exportMnemonic()}");
// create a smart wallet signer based on passkeys
// this operation requires biometrics verification from the user
final PassKeyPair pkp =
await PassKeySigner("myapp.xyz", "myapp", "https://myapp.xyz")
.register("<user name>", true);
print("pkp: ${pkp.toJson()}");
```

Optionally the credentials returned from the signer instances can be securely saved on device android encrypted shared preferences or ios keychain using the `SecureStorageMiddleware`.

```dart
// save a signer credential to device
await hd
.withSecureStorage(FlutterSecureStorage())
.saveCredential(CredentialType.hdwallet);
await pkp
.withSecureStorage(FlutterSecureStorage())
.saveCredential(CredentialType.passkeypair);
// load a credential from the device
final ss = SecureStorageMiddleware(secureStorage: FlutterSecureStorage());
final hdInstance =
await HDWalletSigner.loadFromSecureStorage(storageMiddleware: ss);
print("pkp: ${hdInstance?.exportMnemonic()}");
// NOTE: interactions with securestorage can be authenticated when using `SecureStorageMiddleware`
final ss = SecureStorageMiddleware(secureStorage: FlutterSecureStorage(), authMiddleware: AuthenticationMiddleware());
// then used with `SecureStorageMiddleware` in the following way
ss.save("key", "value", options: SSAuthOperationOptions(requiresAuth: true, authReason: "reason"));
ss.read("key"); // default options are used i.e requiresAuth: false
ss.delete("key", options: SSAuthOperationOptions(requiresAuth: false)); // explicitly reject authentication
```

Interacting with the smart wallet:

```dart
// create a smart wallet client
final walletClient = SmartWallet(
chain: chain,
signer: hd,
bundler: BundlerProvider(chain, RPCProvider(chain.bundlerUrl!)),
);
// create a simple account based on hd
final SmartWallet simpleSmartAccount =
await walletClient.createSimpleAccount(salt);
print("simple account address: ${simpleSmartAccount.address}");
// create a simple account based on pkp
final SmartWallet simplePkpAccount =
await walletClient.createSimplePasskeyAccount(pkp, salt);
print("simple pkp account address: ${simplePkpAccount.address}");
// retrieve the balance of a smart wallet
final EtherAmount balance = await simpleSmartAccount.balance;
print("account balance: ${balance.getInWei}");
// retrive the account nonce
final Uint256 nonce = await simpleSmartAccount.nonce;
print("account nonce: ${nonce.toInt()}");
// check if a smart wallet has been deployed
final bool deployed = await simpleSmartAccount.deployed;
print("account deployed: $deployed");
// get the init code of the smart wallet
final String initCode = simpleSmartAccount.initCode;
print("account init code: $initCode");
// perform a simple transaction (send ether to another account)
// account must be prefunded with native token. paymaster is not yet implemented
await simpleSmartAccount.send(
EthereumAddress.fromHex(
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"), // receive address
getConversion("0.7142"), // 0.7142 ether
);
// utility function to convert eth amount from string to wei
EtherAmount getConversion(String amount) {
final amtToDb = double.parse(amount);
return EtherAmount.fromBigInt(
EtherUnit.wei, BigInt.from(amtToDb * pow(10, 18)));
}
```

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.

## API Reference

Detailed API reference and examples can be found in the [API reference](https://pub.dev/documentation/variance_dart/latest/variance/variance-library.html).

## Contributing

We are committed to maintaining variance as an open source sdk, take a look at existing issues, open a pull request etc.

## License

This project is licensed under the **BSD-3-Clause** - see the [LICENSE](./LICENSE) file for details.
6 changes: 3 additions & 3 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
#
analyzer:
exclude:
- "**/*.g.dart"
- "**/*.freezed.dart"
- "lib/src/abis/*.g.dart"
- "lib/src/utils/models/*.dart"

errors:
invalid_annotation_target: ignore
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
Expand All @@ -31,6 +32,5 @@ linter:
constant_identifier_names: false
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule

# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
103 changes: 103 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// ignore_for_file: public_member_api_docs

import 'dart:math';

import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:variance_dart/utils.dart';
import 'package:variance_dart/variance.dart';
import 'package:web3dart/web3dart.dart';

const String rpcUrl = 'http://localhost:8545';
const String bundlerUrl = 'http://localhost:3000/rpc';

Future<void> main() async {
final Uint256 salt = Uint256.zero;

// configure your chain
final Chain chain = Chain(
ethRpcUrl: rpcUrl,
bundlerUrl: bundlerUrl,
entrypoint: Constants.entrypoint,
accountFactory: Constants.accountFactory,
chainId: 1337,
explorer: "");

// create smart wallet signer based of seed phrase
final HDWalletSigner hd = HDWalletSigner.createWallet();
print("mnemonic: ${hd.exportMnemonic()}");

// create a smart wallet signer based on passkeys
// this operation requires biometrics verification from the user
final PassKeyPair pkp =
await PassKeySigner("myapp.xyz", "myapp", "https://myapp.xyz")
.register("<user name>", true);
print("pkp: ${pkp.toJson()}");

// save a signer credential to device
await hd
.withSecureStorage(FlutterSecureStorage())
.saveCredential(CredentialType.hdwallet);

// load a credential from the device
final ss = SecureStorageMiddleware(secureStorage: FlutterSecureStorage());
final hdInstance =
await HDWalletSigner.loadFromSecureStorage(storageMiddleware: ss);
print("pkp: ${hdInstance?.exportMnemonic()}");

// NOTE: interactions with securestorage can be authenticated when using `SecureStorageMiddleware`
//
// final ss = SecureStorageMiddleware(secureStorage: FlutterSecureStorage(), authMiddleware: AuthenticationMiddleware());
// then used with `SecureStorageMiddleware` in the following way
//
// ss.save("key", "value", options: SSAuthOperationOptions(requiresAuth: true, authReason: "reason"))
// ss.read("key") // default options are used i.e requiresAuth: false
// ss.delete("key", options: SSAuthOperationOptions(requiresAuth: false)) // explicitly reject authentication
//;

// create a smart wallet client
final walletClient = SmartWallet(
chain: chain,
signer: hd,
bundler: BundlerProvider(chain, RPCProvider(chain.bundlerUrl!)),
);

// create a simple account based on hd
final SmartWallet simpleSmartAccount =
await walletClient.createSimpleAccount(salt);
print("simple account address: ${simpleSmartAccount.address}");

// create a simple account based on pkp
final SmartWallet simplePkpAccount =
await walletClient.createSimplePasskeyAccount(pkp, salt);
print("simple pkp account address: ${simplePkpAccount.address}");

// retrieve the balance of a smart wallet
final EtherAmount balance = await simpleSmartAccount.balance;
print("account balance: ${balance.getInWei}");

// retrive the account nonce
final Uint256 nonce = await simpleSmartAccount.nonce;
print("account nonce: ${nonce.toInt()}");

// check if a smart wallet has been deployed
final bool deployed = await simpleSmartAccount.deployed;
print("account deployed: $deployed");

// get the init code of the smart wallet
final String initCode = simpleSmartAccount.initCode;
print("account init code: $initCode");

// perform a simple transaction (send ether to another account)
// account must be prefunded with native token. paymaster is not yet implemented
await simpleSmartAccount.send(
EthereumAddress.fromHex(
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"), // receive address
getConversion("0.7142"), // 0.7142 ether
);
}

EtherAmount getConversion(String amount) {
final amtToDb = double.parse(amount);
return EtherAmount.fromBigInt(
EtherUnit.wei, BigInt.from(amtToDb * pow(10, 18)));
}
16 changes: 16 additions & 0 deletions example/pubspec.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: example
publish_to: none
version: 0.0.1
homepage: https://variance.space
repository: https://github.com/vaariance/variance-dart

environment:
sdk: ">=2.12.0 <4.0.0"

dependencies:
variance_dart:
path: ../

dev_dependencies:
coverage: ^1.1.0
lints: ^2.0.0
Loading

0 comments on commit 17ebfae

Please sign in to comment.