Skip to content

Commit 6158c6c

Browse files
committed
Merge branch 'main' of github.com:Panda-Wallet/panda-wallet
2 parents e2ab11d + c51f0a4 commit 6158c6c

File tree

10 files changed

+229
-33
lines changed

10 files changed

+229
-33
lines changed

src/components/ToggleSwitch.tsx

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import styled from "styled-components";
2+
import { ChangeEvent } from "react";
3+
import { Theme } from "../theme";
4+
5+
const Label = styled.label`
6+
display: flex;
7+
align-items: center;
8+
gap: 10px;
9+
cursor: pointer;
10+
`;
11+
12+
const Switch = styled.div`
13+
position: relative;
14+
width: 60px;
15+
height: 28px;
16+
background: #b3b3b3;
17+
border-radius: 32px;
18+
padding: 4px;
19+
transition: 300ms all;
20+
21+
&:before {
22+
transition: 300ms all;
23+
content: "";
24+
position: absolute;
25+
width: 28px;
26+
height: 28px;
27+
border-radius: 35px;
28+
top: 50%;
29+
left: 4px;
30+
background: white;
31+
transform: translate(0, -50%);
32+
}
33+
`;
34+
35+
const Input = styled.input`
36+
opacity: 0;
37+
position: absolute;
38+
39+
&:checked + ${Switch} {
40+
background: green;
41+
42+
&:before {
43+
transform: translate(32px, -50%);
44+
}
45+
}
46+
`;
47+
48+
export type ToggleSwitchProps = {
49+
on: boolean;
50+
label: string;
51+
onLabel: string;
52+
offLabel: string;
53+
theme: Theme;
54+
onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
55+
};
56+
57+
export const ToggleSwitch = (props: ToggleSwitchProps) => {
58+
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
59+
if (props.onChange) {
60+
props.onChange(e);
61+
}
62+
};
63+
64+
return (
65+
<Label>
66+
<span>
67+
{props.label}: {props.on ? props.onLabel : props.offLabel}
68+
</span>
69+
<Input checked={props.on} type="checkbox" onChange={handleChange} />
70+
<Switch />
71+
</Label>
72+
);
73+
};

