diff --git a/src/core/calculateAmounts.ts b/src/core/calculateAmounts.ts index 93f4468..aaa9f75 100644 --- a/src/core/calculateAmounts.ts +++ b/src/core/calculateAmounts.ts @@ -6,11 +6,15 @@ import { } from "../typing"; // Used to calculate WEI amounts +// Allow for up to 2 decimal places (percentage) to avoid loss of precision export const calculatePercentageInt = ( percentage: number, amount: bigint, ): bigint => { - return (amount * BigInt(Math.round(percentage * 1e18))) / BigInt(1e20); + const scaleFactor = 1e2; + const scaledPercentage = BigInt(Math.round(percentage * scaleFactor)); + const result = (amount * scaledPercentage) / (BigInt(scaleFactor) * 100n); + return result; }; // Used to calculate percentages (of percentages) diff --git a/src/helpers/index.ts b/src/helpers/index.ts index f363b13..8632175 100644 --- a/src/helpers/index.ts +++ b/src/helpers/index.ts @@ -4,6 +4,7 @@ export { displayChallengePeriod } from "./displayChallengePeriod"; export { formatAmountToUSD } from "./formatAmountToUSD"; export { percentageToBips } from "./percentageToBips"; export { bipsToPercentage } from "./bipsToPercentage"; +export { roundPercentage } from "./roundPercentage"; export { isSameAddress } from "./isSameAddress"; export { toDate } from "./toDate"; export { getExchangeRates } from "./getExchangeRates"; diff --git a/src/helpers/roundPercentage.ts b/src/helpers/roundPercentage.ts new file mode 100644 index 0000000..2008fd6 --- /dev/null +++ b/src/helpers/roundPercentage.ts @@ -0,0 +1,18 @@ +/** + * Round a percentage to max 2 decimals. + * If amount > 100, returns 100 + * If amount < 0, returns 0 + */ +export const roundPercentage = (amount: number) => { + if (amount > 100) { + return 100; + } + + if (amount < 0) { + return 0; + } + + amount = Math.round(Number(amount) * 100) / 100; + + return amount; +}; diff --git a/src/ui/internal/modals/AddApproveArbitrator.tsx b/src/ui/internal/modals/AddApproveArbitrator.tsx index aa24950..ef6f33d 100644 --- a/src/ui/internal/modals/AddApproveArbitrator.tsx +++ b/src/ui/internal/modals/AddApproveArbitrator.tsx @@ -17,7 +17,7 @@ import { } from "../../../typing"; import { useModalStates } from "../hooks/useModalStates"; import { ScopedModal } from "../components"; -import { BUYER, SELLER } from "../../../helpers"; +import { BUYER, SELLER, roundPercentage } from "../../../helpers"; import { ModalAction } from "../components/Modal"; import { useModalCloseHandler } from "../hooks/useModalCloseHandler"; import { useEscrowData } from "ui/internal/hooks/useEscrowData"; @@ -189,6 +189,12 @@ export const AddApproveArbitrator = ({ } }; + const handleInputChange = (event: React.ChangeEvent) => { + event.target.value = String(roundPercentage(Number(event.target.value))); + setArbitratorFee(event.target.value); + setFocus("arbitratorFee"); + }; + const ModalBody = () => { if (!escrowData) return null; @@ -214,10 +220,7 @@ export const AddApproveArbitrator = ({ name="arbitratorFee" id="arbitratorFee" label="Fee" - onChange={(event) => { - setArbitratorFee(event.target.value); - setFocus("arbitratorFee"); - }} + onChange={handleInputChange} value={arbitratorFee} min="0" max="100" diff --git a/src/ui/internal/modals/Arbitrate.tsx b/src/ui/internal/modals/Arbitrate.tsx index bc345e8..1028af5 100644 --- a/src/ui/internal/modals/Arbitrate.tsx +++ b/src/ui/internal/modals/Arbitrate.tsx @@ -1,4 +1,4 @@ -import React, { ChangeEvent } from "react"; +import React from "react"; import { InputText, Button, @@ -6,6 +6,7 @@ import { FormattedPercentageAmountAdornment, ScopedModal, } from "../../../ui/internal/components"; +import { roundPercentage } from "../../../helpers/roundPercentage"; import { arbitrate } from "../../../core"; import { toast } from "../notification/toast"; import { IArbitrateModalProps } from "../../../typing"; @@ -99,16 +100,20 @@ export const Arbitrate = ({ }); }; - const handleInputChange = (event: ChangeEvent) => { + const handleInputChange = ( + event: React.ChangeEvent, + ) => { + event.target.value = String(roundPercentage(Number(event.target.value))); + if (event.target.name === "seller") { setSellerValue(event.target.value); setBuyerValue(String(100 - Number(event.target.value))); - setFocus("seller"); } else { setSellerValue(String(100 - Number(event.target.value))); setBuyerValue(event.target.value); - setFocus("buyer"); } + + setFocus(event.target.name); }; const ModalBody = () => { diff --git a/src/ui/internal/modals/SettlementOffer.tsx b/src/ui/internal/modals/SettlementOffer.tsx index a127efe..2f54e03 100644 --- a/src/ui/internal/modals/SettlementOffer.tsx +++ b/src/ui/internal/modals/SettlementOffer.tsx @@ -11,7 +11,7 @@ import { Button } from "../../../ui/internal/components/Button"; import styled from "styled-components"; import { offerSettlement } from "../../../core/offerSettlement"; import { toast } from "../notification/toast"; -import { SELLER, BUYER } from "../../../helpers"; +import { SELLER, BUYER, roundPercentage } from "../../../helpers"; import { FormattedPercentageAmountAdornment } from "../../../ui/internal/components/FormattedPercentageAmountAdornment"; import { renderModal } from "../config/render"; import { ApproveSettlementModal } from "./ApproveSettlement"; @@ -133,9 +133,11 @@ export function SettlementOfferModal({ return ["Buyer should get back", "You should receive"]; }, [escrowData]); - const handleChange = ( + const handleInputChange = ( event: React.ChangeEvent, ) => { + event.target.value = String(roundPercentage(Number(event.target.value))); + if (event.target.name === "seller") { setSellerValue(event.target.value); setBuyerValue(String(100 - Number(event.target.value))); @@ -269,7 +271,7 @@ export function SettlementOfferModal({ key="buyer" label={labelBuyer} placeholder="0" - onChange={handleChange} + onChange={handleInputChange} value={buyerValue} min="0" max="100" @@ -302,7 +304,7 @@ export function SettlementOfferModal({ min="0" max="100" type="number" - onChange={handleChange} + onChange={handleInputChange} adornmentStart={{ content: %, }}