Skip to content

Commit

Permalink
Merge pull request #295 from WalletConnect/feature/one_click_auth_sup…
Browse files Browse the repository at this point in the history
…port

Feature/one click auth support
  • Loading branch information
quetool committed Jun 28, 2024
2 parents 1181b88 + 6ab65ec commit 3372606
Show file tree
Hide file tree
Showing 103 changed files with 8,171 additions and 2,379 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.3.0-beta01

- One-Click Auth support

## 2.2.3

- Full web support
Expand Down
170 changes: 129 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,44 @@ WalletConnect Dart v2 library for Flutter, heavily inspired by the WalletConnect
// If you just need one of the other, replace Web3App with SignClient or AuthClient
// SignClient wcClient = await SignClient.createInstance(
// AuthClient wcClient = await AuthClient.createInstance(
// BE MINDFUL THAT AuthClient is currently deprecated and will be removed soon.
// Authentication methods, including One-Click Auth, are now withing SignClient
Web3App wcClient = await Web3App.createInstance(
relayUrl: 'wss://relay.walletconnect.com', // The relay websocket URL, leave blank to use the default
projectId: '123',
relayUrl: 'wss://relay.walletconnect.com', // The relay websocket URL, leave blank to use the default
metadata: PairingMetadata(
name: 'dApp (Requester)',
name: 'Your dApp Name (Requester)',
description: 'A dapp that can request that transactions be signed',
url: 'https://walletconnect.com',
icons: ['https://avatars.githubusercontent.com/u/37784886'],
redirect: Redirect( // Specially important object if you the Wallet to navigate back to your dapp
native: 'mydapp://',
universal: 'https://mydapp.com/app',
),
),
);
// For a dApp, you would connect with specific parameters, then display
// the returned URI.
ConnectResponse resp = await wcClient.connect(
requiredNamespaces: {
'eip155': RequiredNamespace(
chains: ['eip155:1'], // Ethereum chain
methods: ['personal_sign'], // Requestable Methods, see MethodsConstants for reference
events: ['chainChanged'], // Requestable Events, see EventsConstants for reference
),
},
optionalNamespaces: {
'eip155': RequiredNamespace(
chains: ['eip155:1', 'eip155:5'], // Any other optional Ethereum chain
methods: ['eth_signTransaction'], // Optional requestable Methods, see MethodsConstants for reference
events: ['accountsChanged'], // Optional requestable events, see EventsConstants for reference
// Any Ethereum chain you want to connect with
chains: ['eip155:1', 'eip155:5'],
// Requestable Methods, see MethodsConstants class for reference
methods: ['personal_sign', 'eth_sendTransaction'],
// Optional requestable events, see EventsConstants for reference
events: ['accountsChanged'],
),
},
);
// display connection uri withih a QR code or use it to launch a wallet
Uri? uri = resp.uri;
// Example:
// final encodedUri = Uri.encodeComponent(uri.toString());
// launchUrlString('metamask://wc?uri=$encodedUri', mode: LaunchMode.externalApplication);
// Once you've display the URI, you can wait for the future, and hide the QR code once you've received session data
// Once you've displayed the URI, you can wait for the future, and hide the QR code once you've received session data
final SessionData session = await resp.session.future;
// Now that you have a session, you can request signatures
Expand All @@ -59,6 +65,7 @@ final dynamic signResponse = await wcClient.request(
// Structure is dependant upon the JSON RPC call you made.
// [DEPRECATED]
// You can also request authentication
final AuthRequestResponse authReq = await wcClient.requestAuth(
params: AuthRequestParams(
Expand All @@ -71,7 +78,7 @@ final AuthRequestResponse authReq = await wcClient.requestAuth(
);
// Await the auth response using the provided completer
final AuthResponse authResponse = await authResponse.completer.future;
final AuthResponse authResponse = await authReq.completer.future;
if (authResponse.result != null) {
// Having a result means you have the signature and it is verified.
Expand All @@ -85,6 +92,39 @@ else {
final JsonRpcError? error = authResponse.jsonRpcError;
}
// Instead of connect() and then requestAuth() you can leverage One-Click Auth
// Which is connection (session proposal) and authentication (SIWE) in just 1 step
final SessionAuthRequestResponse authReq = await wcClient.authenticate(
params: SessionAuthRequestParams(
chains: ['eip155:1', 'eip155:5'],
domain: 'yourdomain.com',
uri: 'https://yourdomain.com/login',
nonce: AuthUtils.generateNonce(),
statement: 'Welcome to my example dApp.',
methods: ['personal_sign', 'eth_sendTransaction'],
),
);
// display authentication uri withih a QR code or use it to launch a wallet
Uri? uri = authReq.uri;
// Example:
// final encodedUri = Uri.encodeComponent(uri.toString());
// launchUrlString('metamask://wc?uri=$encodedUri', mode: LaunchMode.externalApplication);
// IMPORTANT: Not every wallet supports One-Click Auth yet but don't worry, if wallet does not support it,
// it will fallback to regular session proposal automatically
// Once you've displayed the URI, you can wait for the future, and hide the QR code once you've received session data
final SessionAuthResponse authResponse = await authReq.completer.future;
if (authResponse.session != null) {
// Having a result means you have succesfully authenticated and created a session
}
else {
// Otherwise, you might have gotten a WalletConnectError if there was un issue verifying the signature.
final WalletConnectError? error = authResponse.error;
// Of a JsonRpcError if something went wrong when signing with the wallet.
final JsonRpcError? error = authResponse.jsonRpcError;
}
// You can also respond to events from the wallet, like session events
wcClient.registerEventHandler(
Expand All @@ -99,13 +139,17 @@ wcClient.onSessionEvent.subscribe((SessionEvent? session) {
### Wallet Flow
```dart
Web3Wallet wcClient = await Web3Wallet.createInstance(
relayUrl: 'wss://relay.walletconnect.com', // The relay websocket URL, leave blank to use the default
projectId: '123',
relayUrl: 'wss://relay.walletconnect.com', // The relay websocket URL, leave blank to use the default
metadata: PairingMetadata(
name: 'Wallet (Responder)',
name: 'Your Wallet Name (Responder)',
description: 'A wallet that can be requested to sign transactions',
url: 'https://walletconnect.com',
icons: ['https://avatars.githubusercontent.com/u/37784886'],
redirect: Redirect( // Specially important object if you want dApps to be able to open you wallet
native: 'mywallet://',
universal: 'https://mywallet.com/app',
),
),
);
Expand All @@ -122,6 +166,72 @@ wcClient.onSessionProposal.subscribe((SessionProposal? args) async {
final isInvalidApp = args.verifyContext?.validation.invalid;
final isValidApp = args.verifyContext?.validation.valid;
final unknown = args.verifyContext?.validation.unknown;
//
// Present the UI to the user, and allow them to reject or approve the proposal
await wcClient.approveSession(
id: args.id,
namespaces: args.params.generatedNamespaces!,
sessionProperties: args.params.sessionProperties,
);
// Or to reject...
// Error codes and reasons can be found here: https://docs.walletconnect.com/2.0/specs/clients/sign/error-codes
await wcClient.rejectSession(
id: id,
reason: Errors.getSdkError(Errors.USER_REJECTED),
);
}
});
// If you are planning to support One-Click Auth then you would have to subscribe to onSessionAuthRequest events
wcClient.onSessionAuthRequest.subscribe((SessionAuthRequest? args) async {
// Handle UI updates using the args.params
// Keep track of the args.id for the approval response
if (args != null) {
id = args!.id;
// To check VerifyAPI validation in regards of the dApp is trying to connnect you can check verifyContext
// More info about VerifyAPI https://docs.walletconnect.com/web3wallet/verify
final isScamApp = args.verifyContext?.validation.scam;
final isInvalidApp = args.verifyContext?.validation.invalid;
final isValidApp = args.verifyContext?.validation.valid;
final unknown = args.verifyContext?.validation.unknown;
//
// Process Authentication request
final SessionAuthPayload requestPayload = args.authPayload;
final responsePayload = AuthSignature.populateAuthPayload(
authPayload: requestPayload,
chains: ['eip155:1', 'eip155:5'], // Your supported EVM chains
methods: ['personal_sign', 'etg_sendTransaction'], // Your supported methods
);
// For every chain you support you decide to sign a the message
final message = _web3Wallet!.formatAuthMessage(
iss: 'did:pkh:eip155:1:0xADDRESS.....',
cacaoPayload: CacaoRequestPayload.fromSessionAuthPayload(
responsePayload,
),
);
// final hexSignature = * signMessage(message) *
// And creates a Cacao object with it
final cacao = AuthSignature.buildAuthObject(
requestPayload: CacaoRequestPayload.fromSessionAuthPayload(
responsePayload,
),
signature: CacaoSignature(
t: CacaoSignature.EIP191,
s: hexSignature,
),
iss: 'did:pkh:eip155:1:0xADDRESS.....',
);
//
// To respond with the signed messages and create a session for the dapp you use approveSessionAuthenticate
await _web3Wallet!.approveSessionAuthenticate(
id: args.id,
auths: [cacao], // You would have here as many cacaos as messages your wallet signed
);
// To reject to session authenticate request you use rejectSessionAuthenticate
await _web3Wallet!.rejectSessionAuthenticate(
id: args.id,
reason: Errors.getSdkError(Errors.USER_REJECTED_AUTH),
);
}
});
Expand Down Expand Up @@ -173,6 +283,7 @@ final signRequestHandler = (String topic, dynamic parameters) async {
throw Errors.getSdkError(Errors.USER_REJECTED_SIGN);
}
}
wcClient.registerRequestHandler(
chainId: 'eip155:1',
method: 'eth_sendTransaction',
Expand All @@ -196,6 +307,7 @@ wcClient.onSessionProposalError.subscribe((SessionProposalError? args) {
// Handle the error
});
/* [DEPRECATED] */
// Setup the auth handling
clientB.onAuthRequest.subscribe((AuthRequest? args) async {
Expand All @@ -212,31 +324,6 @@ clientB.onAuthRequest.subscribe((AuthRequest? args) async {
);
});
// Then, scan the QR code and parse the URI, and pair with the dApp
// On the first pairing, you will immediately receive onSessionProposal and onAuthRequest events.
Uri uri = Uri.parse(scannedUriString);
final PairingInfo pairing = await wcClient.pair(uri: uri);
// Present the UI to the user, and allow them to reject or approve the proposal
final walletNamespaces = {
'eip155': Namespace(
accounts: ['eip155:1:abc'],
methods: ['eth_signTransaction'],
events: ['accountsChanged'],
),
}
await wcClient.approveSession(
id: id,
namespaces: walletNamespaces // This will have the accounts requested in params
// If you registered correctly events emitters, methods handlers and accounts for your supported chains you can just us `args.params.generatedNamespaces!` value from SessionProposalEvent
);
// Or to reject...
// Error codes and reasons can be found here: https://docs.walletconnect.com/2.0/specs/clients/sign/error-codes
await wcClient.rejectSession(
id: id,
reason: Errors.getSdkError(Errors.USER_REJECTED),
);
// For auth, you can do the same thing: Present the UI to them, and have them approve the signature.
// Then respond with that signature. In this example I use EthSigUtil, but you can use any library that can perform
// a personal eth sign.
Expand All @@ -256,6 +343,7 @@ await wcClient.respondAuthRequest(
iss: 'did:pkh:eip155:1:ETH_ADDRESS',
error: Errors.getSdkError(Errors.USER_REJECTED_AUTH),
);
//
// You can also emit events for the dApp
await wcClient.emitSessionEvent(
Expand Down
2 changes: 1 addition & 1 deletion example/dapp/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:name="com.example.dapp.MainActivity"
android:exported="true"
android:launchMode="singleTask"
android:taskAffinity="com.walletconnect.flutterdapp.activity"
Expand Down
1 change: 1 addition & 0 deletions example/dapp/ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
<key>LSApplicationQueriesSchemes</key>
<array>
<string>wcflutterwallet</string>
<string>walletapp</string>
</array>
<key>LSRequiresIPhoneOS</key>
<true/>
Expand Down
Loading

0 comments on commit 3372606

Please sign in to comment.