Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DO NOT MERGE - Iceboxed] Add Iframe and Summary card for the KYC #127

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d345c90
adding iframe flow
gianfra-t Sep 2, 2024
93f3bf6
move signing modal to progress screem, mock responses to test UI
gianfra-t Sep 2, 2024
a2f475b
show failure screen on initial process
gianfra-t Sep 2, 2024
6172629
make mobile iframe larger
gianfra-t Sep 2, 2024
cddc8ca
adjust size for large screen
gianfra-t Sep 2, 2024
171066d
delay mock 15 seconds only
gianfra-t Sep 2, 2024
9d5fc4e
remove second confirmation button
gianfra-t Sep 3, 2024
dc6f6d0
don't store mocked data in sheet
gianfra-t Sep 3, 2024
bda9638
first card iteration
gianfra-t Sep 5, 2024
f253afc
adjusting summary card
gianfra-t Sep 9, 2024
52d2749
refinements to card design, element props
gianfra-t Sep 9, 2024
bc019e6
fix iframe height to 50%
gianfra-t Sep 9, 2024
1ef6395
fix missing eur icon
gianfra-t Sep 9, 2024
24afd1e
fix asset text for large screens
gianfra-t Sep 9, 2024
0080740
adressing review improvements
gianfra-t Sep 20, 2024
942a397
breaking summary card into inner cards
gianfra-t Sep 20, 2024
3d52961
solving merge conflicts
gianfra-t Sep 20, 2024
9647226
readd iframe to swap page
gianfra-t Sep 20, 2024
e7635cd
add remarked section to summary card:
gianfra-t Sep 20, 2024
4ced67a
fixing lower summary card issue
gianfra-t Sep 20, 2024
88e5887
Adjust styling of summary cards
ebma Sep 23, 2024
fc92db8
Add gas icon
ebma Sep 23, 2024
8b08240
Commit to form values to avoid fluctuations of the exchange rates whe…
ebma Sep 23, 2024
37304c5
remove mocked values
gianfra-t Sep 23, 2024
f78ae4c
remove mock sep 24 second response
gianfra-t Sep 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ types/
coverage/
gql/
signer-service
mooncontracts/

*.d.ts
*.config.*
Expand Down
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ preact/
internals/
docs/
.lighthouseci/
mooncontracts/

*.yml

package-lock.json
package.json
yarn.lock
.eslintignore
.prettierignore

CHANGELOG.md
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,9 @@ matter what URL the browser requests.
## Env Variables

- `VITE_SIGNING_SERVICE_URL`: Optional variable to point to a specific signing backend service URL. If undefined, it
will
default to either:
- http://localhost:3000 (if in development mode)
- https://prototype-signer-service-polygon.pendulumchain.tech (if in production mode)
will default to either:
- http://localhost:3000 (if in development mode)
- https://prototype-signer-service-polygon.pendulumchain.tech (if in production mode)
- `VITE_ALCHEMY_API_KEY`: Optional variable to set the Alchemy API key for the custom RPC provider. If undefined, it
will use dhe default endpoint.

Expand Down
46 changes: 46 additions & 0 deletions src/components/Iframe/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from 'preact/compat';
import { BaseLayout } from '../../layouts';
import { SummaryCard, SummaryCardProps } from '../../components/SummaryCard';

export interface IframeProps extends SummaryCardProps {
src: string;
title: string;
subtitle: string;
}

