Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
29 changes: 29 additions & 0 deletions demo/examples/src/appkit/actions/onramp/build-onramp-url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Copyright (c) TonTech.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import type { AppKit } from '@ton/appkit';
import { buildOnrampUrl, getOnrampQuotes } from '@ton/appkit/onramp';

export const buildOnrampUrlExample = async (appKit: AppKit) => {
// SAMPLE_START: BUILD_ONRAMP_URL
const quotes = await getOnrampQuotes(appKit, {
fiatCurrency: 'USD',
cryptoCurrency: 'TON',
amount: '100',
});

const [quote] = quotes;
if (!quote) throw new Error('No onramp quotes available');

const url = await buildOnrampUrl(appKit, {
quote,
userAddress: 'UQ...wallet-address...',
});
console.log('Onramp URL:', url);
// SAMPLE_END: BUILD_ONRAMP_URL
};
22 changes: 22 additions & 0 deletions demo/examples/src/appkit/actions/onramp/get-onramp-quotes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Copyright (c) TonTech.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import type { AppKit } from '@ton/appkit';
import { getOnrampQuotes } from '@ton/appkit/onramp';

export const getOnrampQuotesExample = async (appKit: AppKit) => {
// SAMPLE_START: GET_ONRAMP_QUOTES
const quotes = await getOnrampQuotes(appKit, {
fiatCurrency: 'USD',
cryptoCurrency: 'TON',
amount: '100',
isFiatAmount: true,
});
console.log('Onramp Quotes:', quotes);
// SAMPLE_END: GET_ONRAMP_QUOTES
};
2 changes: 2 additions & 0 deletions packages/appkit-react/.storybook/app-kit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { createOmnistonProvider } from '@ton/appkit/swap/omniston';
import { createDeDustProvider } from '@ton/appkit/swap/dedust';
import { createTonstakersProvider } from '@ton/appkit/staking/tonstakers';
import { createLayerswapProvider } from '@ton/appkit/crypto-onramp/layerswap';
import { createAppkitOnrampProvider } from '@ton/appkit/onramp/appkit-onramp';

