v1.8.0-beta2

node v14

fix publish

printversion

fix version

fix win icon
This commit is contained in:
Aditya Kulkarni 2022-06-24 09:52:13 -05:00
parent 16b9c39c89
commit e9191729b6
20 changed files with 302 additions and 104 deletions

View File

@ -21,10 +21,10 @@ jobs:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Use Node.js 12.x
- name: Use Node.js 14.x
uses: actions/setup-node@v1
with:
node-version: 12.x
node-version: 14.x
- name: yarn install
run: |
yarn install

2
.vscode/tasks.json vendored
View File

@ -5,7 +5,7 @@
"tasks": [
{
"type": "npm",
"script": "dev",
"script": "electron:start",
"group": { "kind": "build", "isDefault": true },
"problemMatcher": []
},

View File

@ -14,13 +14,13 @@ Head over to the releases page and grab the latest installers or binary. https:/
If you are on Debian/Ubuntu, please download the '.AppImage' package and just run it.
```
./Zecwallet.Fullnode-1.8.0-beta1.AppImage
./Zecwallet.Fullnode-1.8.0-beta2.AppImage
```
If you prefer to install a `.deb` package, that is also available.
```
sudo apt install -f ./zecwallet_1.8.0-beta1_amd64.deb
sudo apt install -f ./zecwallet_1.8.0-beta2_amd64.deb
```
### Windows

1
bin/printversion.ps1 Normal file
View File

@ -0,0 +1 @@
echo "VERSION=1.8.0-beta2" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append

3
bin/printversion.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
VERSION="1.8.0-beta2"
echo "VERSION=$VERSION" >> $GITHUB_ENV

47
bin/signbinaries.sh Executable file
View File

@ -0,0 +1,47 @@
#!/bin/bash
# Accept the variables as command line arguments as well
POSITIONAL=()
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
-v|--version)
APP_VERSION="$2"
shift # past argument
shift # past value
;;
*) # unknown option
POSITIONAL+=("$1") # save it in an array for later
shift # past argument
;;
esac
done
set -- "${POSITIONAL[@]}" # restore positional parameters
if [ -z $APP_VERSION ]; then echo "APP_VERSION is not set"; exit 1; fi
# Store the hash and signatures here
cd release
rm -rf signatures
mkdir signatures
# Remove previous signatures/hashes
rm -f sha256sum-$APP_VERSION.txt
rm -f signatures-$APP_VERSION.zip
# sha256sum the binaries
sha256sum Zecwallet*$APP_VERSION* > sha256sum-$APP_VERSION.txt
OIFS="$IFS"
IFS=$'\n'
for i in `find ./ -iname "Zecwallet*$APP_VERSION*" -o -iname "sha256sum-$APP_VERSION.txt"`; do
echo "Signing" "$i"
gpg --batch --output "signatures/$i.sig" --detach-sig "$i"
done
cp sha256sum-$APP_VERSION.txt signatures/
cp ../configs/SIGNATURES_README signatures/
zip -r signatures-$APP_VERSION.zip signatures/

View File