export const IframeComponent: React.FC<IframeProps> = ({
src, title, subtitle, assetIn, assetOut, fromAmount, toAmount
}) => {

const main = (
<div className="flex flex-col justify-center items-center p-4 w-full">
<div className="md:w-[535px] w-full">
<div className="text-center">
<h1 className="text-2xl font-bold text-blue-700 mb-4 mt-4">{title}</h1>
</div>
<div className="flex justify-center items-center w-full">
<SummaryCard {...{
assetIn,
assetOut,
fromAmount,
toAmount,
}} />
</div>
<div className="text-center">
<h2 className="text-md mb-4 mt-4">{subtitle}</h2>
</div>
</div>
<div className="flex justify-center items-center relative w-full h-[50vh] md:w-[535px] ">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember that we discussed once that we want to reduce double scroll and the that the design solution was to prevent scrolling of the outer screen by making the iframe always fit into the screen (i.e., scrolling only happens within the iframe).

We then discussed the other option: make the iframe large enough that it will not scroll and only the outer screen is scrolling.

I actually remember that we decided to use that latter option, am I wrong (I don't find notes about this).

Same for the width of the iframe: it feels right now like looking through a peephole and I don't think that this is such a good UX.

<iframe
Sharqiewicz marked this conversation as resolved.
Show resolved Hide resolved
title="Anchor KYC"
src={src}
style={{ border: 'border-0' }}
className="absolute top-0 left-0 w-full h-full"
allowFullScreen
/>
</div>
</div>
);
return <BaseLayout main={main} />;
};

34 changes: 34 additions & 0 deletions src/components/SummaryCard/LowerSummaryCard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
interface LowerSummaryCardProps {
label: string;
amount: string;
startIcon?: string | JSX.Element;
endIcon?: string | JSX.Element;
symbol?: string;
remarked?: boolean;
}

export const LowerSummaryCard = ({ label, amount, startIcon, endIcon, symbol, remarked }: LowerSummaryCardProps) => (
<div
className={`px-3 py-1 h-auto flex flex-col justify-between space-y-0 rounded ${
remarked ? 'bg-blue-100 bg-opacity-50' : ''
}`}
>
<div className="place-self-start">
<span className="text-xs md:text-sm font-thin">{label}</span>
</div>
<div className="flex items-center w-full">
<div className="pr-1 p-0">
{startIcon && typeof startIcon === 'string' ? (
<img src={startIcon} className="w-3 md:w-5 h-3 md:h-5" />
) : (
startIcon
)}
</div>
<span className="text-xs md:text-lg mr-1">{amount}</span>
<div className="flex items-center">
{endIcon && typeof endIcon === 'string' ? <img src={endIcon} className="w-3 md:w-5 h-3 md:h-5" /> : endIcon}
{symbol && <span className="ml-1 text-xs md:text-lg">{symbol}</span>}
</div>
</div>
</div>
);
21 changes: 21 additions & 0 deletions src/components/SummaryCard/UpperSummaryCard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
interface UpperSummaryCardProps {
label: string;
amount: string;
icon: string;
symbol: string;
}

export const UpperSummaryCard = ({ label, amount, icon, symbol }: UpperSummaryCardProps) => (
<div className="border rounded px-3 py-1 h-auto flex flex-col justify-between space-y-0">
<div className="place-self-start">
<span className="text-xs md:text-sm font-thin">{label}</span>
</div>
<div className="flex justify-between items-center w-full">
<span className="text-md md:text-xl font-medium text-blue-800">{amount}</span>
<div className="flex items-center mr-2">
<img src={icon} className="w-4 md:w-6 h-4 md:h-6" />
<span className="ml-1 text-blue-800">{symbol}</span>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use everywhere text-blue-700

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to avoid such confusion I think we should make a class like

vortex-text-blue {
@apply .text-blue-700
}

</div>
</div>
</div>
);
69 changes: 69 additions & 0 deletions src/components/SummaryCard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { FC } from 'preact/compat';
import { useGetIcon } from '../../hooks/useGetIcon';
import { InputTokenType, OutputTokenType, INPUT_TOKEN_CONFIG, OUTPUT_TOKEN_CONFIG } from '../../constants/tokenConfig';
import Big from 'big.js';
import { calculateTotalReceive } from '../FeeCollapse';
import { roundDownToSignificantDecimals } from '../../helpers/parseNumbers';
import { UpperSummaryCard } from './UpperSummaryCard';
import { LowerSummaryCard } from './LowerSummaryCard';
import LocalGasStationIcon from '@mui/icons-material/LocalGasStation';
export interface SummaryCardProps {
assetIn: InputTokenType;
assetOut: OutputTokenType;
fromAmount: Big;
toAmount: Big;
}

export const SummaryCard: FC<SummaryCardProps> = ({ assetIn, assetOut, fromAmount, toAmount }) => {
const assetInSymbol = INPUT_TOKEN_CONFIG[assetIn].assetSymbol;
const assetOutSymbol = OUTPUT_TOKEN_CONFIG[assetOut].fiat.symbol;

const assetInIcon = useGetIcon(INPUT_TOKEN_CONFIG[assetIn].polygonAssetIcon);
const assetOutIcon = useGetIcon(OUTPUT_TOKEN_CONFIG[assetOut].fiat.assetIcon);

const receiveAmount = calculateTotalReceive(toAmount.toString(), OUTPUT_TOKEN_CONFIG[assetOut]);

const approximateExchangeRate = roundDownToSignificantDecimals(toAmount.div(fromAmount), 2).toString();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be better to also use 4 decimals as on the details box on the main page? E.g., reuse the ExchangeRate component.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use toFixed(2, 0) instead of toString() for every number stringification of amounts in this component. (see #171)

const offrampFees = roundDownToSignificantDecimals(toAmount.sub(receiveAmount), 2).toString();

return (
<div className="bg-white card p-4 rounded-lg border w-full shadow">
<div className="grid grid-cols-2 gap-4 mb-2">
<UpperSummaryCard
label="You withdraw"
amount={roundDownToSignificantDecimals(fromAmount, 2).toString()}
icon={assetInIcon}
symbol={assetInSymbol}
/>
<UpperSummaryCard label="You receive" amount={receiveAmount} icon={assetOutIcon} symbol={assetOutSymbol} />
</div>

<div className="grid grid-cols-10 gap-4">
<div className="col-span-3">
<LowerSummaryCard
label="Your quote"
amount={roundDownToSignificantDecimals(toAmount, 2).toString()}
endIcon={assetOutIcon}
symbol={assetOutSymbol}
remarked={true}
/>
</div>
<div className="col-span-4">
<LowerSummaryCard
label="Exchange rate"
amount={`1 ${assetInSymbol} ≈ ${approximateExchangeRate} ${assetOutSymbol}`}
/>
</div>
<div className="col-span-3">
<LowerSummaryCard
label="Offramp fees"
amount={offrampFees}
startIcon={<LocalGasStationIcon className="text-blue-700" fontSize="small" />}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I argue that we should remove the gas station icon: #102 (comment)

endIcon={assetOutIcon}
symbol={assetOutSymbol}
/>
</div>
</div>
</div>
);
};
2 changes: 2 additions & 0 deletions src/pages/progress/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { ExclamationCircleIcon } from '@heroicons/react/20/solid';
import { OfframpingPhase, OfframpingState } from '../../services/offrampingFlow';
import { Box } from '../../components/Box';
import { BaseLayout } from '../../layouts';
import { SigningBox } from '../../components/SigningBox';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two imports are not used.

import { SigningPhase } from '../../hooks/useMainProcess';

const OFFRAMPING_PHASE_MESSAGES: Record<OfframpingPhase, string> = {
prepareTransactions: 'Preparing transactions',
Expand Down
60 changes: 43 additions & 17 deletions src/pages/swap/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { SuccessPage } from '../success';
import { FailurePage } from '../failure';
import { useInputTokenBalance } from '../../hooks/useInputTokenBalance';
import { UserBalance } from '../../components/UserBalance';
import { IframeComponent, IframeProps } from '../../components/Iframe';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IframeProps import not used.


const Arrow = () => (
<div className="flex justify-center w-full my-5">
Expand All @@ -37,6 +38,17 @@ export const SwapPage = () => {
const formRef = useRef<HTMLDivElement | null>(null);
const [api, setApi] = useState<ApiPromise | null>(null);

// This is used to store the values that were submitted to the form. We need to store them because the form values
// might change due to price fluctuations, but we need to keep the original values to submit the transaction.
// This is important for the summary shown above the iframe.
const [committedFormValues, setCommittedFormValues] = useState<
| {
fromAmount: Big;
toAmount: Big;
}
| undefined
>(undefined);

const { isDisconnected } = useAccount();

useEffect(() => {
Expand Down Expand Up @@ -110,6 +122,12 @@ export const SwapPage = () => {

console.log('starting ....');

// Commit the amounts so that we can show them in the summary above the iframe
setCommittedFormValues({
fromAmount: new Big(fromAmountString),
toAmount: new Big(minimumOutputAmount.preciseString),
});

handleOnSubmit({
inputTokenType: from as InputTokenType,
outputTokenType: to as OutputTokenType,
Expand Down Expand Up @@ -234,6 +252,26 @@ export const SwapPage = () => {
if (offrampingState !== undefined || offrampingStarted) {
const showMainScreenAnyway =
offrampingState === undefined || ['prepareTransactions', 'squidRouter'].includes(offrampingState.phase);

if (
sep24Url &&
committedFormValues &&
showMainScreenAnyway &&
fromAmount &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does fromAmount and tokenOutData.data?.amountOut.preciseBigDecimal actually matter here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before we changed to using commitedFormValues, it did. We can remove them now.

tokenOutData.data?.amountOut.preciseBigDecimal
) {
return (
<IframeComponent
src={sep24Url}
title="Verify Your Identity"
subtitle="Please follow the steps below to complete the identity verification."
assetIn={from}
assetOut={to}
fromAmount={committedFormValues.fromAmount}
toAmount={committedFormValues.toAmount}
/>
);
}
if (!showMainScreenAnyway) {
return <ProgressPage offrampingState={offrampingState} />;
}
Expand Down Expand Up @@ -268,23 +306,11 @@ export const SwapPage = () => {
<section className="flex items-center justify-center w-full mt-5">
<BenefitsList amount={fromAmount} currency={from} />
</section>
{sep24Url !== undefined ? (
<a
href={sep24Url}
target="_blank"
rel="noreferrer"
className="w-full mt-5 text-white bg-blue-700 btn rounded-xl"
onClick={resetSep24Url}
>
Enter details
</a>
) : (
<SwapSubmitButton
text={isInitiating ? 'Confirming' : offrampingStarted ? 'Processing Details' : 'Confirm'}
disabled={Boolean(getCurrentErrorMessage()) || !inputAmountIsStable}
pending={isInitiating || offrampingStarted || offrampingState !== undefined}
/>
)}
<SwapSubmitButton
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a breaking change here. The button text has been changed in the mean time, can you please change accordingly?

(also the logic for the pending state)

text={offrampingStarted ? 'Offramping in Progress' : 'Start Offramping'}
disabled={Boolean(getCurrentErrorMessage()) || !inputAmountIsStable}
pending={offrampingStarted || offrampingState !== undefined}
/>
</form>
</main>
);
Expand Down
10 changes: 6 additions & 4 deletions src/services/anchor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,9 @@ export async function sep12First(sessionParams: IAnchorSessionParams): Promise<v
}*/

export async function sep24First(sessionParams: IAnchorSessionParams): Promise<ISep24Intermediate> {
if (config.test.mockSep24) {
return { url: 'https://www.example.com', id: '1234' };
}
// if (config.test.mockSep24) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove, not comment out.

// return { url: 'https://www.example.com', id: '1234' };
// }

const { token, tomlValues } = sessionParams;
const { sep24Url } = tomlValues;
Expand Down Expand Up @@ -227,11 +227,13 @@ export async function sep24Second(
const { sep24Url } = tomlValues;

if (config.test.mockSep24) {
// sleep 15 seconds, mock user completion of KYC
await new Promise((resolve) => setTimeout(resolve, 150000));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually 150 seconds, not 15 seconds.

return {
amount: sessionParams.offrampAmount,
memo: 'MYK1722323689',
memoType: 'text',
offrampingAccount: (await fetchSigningServiceAccountId()).stellar.public,
offrampingAccount: 'GSAPDFASJFPASFOKNASOFKNAS',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change? The previous setting is better in my opinion as it sends the funds back to the funding account I use locally.

};
}

Expand Down
2 changes: 1 addition & 1 deletion src/services/offrampingFlow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export interface ExecutionContext {
setSigningPhase: (n: SigningPhase) => void;
}

const OFFRAMPING_STATE_LOCAL_STORAGE_KEY = 'offrampingState';
export const OFFRAMPING_STATE_LOCAL_STORAGE_KEY = 'offrampingState';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this exported now? It is not used outside this file.


export interface InitiateStateArguments {
sep24Id: string;
Expand Down
Loading