| 
 | 1 | +//  Copyright 2022. The Tari Project  | 
 | 2 | +//  | 
 | 3 | +//  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the  | 
 | 4 | +//  following conditions are met:  | 
 | 5 | +//  | 
 | 6 | +//  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following  | 
 | 7 | +//  disclaimer.  | 
 | 8 | +//  | 
 | 9 | +//  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the  | 
 | 10 | +//  following disclaimer in the documentation and/or other materials provided with the distribution.  | 
 | 11 | +//  | 
 | 12 | +//  3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote  | 
 | 13 | +//  products derived from this software without specific prior written permission.  | 
 | 14 | +//  | 
 | 15 | +//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,  | 
 | 16 | +//  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  | 
 | 17 | +//  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,  | 
 | 18 | +//  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR  | 
 | 19 | +//  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  | 
 | 20 | +//  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE  | 
 | 21 | +//  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  | 
 | 22 | + | 
 | 23 | +import { useState } from "react";  | 
 | 24 | +import { Form } from "react-router-dom";  | 
 | 25 | +import Button from "@mui/material/Button";  | 
 | 26 | +import CheckBox from "@mui/material/Checkbox";  | 
 | 27 | +import TextField from "@mui/material/TextField";  | 
 | 28 | +import Dialog from "@mui/material/Dialog";  | 
 | 29 | +import DialogContent from "@mui/material/DialogContent";  | 
 | 30 | +import DialogTitle from "@mui/material/DialogTitle";  | 
 | 31 | +import FormControlLabel from "@mui/material/FormControlLabel";  | 
 | 32 | +import Box from "@mui/material/Box";  | 
 | 33 | +import { useAccountsList, useAccountsTransfer } from "../../../api/hooks/useAccounts";  | 
 | 34 | +import { useTheme } from "@mui/material/styles";  | 
 | 35 | +import useAccountStore from "../../../store/accountStore";  | 
 | 36 | +import { FormControl, InputLabel, MenuItem, Select, SelectChangeEvent } from "@mui/material";  | 
 | 37 | +import { claimFees } from "../../../utils/json_rpc";  | 
 | 38 | +import { useKeysList } from "../../../api/hooks/useKeys";  | 
 | 39 | + | 
 | 40 | +export default function ClaimFees() {  | 
 | 41 | +  const [open, setOpen] = useState(false);  | 
 | 42 | +  const [disabled, setDisabled] = useState(false);  | 
 | 43 | +  const [estimatedFee, setEstimatedFee] = useState(0);  | 
 | 44 | +  const [claimFeesFormState, setClaimFeesFormState] = useState({  | 
 | 45 | +    account: "",  | 
 | 46 | +    fee: "",  | 
 | 47 | +    validatorNodePublicKey: "",  | 
 | 48 | +    epoch: "",  | 
 | 49 | +    key: "",  | 
 | 50 | +    newAccount: false,  | 
 | 51 | +    disabled: false,  | 
 | 52 | +  });  | 
 | 53 | + | 
 | 54 | + | 
 | 55 | +  const { data: dataAccountsList } = useAccountsList(0, 10);  | 
 | 56 | +  const { data: dataKeysList } = useKeysList();  | 
 | 57 | +  const { setPopup } = useAccountStore();  | 
 | 58 | + | 
 | 59 | +  const theme = useTheme();  | 
 | 60 | + | 
 | 61 | +  const isFormFilled = () => {  | 
 | 62 | +    if (claimFeesFormState.validatorNodePublicKey.length !== 64) {  | 
 | 63 | +      return false;  | 
 | 64 | +    }  | 
 | 65 | +    return claimFeesFormState.account !== "" && claimFeesFormState.validatorNodePublicKey !== "" && claimFeesFormState.epoch !== "";  | 
 | 66 | +  };  | 
 | 67 | + | 
 | 68 | +  const is_filled = isFormFilled();  | 
 | 69 | + | 
 | 70 | +  const onClaimFeesKeyChange = (e: SelectChangeEvent<string>) => {  | 
 | 71 | +    let key = e.target.value;  | 
 | 72 | +    let key_index = dataKeysList.keys.indexOf(key);  | 
 | 73 | +    let account = claimFeesFormState.account;  | 
 | 74 | +    let selected_account = dataAccountsList.accounts.find((account: any) => account.account.key_index === key_index);  | 
 | 75 | +    let new_account_name;  | 
 | 76 | +    if (selected_account !== undefined) {  | 
 | 77 | +      account = selected_account.account.name;  | 
 | 78 | +      new_account_name = false;  | 
 | 79 | +    } else {  | 
 | 80 | +      if (claimFeesFormState.newAccount === false) {  | 
 | 81 | +        account = "";  | 
 | 82 | +      }  | 
 | 83 | +      new_account_name = true;  | 
 | 84 | +    }  | 
 | 85 | +    setClaimFeesFormState({  | 
 | 86 | +      ...claimFeesFormState,  | 
 | 87 | +      "key": key,  | 
 | 88 | +      "account": account,  | 
 | 89 | +      "newAccount": new_account_name,  | 
 | 90 | +    });  | 
 | 91 | +  };  | 
 | 92 | + | 
 | 93 | +  const onEpochChange = (e: React.ChangeEvent<HTMLInputElement>) => {  | 
 | 94 | +    if (/^[0-9]*$/.test(e.target.value)) {  | 
 | 95 | +      setClaimFeesFormState({  | 
 | 96 | +        ...claimFeesFormState,  | 
 | 97 | +        [e.target.name]: e.target.value,  | 
 | 98 | +      });  | 
 | 99 | +    }  | 
 | 100 | +  }  | 
 | 101 | + | 
 | 102 | +  const onClaimBurnAccountNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {  | 
 | 103 | +    setClaimFeesFormState({  | 
 | 104 | +      ...claimFeesFormState,  | 
 | 105 | +      [e.target.name]: e.target.value,  | 
 | 106 | +    });  | 
 | 107 | +  }  | 
 | 108 | + | 
 | 109 | +  const onPublicKeyChange = (e: React.ChangeEvent<HTMLInputElement>) => {  | 
 | 110 | +    if (/^[0-9a-fA-F]*$/.test(e.target.value)) {  | 
 | 111 | +      setClaimFeesFormState({  | 
 | 112 | +        ...claimFeesFormState,  | 
 | 113 | +        [e.target.name]: e.target.value,  | 
 | 114 | +      });  | 
 | 115 | +    }  | 
 | 116 | +    setEstimatedFee(0);  | 
 | 117 | +  };  | 
 | 118 | + | 
 | 119 | +  const onClaim = async () => {  | 
 | 120 | +    if (claimFeesFormState.account) {  | 
 | 121 | +      setDisabled(true);  | 
 | 122 | +      claimFees(claimFeesFormState.account, 3000, claimFeesFormState.validatorNodePublicKey, parseInt(claimFeesFormState.epoch)).then((resp) => {  | 
 | 123 | +        console.log(resp);  | 
 | 124 | +      }).catch((e) => {  | 
 | 125 | +        console.error(e);  | 
 | 126 | +      }).finally(() => {  | 
 | 127 | +        setDisabled(false);  | 
 | 128 | +      });  | 
 | 129 | +    };  | 
 | 130 | +  }  | 
 | 131 | + | 
 | 132 | +  const handleClickOpen = () => {  | 
 | 133 | +    setOpen(true);  | 
 | 134 | +  };  | 
 | 135 | + | 
 | 136 | +  const handleClose = () => {  | 
 | 137 | +    setOpen(false);  | 
 | 138 | +  };  | 
 | 139 | + | 
 | 140 | +  const formattedKey = (key: any[]) => {  | 
 | 141 | +    let account = dataAccountsList.accounts.find((account: any) => account.account.key_index === +key[0])  | 
 | 142 | +    if (account === undefined) {  | 
 | 143 | +      return null  | 
 | 144 | +      return <div><b>{key[0]}</b> {key[1]}</div>  | 
 | 145 | +    }  | 
 | 146 | +    return <div><b>{key[0]}</b> {key[1]}<br></br>Account <i>{account.account.name}</i></div>  | 
 | 147 | +  }  | 
 | 148 | + | 
 | 149 | +  return (  | 
 | 150 | +    <div>  | 
 | 151 | +      <Button variant="outlined" onClick={handleClickOpen}>  | 
 | 152 | +        Claim Fees  | 
 | 153 | +      </Button>  | 
 | 154 | +      <Dialog open={open} onClose={handleClose}>  | 
 | 155 | +        <DialogTitle>Claim Fees</DialogTitle>  | 
 | 156 | +        <DialogContent className="dialog-content">  | 
 | 157 | +          <Form onSubmit={onClaim} className="flex-container-vertical" style={{ paddingTop: theme.spacing(1) }}>  | 
 | 158 | +            <FormControl>  | 
 | 159 | +              <InputLabel id="key">Key</InputLabel>  | 
 | 160 | +              <Select  | 
 | 161 | +                labelId="key"  | 
 | 162 | +                name="key"  | 
 | 163 | +                label="Key"  | 
 | 164 | +                value={claimFeesFormState.key}  | 
 | 165 | +                onChange={onClaimFeesKeyChange}  | 
 | 166 | +                style={{ flexGrow: 1, minWidth: "200px" }}  | 
 | 167 | +                disabled={claimFeesFormState.disabled}  | 
 | 168 | +              >  | 
 | 169 | +                {dataKeysList?.keys?.map((key: any) => (  | 
 | 170 | +                  <MenuItem key={key} value={key}>  | 
 | 171 | +                    {formattedKey(key)}  | 
 | 172 | +                  </MenuItem>  | 
 | 173 | +                ))}  | 
 | 174 | +              </Select>  | 
 | 175 | +            </FormControl>  | 
 | 176 | +            <TextField name="account"  | 
 | 177 | +              label="Account Name"  | 
 | 178 | +              value={claimFeesFormState.account}  | 
 | 179 | +              onChange={onClaimBurnAccountNameChange}  | 
 | 180 | +              style={{ flexGrow: 1 }}  | 
 | 181 | +              disabled={claimFeesFormState.disabled || !claimFeesFormState.newAccount}>  | 
 | 182 | +            </TextField>  | 
 | 183 | +            <TextField  | 
 | 184 | +              name="fee"  | 
 | 185 | +              label="Fee"  | 
 | 186 | +              value={estimatedFee || "Press fee estimate to calculate"}  | 
 | 187 | +              style={{ flexGrow: 1 }}  | 
 | 188 | +              InputProps={{ readOnly: true }}  | 
 | 189 | +              disabled={disabled}  | 
 | 190 | +            />  | 
 | 191 | +            <TextField  | 
 | 192 | +              name="validatorNodePublicKey"  | 
 | 193 | +              label="Validator Node Public Key"  | 
 | 194 | +              value={claimFeesFormState.validatorNodePublicKey}  | 
 | 195 | +              onChange={onPublicKeyChange}  | 
 | 196 | +              style={{ flexGrow: 1 }}  | 
 | 197 | +              disabled={disabled}  | 
 | 198 | +            />  | 
 | 199 | +            <TextField  | 
 | 200 | +              name="epoch"  | 
 | 201 | +              label="Epoch"  | 
 | 202 | +              value={claimFeesFormState.epoch}  | 
 | 203 | +              style={{ flexGrow: 1 }}  | 
 | 204 | +              onChange={onEpochChange}  | 
 | 205 | +              disabled={disabled}  | 
 | 206 | +            />  | 
 | 207 | +            <Box  | 
 | 208 | +              className="flex-container"  | 
 | 209 | +              style={{  | 
 | 210 | +                justifyContent: "flex-end",  | 
 | 211 | +              }}  | 
 | 212 | +            >  | 
 | 213 | +              <Button variant="outlined" onClick={handleClose} disabled={disabled}>  | 
 | 214 | +                Cancel  | 
 | 215 | +              </Button>  | 
 | 216 | +              <Button variant="contained" type="submit" disabled={disabled || !is_filled}>  | 
 | 217 | +                {estimatedFee ? "Claim" : "Estimate fee"}  | 
 | 218 | +              </Button>  | 
 | 219 | +            </Box>  | 
 | 220 | +          </Form>  | 
 | 221 | +        </DialogContent>  | 
 | 222 | +      </Dialog>  | 
 | 223 | +    </div>  | 
 | 224 | +  );  | 
 | 225 | +}  | 
0 commit comments