Skip to content

Commit

Permalink
Merge pull request #3 from lindlof/keplr-wallet
Browse files Browse the repository at this point in the history
Keplr wallet
  • Loading branch information
lindlof authored Nov 28, 2020
2 parents e828534 + 9e7ed24 commit 523ea2d
Show file tree
Hide file tree
Showing 15 changed files with 1,256 additions and 870 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/
2 changes: 1 addition & 1 deletion web/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:15
FROM node:15.2.1
WORKDIR /app
COPY package.json package-lock.json /app/
RUN npm install
Expand Down
5 changes: 4 additions & 1 deletion web/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ 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'
volumes:
- '/app/node_modules'
- './:/app'
1,654 changes: 863 additions & 791 deletions web/package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"react": "^16.14.0",
"react-dom": "^16.14.0",
"react-scripts": "3.4.3",
"secretjs": "^0.10.0"
"secretjs": "0.10.3"
},
"scripts": {
"start": "react-scripts start",
Expand Down
73 changes: 15 additions & 58 deletions web/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
import React, { useEffect, useState } from 'react';
import React, { 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';
import { envConfig } from './config';
import * as Game from './game';
import GamePlaying from './GamePlaying';
import { useSnackbar } from 'notistack';
import Wallet from './Wallet';
import Wallet from './wallet/Wallet';
import ClientProvider from './wallet/ClientProvider';
import Banner from './Banner';
import Grid from '@material-ui/core/Grid';
import CircularProgress from '@material-ui/core/CircularProgress';
import GameTicker from './components/GameTicker';

const config = Config();
const config = envConfig();

export const App: React.FC = () => {
const [client, setClient] = useState<SecretJS.SigningCosmWasmClient | undefined>();
const [game, setGame] = useLocalStorage<Game.Game | null | undefined>('game', undefined);
const { enqueueSnackbar } = useSnackbar();
useEffect(() => {
initClient(setClient);
}, []);

return (
<div>
<ClientProvider client={client} setClient={setClient}>
<Grid container spacing={3} alignItems="flex-end">
<Grid item xs={12} sm={8}>
<Banner />
Expand Down Expand Up @@ -58,7 +55,7 @@ export const App: React.FC = () => {
</Button>
</div>
)}
</div>
</ClientProvider>
);
};

Expand All @@ -70,21 +67,23 @@ const playGame = async (
) => {
setGame(null, false);

const game = Game.create(contract);
try {
const game = Game.create(contract);
await client.execute(contract, { join_game: { locator: game.locator } }, undefined, [
{
amount: '10000000',
denom: 'uscrt',
},
]);
setGame(game);
} catch (e) {
setGame(undefined);
enqueueSnackbar('Fail. Try funding wallet?', { variant: 'error' });
console.log('playGame error', e);
return;
if (e.message !== 'ciphertext not set') {
setGame(undefined);
enqueueSnackbar('Fail. Try funding wallet?', { variant: 'error' });
console.log('playGame error', e);
return;
}
}
setGame(game);
};

const playHandsign = async (
Expand Down Expand Up @@ -116,46 +115,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;
29 changes: 15 additions & 14 deletions web/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
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();
const envConfig = (): Config => {
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 requiredEnv = (key: string): string => {
const val = process.env[key];
if (!val) throw new Error(`${key} not configured`);
return val;
};

const contractFromEnv = (): string => {
const reactAppContract = process.env.REACT_APP_CONTRACT;
if (!reactAppContract) throw new Error('REACT_APP_CONTRACT not configured');
return reactAppContract;
};
export type { Config };
export { envConfig };
12 changes: 10 additions & 2 deletions web/src/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,19 @@ const playHandsign = async (
game: Game,
handsign: Msg.Handsign,
) => {
await client.execute(game.contract, { play_hand: { handsign, locator: game.locator } });
try {
await client.execute(game.contract, { play_hand: { handsign, locator: game.locator } });
} catch (e) {
if (e.message !== 'ciphertext not set') throw e;
}
};

const claimInactivity = async (client: SecretJS.SigningCosmWasmClient, game: Game) => {
await client.execute(game.contract, { claim_inactivity: { locator: game.locator } });
try {
await client.execute(game.contract, { claim_inactivity: { locator: game.locator } });
} catch (e) {
if (e.message !== 'ciphertext not set') throw e;
}
};

export type Game = Game_;
Expand Down
56 changes: 56 additions & 0 deletions web/src/wallet/ClientLoader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React, { useEffect } from 'react';
import { Button } from '@material-ui/core';
import { Config } from '../config';
import CircularProgress from '@material-ui/core/CircularProgress';
import Grid from '@material-ui/core/Grid';
import localWallet from './localWallet';
import keplr from './keplrWallet';
import { WalletType, walletTypeName } from './model';

interface Props {
walletType: WalletType;
config: Config;
setClient: Function;
cancel: Function;
}

export default (props: React.PropsWithChildren<Props>) => {
const { walletType, config, setClient, cancel } = props;

useEffect(() => {
const timer = setInterval(async () => {
switch (walletType) {
case WalletType.Keplr:
keplr(config.chainId, config.chainName, config.lcdUrl, config.rpcUrl, setClient);
break;
case WalletType.LocalWallet:
localWallet(config.lcdUrl, setClient);
}
}, 1000);
return () => clearInterval(timer);
}, [walletType, config.chainId, config.chainName, config.lcdUrl, config.rpcUrl, setClient]);

return (
<div>
<h2>Loading wallet</h2>
<p>
{walletType === WalletType.LocalWallet &&
`${walletTypeName[walletType]} should be ready shortly`}
{walletType === WalletType.Keplr &&
`Make sure you have ${walletTypeName[walletType]} extension installed`}
</p>
<div>
<Grid container spacing={3} alignItems="flex-end">
<Grid item xs={4} sm={6}>
<CircularProgress />
</Grid>
<Grid item xs={8} sm={6}>
<Button variant="contained" color="primary" onClick={() => cancel()}>
Cancel
</Button>
</Grid>
</Grid>
</div>
</div>
);
};
80 changes: 80 additions & 0 deletions web/src/wallet/ClientProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React, { useEffect, useState } from 'react';
import * as SecretJS from 'secretjs';
import { makeStyles } from '@material-ui/core/styles';
import { Button } from '@material-ui/core';
import Paper from '@material-ui/core/Paper';
import Modal from '@material-ui/core/Modal';
import { envConfig } from '../config';
import ClientLoader from './ClientLoader';
import { WalletType } from './model';

const config = envConfig();

const useStyles = makeStyles((theme) => ({
paper: {
position: 'absolute',
width: 270,
backgroundColor: theme.palette.background.paper,
border: '2px solid #000',
boxShadow: theme.shadows[5],
padding: theme.spacing(1, 1, 4),
textAlign: 'center',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
},
}));

interface Props {
client: SecretJS.SigningCosmWasmClient | undefined;
setClient: (client: SecretJS.SigningCosmWasmClient) => void;
}

export default (props: React.PropsWithChildren<Props>) => {
const classes = useStyles();
const { client, setClient } = props;
const [loadWallet, setLoadWallet] = useState<WalletType | undefined>();

useEffect(() => {
if (client) setLoadWallet(undefined);
}, [client]);

return (
<div>
{!client && (
<Modal
open={true}
aria-labelledby="simple-modal-title"
aria-describedby="simple-modal-description"
>
<Paper className={classes.paper}>
{!loadWallet && (
<div>
<h2>Select wallet</h2>
<Button
variant="contained"
color="primary"
onClick={() => setLoadWallet(WalletType.Keplr)}
>
Use Keplr
</Button>
<Button variant="contained" onClick={() => setLoadWallet(WalletType.LocalWallet)}>
Local wallet
</Button>
</div>
)}
{loadWallet && (
<ClientLoader
walletType={loadWallet}
config={config}
setClient={setClient}
cancel={() => setLoadWallet(undefined)}
/>
)}
</Paper>
</Modal>
)}
{props.children}
</div>
);
};
File renamed without changes.
Loading

0 comments on commit 523ea2d

Please sign in to comment.