@ -1,7 +1,7 @@
{
"name": "zecwallet",
"productName": "Zecwallet Fullnode",
"version": "1.8.0-beta1",
"version": "1.8.0-beta2",
"description": "Zecwallet Fullnode (Electron version)",
"private": true,
"main": "./public/electron.js",
@ -41,9 +41,9 @@
"eject": "react-scripts eject",
"electron:prod": "yarn build && cross-env NODE_ENV=production && electron build/electron.js",
"electron:start": "concurrently -k \"cross-env BROWSER=none yarn start\" \"wait-on http://localhost:3000 && electronmon .\"",
"package-mac": "yarn build && electron-builder -m -c.extraMetadata.main=build/electron.js",
"package-win": "yarn build && electron-builder -w -c.extraMetadata.main=build/electron.js",
"package-linux": "yarn build && electron-builder -l -c.extraMetadata.main=build/electron.js"
"package-mac": "yarn build && electron-builder -m -c.extraMetadata.main=build/electron.js --publish never",
"package-win": "yarn build && electron-builder -w -c.extraMetadata.main=build/electron.js --publish never",
"package-linux": "yarn build && electron-builder -l -c.extraMetadata.main=build/electron.js --publish never"
},
"eslintConfig": {
"extends": [
@ -115,6 +115,16 @@
]
},
"win": {
"icon": "./resources/icon.ico",
"extraFiles": [
{
"from": "bin/win",
"to": "resources/bin/win",
"filter": [
"**/**"
]
}
],
"target": [
"zip",
"msi"

View File

@ -32,16 +32,21 @@ ipcMain.handle("doRPC_IPC", async (_, config, method, params) => {
})
.then((r) => resolve(r.data))
.catch((err) => {
const e = { ...err };
const e = { ...err };
console.log(`Error calling ${method} with ${JSON.stringify(params)}`);
if (e.response && e.response.data && e.response.data.error) {
console.log(JSON.stringify(e.response.data.error));
}
console.log(
`Caught error: ${e.response} - ${
e.response ? e.response.data : ""
e.response ? e.response.data.error : ""
}`
);
if (e.response && e.response.data) {
reject(e.response.data.toString());
if (e.response && e.response.data && e.response.data.error) {
reject(JSON.stringify(e.response.data.error));
} else {
reject("NO_CONNECTION");
}
@ -49,7 +54,7 @@ ipcMain.handle("doRPC_IPC", async (_, config, method, params) => {
});
return response;
});
});
ipcMain.handle("getZecPrice_IPC", async () => {
const response = await new Promise((resolve, reject) => {
@ -82,8 +87,10 @@ ipcMain.handle("readfile_IPC", async (_, filename) => {
// Create the native browser window.
function createWindow() {
const width = app.isPackaged ? 1200 : 2400;
const mainWindow = new BrowserWindow({
width: 2400,
width: width,
height: 800,
// Set the path of an additional "preload" script that can be used to
// communicate between node-land and browser-land.
@ -106,12 +113,11 @@ function createWindow() {
// Automatically open Chrome's DevTools in development mode.
// if (!app.isPackaged) {
mainWindow.webContents.openDevTools();
mainWindow.webContents.openDevTools();
// }
const menuBuilder = new MenuBuilder(mainWindow);
menuBuilder.buildMenu();
}
// Setup a local proxy to adjust the paths of requested files when loading

View File

@ -7,7 +7,7 @@
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
content="Zecwallet Fullnode"
/>
<meta
@ -30,7 +30,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>Zecwallet Fullnode</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

View File

@ -1,6 +1,6 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"short_name": "ZecwalletFullNode",
"name": "Zecwallet Fullnode",
"icons": [
{
"src": "favicon.ico",

View File

@ -51,13 +51,6 @@ const loadZcashConf = async () => {
rpcConfig.username = confValues.rpcuser;
rpcConfig.password = confValues.rpcpassword;
if (!rpcConfig.username || !rpcConfig.password) {
console.log(
"Your zcash.conf is missing a &quot;rpcuser&quot; or &quot;rpcpassword&quot;"
);
return;
}
const isTestnet = (confValues.testnet && confValues.testnet === "1") || false;
const server = confValues.rpcbind || "127.0.0.1";
const port = confValues.rpcport || (isTestnet ? "18232" : "8232");

View File

@ -23,6 +23,7 @@ import AppState, {
ReceivePageState,
AddressBookEntry,
AddressType,
AddressDetail,
} from "./components/AppState";
import RPC from "./rpc";
import Utils from "./utils/utils";
@ -147,7 +148,7 @@ export default class RouteApp extends React.Component<Props, AppState> {
this.setState({ transactions });
};
setAllAddresses = (addresses: string[]) => {
setAllAddresses = (addresses: AddressDetail[]) => {
this.setState({ addresses });
};
@ -231,6 +232,45 @@ export default class RouteApp extends React.Component<Props, AppState> {
this.setState({ sendPageState: newSendPageState });
};
getSendManyJSON = (): any[] => {
const { sendPageState, info, addresses } = this.state;
const json = [];
if (sendPageState.fromaddr.startsWith("UA")) {
// Find an address with the account number
const fromaddr = addresses.find(
(ad) =>
ad.type === AddressType.unified &&
ad.account === Utils.UAAccountfromString(sendPageState.fromaddr)
);
json.push(fromaddr?.address);
} else {
json.push(sendPageState.fromaddr);
}
json.push(
sendPageState.toaddrs.map((to) => {
const textEncoder = new TextEncoder();
const memo = to.memo
? Utils.bytesToHexString(textEncoder.encode(to.memo))
: "";
if (memo === "") {
return { address: to.to, amount: to.amount };
} else {
return { address: to.to, amount: to.amount, memo };
}
})
);
json.push(1); // minconf = 1
json.push(Utils.getDefaultFee(info.latestBlock)); // Control the default fee as well.
json.push(sendPageState.privacyLevel);
console.log("Sending:");
console.log(json);
return json;
};
setRPCConfig = (rpcConfig?: RPCConfig) => {
if (rpcConfig) {
this.setState({ rpcConfig });
@ -340,7 +380,7 @@ export default class RouteApp extends React.Component<Props, AppState> {
createNewAddress = async (type: AddressType) => {
if (!this.rpc) {
return;
return;
}
// Create a new address
@ -360,10 +400,15 @@ export default class RouteApp extends React.Component<Props, AppState> {
this.setState({ receivePageState: newReceivePageState });
} catch (e) {
this.openErrorModal("Failed to Create Address", <div>
Couldn't create a new address. Please make sure your wallet has been initialized by running<br/>
"zcashd-wallet-tool"
</div>)
this.openErrorModal(
"Failed to Create Address",
<div>
Couldn't create a new address. Please make sure your wallet has been
initialized by running
<br />
"zcashd-wallet-tool"
</div>
);
}
};
@ -405,7 +450,7 @@ export default class RouteApp extends React.Component<Props, AppState> {
/>
<div style={{ overflow: "hidden" }}>
{(info && info.version > 0) && (
{info && info.version > 0 && (
<div className={cstyles.sidebarcontainer}>
<Sidebar
getPrivKeyAsString={this.getPrivKeyAsString}
@ -423,6 +468,7 @@ export default class RouteApp extends React.Component<Props, AppState> {
element={
<Send
addressesWithBalance={addressesWithBalance}
getSendManyJSON={this.getSendManyJSON}
sendTransaction={this.sendTransaction}
sendPageState={sendPageState}
setSendPageState={this.setSendPageState}

View File

@ -179,6 +179,25 @@ export class Info {
}
}
export class AddressDetail {
address: string;
type: AddressType;
account?: number;
diversifier?: number;
constructor(
address: string,
type: AddressType,
account?: number,
diversifier?: number
) {
this.address = address;
this.type = type;
this.account = account;
this.diversifier = diversifier;
}
}
// eslint-disable-next-line max-classes-per-file
export default class AppState {
// The total confirmed and unconfirmed balance in this wallet
@ -191,12 +210,11 @@ export default class AppState {
// A map type that contains address -> privatekey/viewkey mapping, for display on the receive page
// This mapping is ephemeral, and will disappear when the user navigates away.
addressPrivateKeys: Map<string, string>;
addressViewKeys: Map<string, string>;
// List of all addresses in the wallet, including change addresses and addresses
// that don't have any balance or are unused
addresses: string[];
addresses: AddressDetail[];
// List of Address / Label pairs
addressBook: AddressBookEntry[];

View File

@ -198,6 +198,10 @@
background-color: rgba(100, 100, 100, 0.5);
}
.accordionHeader {
word-break: break-all;
}
.accordionHeader:hover {
cursor: pointer;
}

View File

@ -88,7 +88,7 @@ const AddressBalanceItem = ({
<AccordionItem
key={item.address}
className={[cstyles.well, cstyles.margintopsmall].join(" ")}
uuid={item.address}
uuid={item.address.replaceAll(" ", "_")}
>
<AccordionItemHeading>
<AccordionItemButton className={cstyles.accordionHeader}>

View File

@ -17,10 +17,12 @@ import {
ReceivePageState,
AddressBookEntry,
AddressType,
AddressDetail,
} from "./AppState";
import ScrollPane from "./ScrollPane";
type AddressBlockProps = {
addresses: AddressDetail[];
addressBalance: AddressBalance;
label?: string;
currencyName: string;
@ -32,6 +34,7 @@ type AddressBlockProps = {
};
const AddressBlock = ({
addresses,
addressBalance,
label,
currencyName,
@ -89,6 +92,26 @@ const AddressBlock = ({
</div>
)}
{Utils.isUnified(address) && (
<>
<div
className={[cstyles.sublight, cstyles.margintoplarge].join(
" "
)}
>
Details
</div>
<div className={[cstyles.padtopsmall].join(" ")}>
Account Number:{" "}
{addresses.find((ad) => ad.address === address)?.account}
</div>
<div className={[cstyles.padtopsmall].join(" ")}>
Diversifier Index:{" "}
{addresses.find((ad) => ad.address === address)?.diversifier}
</div>
</>
)}
<div
className={[cstyles.sublight, cstyles.margintoplarge].join(" ")}
>
@ -216,7 +239,7 @@ const AddressBlock = ({
};
type Props = {
addresses: string[];
addresses: AddressDetail[];
addressesWithBalance: AddressBalance[];
addressBook: AddressBookEntry[];
info: Info;
@ -253,9 +276,11 @@ export default class Receive extends Component<Props> {
}, new Map<string, number>());
const uaddrs = addresses
.filter((a) => Utils.isUnified(a))
.filter((a) => a.type === AddressType.unified)
.slice(0, 100)
.map((a) => new AddressBalance(a, addressMap.get(a) || 0));
.map(
(a) => new AddressBalance(a.address, addressMap.get(a.address) || 0)
);
let defaultUaddr = uaddrs.length ? uaddrs[0].address : "";
if (receivePageState && Utils.isUnified(receivePageState.newAddress)) {
defaultUaddr = receivePageState.newAddress;
@ -271,9 +296,11 @@ export default class Receive extends Component<Props> {
}
const zaddrs = addresses
.filter((a) => Utils.isSapling(a))
.filter((a) => a.type === AddressType.sapling)
.slice(0, 100)
.map((a) => new AddressBalance(a, addressMap.get(a) || 0));
.map(
(a) => new AddressBalance(a.address, addressMap.get(a.address) || 0)
);
let defaultZaddr = zaddrs.length ? zaddrs[0].address : "";
if (receivePageState && Utils.isSapling(receivePageState.newAddress)) {
@ -290,9 +317,11 @@ export default class Receive extends Component<Props> {
}
const taddrs = addresses
.filter((a) => Utils.isTransparent(a))
.filter((a) => a.type === AddressType.transparent)
.slice(0, 100)
.map((a) => new AddressBalance(a, addressMap.get(a) || 0));
.map(
(a) => new AddressBalance(a.address, addressMap.get(a.address) || 0)
);
let defaultTaddr = taddrs.length ? taddrs[0].address : "";
if (receivePageState && Utils.isTransparent(receivePageState.newAddress)) {
@ -329,6 +358,7 @@ export default class Receive extends Component<Props> {
{uaddrs.map((a) => (
<AddressBlock
key={a.address}
addresses={addresses}
addressBalance={a}
currencyName={info.currencyName}
label={addressBookMap.get(a.address)}
@ -360,6 +390,7 @@ export default class Receive extends Component<Props> {
{zaddrs.map((a) => (
<AddressBlock
key={a.address}
addresses={addresses}
addressBalance={a}
currencyName={info.currencyName}
label={addressBookMap.get(a.address)}
@ -393,6 +424,7 @@ export default class Receive extends Component<Props> {
{taddrs.map((a) => (
<AddressBlock
key={a.address}
addresses={addresses}
addressBalance={a}
currencyName={info.currencyName}
zecPrice={info.zecPrice}

View File

@ -141,7 +141,7 @@ const ToAddrBox = ({
</div>
<input
type="text"
placeholder="Z or T address"
placeholder="U | Z | T address"
className={cstyles.inputbox}
value={toaddr.to}
onChange={(e) => updateToField(toaddr.id, e, null, null)}
@ -214,31 +214,6 @@ const ToAddrBox = ({
);
};
function getSendManyJSON(sendPageState: SendPageState, info: Info): any[] {
const json = [];
json.push(sendPageState.fromaddr);
json.push(
sendPageState.toaddrs.map((to) => {
const textEncoder = new TextEncoder();
const memo = to.memo
? Utils.bytesToHexString(textEncoder.encode(to.memo))
: "";
if (memo === "") {
return { address: to.to, amount: to.amount };
} else {
return { address: to.to, amount: to.amount, memo };
}
})
);
json.push(1); // minconf = 1
json.push(Utils.getDefaultFee(info.latestBlock)); // Control the default fee as well.
json.push(sendPageState.privacyLevel);
console.log("Sending:");
console.log(json);
return json;
}
type ConfirmModalToAddrProps = { toaddr: ToAddr; info: Info };
const ConfirmModalToAddr = ({ toaddr, info }: ConfirmModalToAddrProps) => {
const { bigPart, smallPart } = Utils.splitZecAmountIntoBigSmall(
@ -279,6 +254,7 @@ const ConfirmModalToAddr = ({ toaddr, info }: ConfirmModalToAddrProps) => {
type ConfirmModalProps = {
sendPageState: SendPageState;
info: Info;
getSendManyJSON: () => any[];
sendTransaction: (
sendJson: any[],
fnOpenSendErrorModal: (title: string, msg: string) => void
@ -291,6 +267,7 @@ type ConfirmModalProps = {
const ConfirmModal = ({
sendPageState,
info,
getSendManyJSON,
sendTransaction,
clearToAddrs,
closeModal,
@ -317,7 +294,7 @@ const ConfirmModal = ({
// Then send the Tx async
(async () => {
const sendJson = getSendManyJSON(sendPageState, info);
const sendJson = getSendManyJSON();
let success = false;
try {
@ -441,6 +418,7 @@ type Props = {
openErrorModal: (title: string, body: string) => void;
closeErrorModal: () => void;
info: Info;
getSendManyJSON: () => any[];
};
class SendState {
@ -628,7 +606,13 @@ export default class Send extends PureComponent<Props, SendState> {
errorModalBody,
sendButtonEnabled,
} = this.state;
const { sendPageState, info, openErrorModal, closeErrorModal } = this.props;
const {
sendPageState,
info,
openErrorModal,
closeErrorModal,
getSendManyJSON,
} = this.props;
const customStyles = {
option: (provided: any, state: any) => ({
@ -779,6 +763,7 @@ export default class Send extends PureComponent<Props, SendState> {
<ConfirmModal
sendPageState={sendPageState}
getSendManyJSON={getSendManyJSON}
info={info}
sendTransaction={sendTransaction}
openErrorModal={openErrorModal}

View File

@ -276,7 +276,7 @@ class Sidebar extends PureComponent<Props, State> {
"Zecwallet Fullnode",
<div className={cstyles.verticalflex}>
<div className={cstyles.margintoplarge}>
Zecwallet Fullnode v1.8.0-beta1
Zecwallet Fullnode v1.8.0-beta2
</div>
<div className={cstyles.margintoplarge}>
Built with Electron. Copyright (c) 2018-2021, Aditya Kulkarni.

View File

@ -8,6 +8,7 @@ import {
TxDetail,
Info,
AddressType,
AddressDetail,
} from "./components/AppState";
import Utils from "./utils/utils";
import SentTxStore from "./utils/SentTxStore";
@ -46,7 +47,7 @@ export default class RPC {
fnSetTotalBalance: (totalBalance: TotalBalance) => void;
fnSetAddressesWithBalance: (ab: AddressBalance[]) => void;
fnSetTransactionsList: (txns: Transaction[]) => void;
fnSetAllAddresses: (allAddresses: string[]) => void;
fnSetAllAddresses: (allAddresses: AddressDetail[]) => void;
fnSetZecPrice: (price: number) => void;
fnSetDisconnected: (msg: string) => void;
@ -61,7 +62,7 @@ export default class RPC {
fnSetTotalBalance: (totalBalance: TotalBalance) => void,
fnSetAddressesWithBalance: (ab: AddressBalance[]) => void,
fnSetTransactionsList: (txns: Transaction[]) => void,
fnSetAllAddresses: (allAddresses: string[]) => void,
fnSetAllAddresses: (allAddresses: AddressDetail[]) => void,
fnSetZecPrice: (price: number) => void,
fnSetDisconnected: (msg: string) => void
) {
@ -268,7 +269,7 @@ export default class RPC {
}
async createNewAddress(type: AddressType): Promise<string> {
if (type === AddressType.unified) {
if (type === AddressType.unified) {
// First make sure that at least one account has been created.
const accounts = await RPC.doRPC("z_listaccounts", [], this.rpcConfig);
if (accounts.result.length === 0) {
@ -330,24 +331,22 @@ export default class RPC {
async fetchTandZAddressesWithBalances() {
const zresponse = RPC.doRPC("z_listunspent", [0], this.rpcConfig);
const tresponse = RPC.doRPC("listunspent", [0], this.rpcConfig);
const uresponse = RPC.doRPC("z_getbalanceforaccount", [0], this.rpcConfig);
// Do the Z addresses
// response.result has all the unspent notes.
const unspentNotes = (await zresponse).result;
const zgroups = _.groupBy(unspentNotes, "address");
const zaddresses = Object.keys(zgroups).map((address) => {
const balance = zgroups[address].reduce(
(prev, obj) => prev + obj.amount,
0
);
const zaddresses = Object.keys(zgroups)
.filter((address) => Utils.isSapling(address))
.map((address) => {
const balance = zgroups[address].reduce(
(prev, obj) => prev + obj.amount,
0
);
// Orchard change notes mysteriously don't have an address
if (!address || address === "undefined") {
address = "(change)";
}
return new AddressBalance(address, Number(balance.toFixed(8)));
});
return new AddressBalance(address, Number(balance.toFixed(8)));
});
// Do the T addresses
const unspentTXOs = (await tresponse).result;
@ -360,7 +359,16 @@ export default class RPC {
return new AddressBalance(address, Number(balance.toFixed(8)));
});
const addresses = zaddresses.concat(taddresses);
// Do the U addresses
const ubalances = (await uresponse).result;
const uaddresses = new AddressBalance(
Utils.UAStringfromAccount(0),
ubalances.pools.orchard
? Utils.zatToZec(ubalances.pools.orchard.valueZat)
: 0
);
const addresses = zaddresses.concat(taddresses).concat(uaddresses);
this.fnSetAddressesWithBalance(addresses);
}
@ -389,7 +397,8 @@ export default class RPC {
// Now get Z txns
const zaddresses = (await zaddressesPromise).filter(
(addr) => Utils.isSapling(addr) || Utils.isUnified(addr)
(addr) =>
addr.type === AddressType.sapling || addr.type === AddressType.unified
);
const alltxns = new Array<any>();
@ -399,14 +408,14 @@ export default class RPC {
// For each zaddr, get the list of incoming transactions
const incomingTxns: any = await RPC.doRPC(
"z_listreceivedbyaddress",
[zaddr, 0],
[zaddr.address, 0],
this.rpcConfig
);
const txns = incomingTxns.result
.filter((itx: any) => !itx.change)
.map((incomingTx: any) => {
return {
address: zaddr,
address: zaddr.address,
txid: incomingTx.txid,
memo: parseMemo(incomingTx.memo),
amount: incomingTx.amount,
@ -470,25 +479,35 @@ export default class RPC {
}
// Get all Addresses, including T and Z addresses
async getAllAddresses(): Promise<string[]> {
async getAllAddresses(): Promise<AddressDetail[]> {
const allAddresses = await RPC.doRPC("listaddresses", [], this.rpcConfig);
// This returns multiple objects, each has transparent, sapling and unified addresses
let allT: string[] = [];
let allZ: string[] = [];
let allU: string[] = [];
let allT: AddressDetail[] = [];
let allZ: AddressDetail[] = [];
let allU: AddressDetail[] = [];
allAddresses.result.forEach((addressSet: any) => {
const transparent: any = addressSet["transparent"];
if (transparent) {
const tAddrs: string[] = transparent["addresses"];
const tAddrs = transparent["addresses"];
if (tAddrs) {
allT = allT.concat(tAddrs);
allT = allT.concat(
tAddrs.map(
(taddr: string) =>
new AddressDetail(taddr, AddressType.transparent)
)
);
}
const changeTaddrs: string[] = transparent["changeAddresses"];
const changeTaddrs = transparent["changeAddresses"];
if (changeTaddrs) {
allT = allT.concat(changeTaddrs);
allT = allT.concat(
changeTaddrs.map(
(taddr: string) =>
new AddressDetail(taddr, AddressType.transparent)
)
);
}
}
@ -497,7 +516,11 @@ export default class RPC {
saplingAddrs.forEach((saplingA) => {
const zAddrs: string[] = saplingA["addresses"];
if (zAddrs) {
allZ = allZ.concat(zAddrs);
allZ = allZ.concat(
zAddrs.map(
(zaddr: string) => new AddressDetail(zaddr, AddressType.sapling)
)
);
}
});
}
@ -505,12 +528,21 @@ export default class RPC {
const unifiedAddrs: any[] = addressSet["unified"];
if (unifiedAddrs) {
unifiedAddrs.forEach((uaSet) => {
const account = uaSet["account"];
const uAddrs: any[] = uaSet["addresses"];
if (uAddrs) {
uAddrs.forEach((uaddr) => {
const diversifier = uaddr["diversifier_index"];
const ua = uaddr["address"];
if (ua) {
allU = allU.concat([ua]);
allU = allU.concat([
new AddressDetail(
ua,
AddressType.unified,
account,
diversifier
),
]);
}
});
}

View File

@ -4,6 +4,19 @@
export const NO_CONNECTION: string = "Could not connect to zcashd";
export default class Utils {
static UAStringfromAccount(account: number): string {
return `UA account ${account}`;
}
static UAAccountfromString(account_str: string): number {
const matches = account_str.match(new RegExp("^UA account ([0-9]+)$"));
if (matches && matches.length === 2) {
return parseInt(matches[1]);
} else {
return 0;
}
}
static isUnified(addr: string): boolean {
if (!addr) return false;
return addr.startsWith("u");
@ -39,6 +52,14 @@ export default class Utils {
);
}
static zatToZec(amount: number): number {
if (amount && amount > 0) {
return amount / 10 ** 8;
} else {
return 0;
}
}
// Convert to max 8 decimal places, and remove trailing zeros
static maxPrecision(v: number): string {
return v.toFixed(8);