Add details to setup wallet connect in readme (#58)

* Add details to setup wallet connect in readme

* Add hyperlinks for wallet connect terms

* Handle review changes

* Add keystore data structure and troubleshooting steps in readme

* Update readme

* Add pre-commit lint hook

* Resolve linter warnings

* Handle review changes

* Fix heading level in readme
This commit is contained in:
shreerang6921 2024-04-19 16:21:21 +05:30 committed by Nabarun Gogoi
parent 1172e67f5f
commit 8c0751f84b
8 changed files with 206 additions and 12 deletions

1
.husky/pre-commit Normal file
View File

@ -0,0 +1 @@
yarn lint

View File

@ -1,6 +1,6 @@
# laconic-wallet
### Install
## Install
- Install [Node](https://nodejs.org/en/download/package-manager/)
@ -87,7 +87,7 @@
```
cp .env.example .env
```
- In [.env](./.env) file add WalletConnect project id. You can generate your own ProjectId at https://cloud.walletconnect.com
```
@ -134,3 +134,31 @@
5. Press `a` to run the application on android
You should see both the apps running on your emulator or physical device.
## Flow for the app
- User scans QR Code on dApp from wallet to connect
- After clicking on pair button, dApp emits an event 'session_proposal'
- Wallet listens to this event and opens a modal to either accept or reject the proposal
- Modal shows information about methods, chains and events the dApp is requesting for
- This information is taken from [namespaces](https://docs.walletconnect.com/advanced/glossary#namespaces) object received from dApp
- On accepting, wallet sends the [namespaces](https://docs.walletconnect.com/advanced/glossary#namespaces) object with information about the accounts that are present in the wallet in response
- Once [session](https://docs.walletconnect.com/advanced/glossary#session) is established, it is shown in the active sessions page
- In this way, wallet can connect to multiple dApps
## Troubleshooting
- To clean the buid
```
cd adroid
./gradlew clean
```

160
development.md Normal file
View File

@ -0,0 +1,160 @@
# Development
## WalletConnect details
- Docs - https://docs.walletconnect.com/api/sign/overview
- Doc for terminologies - https://docs.walletconnect.com/advanced/glossary
- Function for creating web3 wallet
```js
export let web3wallet: IWeb3Wallet;
export let core: ICore;
export async function createWeb3Wallet() {
const core = new Core({
projectId: Config.WALLET_CONNECT_PROJECT_ID,
});
web3wallet = await Web3Wallet.init({
core,
metadata: {
name: 'Laconic Wallet',
description: 'Laconic Wallet',
url: 'https://wallet.laconic.com/',
icons: ['https://avatars.githubusercontent.com/u/92608123'],
},
});
}
```
- Hook used for intializing web3 wallet
```js
export default function useInitialization() {
const [initialized, setInitialized] = useState(false);
const onInitialize = useCallback(async () => {
try {
await createWeb3Wallet();
setInitialized(true);
} catch (err: unknown) {
console.log('Error for initializing', err);
}
}, []);
useEffect(() => {
if (!initialized) {
onInitialize();
}
}, [initialized, onInitialize]);
return initialized;
}
```
- Once user clicks on pair, this function is triggered
```js
export async function web3WalletPair(params: { uri: string }) {
return await web3wallet.core.pairing.pair({ uri: params.uri });
}
```
- In a useEffect, we keep on listening to events that are emitted by dapp and do actions based on it
```js
useEffect(() => {
web3wallet?.on('session_proposal', onSessionProposal);
web3wallet?.on('session_request', onSessionRequest);
web3wallet?.on('session_delete', onSessionDelete);
return () => {
web3wallet?.off('session_proposal', onSessionProposal);
web3wallet?.off('session_request', onSessionRequest);
web3wallet?.off('session_delete', onSessionDelete);
}
})
```
- Signing messages
- Cosmos methods info
- The signDoc format for signAmino and signDirect is different
- Reference - https://docs.leapwallet.io/cosmos/for-dapps-connect-to-leap/api-reference
- For signDirect, the message is protobuf encoded , hence to sign it , we have to first convert it to uint8Array
```js
const bodyBytesArray = Uint8Array.from(
Buffer.from(request.params.signDoc.bodyBytes, 'hex'),
);
const authInfoBytesArray = Uint8Array.from(
Buffer.from(request.params.signDoc.authInfoBytes, 'hex'),
);
```
- This will give us correct signature
## Data structure of keystore
```json
{
// Accounts data -> hdpath, privateKey, publicKey, address
"accounts/eip155:1/0":{
"username": "",
"password": "m/44'/60'/0'/0/0,0x0654623fe7a0e3d74f518e22818c1cfd58517e80a232df5d6d20a3afd99fd3bb,0x02cced178c903835bb29e337fa227ba0204a3285eb35797b766ed975b478b4beb6,0xe5fA0Dd92659e287e5e3Fa582Ee20fcf74fb1116"
},
"accounts/cosmos:cosmoshub-4/0":{
"username": "",
"password": "m/44'/118'/0'/0/0,0xbb9ccc3029178a61ba847c22108318ba119220451c99e6358efd7b85d7d49ed5,0x03a8002f56f99126f930ca3ac1963731e3f08df30822031db7c1dd50851bdfcc3c,cosmos1s5a5ls3d6xmks5fc8tst9x7tx2jv8mfjmn0aq8"
},
// Networks Data
"networks":{
"username":"",
"password":[
{
"networkId":"1",
"namespace": "eip155",
"chainId": "1",
"networkName": "Ethereum",
"rpcUrl": "https://cloudflare-eth.com/",
"currencySymbol": "ETH",
"coinType": "60"
},
{
"networkId":"2",
"namespace": "cosmos",
"chainId": "theta-testnet-001",
"networkName": "Cosmos Hub Testnet",
"rpcUrl": "https://rpc-t.cosmos.nodestake.top",
"nativeDenom": "ATOM",
"addressPrefix": "cosmos",
"coinType": "118"
}
]
},
// Stores count of total accounts for specific chain
"addAccountCounter/eip155:1":{
"username": "",
"password": "3"
},
"addAccountCounter/cosmos:cosmoshub-4":{
"username": "",
"password": "3"
},
// Stores ids of accounts for specific chain
"accountIndices/eip155:1":{
"username": "",
"password": "0,1,2"
},
"accountIndices/cosmos:cosmoshub-4":{
"username": "",
"password": "0,1,2"
}
}
```

View File

@ -5,10 +5,11 @@
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
"lint": "eslint .",
"lint": "eslint . --max-warnings=0",
"start": "react-native start",
"test": "jest",
"postinstall": "patch-package"
"postinstall": "patch-package",
"prepare": "husky"
},
"dependencies": {
"@cosmjs/amino": "^0.32.3",
@ -66,6 +67,7 @@
"babel-jest": "^29.6.3",
"babel-plugin-module-resolver": "^5.0.0",
"eslint": "^8.19.0",
"husky": "^9.0.11",
"jest": "^29.6.3",
"metro-babel7-plugin-react-transform": "^0.54.1",
"prettier": "2.8.8",

View File

@ -23,13 +23,7 @@ const SelectNetworkType = ({
return (
<View style={styles.networkDropdown}>
<Text
style={{
fontWeight: 'bold',
marginVertical: 10,
}}>
Select Network Type
</Text>
<Text style={styles.selectNetworkText}>Select Network Type</Text>
<List.Accordion
title={selectedNetwork}
expanded={expanded}

View File

@ -265,6 +265,10 @@ const styles = StyleSheet.create({
sessionsContainer: { paddingLeft: 12, borderBottomWidth: 0.5 },
walletConnectUriText: { padding: 10 },
walletConnectLogo: { width: 24, height: 15, margin: 0 },
selectNetworkText: {
fontWeight: 'bold',
marginVertical: 10,
},
});
export default styles;

View File

@ -10,7 +10,7 @@ export let web3wallet: IWeb3Wallet | undefined;
export let core: ICore;
export async function createWeb3Wallet() {
const core = new Core({
core = new Core({
projectId: Config.WALLET_CONNECT_PROJECT_ID,
});

View File

@ -5494,6 +5494,11 @@ human-signals@^5.0.0:
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28"
integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==
husky@^9.0.11:
version "9.0.11"
resolved "https://registry.yarnpkg.com/husky/-/husky-9.0.11.tgz#fc91df4c756050de41b3e478b2158b87c1e79af9"
integrity sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==
idb-keyval@^6.2.1:
version "6.2.1"
resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-6.2.1.tgz#94516d625346d16f56f3b33855da11bfded2db33"