Skip to content

Commit

Permalink
♻️ Use walletconnect v2 for likerland app
Browse files Browse the repository at this point in the history
  • Loading branch information
williamchong committed Oct 5, 2023
1 parent a2870f4 commit d333e9c
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 141 deletions.
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@
"@types/react": "^18.0.14",
"@types/react-dom": "^18.0.5",
"@types/react-modal": "^3.13.1",
"@walletconnect/legacy-types": "^2.0.0",
"@walletconnect/types": "^2.8.0",
"autoprefixer": "^10.4.7",
"babel-loader": "^8.2.5",
"husky": "^8.0.1",
Expand All @@ -79,11 +81,8 @@
"@cosmjs/proto-signing": "^0.28.13",
"@headlessui/react": "1.7.11",
"@walletconnect/browser-utils": "^1.8.0",
"@walletconnect/client": "^1.8.0",
"@walletconnect/legacy-types": "^2.0.0",
"@walletconnect/modal": "^2.4.5",
"@walletconnect/sign-client": "^2.8.0",
"@walletconnect/types": "^2.8.0",
"@walletconnect/utils": "^1.8.0",
"classnames": "^2.3.1",
"cosmjs-types": "^0.5.1",
Expand Down
11 changes: 2 additions & 9 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as React from 'react';
import { createRoot, Root } from 'react-dom/client';
import { AccountData } from '@cosmjs/proto-signing';
import WalletConnect from '@walletconnect/client';
import { IQRCodeModal } from '@walletconnect/legacy-types';
import EventEmitter from 'events';

