Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inject keplr provider #86

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,210 changes: 2,161 additions & 49 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
}
},
"devDependencies": {
"@keplr-wallet/types": "^0.11.12",
"@parcel/config-webextension": "^2.4.1",
"@parcel/runtime-browser-hmr": "^2.4.1",
"@parcel/transformer-image": "^2.4.1",
Expand Down Expand Up @@ -62,6 +63,8 @@
"webextension-polyfill-ts": "^0.25.0"
},
"dependencies": {
"@cosmjs/amino": "^0.29.3",
"@cosmjs/launchpad": "^0.27.1",
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@ethersproject/bignumber": "^5.6.2",
Expand All @@ -75,6 +78,7 @@
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^6.3.0",
"sharp": "^0.31.1",
"stream-browserify": "^3.0.0",
"sweetalert2": "^11.4.33",
"uuid": "^8.3.2",
Expand Down
5 changes: 5 additions & 0 deletions src/background/messaging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ export const rpcListener = async (message: any) => {
case 'solana':
network = 'solana_devnet'; // TODO
break;
case 'keplr':
// need to improve here
network = 'cosmos_testnet';
// config here
break;
default:
throw new Error(`Unknown or missing provider type ${message.__provider}`);
}
Expand Down
2 changes: 2 additions & 0 deletions src/confirmation-window/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import App from './App';
import getSdk from 'pointsdk/pointsdk/sdk';
import getEthProvider from 'pointsdk/pointsdk/ethProvider';
import getSolanaProvider from 'pointsdk/pointsdk/solanaProvider';
import getKeplrProvider from '../pointsdk/keplrProvider';
const version = browser.runtime.getManifest().version;
const point = getSdk('https://confirmation-window', version);
const ethereum = getEthProvider();
Expand All @@ -14,6 +15,7 @@ const renderWindow = () => {
window.point = point;
window.ethereum = ethereum;
window.solana = solana;
window.keplr = getKeplrProvider();
ReactDOM.render(<App />, document.getElementById('point-confirmation-window'));
} catch (e) {
console.error('Failed to render confirmation window', e);
Expand Down
2 changes: 2 additions & 0 deletions src/pointsdk/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import getEthProvider from 'pointsdk/pointsdk/ethProvider';
import getSolanaProvider from 'pointsdk/pointsdk/solanaProvider';
import NETWORKS from 'pointsdk/constants/networks';
import swal from 'sweetalert2';
import getKeplrProvider from './keplrProvider';

// eslint-disable-next-line prettier/prettier
window.point = getSdk(window.location.origin, String(process.env.PACKAGE_VERSION), swal);
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
window.point.networks = NETWORKS;
window.ethereum = getEthProvider();
window.solana = getSolanaProvider();
window.keplr = getKeplrProvider();
7 changes: 7 additions & 0 deletions src/pointsdk/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import getSdk from 'pointsdk/pointsdk/sdk';
import getEthProvider from 'pointsdk/pointsdk/ethProvider';
import getSolanaProvider from 'pointsdk/pointsdk/solanaProvider';
import NETWORKS from 'pointsdk/constants/networks';
import getKeplrProvider from './keplrProvider';

const version = browser.runtime.getManifest().version;
try {
Expand All @@ -25,3 +26,9 @@ try {
} catch (e) {
console.error('Failed to inject window.solana: ', e);
}

try {
window.wrappedJSObject.eval(`window.keplr = (${getKeplrProvider.toString()})();`);
} catch (e) {
console.error('Failed to inject window.keplr: ', e);
}
144 changes: 144 additions & 0 deletions src/pointsdk/keplrProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import {AccountData, AminoSignResponse, BroadcastMode} from '@cosmjs/launchpad';
import {Coin} from '@cosmjs/amino';

/* eslint-disable @typescript-eslint/no-unsafe-member-access */
export interface AminoMsg {
readonly type: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
readonly value: any;
}

export interface StdFee {
readonly amount: readonly Coin[];
readonly gas: string;
/** The granter address that is used for paying with feegrants */
readonly granter?: string;
/** The fee payer address. The payer must have signed the transaction. */
readonly payer?: string;
}
export interface StdSignDoc {
readonly chain_id: string;
readonly account_number: string;
readonly sequence: string;
readonly fee: StdFee;
readonly msgs: readonly AminoMsg[];
readonly memo: string;
}

export interface KeplrKey {
bech32Address: string;
pubKey: Uint8Array;
}

export default function getKeplrProvider() {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const handleRequest = (request: Record<string, any>) =>
new Promise((resolve, reject) => {
const id = Math.random();
const handler = (e: MessageEvent) => {
if (e.data.__page_req_id === id && e.data.__direction === 'to_page') {
window.removeEventListener('message', handler);
if (e.data.__error) {
const {module, code, message} = e.data.__error;
return reject({module, code, message});
}
delete e.data.__page_req_id;
delete e.data.__direction;
resolve(Object.keys(e.data).length > 0 ? e.data : undefined);
}
};

window.addEventListener('message', handler);

window.postMessage({
...request,
__message_type: 'rpc',
__provider: 'keplr',
__page_req_id: id,
__direction: 'to_bg'
});
});
const keplrInstance = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
enable: async function (chainIds: string | string[]) {
return handleRequest({
method: `keplr_enable`,
params: [chainIds]
});
},
getKey: async function (chainId: string) {
return handleRequest({
method: 'keplr_getKey',
params: [chainId]
});
},
getOfflineSigner: function (chainId: string) {
return {
getAccounts: async (): Promise<AccountData[]> => {
const key = (await keplrInstance.getKey(chainId)) as KeplrKey;
return [
{
address: key.bech32Address,
// Currently, only secp256k1 is supported.
algo: 'secp256k1',
pubkey: key.pubKey
}
];
},

signAmino: async (
signerAddress: string,
signDoc: StdSignDoc
): Promise<AminoSignResponse> => {
if (chainId !== signDoc.chain_id) {
throw new Error('Unmatched chain id with the offline signer');
}
const key = (await keplrInstance.getKey(signDoc.chain_id)) as KeplrKey;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (key.bech32Address !== signerAddress) {
throw new Error('Unknown signer address');
}
return (await keplrInstance.signAmino(
chainId,
signerAddress,
signDoc
)) as AminoSignResponse;
},

// Fallback function for the legacy cosmjs implementation before the staragte.
sign: async (
signerAddress: string,
signDoc: StdSignDoc
): Promise<AminoSignResponse> =>
(await keplrInstance.signAmino(
chainId,
signerAddress,
signDoc
)) as AminoSignResponse
};
},
signAmino: async function (chainId: string, signerAddress: string, signDoc: StdSignDoc) {
return handleRequest({
method: 'keplr_signAmino',
params: [chainId, signerAddress, signDoc]
});
},
sendTx: async function (chainId: string, tx: Uint8Array, mode: BroadcastMode) {
return handleRequest({
method: 'keplr_sendTx',
params: [chainId, tx, mode]
});
}
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(window as any).getOfflineSigner = keplrInstance.getOfflineSigner.bind(keplrInstance);
return keplrInstance;
}
/* TODO
signDirect(chainId:string, signer:string, signDoc: {
sendBack(result)
signArbitrary(chainId: string, signer: string, data: string | Uint8Array): Promise<StdSignature>;
verifyArbitrary(chainId: string, signer: string, data: string | Uint8Array, signature: StdSignature): Promise<boolean>;
signEthereum(chainId: string, signer: string, // Bech32 address, not hex data: string | Uint8Array, type: 'message' | 'transaction'
*/
2 changes: 1 addition & 1 deletion src/pointsdk/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const getSdk = (host: string, version: string, swal: any): PointType => {
const gatewayAlert = async () => {
swal.fire({
title: 'Demo mode',
html: `You cannot make writing operations right here but you can download Point Browser and have the full web3 experience.
html: `You cannot make writing operations right here but you can download Point Browser and have the full web3 experience.
<a href="https://pointnetwork.io/download" target="_blank" rel="noopener noreferrer">https://pointnetwork.io/download</a>`,
icon: 'info'
});
Expand Down
1 change: 1 addition & 0 deletions src/types/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ declare global {
point: any;
ethereum: any;
solana: any;
keplr: any;
}
}