export const appKit = new AppKit({
networks: {
Expand Down Expand Up @@ -40,5 +41,6 @@ export const appKit = new AppKit({
createDeDustProvider(),
createTonstakersProvider(),
createLayerswapProvider(),
createAppkitOnrampProvider({ apiKey: 'ak_test_66546d3cebb69dc4397570d65aad14dd' }),
],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Copyright (c) TonTech.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

export { OnrampAmountReversed } from './onramp-amount-reversed';
export type { OnrampAmountReversedProps } from './onramp-amount-reversed';
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.container {
composes: bodySemibold from '../../../../styles/typography.module.css';

display: flex;
align-items: center;
justify-content: center;
cursor: text;
width: 100%;
overflow: hidden;
color: var(--ta-color-text-secondary);
gap: 8px;
}

.changeDirection {
width: 14px;
height: 14px;
background-color: transparent;
border: none;
cursor: pointer;
padding: 0;
outline: none;
color: var(--ta-color-text);
transition: opacity 0.2s ease-in-out;
}

.changeDirection:hover {
opacity: 0.8;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Copyright (c) TonTech.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import type { FC } from 'react';

import { AmountReversed } from '../../../../components/ui/amount-reversed';
import type { AmountReversedProps } from '../../../../components/ui/amount-reversed';

export type OnrampAmountReversedProps = AmountReversedProps;

export const OnrampAmountReversed: FC<OnrampAmountReversedProps> = ({ decimals, ...props }) => (
<AmountReversed {...props} decimals={decimals ?? 2} />
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Copyright (c) TonTech.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

export { OnrampCurrencyItem } from './onramp-currency-item';
export type { OnrampCurrencyItemProps } from './onramp-currency-item';
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Copyright (c) TonTech.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import type { ComponentProps, FC } from 'react';

import { CurrencyItem } from '../../../../components/shared/currency-item';
import type { OnrampCurrency } from '../../types';

export interface OnrampCurrencyItemProps extends ComponentProps<typeof CurrencyItem.Container> {
currency: OnrampCurrency;
}

export const OnrampCurrencyItem: FC<OnrampCurrencyItemProps> = ({ currency, ...props }) => {
return (
<CurrencyItem.Container {...props}>
<CurrencyItem.Logo src={currency.logo} fallback={currency.code[0]} alt={currency.code} />
<CurrencyItem.Info>
<CurrencyItem.Header>
<CurrencyItem.Name>{currency.name}</CurrencyItem.Name>
</CurrencyItem.Header>

<CurrencyItem.Ticker>{currency.code}</CurrencyItem.Ticker>
</CurrencyItem.Info>
</CurrencyItem.Container>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Copyright (c) TonTech.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

export { OnrampCurrencySelectModal } from './onramp-currency-select-modal';
export type { OnrampCurrencySelectModalProps } from './onramp-currency-select-modal';
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/**
* Copyright (c) TonTech.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import { useMemo, useState } from 'react';
import type { FC } from 'react';

import { CurrencySelect } from '../../../../components/shared/currency-select-modal';
import type { OnrampCurrency, CurrencySectionConfig } from '../../types';
import { OnrampCurrencyItem } from '../onramp-currency-item';
import { useI18n } from '../../../settings/hooks/use-i18n';
import { filterCurrencies, groupCurrencySections } from './utils';
import type { CurrencySection } from './utils';

export interface OnrampCurrencySelectModalProps {
open: boolean;
onClose: () => void;
currencies: OnrampCurrency[];
currencySections?: CurrencySectionConfig[];
onSelect: (currency: OnrampCurrency) => void;
}

export const OnrampCurrencySelectModal: FC<OnrampCurrencySelectModalProps> = ({
open,
onClose,
currencies,
currencySections,
onSelect,
}) => {
const { t } = useI18n();
const [search, setSearch] = useState('');

const displaySections = useMemo((): CurrencySection[] => {
if (search) {
return [{ title: '', currencies: filterCurrencies(currencies, search) }];
}
if (currencySections) {
return groupCurrencySections(currencies, currencySections, t('tokenSelect.otherCurrencies'));
}
return [{ title: '', currencies }];
}, [currencies, currencySections, search, t]);

const isEmpty = displaySections.every((s) => s.currencies.length === 0);

const handleSelect = (currency: OnrampCurrency) => () => {
onSelect(currency);
onClose();
setSearch('');
};

const handleOpenChange = (isOpen: boolean) => {
if (!isOpen) {
onClose();
setSearch('');
}
};

return (
<CurrencySelect.Modal open={open} onOpenChange={handleOpenChange} title={t('onramp.selectCurrency')}>
<CurrencySelect.Search
searchValue={search}
onSearchChange={setSearch}
placeholder={t('onramp.searchCurrency')}
/>

<CurrencySelect.ListContainer isEmpty={isEmpty}>
{displaySections.map((section) => (
<CurrencySelect.Section key={section.title}>
{section.title && <CurrencySelect.SectionHeader>{section.title}</CurrencySelect.SectionHeader>}
{section.currencies.map((currency) => (
<OnrampCurrencyItem
key={currency.code}
currency={currency}
onClick={handleSelect(currency)}
/>
))}
</CurrencySelect.Section>
))}
</CurrencySelect.ListContainer>
</CurrencySelect.Modal>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Copyright (c) TonTech.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import type { OnrampCurrency, CurrencySectionConfig } from '../../types';

export interface CurrencySection {
title: string;
currencies: OnrampCurrency[];
}

export const filterCurrencies = (currencies: OnrampCurrency[], search: string): OnrampCurrency[] => {
if (!search) return currencies;
const lower = search.toLowerCase();
return currencies.filter((c) => c.name.toLowerCase().includes(lower) || c.code.toLowerCase().includes(lower));
};

export const groupCurrencySections = (
currencies: OnrampCurrency[],
sections: CurrencySectionConfig[],
otherTitle: string,
): CurrencySection[] => {
const currencyById = new Map(currencies.map((c) => [c.id, c]));
const assignedIds = new Set<string>();

const result: CurrencySection[] = sections.map(({ title, ids }) => ({
title,
currencies: ids.flatMap((id) => {
const c = currencyById.get(id);
if (c) {
assignedIds.add(id);
return [c];
}
return [];
}),
}));

const remaining = currencies.filter((c) => !assignedIds.has(c.id));
if (remaining.length > 0) {
result.push({ title: otherTitle, currencies: remaining });
}

return result.filter((s) => s.currencies.length > 0);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Copyright (c) TonTech.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

export { OnrampProviderItem } from './onramp-provider-item';
export type { OnrampProviderItemProps } from './onramp-provider-item';
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.container {
display: flex;
text-align: left;
align-items: center;
padding: 16px;
gap: 12px;
border: var(--ta-border-width-m) solid var(--ta-color-background-tertiary);
border-radius: var(--ta-border-radius-xl);
cursor: pointer;
background: transparent;
}

.icon {
border-radius: var(--ta-border-radius-s);
}

.name {
composes: bodySemibold from "../../../../styles/typography.module.css";
color: var(--ta-color-text);
}

.methods {
composes: labelRegular from "../../../../styles/typography.module.css";
color: var(--ta-color-text-secondary);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Copyright (c) TonTech.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import type { ComponentProps, FC } from 'react';
import clsx from 'clsx';

import type { OnrampProvider } from '../../types';
import { Logo } from '../../../../components/ui/logo';
import styles from './onramp-provider-item.module.css';

export interface OnrampProviderItemProps extends ComponentProps<'button'> {
provider: OnrampProvider;
}

export const OnrampProviderItem: FC<OnrampProviderItemProps> = ({ provider, className, ...props }) => {
return (
<button type="button" className={clsx(styles.container, className)} {...props}>
<Logo
className={styles.icon}
size={40}
src={provider.logo}
fallback={provider.name[0]}
alt={provider.name}
/>
<div className={styles.info}>
<div className={styles.name}>{provider.name}</div>
<div className={styles.methods}>{provider.description}</div>
</div>
</button>
);
};
Loading
Loading