Skip to content

Commit

Permalink
Add keplr wallet
Browse files Browse the repository at this point in the history
  • Loading branch information
lindlof committed Nov 19, 2020
1 parent e828534 commit 2a0eba0
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 60 deletions.
8 changes: 7 additions & 1 deletion script/react.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,10 @@ secretcli tx compute instantiate "$CODE" "{}" --from a --amount 1000000uscrt --l
echo Contract $CONTRACT

cd web
REACT_APP_LCD_URL=http://localhost:1338 REACT_APP_CONTRACT=$CONTRACT docker-compose up --build

export REACT_APP_CHAIN_ID=enigma-pub-testnet-3
export REACT_APP_CHAIN_NAME="Secret localhost"
export REACT_APP_LCD_URL=http://localhost:1338
export REACT_APP_RPC_URL=http://localhost:26657
export REACT_APP_CONTRACT="$CONTRACT"
docker-compose up --build
5 changes: 4 additions & 1 deletion web/.env
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
REACT_APP_LCD_URL=https://bootstrap.secrettestnet.io/
REACT_APP_CHAIN_ID=holodeck-2
REACT_APP_CHAIN_NAME=Secret Holodeck 2
REACT_APP_LCD_URL=https://bootstrap.secrettestnet.io
REACT_APP_RPC_URL=http://bootstrap.secrettestnet.io:26657
REACT_APP_CONTRACT=secret17c7j5xgylhty8jq5lqnuaqd68ndazs022mqcuu
REACT_APP_FAUCET=https://faucet.secrettestnet.io/
4 changes: 4 additions & 0 deletions web/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ services:
build:
context: .
environment:
- 'REACT_APP_CHAIN_ID=${REACT_APP_CHAIN_ID}'
- 'REACT_APP_CHAIN_NAME=${REACT_APP_CHAIN_NAME}'
- 'REACT_APP_LCD_URL=${REACT_APP_LCD_URL}'
- 'REACT_APP_RPC_URL=${REACT_APP_RPC_URL}'
- 'REACT_APP_CONTRACT=${REACT_APP_CONTRACT}'
ports:
- '3000:3000'
Expand Down
48 changes: 4 additions & 44 deletions web/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { useEffect, useState } from 'react';
import { Button } from '@material-ui/core';
import * as SecretJS from 'secretjs';
import * as bip39 from 'bip39';
import { useLocalStorage } from './utils';
import * as Msg from './msg';
import Config from './config';
Expand All @@ -13,6 +12,8 @@ import Banner from './Banner';
import Grid from '@material-ui/core/Grid';
import CircularProgress from '@material-ui/core/CircularProgress';
import GameTicker from './components/GameTicker';
import localWallet from './localWallet';
import keplr from './keplrWallet';

const config = Config();