Expand Down Expand Up @@ -31,7 +30,7 @@ import {
import { onKeplrMobileDisconnect, initKeplrMobile } from './utils/keplr-mobile';
import {
checkIsInLikerLandAppInAppBrowser,
getLikerLandAppWCConnector,
onLikerLandAppDisconnect,
initLikerLandApp,
} from './utils/liker-land-app';
import {
Expand Down Expand Up @@ -264,7 +263,6 @@ export class LikeCoinWalletConnector {
disconnect = async () => {
const session = this.loadSession();
if (session) {
let wcConnector: WalletConnect | undefined;
switch (session.method) {
case LikeCoinWalletConnectorMethodType.Keplr:
removeKeplrKeyStoreChangeListener(this._accountChangeListener);
Expand All @@ -283,9 +281,7 @@ export class LikeCoinWalletConnector {
break;

case LikeCoinWalletConnectorMethodType.LikerId:
wcConnector = getLikerLandAppWCConnector({
bridge: this.options.likerLandAppWCBridge,
});
await onLikerLandAppDisconnect();
break;

case LikeCoinWalletConnectorMethodType.Leap:
Expand All @@ -299,9 +295,6 @@ export class LikeCoinWalletConnector {
default:
break;
}
if (wcConnector) {
await wcConnector.killSession();
}
}
this.deleteSession();
this._events.removeAllListeners();
Expand Down
178 changes: 117 additions & 61 deletions src/utils/liker-land-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@ import {
DirectSignResponse,
OfflineSigner,
} from '@cosmjs/proto-signing';
import WalletConnect from '@walletconnect/client';
import {
IQRCodeModal,
IWalletConnectOptions,
} from '@walletconnect/legacy-types';
import { payloadId } from '@walletconnect/utils';
import { IQRCodeModal } from '@walletconnect/legacy-types';
import { SignDoc } from 'cosmjs-types/cosmos/tx/v1beta1/tx';

import {
Expand All @@ -18,90 +13,137 @@ import {
LikeCoinWalletConnectorOptions,
} from '../types';

import { convertWalletConnectAccountResponse } from './wallet';
import Client from '@walletconnect/sign-client';
import { SessionTypes } from '@walletconnect/types';
import {
getWalletConnectV2Accounts,
getWalletConnectV2Connector,
} from './wallet-connect-v2';

const LIKER_LAND_APP_USER_AGENT_KEY = 'LikeCoinApp';

// Ref: https://github.com/likecoin/likecoin-app/blob/b1109871821b20228bf54cf736c032a8e9fe6ed0/app/services/api/api-config.ts#L6-L7
export const checkIsInLikerLandAppInAppBrowser = () =>
navigator.userAgent.includes(LIKER_LAND_APP_USER_AGENT_KEY);

export function getLikerLandAppWCConnector(
options: Partial<IWalletConnectOptions> = {}
) {
const wc = new WalletConnect({
signingMethods: ['cosmos_getAccounts', 'cosmos_signAmino'],
...options,
});

if (checkIsInLikerLandAppInAppBrowser()) {
// Ref: https://github.com/osmosis-labs/osmosis-frontend/blob/49bede85f9a772fc40ffcdcd03d193b4d8178179/packages/web/hooks/use-keplr/context.tsx#L133
// @ts-ignore
wc._clientMeta = {
name: LIKER_LAND_APP_USER_AGENT_KEY,
};
}

return wc;
}
let client: Client | null = null;
let session: SessionTypes.Struct | null = null;

export async function initLikerLandApp(
options: LikeCoinWalletConnectorOptions,
qrcodeModal: IQRCodeModal,
sessionMethod?: LikeCoinWalletConnectorMethodType,
sessionAccounts: AccountData[] = []
): Promise<LikeCoinWalletConnectorInitResponse> {
const wcConnector = getLikerLandAppWCConnector({
bridge: options.likerLandAppWCBridge,
qrcodeModal,
const metadata = options.walletConnectMetadata;
if (checkIsInLikerLandAppInAppBrowser()) {
metadata.name = LIKER_LAND_APP_USER_AGENT_KEY;
}
const wcConnector = await getWalletConnectV2Connector({
projectId: options.walletConnectProjectId,
metadata,
});
client = wcConnector;
// TODO: allow selecting sessions
if (!session) {
const currentSessions = wcConnector.session.getAll();
const reuseSession = currentSessions
.reverse()
.find((session: SessionTypes.Struct) => {
return session.peer.metadata.name.includes('LikerLand');
});
if (reuseSession) {
session = reuseSession;
}
}
let accounts: AccountData[] = [];
if (
wcConnector.connected &&
client &&
session &&
sessionMethod === LikeCoinWalletConnectorMethodType.LikerId &&
sessionAccounts.length > 0
) {
accounts = sessionAccounts;
} else {
if (wcConnector.connected) {
await wcConnector.killSession();
let connectRes;
try {
connectRes = await wcConnector.connect({
pairingTopic: session?.topic,
requiredNamespaces: {
cosmos: {
methods: [
'cosmos_getAccounts',
'cosmos_signDirect',
'cosmos_signAmino',
],
chains: [`cosmos:${options.chainId}`],
events: [],
},
},
});
} catch (err) {
if (session) {
console.error(err);
session = null;
connectRes = await wcConnector.connect({
requiredNamespaces: {
cosmos: {
methods: [
'cosmos_getAccounts',
'cosmos_signDirect',
'cosmos_signAmino',
],
chains: [`cosmos:${options.chainId}`],
events: [],
},
},
});
} else {
throw err;
}
}
const { uri, approval } = connectRes;
if (uri) {
qrcodeModal.open(uri, () => {});
}
await wcConnector.connect();
const [account] = await wcConnector.sendCustomRequest({
id: payloadId(),
jsonrpc: '2.0',
method: 'cosmos_getAccounts',
params: [options.chainId],
});
accounts = [convertWalletConnectAccountResponse(account)];
}
if (!accounts.length) {
throw new Error('WALLETCONNECT_ACCOUNT_NOT_FOUND');
}

session = await approval();

accounts = await getWalletConnectV2Accounts(
wcConnector,
options.chainId,
session.topic
);
qrcodeModal.close();
}
const offlineSigner: OfflineSigner = {
getAccounts: () => Promise.resolve(accounts),
getAccounts: async () => Promise.resolve(accounts),
signAmino: async (signerBech32Address, signDoc) => {
const signedTx: AminoSignResponse[] = await wcConnector.sendCustomRequest(
{
id: payloadId(),
jsonrpc: '2.0',
const result = await wcConnector.request({
topic: session!.topic,
chainId: `cosmos:${options.chainId}`,
request: {
method: 'cosmos_signAmino',
params: [options.chainId, signerBech32Address, signDoc],
}
);
return signedTx[0];
params: {
signerAddress: signerBech32Address,
signDoc,
},
},
});
return result as AminoSignResponse;
},
signDirect: async (signerBech32Address, signDoc) => {
const {
signed: signedInJSON,
signature,
} = await wcConnector.sendCustomRequest({
id: payloadId(),
jsonrpc: '2.0',
method: 'cosmos_signDirect',
params: [signerBech32Address, SignDoc.toJSON(signDoc)],
});
const { signed: signedInJSON, signature } = (await wcConnector.request({
topic: session!.topic,
chainId: `cosmos:${options.chainId}`,
request: {
method: 'cosmos_signDirect',
params: {
signerAddress: signerBech32Address,
signDoc: SignDoc.toJSON(signDoc),
},
},
})) as any;
return {
signed: SignDoc.fromJSON(signedInJSON),
signature,
Expand All @@ -114,3 +156,17 @@ export async function initLikerLandApp(
offlineSigner,
};
}

export async function onLikerLandAppDisconnect(topic = session?.topic) {
if (topic) {
if (!client) return;
await client.disconnect({
topic,
reason: {
code: 6000,
message: 'USER_DISCONNECTED',
},
});
session = null;
}
}
Loading

0 comments on commit d333e9c

Please sign in to comment.