-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #322 from pendulum-chain/feature/terms-and-conditi…
…ons-refactor Don't show Terms And Conditions modal
- Loading branch information
Showing
8 changed files
with
304 additions
and
113 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,50 +1,65 @@ | ||
import { useState } from 'preact/compat'; | ||
import { Button, Checkbox, Link } from 'react-daisyui'; | ||
import { useLocalStorage } from '../../hooks/useLocalStorage'; | ||
import { Dialog } from '../Dialog'; | ||
import { AnimatePresence, motion } from 'framer-motion'; | ||
import { StateUpdater } from 'preact/hooks'; | ||
import { Checkbox, Link } from 'react-daisyui'; | ||
|
||
export const TermsAndConditions = () => { | ||
const { set, state } = useLocalStorage<string | undefined>({ key: 'TERMS_AND_CONDITIONS' }); | ||
const [checked, setChecked] = useState<boolean>(false); | ||
interface TermsAndConditionsProps { | ||
toggleTermsChecked: () => void; | ||
setTermsError: StateUpdater<boolean>; | ||
termsChecked: boolean; | ||
termsAccepted: boolean; | ||
termsError: boolean; | ||
} | ||
|
||
const acceptTerms = () => { | ||
set('accepted'); | ||
}; | ||
|
||
const content = ( | ||
<> | ||
<div className="mb-5 text-lg"> | ||
<Link | ||
style={{ textDecoration: 'underline' }} | ||
color="accent" | ||
target="_blank" | ||
rel="noreferrer" | ||
href="https://www.vortexfinance.co/terms-conditions" | ||
> | ||
View Terms and Conditions | ||
</Link> | ||
</div> | ||
<div className="flex text-lg"> | ||
<Checkbox checked={checked} onClick={() => setChecked(!checked)} color="primary" size="md" /> | ||
<span className="pl-2">I have read and accept the terms and conditions</span> | ||
</div> | ||
</> | ||
); | ||
const fadeOutAnimation = { | ||
scale: [1, 1.05, 0], | ||
opacity: [1, 1, 0], | ||
transition: { duration: 0.3 }, | ||
}; | ||
|
||
const actions = ( | ||
<Button className="w-full px-12 text-thin" color="primary" onClick={acceptTerms} disabled={!checked}> | ||
Agree | ||
</Button> | ||
); | ||
export const TermsAndConditions = (props: TermsAndConditionsProps) => { | ||
const { termsAccepted } = props; | ||
|
||
return ( | ||
<Dialog | ||
content={content} | ||
headerText="T&Cs" | ||
visible={!state} | ||
actions={actions} | ||
hideCloseButton={true} | ||
disableNativeEvents={true} | ||
/> | ||
); | ||
return <AnimatePresence mode="wait">{!termsAccepted && <TermsAndConditionsContent {...props} />}</AnimatePresence>; | ||
}; | ||
|
||
const TermsAndConditionsContent = ({ | ||
toggleTermsChecked, | ||
setTermsError, | ||
termsChecked, | ||
termsError, | ||
}: TermsAndConditionsProps) => ( | ||
<motion.div key="terms-conditions" exit={fadeOutAnimation}> | ||
<div className="mb-5 text-sm" /> | ||
<div className="flex text-sm"> | ||
<Checkbox | ||
checked={termsChecked} | ||
onClick={() => { | ||
toggleTermsChecked(); | ||
setTermsError(false); | ||
}} | ||
color="primary" | ||
size="sm" | ||
/> | ||
<TermsText error={termsError} /> | ||
</div> | ||
</motion.div> | ||
); | ||
|
||
const TermsText = ({ error }: { error: boolean }) => ( | ||
<motion.span | ||
className={`pl-2 ${error ? 'text-red-600' : ''}`} | ||
animate={{ scale: [1, 1.02, 1], transition: { duration: 0.2 } }} | ||
> | ||
I have read and accept the{' '} | ||
<Link | ||
href="https://www.vortexfinance.co/terms-conditions" | ||
color={error ? 'error' : 'accent'} | ||
className={`transition-all duration-300 ${error ? 'text-red-600 font-bold' : ''}`} | ||
target="_blank" | ||
rel="noreferrer" | ||
style={{ textDecoration: 'underline' }} | ||
> | ||
Terms and Conditions | ||
</Link> | ||
</motion.span> | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { useState } from 'preact/hooks'; | ||
import { useLocalStorage } from './useLocalStorage'; | ||
|
||
export const useTermsAndConditions = () => { | ||
const { set, state } = useLocalStorage<string | undefined>({ key: 'TERMS_AND_CONDITIONS' }); | ||
|
||
// Terms and Conditions are accepted only when user has submitted the swap in `confirmSwap.ts` | ||
const [termsAccepted, setTermsAccepted] = useState<boolean>(state === 'accepted'); | ||
|
||
// termsChecked is used to determine if the Terms and Conditions checkbox is checked and the Swap form can be submitted in `swap/index.tsx` | ||
const [termsChecked, setTermsChecked] = useState<boolean>(false); | ||
|
||
const [termsError, setTermsError] = useState<boolean>(false); | ||
|
||
const toggleTermsChecked = () => { | ||
setTermsChecked((state) => !state); | ||
}; | ||
|
||
return { | ||
termsChecked, | ||
toggleTermsChecked, | ||
termsAccepted, | ||
setTermsAccepted: (accepted: boolean) => { | ||
set(accepted ? 'accepted' : undefined); | ||
setTermsAccepted(accepted); | ||
}, | ||
termsError, | ||
setTermsError, | ||
}; | ||
}; |
25 changes: 25 additions & 0 deletions
25
src/pages/swap/helpers/swapConfirm/calculateSwapAmountsWithMargin.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import Big from 'big.js'; | ||
|
||
import { ContractBalance, multiplyByPowerOfTen } from '../../../../helpers/contracts'; | ||
import { InputTokenDetails, OutputTokenDetails } from '../../../../constants/tokenConfig'; | ||
import { SPACEWALK_REDEEM_SAFETY_MARGIN } from '../../../../constants/constants'; | ||
|
||
export const calculateSwapAmountsWithMargin = ( | ||
fromAmount: Big, | ||
preciseQuotedAmountOut: ContractBalance, | ||
inputToken: InputTokenDetails, | ||
outputToken: OutputTokenDetails, | ||
) => { | ||
// Calculate output amount with margin | ||
const outputAmountBigMargin = preciseQuotedAmountOut.preciseBigDecimal | ||
.round(2, 0) | ||
.mul(1 + SPACEWALK_REDEEM_SAFETY_MARGIN); | ||
const expectedRedeemAmountRaw = multiplyByPowerOfTen(outputAmountBigMargin, outputToken.decimals).toFixed(); | ||
|
||
// Calculate input amount with margin | ||
const inputAmountBig = Big(fromAmount); | ||
const inputAmountBigMargin = inputAmountBig.mul(1 + SPACEWALK_REDEEM_SAFETY_MARGIN); | ||
const inputAmountRaw = multiplyByPowerOfTen(inputAmountBigMargin, inputToken.decimals).toFixed(); | ||
|
||
return { expectedRedeemAmountRaw, inputAmountRaw }; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import { StateUpdater } from 'preact/hooks'; | ||
import { ApiPromise } from '@polkadot/api'; | ||
import Big from 'big.js'; | ||
|
||
import { | ||
getInputTokenDetailsOrDefault, | ||
InputTokenType, | ||
OutputTokenType, | ||
OUTPUT_TOKEN_CONFIG, | ||
} from '../../../../constants/tokenConfig'; | ||
|
||
import { ExecutionInput } from '../../../../hooks/offramp/useMainProcess'; | ||
import { TokenOutData } from '../../../../hooks/nabla/useTokenAmountOut'; | ||
import { Networks } from '../../../../contexts/network'; | ||
|
||
import { calculateSwapAmountsWithMargin } from './calculateSwapAmountsWithMargin'; | ||
import { performSwapInitialChecks } from './performSwapInitialChecks'; | ||
import { validateSwapInputs } from './validateSwapInputs'; | ||
|
||
interface SwapConfirmParams { | ||
address: `0x${string}` | undefined; | ||
api: ApiPromise | null; | ||
from: InputTokenType; | ||
fromAmount: Big | undefined; | ||
fromAmountString: string; | ||
handleOnSubmit: (executionInput: ExecutionInput) => void; | ||
inputAmountIsStable: boolean; | ||
selectedNetwork: Networks; | ||
setInitializeFailed: StateUpdater<boolean>; | ||
setIsInitiating: StateUpdater<boolean>; | ||
setTermsAccepted: (accepted: boolean) => void; | ||
to: OutputTokenType; | ||
tokenOutAmount: { data: TokenOutData | undefined }; | ||
} | ||
|
||
export function swapConfirm(e: Event, params: SwapConfirmParams) { | ||
e.preventDefault(); | ||
|
||
const { | ||
address, | ||
api, | ||
from, | ||
fromAmount, | ||
fromAmountString, | ||
handleOnSubmit, | ||
inputAmountIsStable, | ||
selectedNetwork, | ||
setInitializeFailed, | ||
setIsInitiating, | ||
setTermsAccepted, | ||
to, | ||
tokenOutAmount, | ||
} = params; | ||
|
||
const validInputs = validateSwapInputs(inputAmountIsStable, address, fromAmount, tokenOutAmount.data); | ||
if (!validInputs) { | ||
return; | ||
} | ||
|
||
setIsInitiating(true); | ||
|
||
const outputToken = OUTPUT_TOKEN_CONFIG[to]; | ||
const inputToken = getInputTokenDetailsOrDefault(selectedNetwork, from); | ||
|
||
const { expectedRedeemAmountRaw, inputAmountRaw } = calculateSwapAmountsWithMargin( | ||
validInputs.fromAmount, | ||
validInputs.tokenOutAmountData.preciseQuotedAmountOut, | ||
inputToken, | ||
outputToken, | ||
); | ||
|
||
performSwapInitialChecks(api!, outputToken, inputToken, expectedRedeemAmountRaw, inputAmountRaw, address!) | ||
.then(() => { | ||
console.log('Initial checks completed. Starting process..'); | ||
|
||
// here we should set that the user has accepted the terms and conditions in the local storage | ||
setTermsAccepted(true); | ||
|
||
handleOnSubmit({ | ||
inputTokenType: from, | ||
outputTokenType: to, | ||
amountInUnits: fromAmountString, | ||
offrampAmount: validInputs.tokenOutAmountData.roundedDownQuotedAmountOut, | ||
setInitializeFailed, | ||
}); | ||
}) | ||
.catch((_error) => { | ||
console.error('Error during swap confirmation:', _error); | ||
setIsInitiating(false); | ||
setInitializeFailed(true); | ||
}); | ||
} |
24 changes: 24 additions & 0 deletions
24
src/pages/swap/helpers/swapConfirm/performSwapInitialChecks.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { ApiPromise } from '@polkadot/api'; | ||
|
||
import { InputTokenDetails, OutputTokenDetails } from '../../../../constants/tokenConfig'; | ||
import { getVaultsForCurrency } from '../../../../services/phases/polkadot/spacewalk'; | ||
import { testRoute } from '../../../../services/phases/squidrouter/route'; | ||
|
||
export const performSwapInitialChecks = async ( | ||
api: ApiPromise, | ||
outputToken: OutputTokenDetails, | ||
fromToken: InputTokenDetails, | ||
expectedRedeemAmountRaw: string, | ||
inputAmountRaw: string, | ||
address: `0x${string}`, | ||
) => { | ||
await Promise.all([ | ||
getVaultsForCurrency( | ||
api, | ||
outputToken.stellarAsset.code.hex, | ||
outputToken.stellarAsset.issuer.hex, | ||
expectedRedeemAmountRaw, | ||
), | ||
testRoute(fromToken, inputAmountRaw, address), | ||
]); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { TokenOutData } from '../../../../hooks/nabla/useTokenAmountOut'; | ||
|
||
type ValidSwapInputs = { | ||
inputAmountIsStable: true; | ||
address: string; | ||
fromAmount: Big; | ||
tokenOutAmountData: TokenOutData; | ||
}; | ||
|
||
export const validateSwapInputs = ( | ||
inputAmountIsStable: boolean, | ||
address: string | undefined, | ||
fromAmount: Big | undefined, | ||
tokenOutAmountData: TokenOutData | undefined, | ||
): ValidSwapInputs | false => { | ||
if (!inputAmountIsStable) return false; | ||
if (!address) return false; | ||
if (fromAmount === undefined) { | ||
console.log('Input amount is undefined'); | ||
return false; | ||
} | ||
if (!tokenOutAmountData) { | ||
console.log('Output amount is undefined'); | ||
return false; | ||
} | ||
|
||
return { inputAmountIsStable, address, fromAmount, tokenOutAmountData }; | ||
}; |
Oops, something went wrong.