src/hooks/useBsv.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useEffect, useState } from "react";
22
import { useKeys } from "./useKeys";
33
import {
4+
ChainParams,
45
ECDSA,
56
Hash,
67
P2PKHAddress,
@@ -16,6 +17,7 @@ import {
1617
import { UTXO, WocUtxo, useWhatsOnChain } from "./useWhatsOnChain";
1718
import { SignMessageResponse } from "../pages/requests/SignMessageRequest";
1819
import { useBsvWasm } from "./useBsvWasm";
20+
import { NetWork, useNetwork } from "./useNetwork";
1921

2022
type SendBsvResponse = {
2123
txid?: string;
@@ -53,8 +55,12 @@ export const useBsv = () => {
5355
useWhatsOnChain();
5456
const { retrieveKeys, bsvAddress, verifyPassword, bsvPubKey } = useKeys();
5557
const { bsvWasmInitialized } = useBsvWasm();
58+
const { network } = useNetwork();
59+
useEffect(() => { }, []);
5660

57-
useEffect(() => {}, []);
61+
const getChainParams = (network: NetWork): ChainParams => {
62+
return network === NetWork.Mainnet ? ChainParams.mainnet() : ChainParams.testnet();
63+
}
5864

5965
const sendBsv = async (
6066
request: Web3SendBsvRequest,
@@ -74,7 +80,7 @@ export const useBsv = () => {
7480
if (!keys?.walletWif || !keys.walletPubKey) throw Error("Undefined key");
7581
const paymentPk = PrivateKey.from_wif(keys.walletWif);
7682
const pubKey = paymentPk.to_public_key();
77-
const fromAddress = pubKey.to_address().to_string();
83+
const fromAddress = pubKey.to_address().set_chain_params(getChainParams(network)).to_string();
7884
const amount = request.reduce((a, r) => a + r.satAmount, 0);
7985

8086
// Format in and outs
@@ -227,7 +233,7 @@ export const useBsv = () => {
227233
const hash = Hash.sha_256(Buffer.from(message)).to_hex();
228234
const privateKey = PrivateKey.from_wif(keys.walletWif);
229235
const publicKey = privateKey.to_public_key();
230-
const address = publicKey.to_address().to_string();
236+
const address = publicKey.to_address().set_chain_params(getChainParams(network)).to_string();
231237
const encoder = new TextEncoder();
232238
const encodedMessage = encoder.encode(hash);
233239
const signature = privateKey.sign_message(encodedMessage);

src/hooks/useKeys.ts

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import {
77
} from "../utils/crypto";
88
import { Keys, getKeys } from "../utils/keys";
99
import { storage } from "../utils/storage";
10+
import { NetWork, useNetwork } from "./useNetwork";
11+
import { ChainParams, P2PKHAddress } from "bsv-wasm-web";
12+
import { useBsvWasm } from "./useBsvWasm";
1013

1114
export type KeyStorage = {
1215
encryptedKeys: string;
@@ -19,6 +22,14 @@ export const useKeys = () => {
1922
const [ordAddress, setOrdAddress] = useState("");
2023
const [bsvPubKey, setBsvPubKey] = useState("");
2124
const [ordPubKey, setOrdPubKey] = useState("");
25+
const { network } = useNetwork();
26+
const { bsvWasmInitialized } = useBsvWasm();
27+
28+
const getChainParams = (network: NetWork): ChainParams => {
29+
return network === NetWork.Mainnet ? ChainParams.mainnet() : ChainParams.testnet();
30+
}
31+
32+
2233
const generateSeedAndStoreEncrypted = (
2334
password: string,
2435
mnemonic?: string
@@ -42,20 +53,32 @@ export const useKeys = () => {
4253
["encryptedKeys", "passKey", "salt"],
4354
async (result: KeyStorage) => {
4455
try {
56+
if (!bsvWasmInitialized) throw Error("bsv-wasm not initialized!");
4557
if (!result.encryptedKeys || !result.passKey) return;
4658
const d = decrypt(result.encryptedKeys, result.passKey);
4759
const keys: Keys = JSON.parse(d);
48-
setBsvAddress(keys.walletAddress);
49-
setOrdAddress(keys.ordAddress);
60+
61+
const walletAddress = P2PKHAddress.from_string(keys.walletAddress)
62+
.set_chain_params(getChainParams(network))
63+
.to_string();
64+
65+
const ordAddress = P2PKHAddress.from_string(keys.ordAddress)
66+
.set_chain_params(getChainParams(network))
67+
.to_string();
68+
setBsvAddress(walletAddress);
69+
setOrdAddress(ordAddress);
5070
setBsvPubKey(keys.walletPubKey);
5171
setOrdPubKey(keys.ordPubKey);
5272
if (password) {
5373
const isVerified = await verifyPassword(password);
54-
isVerified ? resolve(keys) : reject("Unauthorized!");
74+
isVerified ? resolve(Object.assign({}, keys, {
75+
ordAddress,
76+
walletAddress,
77+
})) : reject("Unauthorized!");
5578
} else {
5679
resolve({
57-
ordAddress: keys.ordAddress,
58-
walletAddress: keys.walletAddress,
80+
ordAddress,
81+
walletAddress,
5982
walletPubKey: keys.walletPubKey,
6083
ordPubKey: keys.ordPubKey,
6184
});
@@ -82,9 +105,10 @@ export const useKeys = () => {
82105
};
83106

84107
useEffect(() => {
108+
if (!bsvWasmInitialized) return
85109
retrieveKeys();
86110
// eslint-disable-next-line react-hooks/exhaustive-deps
87-
}, []);
111+
}, [bsvWasmInitialized]);
88112

89113
return {
90114
generateSeedAndStoreEncrypted,

src/hooks/useNetwork.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { useEffect, useState } from "react";
2+
import { storage } from "../utils/storage";
3+
4+
export type NetWorkStorage = {
5+
network: NetWork;
6+
};
7+
8+
export const enum NetWork {
9+
Mainnet = "mainnet",
10+
Testnet = "testnet",
11+
}
12+
13+
export const useNetwork = () => {
14+
const [network, setNetwork] = useState(NetWork.Mainnet);
15+
16+
const retrieveNetwork = (): Promise<NetWork> => {
17+
return new Promise((resolve, reject) => {
18+
storage.get(
19+
["network"],
20+
async (result: NetWorkStorage) => {
21+
try {
22+
if (!result.network) {
23+
setNetwork(NetWork.Mainnet);
24+
resolve(NetWork.Mainnet);
25+
return;
26+
}
27+
28+
setNetwork(result.network);
29+
resolve(result.network);
30+
} catch (error) {
31+
reject(error);
32+
}
33+
}
34+
);
35+
});
36+
};
37+
38+
const updateNetwork = (n: NetWork): void => {
39+
storage.set({
40+
network: n
41+
});
42+
setNetwork(n)
43+
};
44+
45+
46+
useEffect(() => {
47+
retrieveNetwork();
48+
// eslint-disable-next-line react-hooks/exhaustive-deps
49+
}, []);
50+
51+
return {
52+
retrieveNetwork,
53+
network,
54+
updateNetwork
55+
};
56+
};

src/hooks/useOrds.ts

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import axios from "axios";
22
import { useEffect, useState } from "react";
3-
import { FEE_PER_BYTE, GP_BASE_URL } from "../utils/constants";
3+
import { FEE_PER_BYTE, GP_BASE_URL, GP_TESTNET_BASE_URL } from "../utils/constants";
44
import { useKeys } from "./useKeys";
55
import { UTXO, WocUtxo, useWhatsOnChain } from "./useWhatsOnChain";
66
import { sendOrdinal } from "js-1sat-ord-web";
77
import { P2PKHAddress, PrivateKey, Transaction } from "bsv-wasm-web";
88
import { useBsvWasm } from "./useBsvWasm";
99
import { Outpoint } from "../utils/outpoint";
10+
import { NetWork, useNetwork } from "./useNetwork";
1011

1112
export class InscriptionData {
1213
type?: string = '';
@@ -30,7 +31,7 @@ export class Origin {
3031
outpoint: Outpoint = new Outpoint();
3132
data?: TxoData;
3233
num?: number;
33-
map?: {[key: string]:any};
34+
map?: { [key: string]: any };
3435
claims?: Claim[]
3536
}
3637

@@ -43,20 +44,20 @@ export enum Bsv20Status {
4344
export class TxoData {
4445
types?: string[];
4546
insc?: File;
46-
map?: {[key: string]:any};
47+
map?: { [key: string]: any };
4748
b?: File;
4849
sigma?: Sigma[];
4950
list?: {
50-
price: number;
51-
payout: string;
51+
price: number;
52+
payout: string;
5253
};
5354
bsv20?: {
54-
id?: Outpoint;
55-
p: string;
56-
op: string;
57-
tick?: string;
58-
amt: string;
59-
status?: Bsv20Status
55+
id?: Outpoint;
56+
p: string;
57+
op: string;
58+
tick?: string;
59+
amt: string;
60+
status?: Bsv20Status
6061
};
6162
}
6263

@@ -145,13 +146,18 @@ export const useOrds = () => {
145146
const [ordinals, setOrdinals] = useState<OrdinalResponse>([]);
146147
const [isProcessing, setIsProcessing] = useState(false);
147148
const { bsvWasmInitialized } = useBsvWasm();
149+
const { network } = useNetwork();
150+
151+
const getOrdinalsBaseUrl = () => {
152+
return network === NetWork.Mainnet ? GP_BASE_URL : GP_TESTNET_BASE_URL;
153+
};
148154

149155
const getOrdinals = async () => {
150156
try {
151157
// setIsProcessing(true); // TODO: set this to true if call is taking more than a second
152158
//TODO: Implement infinite scroll to handle instances where user has more than 100 items.
153159
const res = await axios.get(
154-
`${GP_BASE_URL}/api/txos/address/${ordAddress}/unspent?limit=100&offset=0`
160+
`${getOrdinalsBaseUrl()}/api/txos/address/${ordAddress}/unspent?limit=100&offset=0`
155161
);
156162

157163
const ordList: OrdinalResponse = res.data;
@@ -313,7 +319,7 @@ export const useOrds = () => {
313319
return [];
314320
}
315321
const r = await axios.get(
316-
`${GP_BASE_URL}/api/txos/address/${ordAddress}/unspent?limit=100&offset=0`
322+
`${getOrdinalsBaseUrl()}/api/txos/address/${ordAddress}/unspent?limit=100&offset=0`
317323
);
318324

319325
const utxos = r.data as OrdinalResponse;
@@ -343,5 +349,6 @@ export const useOrds = () => {
343349
isProcessing,
344350
transferOrdinal,
345351
setIsProcessing,
352+
getOrdinalsBaseUrl,
346353
};
347354
};

0 commit comments

Comments
 (0)