Expand All @@ -21,7 +22,8 @@ export const App: React.FC = () => {
const [game, setGame] = useLocalStorage<Game.Game | null | undefined>('game', undefined);
const { enqueueSnackbar } = useSnackbar();
useEffect(() => {
initClient(setClient);
//localWallet(config.lcdUrl, setClient);
keplr(config.chainId, config.chainName, config.lcdUrl, config.rpcUrl, setClient);
}, []);

return (
Expand Down Expand Up @@ -116,46 +118,4 @@ const claimInactivity = async (
}
};

const initClient = async (setClient: Function) => {
let mnemonic = localStorage.getItem('mnemonic');
if (!mnemonic) {
mnemonic = bip39.generateMnemonic();
localStorage.setItem('mnemonic', mnemonic);
}

let tx_encryption_seed: Uint8Array;
const tx_encryption_seed_storage = localStorage.getItem('tx_encryption_seed');
if (tx_encryption_seed_storage) {
tx_encryption_seed = Uint8Array.from(JSON.parse(`[${tx_encryption_seed_storage}]`));
} else {
tx_encryption_seed = SecretJS.EnigmaUtils.GenerateNewSeed();
localStorage.setItem('tx_encryption_seed', tx_encryption_seed.toString());
}

const signingPen = await SecretJS.Secp256k1Pen.fromMnemonic(mnemonic);
const walletAddress = SecretJS.pubkeyToAddress(
SecretJS.encodeSecp256k1Pubkey(signingPen.pubkey),
'secret',
);

const secretJsClient = new SecretJS.SigningCosmWasmClient(
config.lcdUrl,
walletAddress,
(signBytes) => signingPen.sign(signBytes),
tx_encryption_seed,
{
init: {
amount: [{ amount: '250000', denom: 'uscrt' }],
gas: '250000',
},
exec: {
amount: [{ amount: '250000', denom: 'uscrt' }],
gas: '250000',
},
},
);
setClient(secretJsClient);
return secretJsClient;
};

export default App;
26 changes: 12 additions & 14 deletions web/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
interface Config {
readonly chainId: string;
readonly chainName: string;
readonly lcdUrl: string;
readonly rpcUrl: string;
readonly contract: string;
readonly faucetUrl: string | undefined;
}

export default (): Config => {
const lcdUrl: string = lcdUrlFromEnv();
const contract: string = contractFromEnv();
return {
lcdUrl,
contract,
chainId: requiredEnv('REACT_APP_CHAIN_ID'),
chainName: requiredEnv('REACT_APP_CHAIN_NAME'),
lcdUrl: requiredEnv('REACT_APP_LCD_URL'),
rpcUrl: requiredEnv('REACT_APP_RPC_URL'),
contract: requiredEnv('REACT_APP_CONTRACT'),
faucetUrl: process.env.REACT_APP_FAUCET,
};
};

const lcdUrlFromEnv = (): string => {
const reactAppContract = process.env.REACT_APP_LCD_URL;
if (!reactAppContract) throw new Error('REACT_APP_LCD_URL not configured');
return reactAppContract;
};

const contractFromEnv = (): string => {
const reactAppContract = process.env.REACT_APP_CONTRACT;
if (!reactAppContract) throw new Error('REACT_APP_CONTRACT not configured');
return reactAppContract;
const requiredEnv = (key: string): string => {
const val = process.env[key];
if (!val) throw new Error(`${key} not configured`);
return val;
};
123 changes: 123 additions & 0 deletions web/src/keplrWallet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import * as SecretJS from 'secretjs';

export default async (chainId, chainName, lcdUrl, rpcUrl, setClient) => {
// Keplr extension injects the offline signer that is compatible with cosmJS.
// You can get this offline signer from `window.getOfflineSigner(chainId:string)` after load event.
// And it also injects the helper function to `window.keplr`.
// If `window.getOfflineSigner` or `window.keplr` is null, Keplr extension may be not installed on browser.
if (!window.getOfflineSigner || !window.keplr) {
alert('Please install keplr extension');
} else {
if (window.keplr.experimentalSuggestChain) {
try {
await window.keplr.experimentalSuggestChain({
// Chain-id of the Cosmos SDK chain.
chainId,
// The name of the chain to be displayed to the user.
chainName,
// RPC endpoint of the chain.
rpc: rpcUrl,
// REST endpoint of the chain.
rest: lcdUrl,
// Staking coin information
stakeCurrency: {
// Coin denomination to be displayed to the user.
coinDenom: 'SCRT',
// Actual denom (i.e. uatom, uscrt) used by the blockchain.
coinMinimalDenom: 'uscrt',
// # of decimal points to convert minimal denomination to user-facing denomination.
coinDecimals: 6,
// (Optional) Keplr can show the fiat value of the coin if a coingecko id is provided.
// You can get id from https://api.coingecko.com/api/v3/coins/list if it is listed.
// coinGeckoId: ""
},
// (Optional) If you have a wallet webpage used to stake the coin then provide the url to the website in `walletUrlForStaking`.
// The 'stake' button in Keplr extension will link to the webpage.
// walletUrlForStaking: "",
// The BIP44 path.
bip44: {
// You can only set the coin type of BIP44.
// 'Purpose' is fixed to 44.
coinType: 118,
},
// Bech32 configuration to show the address to user.
// This field is the interface of
// {
// bech32PrefixAccAddr: string;
// bech32PrefixAccPub: string;
// bech32PrefixValAddr: string;
// bech32PrefixValPub: string;
// bech32PrefixConsAddr: string;
// bech32PrefixConsPub: string;
// }
bech32Config: {
bech32PrefixAccAddr: 'secret',
bech32PrefixAccPub: 'secretpub',
bech32PrefixValAddr: 'secretvaloper',
bech32PrefixValPub: 'secretvaloperpub',
bech32PrefixConsAddr: 'secretvalcons',
bech32PrefixConsPub: 'secretvalconspub',
},
// List of all coin/tokens used in this chain.
currencies: [
{
coinDenom: 'SCRT',
coinMinimalDenom: 'uscrt',
coinDecimals: 6,
},
],
// List of coin/tokens used as a fee token in this chain.
feeCurrencies: [
{
coinDenom: 'SCRT',
coinMinimalDenom: 'uscrt',
coinDecimals: 6,
},
],
// (Optional) The number of the coin type.
// This field is only used to fetch the address from ENS.
// Ideally, it is recommended to be the same with BIP44 path's coin type.
// However, some early chains may choose to use the Cosmos Hub BIP44 path of '118'.
// So, this is separated to support such chains.
coinType: 118,
// (Optional) This is used to set the fee of the transaction.
// If this field is not provided, Keplr extension will set the default gas price as (low: 0.01, average: 0.025, high: 0.04).
// Currently, Keplr doesn't support dynamic calculation of the gas prices based on on-chain data.
// Make sure that the gas prices are higher than the minimum gas prices accepted by chain validators and RPC/REST endpoint.
gasPriceStep: {
low: 0.01,
average: 0.025,
high: 0.04,
},
});
} catch {
alert('Failed to suggest the chain');
}
} else {
alert('Please use the recent version of keplr extension');
}
}

// You should request Keplr to enable the wallet.
// This method will ask the user whether or not to allow access if they haven't visited this website.
// Also, it will request user to unlock the wallet if the wallet is locked.
// If you don't request enabling before usage, there is no guarantee that other methods will work.
await window.keplr.enable(chainId);

const offlineSigner = window.getOfflineSigner(chainId);

// You can get the address/public keys by `getAccounts` method.
// It can return the array of address/public key.
// But, currently, Keplr extension manages only one address/public key pair.
// XXX: This line is needed to set the sender address for SigningCosmosClient.
const accounts = await offlineSigner.getAccounts();

// Initialize the gaia api with the offline signer that is injected by Keplr extension.
const secretJsClient = new SecretJS.SigningCosmWasmClient(
lcdUrl,
accounts[0].address,
offlineSigner,
);

setClient(secretJsClient);
};
43 changes: 43 additions & 0 deletions web/src/localWallet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import * as SecretJS from 'secretjs';
import * as bip39 from 'bip39';

export default async (lcdUrl: string, setClient: Function) => {
let mnemonic = localStorage.getItem('mnemonic');
if (!mnemonic) {
mnemonic = bip39.generateMnemonic();
localStorage.setItem('mnemonic', mnemonic);
}

let tx_encryption_seed: Uint8Array;
const tx_encryption_seed_storage = localStorage.getItem('tx_encryption_seed');
if (tx_encryption_seed_storage) {
tx_encryption_seed = Uint8Array.from(JSON.parse(`[${tx_encryption_seed_storage}]`));
} else {
tx_encryption_seed = SecretJS.EnigmaUtils.GenerateNewSeed();
localStorage.setItem('tx_encryption_seed', tx_encryption_seed.toString());
}

const signingPen = await SecretJS.Secp256k1Pen.fromMnemonic(mnemonic);
const walletAddress = SecretJS.pubkeyToAddress(
SecretJS.encodeSecp256k1Pubkey(signingPen.pubkey),
'secret',
);

const secretJsClient = new SecretJS.SigningCosmWasmClient(
lcdUrl,
walletAddress,
(signBytes) => signingPen.sign(signBytes),
tx_encryption_seed,
{
init: {
amount: [{ amount: '250000', denom: 'uscrt' }],
gas: '250000',
},
exec: {
amount: [{ amount: '250000', denom: 'uscrt' }],
gas: '250000',
},
},
);
setClient(secretJsClient);
};

0 comments on commit 2a0eba0

Please sign in to comment.