From 32142b3204a0cc12e26e050475c4e988bb32fa81 Mon Sep 17 00:00:00 2001 From: MSG <59928086+MSghais@users.noreply.github.com> Date: Sat, 5 Oct 2024 22:27:47 +0200 Subject: [PATCH 1/2] Fix launchpad (#161) * fix launchpad + backend + screen linking * fix buy + sell + endpoint fields --- .vscode/launch.json | 13 -- .../src/routes/indexer/deploy-launch.ts | 21 ++-- .../src/routes/indexer/token_stats.ts | 8 +- apps/indexer/src/constants.ts | 3 +- apps/mobile/src/app/Router.tsx | 13 ++ .../components/LaunchPad/TokenStats/index.tsx | 26 ++-- .../components/LaunchPad/TokenStats/styles.ts | 4 +- .../pump/TokenLaunchDetail/index.tsx | 16 +-- .../search/TokenLaunchCard/index.tsx | 18 +-- .../launchpad/useBuyCoinByQuoteAmount.ts | 119 ++++++++++-------- .../mobile/src/hooks/launchpad/useSellCoin.ts | 88 +++++++------ .../mobile/src/screens/LaunchDetail/index.tsx | 83 ++++++------ apps/mobile/src/types/keys.ts | 22 +++- packages/common/src/contracts.ts | 4 +- turbo.json | 3 +- 15 files changed, 242 insertions(+), 199 deletions(-) delete mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 12c41e0c..00000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "configurations": [ - { - "name": "Debug Expo Web - Experimental", - "request": "launch", - "type": "reactnativedirect", - "cwd": "${workspaceFolder}", - "platform": "expoweb", - "browserTarget": "chrome", - "url": "http://localhost:8081" - } - ] -} diff --git a/apps/data-backend/src/routes/indexer/deploy-launch.ts b/apps/data-backend/src/routes/indexer/deploy-launch.ts index bc51601d..d4c8ddfd 100644 --- a/apps/data-backend/src/routes/indexer/deploy-launch.ts +++ b/apps/data-backend/src/routes/indexer/deploy-launch.ts @@ -49,7 +49,7 @@ async function deployLaunchRoute( return; } - const launches = await prisma.token_launch.findMany({ + const launchPool= await prisma.token_launch.findFirst({ where: { memecoin_address: launch }, @@ -65,7 +65,7 @@ async function deployLaunchRoute( }); reply.status(HTTPStatus.OK).send({ - data: launches + data: launchPool }); } catch (error) { console.error("Error deploying launch:", error); @@ -88,7 +88,10 @@ async function deployLaunchRoute( return; } - const launches = await prisma.token_launch.findMany({ + const launchStats = await prisma.token_launch.findFirst({ + where:{ + memecoin_address:launch, + }, select: { memecoin_address: true, quote_token: true, @@ -100,16 +103,12 @@ async function deployLaunchRoute( } }); - if (launches.length) { - let statsLaunch = launches[0]; - - reply.status(HTTPStatus.OK).send({ - data: statsLaunch - }); - } + reply.status(HTTPStatus.OK).send({ + data: launchStats + }); reply.status(HTTPStatus.OK).send({ - data: [] + data: undefined }); } catch (error) { console.error("Error deploying launch:", error); diff --git a/apps/data-backend/src/routes/indexer/token_stats.ts b/apps/data-backend/src/routes/indexer/token_stats.ts index 6915dcfa..595b0b1c 100644 --- a/apps/data-backend/src/routes/indexer/token_stats.ts +++ b/apps/data-backend/src/routes/indexer/token_stats.ts @@ -30,7 +30,13 @@ async function tokenStatsRoute( orderBy: { created_at: "desc" }, select: { price: true, - liquidity_raised: true + liquidity_raised: true, + coin_received:true, + total_supply:true, + network:true, + last_price:true, + amount:true, + quote_amount:true } }); diff --git a/apps/indexer/src/constants.ts b/apps/indexer/src/constants.ts index 93b2a494..9df0bfc7 100644 --- a/apps/indexer/src/constants.ts +++ b/apps/indexer/src/constants.ts @@ -4,5 +4,6 @@ export const STARTING_BLOCK = 140_000; export const LAUNCHPAD_ADDRESS = { // SEPOLIA:"0x74acb6752abb734a7b3388567429217988e02409d9bf43c5586dc2c4f8baf40", // SEPOLIA:"0x29a532e6933a6d6f9939e59469d96b52b7c38561745331302e1a29f035e4dd0", - SEPOLIA: "0x3798921000573bfc442d8153fc088db97bd3794f5ed19ea8c0846db5378f4af" + // SEPOLIA: "0x3798921000573bfc442d8153fc088db97bd3794f5ed19ea8c0846db5378f4af", + SEPOLIA:"0x595d9c14d5b52bae1bd5a88f3aefb521eca956fde4de95e400197f1080fa862" }; diff --git a/apps/mobile/src/app/Router.tsx b/apps/mobile/src/app/Router.tsx index 140fa8d2..31996de3 100644 --- a/apps/mobile/src/app/Router.tsx +++ b/apps/mobile/src/app/Router.tsx @@ -346,6 +346,19 @@ const linking = { MainStack: { path: 'app', screens: { + AuthStack: { + path: 'auth', + screens: { + Login: 'login', + CreateAccount: 'create-account', + SaveKeys: 'save-keys', + ImportKeys: 'import-keys', + }, + }, + Login: 'login', + CreateAccount: 'create-account', + SaveKeys: 'save-keys', + ImportKeys: 'import-keys', Home: 'home', Feed: 'feed', Profile: { diff --git a/apps/mobile/src/components/LaunchPad/TokenStats/index.tsx b/apps/mobile/src/components/LaunchPad/TokenStats/index.tsx index fe45b2e1..e00540b6 100644 --- a/apps/mobile/src/components/LaunchPad/TokenStats/index.tsx +++ b/apps/mobile/src/components/LaunchPad/TokenStats/index.tsx @@ -1,10 +1,10 @@ -import {FlatList, View} from 'react-native'; -import {useStyles} from '../../../hooks'; -import {TokenStatsInterface} from '../../../types/keys'; -import {Text} from '../../Text'; +import { FlatList, View } from 'react-native'; +import { useStyles } from '../../../hooks'; +import { TokenStatsInterface } from '../../../types/keys'; +import { Text } from '../../Text'; import stylesheet from './styles'; -import {Fraction} from '@uniswap/sdk-core'; -import {decimalsScale} from '../../../utils/helpers'; +import { Fraction } from '@uniswap/sdk-core'; +import { decimalsScale } from '../../../utils/helpers'; import Loading from '../../Loading'; export type TokenStatsProps = { @@ -12,20 +12,22 @@ export type TokenStatsProps = { stats?: TokenStatsInterface; }; -export const TokenStats: React.FC = ({stats, loading}) => { +export const TokenStats: React.FC = ({ stats, loading }) => { const styles = useStyles(stylesheet); if (!stats && loading) { return ; } - const {price, liquidity_raised} = stats || {}; + const { price, liquidity_raised } = stats || {}; - const lastPrice = price ? new Fraction(String(price), decimalsScale(18)).toFixed(18) : '0'; - const liquidityRaised = liquidity_raised - ? new Fraction(String(liquidity_raised), decimalsScale(18)).toFixed(18) - : '0'; + // const lastPrice = price ? new Fraction(String(price), decimalsScale(18)).toFixed(18) : '0'; + const lastPrice = price; + // const liquidityRaised = liquidity_raised + // ? new Fraction(String(liquidity_raised), decimalsScale(18)).toFixed(18) + // : '0'; + const liquidityRaised = liquidity_raised; return ( Token Statistics diff --git a/apps/mobile/src/components/LaunchPad/TokenStats/styles.ts b/apps/mobile/src/components/LaunchPad/TokenStats/styles.ts index 9e251625..1b71fe60 100644 --- a/apps/mobile/src/components/LaunchPad/TokenStats/styles.ts +++ b/apps/mobile/src/components/LaunchPad/TokenStats/styles.ts @@ -1,7 +1,7 @@ import {Spacing, ThemedStyleSheet} from '../../../styles'; export default ThemedStyleSheet((theme) => ({ card: { - backgroundColor: '#fff', + backgroundColor: theme.colors.background, borderRadius: 8, padding: 16, margin: 16, @@ -29,6 +29,6 @@ export default ThemedStyleSheet((theme) => ({ value: { fontSize: 14, fontWeight: 'bold', - color: '#333', + color: theme.colors.text, }, })); diff --git a/apps/mobile/src/components/pump/TokenLaunchDetail/index.tsx b/apps/mobile/src/components/pump/TokenLaunchDetail/index.tsx index e554f4cf..924dd2be 100644 --- a/apps/mobile/src/components/pump/TokenLaunchDetail/index.tsx +++ b/apps/mobile/src/components/pump/TokenLaunchDetail/index.tsx @@ -65,15 +65,11 @@ export const TokenLaunchDetail: React.FC = ({ // if (!event?.id) return; // navigation.navigate('Profile', { publicKey: event?.pubkey }); // }; - let priceAmount; - if (launch?.price) { - priceAmount = new Fraction(String(launch.price), decimalsScale(18)).toFixed(18); - } - let created_at; - - if (launch?.created_at) { - created_at = new Fraction(String(launch.created_at), decimalsScale(18)).toFixed(18); - } + let priceAmount=launch?.price; + // if (launch?.price) { + // priceAmount = new Fraction(String(launch.price), decimalsScale(18)).toFixed(18); + // } + let created_at=launch?.created_at const sellKeys = async () => { if (!amount) return; @@ -101,12 +97,12 @@ export const TokenLaunchDetail: React.FC = ({ await onConnect(); if (!account || !account?.account) return; + console.log('launch', launch); if (!launch?.owner) return; if (!launch?.token_quote) return; - console.log('launch', launch); // handleBuyKeys(account?.account, launch?.owner, launch?.token_quote, Number(amount),) handleBuyCoins( account?.account, diff --git a/apps/mobile/src/components/search/TokenLaunchCard/index.tsx b/apps/mobile/src/components/search/TokenLaunchCard/index.tsx index b5d4b85b..87a87d05 100644 --- a/apps/mobile/src/components/search/TokenLaunchCard/index.tsx +++ b/apps/mobile/src/components/search/TokenLaunchCard/index.tsx @@ -111,15 +111,15 @@ export const TokenLaunchCard: React.FC = ({ // if (!event?.id) return; // navigation.navigate('Profile', { publicKey: event?.pubkey }); // }; - let priceAmount; - if (token?.price) { - priceAmount = new Fraction(String(token.price), decimalsScale(18)).toFixed(18); - } - let created_at; - - if (token?.created_at) { - created_at = new Fraction(String(token.created_at), decimalsScale(18)).toFixed(18); - } + let priceAmount=token?.price; + // if (token?.price) { + // priceAmount = new Fraction(String(token.price), decimalsScale(18)).toFixed(18); + // } + let created_at=token?.created_at; + + // if (token?.created_at) { + // created_at = new Fraction(String(token.created_at), decimalsScale(18)).toFixed(18); + // } return ( diff --git a/apps/mobile/src/hooks/launchpad/useBuyCoinByQuoteAmount.ts b/apps/mobile/src/hooks/launchpad/useBuyCoinByQuoteAmount.ts index 099d0872..e831bbdb 100644 --- a/apps/mobile/src/hooks/launchpad/useBuyCoinByQuoteAmount.ts +++ b/apps/mobile/src/hooks/launchpad/useBuyCoinByQuoteAmount.ts @@ -1,77 +1,86 @@ -import {useNetwork} from '@starknet-react/core'; +import { useNetwork } from '@starknet-react/core'; // import { LAUNCHPAD_ADDRESS} from '../../constants/contracts'; -import {LAUNCHPAD_ADDRESS} from 'common'; -import {AccountInterface, CallData, constants, RpcProvider} from 'starknet'; +import { LAUNCHPAD_ADDRESS } from 'common'; +import { AccountInterface, CallData, constants, RpcProvider } from 'starknet'; -import {TokenQuoteBuyKeys} from '../../types/keys'; -import {feltToAddress, formatFloatToUint256} from '../../utils/format'; +import { TokenQuoteBuyKeys } from '../../types/keys'; +import { feltToAddress, formatFloatToUint256 } from '../../utils/format'; +import { STRK, TOKEN_ADDRESSES } from '../../constants/tokens'; export const useBuyCoinByQuoteAmount = () => { const chain = useNetwork(); const chainId = chain?.chain?.id; - const provider = new RpcProvider({nodeUrl: process.env.EXPO_PUBLIC_PROVIDER_URL}); + const provider = new RpcProvider({ nodeUrl: process.env.EXPO_PUBLIC_PROVIDER_URL }); const handleBuyCoins = async ( account: AccountInterface, coin_address: string, amount: number, - tokenQuote: TokenQuoteBuyKeys, + // tokenQuote: TokenQuoteBuyKeys, + tokenQuote?: string, contractAddress?: string, ) => { - if (!account) return; - - const addressContract = + try { + const addressContract = contractAddress ?? LAUNCHPAD_ADDRESS[constants.StarknetChainId.SN_SEPOLIA]; - console.log('addressContract', addressContract); - console.log('read asset'); - console.log( - ' feltToAddress(BigInt(tokenQuote?.token_address)', - feltToAddress(BigInt(tokenQuote?.token_address)), - ); - // const asset = await prepareAndConnectContract( - // provider, - // feltToAddress(BigInt(tokenQuote?.token_address)), - // // tokenQuote?.token_address?.toString(), - // account, - // ); - // console.log('read launchpad_contract'); - const quote_address_token = feltToAddress(BigInt(tokenQuote?.token_address)); - // const launchpad_contract = await prepareAndConnectContract(provider, addressContract); - // const launchpad_contract = await prepareAndConnectContract(provider, addressContract, account); - console.log('amount', amount); - const amountUint256 = formatFloatToUint256(amount); - console.log('amountuint256', amountUint256); - // amountUint256 = uint256.bnToUint256(BigInt('0x' + amount*10**18)); - // console.log('amountuint256', amountUint256); - const buyCoinParams = { - coin_address, // token address - amount: amountUint256, - }; - console.log('buyCoinParams', buyCoinParams); + console.log('addressContract', addressContract); + console.log('read asset'); + if (!account) return; + + + // const asset = await prepareAndConnectContract( + // provider, + // feltToAddress(BigInt(tokenQuote?.token_address)), + // // tokenQuote?.token_address?.toString(), + // account, + // ); + // console.log('read launchpad_contract'); + + // const quote_address_token = feltToAddress(BigInt(tokenQuote)); + const quote_address_token = tokenQuote ?? STRK[constants.StarknetChainId.SN_SEPOLIA]?.address; + // const launchpad_contract = await prepareAndConnectContract(provider, addressContract); + // const launchpad_contract = await prepareAndConnectContract(provider, addressContract, account); - const approveCall = { - contractAddress: quote_address_token, - entrypoint: 'approve', - calldata: CallData.compile({ - address: addressContract, + console.log('amount', amount); + const amountUint256 = formatFloatToUint256(amount); + console.log('amountuint256', amountUint256); + // amountUint256 = uint256.bnToUint256(BigInt('0x' + amount*10**18)); + // console.log('amountuint256', amountUint256); + const buyCoinParams = { + coin_address, // token address amount: amountUint256, - }), - }; + }; + console.log('buyCoinParams', buyCoinParams); + + const approveCall = { + contractAddress: quote_address_token, + entrypoint: 'approve', + calldata: CallData.compile({ + address: addressContract, + amount: amountUint256, + }), + }; + + const buyCoinCall = { + contractAddress: addressContract, + entrypoint: 'buy_coin_by_quote_amount', + calldata: CallData.compile({ + coin_address: buyCoinParams.coin_address, + quote_amount: amountUint256, + }), + }; - const buyCoinCall = { - contractAddress: addressContract, - entrypoint: 'buy_coin_by_quote_amount', - calldata: CallData.compile({ - coin_address: buyCoinParams.coin_address, - quote_amount: amountUint256, - }), - }; + const tx = await account?.execute([approveCall, buyCoinCall], undefined, {}); + console.log('tx hash', tx.transaction_hash); + const wait_tx = await account?.waitForTransaction(tx?.transaction_hash); + return wait_tx + } catch (e) { + console.log("Error handleBuyCoins", e) + return undefined; + } - const tx = await account?.execute([approveCall, buyCoinCall], undefined, {}); - console.log('tx hash', tx.transaction_hash); - const wait_tx = await account?.waitForTransaction(tx?.transaction_hash); }; - return {handleBuyCoins}; + return { handleBuyCoins }; }; diff --git a/apps/mobile/src/hooks/launchpad/useSellCoin.ts b/apps/mobile/src/hooks/launchpad/useSellCoin.ts index b02d9e24..0e6c63c1 100644 --- a/apps/mobile/src/hooks/launchpad/useSellCoin.ts +++ b/apps/mobile/src/hooks/launchpad/useSellCoin.ts @@ -1,9 +1,9 @@ -import {useAccount, useNetwork, useProvider} from '@starknet-react/core'; -import {LAUNCHPAD_ADDRESS} from 'common'; -import {AccountInterface, CallData, constants, RpcProvider} from 'starknet'; +import { useAccount, useNetwork, useProvider } from '@starknet-react/core'; +import { LAUNCHPAD_ADDRESS } from 'common'; +import { AccountInterface, CallData, constants, RpcProvider } from 'starknet'; -import {TokenQuoteBuyKeys} from '../../types/keys'; -import {formatFloatToUint256} from '../../utils/format'; +import { TokenQuoteBuyKeys } from '../../types/keys'; +import { formatFloatToUint256 } from '../../utils/format'; export const useSellCoin = () => { const account = useAccount(); @@ -16,43 +16,51 @@ export const useSellCoin = () => { account: AccountInterface, user_address: string, amount: number, - tokenQuote?: TokenQuoteBuyKeys, + tokenQuote?: string, + // tokenQuote?: TokenQuoteBuyKeys, contractAddress?: string, ) => { - if (!account) return; - const addressContract = - contractAddress ?? LAUNCHPAD_ADDRESS[constants.StarknetChainId.SN_SEPOLIA]; - // console.log('addressContract', addressContract); - // let launchpad_contract = await prepareAndConnectContract( - // provider, - // addressContract, - // account - // ); - - const amountUint256 = formatFloatToUint256(amount); - // amountUint256 = uint256.bnToUint256(BigInt('0x' + amount)); - - const sellKeysParams = { - user_address, // token address - amount: amountUint256, - // amount: cairo.uint256(amount), // amount int. Float need to be convert with bnToUint - }; - console.log('sellKeysParams', sellKeysParams); - - const call = { - contractAddress: addressContract, - entrypoint: 'sell_coin', - calldata: CallData.compile({ - user_address: sellKeysParams.user_address, - amount: sellKeysParams.amount, - }), - }; - - console.log('Call', call); - const tx = await account?.execute([call], undefined, {}); - console.log('tx hash', tx.transaction_hash); - const wait_tx = await account?.waitForTransaction(tx?.transaction_hash); + try { + if (!account) return; + const addressContract = + contractAddress ?? LAUNCHPAD_ADDRESS[constants.StarknetChainId.SN_SEPOLIA]; + // console.log('addressContract', addressContract); + // let launchpad_contract = await prepareAndConnectContract( + // provider, + // addressContract, + // account + // ); + + const amountUint256 = formatFloatToUint256(amount); + // amountUint256 = uint256.bnToUint256(BigInt('0x' + amount)); + + const sellKeysParams = { + user_address, // token address + amount: amountUint256, + // amount: cairo.uint256(amount), // amount int. Float need to be convert with bnToUint + }; + console.log('sellKeysParams', sellKeysParams); + + const call = { + contractAddress: addressContract, + entrypoint: 'sell_coin', + calldata: CallData.compile({ + user_address: sellKeysParams.user_address, + amount: sellKeysParams.amount, + }), + }; + + console.log('Call', call); + const tx = await account?.execute([call], undefined, {}); + console.log('tx hash', tx.transaction_hash); + const wait_tx = await account?.waitForTransaction(tx?.transaction_hash); + return wait_tx + } catch (e) { + console.log("Error handleSellCoins", e) + return undefined; + } + }; - return {handleSellCoins}; + return { handleSellCoins }; }; diff --git a/apps/mobile/src/screens/LaunchDetail/index.tsx b/apps/mobile/src/screens/LaunchDetail/index.tsx index 8c6ffa1d..c9569d5d 100644 --- a/apps/mobile/src/screens/LaunchDetail/index.tsx +++ b/apps/mobile/src/screens/LaunchDetail/index.tsx @@ -13,7 +13,7 @@ import { useStyles, useTheme } from '../../hooks'; import { useGetHoldings } from '../../hooks/api/indexer/useHoldings'; import { useBuyCoinByQuoteAmount } from '../../hooks/launchpad/useBuyCoinByQuoteAmount'; import { useSellCoin } from '../../hooks/launchpad/useSellCoin'; -import { useWalletModal } from '../../hooks/modals'; +import { useToast, useWalletModal } from '../../hooks/modals'; import { LaunchDetailScreenProps } from '../../types'; import { @@ -74,7 +74,7 @@ export const LaunchDetail: React.FC = ({ navigation, ro const { data: statsData, isLoading: statsLoading } = useGetTokenStats(coinAddress); - const { data: sharesData, isLoading: sharesLoading } = useGetShares(coinAddress, ""); + const { data: sharesData, isLoading: sharesLoading } = useGetShares(coinAddress, account?.address ?? ""); const { data: tokenData, isLoading: tokenLoading } = useGetTokenLaunch(coinAddress) @@ -84,6 +84,7 @@ export const LaunchDetail: React.FC = ({ navigation, ro const { handleSellCoins } = useSellCoin(); const { handleBuyCoins } = useBuyCoinByQuoteAmount(); + const { showToast } = useToast() const walletModal = useWalletModal(); const [amount, setAmount] = useState(); @@ -98,7 +99,8 @@ export const LaunchDetail: React.FC = ({ navigation, ro useEffect(() => { if (tokenData && tokenData.data) { - setTokens(tokenData.data) + setTokens(tokenData?.data) + setToken(tokenData?.data) } }, [tokenData]); @@ -109,10 +111,12 @@ export const LaunchDetail: React.FC = ({ navigation, ro useEffect(() => { const data = transactionData || []; - setTransaction(data); + console.log("data tx",data) + setTransaction(data?.data); }, [transactionData]); useEffect(() => { + setStats(statsData); }, [statsData]); @@ -122,10 +126,10 @@ export const LaunchDetail: React.FC = ({ navigation, ro setStats(data); }, [sharesData]); - useEffect(() => { - const latestToken = tokens?.sort((a, b) => Number(b.created_at) - Number(a.created_at))[0]; - setToken(latestToken) - }, [tokens]) + // useEffect(() => { + // const latestToken = tokens?.sort((a, b) => Number(b.created_at) - Number(a.created_at))[0]; + // setToken(latestToken) + // }, [tokens]) const onConnect = async () => { @@ -138,41 +142,54 @@ export const LaunchDetail: React.FC = ({ navigation, ro }; const sellKeys = async () => { - if (!amount) return; - + if (!amount) { + return showToast({ title: "Select an amount to buy", type: "info" }) + } await onConnect(); if (!account || !account?.account) return; - if (!token?.owner) return; + if (!token?.memecoin_address) return; + console.log("token", token) - if (!token?.token_quote) return; + // if (!token?.quote_token) return; - handleSellCoins( + const sellResult = await handleSellCoins( account?.account, feltToAddress(BigInt(token?.memecoin_address)), Number(amount), - token?.token_quote, + token?.quote_token, undefined, ); + + if(sellResult) { + return showToast({ title: 'Buy done', type: "success" }) + } }; const buyCoin = async () => { - if (!amount) return; await onConnect(); + if (!amount) { + return showToast({ title: "Select an amount to buy", type: "info" }) + } if (!account || !account?.account) return; - if (!token?.owner) return; + console.log("token", token) + - if (!token?.token_quote) return; + if (!token?.memecoin_address) return; + // if (!token?.token_quote) return; // handleBuyKeys(account?.account, token?.owner, token?.token_quote, Number(amount),) - handleBuyCoins( + const buyResult = await handleBuyCoins( account?.account, - feltToAddress(BigInt(token?.memecoin_address)), + token?.memecoin_address, + // feltToAddress(BigInt(token?.memecoin_address)), Number(amount), - token?.token_quote, + token?.quote_token, ); + + return showToast({ title: 'Buy done', type: "success" }) }; @@ -212,28 +229,14 @@ export const LaunchDetail: React.FC = ({ navigation, ro > - - {selectedTab == SelectedTab.LAUNCH_OVERVIEW && tokens && ( - <> - - - } - // keyExtractor={(item, i) => {`${item.owner + item?.created_at}`}} - keyExtractor={(item, i) => i.toString()} - // numColumns={isDesktop ? 3 : 1} - renderItem={({ item, index }) => { - - return - }} - - /> + {selectedTab == SelectedTab.LAUNCH_OVERVIEW && tokens && ( + <> + + + )} + - - )} {selectedTab == SelectedTab.LAUNCH_HOLDERS && ( <> diff --git a/apps/mobile/src/types/keys.ts b/apps/mobile/src/types/keys.ts index d2a63751..a98bae82 100644 --- a/apps/mobile/src/types/keys.ts +++ b/apps/mobile/src/types/keys.ts @@ -60,11 +60,29 @@ export interface TokenDeployInterface { token_quote: TokenQuoteBuyKeys; threshold_liquidity?: Uint256; slope?: Uint256; + quote_token?: string; + +} + +export interface TokenDeployIndexerInterface { + memecoin_address: string; + price: Uint256; + name: string; + network: string; + owner: string; + total_supply: Uint256; + created_at: Uint256; + liquidity_raised: Uint256; + token_holded: Uint256; + is_liquidity_launch: boolean; + quote_token?: string; + threshold_liquidity?: Uint256; + slope?: Uint256; } export interface TokenStatsInterface { - price?: Uint256; - liquidity_raised?: Uint256; + price?: number; + liquidity_raised?: number; } export interface TokenTxInterface { diff --git a/packages/common/src/contracts.ts b/packages/common/src/contracts.ts index 56037a8e..f8b46959 100644 --- a/packages/common/src/contracts.ts +++ b/packages/common/src/contracts.ts @@ -44,8 +44,8 @@ export const LAUNCHPAD_ADDRESS = { // [constants.StarknetChainId.SN_SEPOLIA]:"0x5cf19613d54ae5e7c229c87cc26322f2ff6c473d2183723010676b8337c0af3", // [constants.StarknetChainId.SN_SEPOLIA]:"0x19084523bd7307c2169ee32a336be3f9d9eb6bf24197156cb6fc7a42feb7a5" // [constants.StarknetChainId.SN_SEPOLIA]:"0x29a532e6933a6d6f9939e59469d96b52b7c38561745331302e1a29f035e4dd0", - [constants.StarknetChainId.SN_SEPOLIA]:"0x3798921000573bfc442d8153fc088db97bd3794f5ed19ea8c0846db5378f4af", - + // [constants.StarknetChainId.SN_SEPOLIA]:"0x3798921000573bfc442d8153fc088db97bd3794f5ed19ea8c0846db5378f4af", + [constants.StarknetChainId.SN_SEPOLIA]:"0x595d9c14d5b52bae1bd5a88f3aefb521eca956fde4de95e400197f1080fa862", } diff --git a/turbo.json b/turbo.json index 09251ff5..92dff789 100644 --- a/turbo.json +++ b/turbo.json @@ -26,7 +26,8 @@ "NEXT_PUBLIC_CANVAS_NFT_CONTRACT_ADDRESS", "NEXT_PUBLIC_CANVAS_STARKNET_CONTRACT_ADDRESS", "NEXT_PUBLIC_CHAIN_ID", - "NEXT_PUBLIC_PROVIDER_URL" + "NEXT_PUBLIC_PROVIDER_URL", + "EXPO_PUBLIC_INDEXER_BACKEND_URL" ], "pipeline": { "build": { From 685cd12dbde27663ad009fcdd0a3f63074344057 Mon Sep 17 00:00:00 2001 From: MSG <59928086+MSghais@users.noreply.github.com> Date: Sat, 5 Oct 2024 23:52:41 +0200 Subject: [PATCH 2/2] Fix/launchpad (#162) * fix launchpad + backend + screen linking * fix buy + sell + endpoint fields * fix router + launch sql + tx + interface --- .../src/routes/indexer/transactions.ts | 3 +- apps/indexer/init.sql | 2 + apps/indexer/src/token-launch.ts | 7 +- apps/mobile/src/app/Router.tsx | 8 +- .../components/LaunchPad/TokenStats/styles.ts | 2 +- .../components/LaunchPad/UserShare/index.tsx | 102 ++++++++--- .../pump/TokenLaunchDetail/index.tsx | 161 +++++++++++++----- .../search/TokenLaunchCard/index.tsx | 4 +- apps/mobile/src/hooks/useCombinedTokens.ts | 10 +- .../mobile/src/screens/LaunchDetail/index.tsx | 27 +-- apps/mobile/src/types/keys.ts | 20 +-- apps/mobile/src/types/tab.ts | 11 +- 12 files changed, 246 insertions(+), 111 deletions(-) diff --git a/apps/data-backend/src/routes/indexer/transactions.ts b/apps/data-backend/src/routes/indexer/transactions.ts index 8b51503b..26bb722c 100644 --- a/apps/data-backend/src/routes/indexer/transactions.ts +++ b/apps/data-backend/src/routes/indexer/transactions.ts @@ -33,7 +33,8 @@ async function transactionsRoute( select: { transaction_type: true, amount: true, - quote_amount: true + quote_amount: true, + price:true } }); diff --git a/apps/indexer/init.sql b/apps/indexer/init.sql index fc1727d2..d9e84f9e 100644 --- a/apps/indexer/init.sql +++ b/apps/indexer/init.sql @@ -1,5 +1,6 @@ create table token_launch( memecoin_address TEXT, + owner_address TEXT, network TEXT, block_hash TEXT, block_number BIGINT, @@ -11,6 +12,7 @@ create table token_launch( total_supply TEXT, current_supply TEXT, liquidity_raised TEXT, + threshold_liquidity TEXT, price TEXT, _cursor BIGINT, time_stamp TEXT diff --git a/apps/indexer/src/token-launch.ts b/apps/indexer/src/token-launch.ts index 735a1286..b7028553 100644 --- a/apps/indexer/src/token-launch.ts +++ b/apps/indexer/src/token-launch.ts @@ -47,7 +47,7 @@ export default function DecodeTokenLaunchDeploy({ header, events }: Block) { const transactionHash = transaction.meta.hash; - const [_caller, token_address] = event.keys!; + const [caller, token_address, quote_token_address] = event.keys!; const [ amount_low, amount_high, @@ -89,6 +89,8 @@ export default function DecodeTokenLaunchDeploy({ header, events }: Block) { const _threshold_liquidity = formatUnits(threshold_liquidity_raw, DECIMALS); return { + owner_address:caller, + quote_token:quote_token_address, memecoin_address: token_address, network: "starknet-sepolia", block_hash: blockHash, @@ -97,7 +99,8 @@ export default function DecodeTokenLaunchDeploy({ header, events }: Block) { transaction_hash: transactionHash, total_supply, price, - created_at: new Date().toISOString() + created_at: new Date().toISOString(), + threshold_liquidity:_threshold_liquidity }; }); } diff --git a/apps/mobile/src/app/Router.tsx b/apps/mobile/src/app/Router.tsx index 31996de3..259fb415 100644 --- a/apps/mobile/src/app/Router.tsx +++ b/apps/mobile/src/app/Router.tsx @@ -236,9 +236,11 @@ const MainNavigator: React.FC = () => { - - - + {isDesktop && + + + + } ); diff --git a/apps/mobile/src/components/LaunchPad/TokenStats/styles.ts b/apps/mobile/src/components/LaunchPad/TokenStats/styles.ts index 1b71fe60..b8351405 100644 --- a/apps/mobile/src/components/LaunchPad/TokenStats/styles.ts +++ b/apps/mobile/src/components/LaunchPad/TokenStats/styles.ts @@ -24,7 +24,7 @@ export default ThemedStyleSheet((theme) => ({ }, label: { fontSize: 14, - color: '#555', + color: theme.colors.text, }, value: { fontSize: 14, diff --git a/apps/mobile/src/components/LaunchPad/UserShare/index.tsx b/apps/mobile/src/components/LaunchPad/UserShare/index.tsx index 230c6005..47043ba9 100644 --- a/apps/mobile/src/components/LaunchPad/UserShare/index.tsx +++ b/apps/mobile/src/components/LaunchPad/UserShare/index.tsx @@ -1,48 +1,108 @@ -import { FlatList, View } from 'react-native'; +import { FlatList, Pressable, View } from 'react-native'; -import { useStyles } from '../../../hooks'; -import { UserShareInterface } from '../../../types/keys'; +import { useStyles, useWaitConnection } from '../../../hooks'; +import { TokenStatsInterface, UserShareInterface } from '../../../types/keys'; import { Text } from '../../Text'; import stylesheet from './styles'; import Loading from '../../Loading'; +import { useGetShares } from '../../../hooks/api/indexer/useUserShare'; +import { useAccount } from 'wagmi'; +import { useEffect, useState } from 'react'; +import { useWalletModal } from '../../../hooks/modals'; export type UserShareProps = { loading: boolean; + coinAddress: string; shares?: UserShareInterface[]; }; -export const UserShare: React.FC = ({ shares, loading }) => { +export const UserShare: React.FC = ({ shares, loading, coinAddress }) => { const styles = useStyles(stylesheet); + const [stats, setStats] = useState(); + const [sharesState, setShares] = useState(); - return loading ? ( - - ) : ( - index.toString()} - renderItem={({ item }) => ( + const account = useAccount() + const { data: sharesData, isLoading: sharesLoading, refetch } = useGetShares(coinAddress, account?.address ?? ""); + + const waitConnection = useWaitConnection(); + const walletModal = useWalletModal(); + const onConnect = async () => { + if (!account.address) { + walletModal.show(); + + const result = await waitConnection(); + if (!result) return; + } + }; + console.log("sharesData", sharesData) + useEffect(() => { + const data = sharesData || []; + setStats(data); + setShares(sharesData) + }, [sharesData]); + + // return loading ? ( + // + // ) + return ( + + + + <> + + {sharesLoading && } + + {account && !account?.address && + + + { + onConnect() + }}> + Connect + + + } + {sharesState && + + - Memecoin Address + Total - {item.token_address} + {Number(sharesState?.total_buy) - Number(sharesState?.total_sell)} - Coin Recieved + Total sell - {Number(item.total_supply)} + {sharesState?.total_sell} + + + + Quote amount paid + + {sharesState?.quote_amount} - - )} - ListEmptyComponent={ - No user share available - + } - /> + + {/* index.toString()} + renderItem={({ item }) => ( + + )} + ListEmptyComponent={ + No user share available + + } + /> */} + + + ); }; diff --git a/apps/mobile/src/components/pump/TokenLaunchDetail/index.tsx b/apps/mobile/src/components/pump/TokenLaunchDetail/index.tsx index 924dd2be..ab9ac437 100644 --- a/apps/mobile/src/components/pump/TokenLaunchDetail/index.tsx +++ b/apps/mobile/src/components/pump/TokenLaunchDetail/index.tsx @@ -1,23 +1,23 @@ -import {NDKEvent, NDKUserProfile} from '@nostr-dev-kit/ndk'; -import {useNavigation} from '@react-navigation/native'; -import {useAccount} from '@starknet-react/core'; -import {Fraction} from '@uniswap/sdk-core'; -import {useProfile} from 'afk_nostr_sdk'; -import {ImageSourcePropType, View} from 'react-native'; - -import {useStyles, useWaitConnection} from '../../../hooks'; -import {MainStackNavigationProps} from '../../../types'; -import {TokenDeployInterface, TokenLaunchInterface} from '../../../types/keys'; -import {feltToAddress} from '../../../utils/format'; -import {decimalsScale} from '../../../utils/helpers'; -import {Button} from '../../Button'; -import {Text} from '../../Text'; +import { NDKEvent, NDKUserProfile } from '@nostr-dev-kit/ndk'; +import { useNavigation } from '@react-navigation/native'; +import { useAccount } from '@starknet-react/core'; +import { Fraction } from '@uniswap/sdk-core'; +import { useProfile } from 'afk_nostr_sdk'; +import { ImageSourcePropType, Pressable, View } from 'react-native'; + +import { useStyles, useWaitConnection } from '../../../hooks'; +import { MainStackNavigationProps } from '../../../types'; +import { TokenDeployInterface, TokenLaunchInterface } from '../../../types/keys'; +import { feltToAddress } from '../../../utils/format'; +import { decimalsScale } from '../../../utils/helpers'; +import { Button } from '../../Button'; +import { Text } from '../../Text'; import stylesheet from './styles'; -import {LaunchActionsForm} from '../../LaunchActionsForm'; -import {useState} from 'react'; -import {useBuyCoinByQuoteAmount} from '../../../hooks/launchpad/useBuyCoinByQuoteAmount'; -import {useSellCoin} from '../../../hooks/launchpad/useSellCoin'; -import {useWalletModal} from '../../../hooks/modals'; +import { LaunchActionsForm } from '../../LaunchActionsForm'; +import { useState } from 'react'; +import { useBuyCoinByQuoteAmount } from '../../../hooks/launchpad/useBuyCoinByQuoteAmount'; +import { useSellCoin } from '../../../hooks/launchpad/useSellCoin'; +import { useToast, useWalletModal } from '../../../hooks/modals'; import { AddressComponent } from '../../AddressComponent'; export type LaunchCoinProps = { @@ -41,15 +41,18 @@ export const TokenLaunchDetail: React.FC = ({ isDisabledInfo, isDisabledForm, }) => { - const {data: profile} = useProfile({publicKey: event?.pubkey}); + const { data: profile } = useProfile({ publicKey: event?.pubkey }); const account = useAccount(); const [amount, setAmount] = useState(); + console.log("launch", launch) + const [isDisabledInfoState, setIsDisabledInfoState] = useState(isDisabledInfo) const styles = useStyles(stylesheet); - const {handleSellCoins} = useSellCoin(); + const { showToast } = useToast() + const { handleSellCoins } = useSellCoin(); // const { handleBuyKeys } = useBuyKeys() - const {handleBuyCoins} = useBuyCoinByQuoteAmount(); + const { handleBuyCoins } = useBuyCoinByQuoteAmount(); const waitConnection = useWaitConnection(); const walletModal = useWalletModal(); const onConnect = async () => { @@ -65,53 +68,108 @@ export const TokenLaunchDetail: React.FC = ({ // if (!event?.id) return; // navigation.navigate('Profile', { publicKey: event?.pubkey }); // }; - let priceAmount=launch?.price; + let priceAmount = launch?.price; // if (launch?.price) { // priceAmount = new Fraction(String(launch.price), decimalsScale(18)).toFixed(18); // } - let created_at=launch?.created_at + let created_at = launch?.created_at - const sellKeys = async () => { - if (!amount) return; + // const sellKeys = async () => { + // if (!amount) return; + + // await onConnect(); + // if (!account || !account?.account) return; + + + // if (!launch?.quote_token) return; + + // // handleSellKeys(account?.account, launch?.owner, Number(amount), launch?.token_quote, undefined) + // handleSellCoins( + // account?.account, + // launch.memecoin_address, + // // feltToAddress(BigInt(launch?.memecoin_address)), + // Number(amount), + // launch?.quote_token, + // undefined, + // ); + // }; + + // const buyCoin = async () => { + // if (!amount) return; + + // await onConnect(); + + // if (!account || !account?.account) return; + // console.log('launch', launch); + + // if (!launch?.owner) return; + + // if (!launch?.token_quote) return; + + // // handleBuyKeys(account?.account, launch?.owner, launch?.token_quote, Number(amount),) + // handleBuyCoins( + // account?.account, + // launch?.memecoin_address, + // // feltToAddress(BigInt(launch?.memecoin_address)), + // Number(amount), + // launch?.quote_token, + // ); + // }; + const sellKeys = async () => { + if (!amount) { + return showToast({ title: "Select an amount to buy", type: "info" }) + } await onConnect(); if (!account || !account?.account) return; - if (!launch?.owner) return; + if (!launch?.memecoin_address) return; + console.log("launch", launch) - if (!launch?.token_quote) return; + // if (!token?.quote_token) return; - // handleSellKeys(account?.account, launch?.owner, Number(amount), launch?.token_quote, undefined) - handleSellCoins( + const sellResult = await handleSellCoins( account?.account, - feltToAddress(BigInt(launch?.memecoin_address)), + launch?.memecoin_address, + // feltToAddress(BigInt(token?.memecoin_address)), Number(amount), - launch?.token_quote, + launch?.quote_token, undefined, ); + + if (sellResult) { + return showToast({ title: 'Buy done', type: "success" }) + } }; const buyCoin = async () => { - if (!amount) return; await onConnect(); + if (!amount) { + return showToast({ title: "Select an amount to buy", type: "info" }) + } if (!account || !account?.account) return; - console.log('launch', launch); - if (!launch?.owner) return; + console.log("launch", launch) - if (!launch?.token_quote) return; - // handleBuyKeys(account?.account, launch?.owner, launch?.token_quote, Number(amount),) - handleBuyCoins( + if (!launch?.memecoin_address) return; + // if (!token?.token_quote) return; + // handleBuyKeys(account?.account, token?.owner, token?.token_quote, Number(amount),) + const buyResult = await handleBuyCoins( account?.account, - feltToAddress(BigInt(launch?.memecoin_address)), + launch?.memecoin_address, + // feltToAddress(BigInt(token?.memecoin_address)), Number(amount), - launch?.token_quote, + launch?.quote_token, ); + + return showToast({ title: 'Buy done', type: "success" }) }; + + return ( @@ -131,7 +189,7 @@ export const TokenLaunchDetail: React.FC = ({ Supply: - {Number(launch?.total_supply) / 10 ** 18} + {Number(launch?.total_supply)} Price: @@ -139,7 +197,14 @@ export const TokenLaunchDetail: React.FC = ({ - {!isDisabledInfo && ( + { + setIsDisabledInfoState(!isDisabledInfoState) + }}> + + {isDisabledInfoState ? "View more info" : "Close info"} + + + {!isDisabledInfoState && ( <> {launch?.threshold_liquidity && ( @@ -163,6 +228,16 @@ export const TokenLaunchDetail: React.FC = ({ )} + + {launch?.quote_token && + + + Quote token: + {launch?.quote_token} + + + + } {launch?.token_quote && ( = ({ Step increase: - {Number(launch.token_quote?.step_increase_linear) / 10 ** 18} + {Number(launch.token_quote?.step_increase_linear)} )} diff --git a/apps/mobile/src/components/search/TokenLaunchCard/index.tsx b/apps/mobile/src/components/search/TokenLaunchCard/index.tsx index 87a87d05..92d318a3 100644 --- a/apps/mobile/src/components/search/TokenLaunchCard/index.tsx +++ b/apps/mobile/src/components/search/TokenLaunchCard/index.tsx @@ -128,7 +128,7 @@ export const TokenLaunchCard: React.FC = ({ {token?.memecoin_address && ( Meme Coin address: - {feltToAddress(BigInt(token.memecoin_address))} + {token?.memecoin_address} )} @@ -146,7 +146,7 @@ export const TokenLaunchCard: React.FC = ({ {token?.owner && ( Owner: - {feltToAddress(BigInt(token.owner))} + {token?.owner} )} diff --git a/apps/mobile/src/hooks/useCombinedTokens.ts b/apps/mobile/src/hooks/useCombinedTokens.ts index bd646679..4f329613 100644 --- a/apps/mobile/src/hooks/useCombinedTokens.ts +++ b/apps/mobile/src/hooks/useCombinedTokens.ts @@ -4,19 +4,19 @@ import { useGetTokenLaunch } from "./api/indexer/useLaunchTokens"; import { TokenDeployInterface } from "../types/keys"; - - export const useCombinedTokenData = (token?: string, launch?: string) => { const { data: deployData, isLoading: isLoadingDeploy, isError: isErrorDeploy, isFetching: tokenIsFetching } = - useGetDeployToken(token); + useGetDeployToken(token); const { data: launchData, isLoading: isLoadingLaunch, isError: isErrorLaunch, isFetching: launchIsFetching } = - useGetTokenLaunch(launch); + useGetTokenLaunch(launch); const [tokens, setTokens] = useState([]) const combinedData = useMemo(() => { - return [...(deployData?.data || []), ...(launchData?.data || [])]; + return [...(deployData?.data || []), + // ...(launchData?.data || []) + ]; }, [deployData, launchData]); useEffect(() => { diff --git a/apps/mobile/src/screens/LaunchDetail/index.tsx b/apps/mobile/src/screens/LaunchDetail/index.tsx index c9569d5d..fa8609c8 100644 --- a/apps/mobile/src/screens/LaunchDetail/index.tsx +++ b/apps/mobile/src/screens/LaunchDetail/index.tsx @@ -54,6 +54,7 @@ export const LaunchDetail: React.FC = ({ navigation, ro const [tokens, setTokens] = useState([]); const [token, setToken] = useState(); + const [launch, setLaunch] = useState(); const [holdings, setHoldings] = useState(); @@ -76,7 +77,7 @@ export const LaunchDetail: React.FC = ({ navigation, ro const { data: sharesData, isLoading: sharesLoading } = useGetShares(coinAddress, account?.address ?? ""); - const { data: tokenData, isLoading: tokenLoading } = useGetTokenLaunch(coinAddress) + const { data: launchData, isLoading: tokenLoading } = useGetTokenLaunch(coinAddress) const [selectedTab, setSelectedTab] = useState( SelectedTab.LAUNCH_OVERVIEW, @@ -98,11 +99,12 @@ export const LaunchDetail: React.FC = ({ navigation, ro useEffect(() => { - if (tokenData && tokenData.data) { - setTokens(tokenData?.data) - setToken(tokenData?.data) + if (launchData && launchData.data) { + setTokens(launchData?.data) + setToken(launchData?.data) + setLaunch(launchData.data) } - }, [tokenData]); + }, [launchData]); useEffect(() => { @@ -111,7 +113,6 @@ export const LaunchDetail: React.FC = ({ navigation, ro useEffect(() => { const data = transactionData || []; - console.log("data tx",data) setTransaction(data?.data); }, [transactionData]); @@ -229,14 +230,16 @@ export const LaunchDetail: React.FC = ({ navigation, ro > - {selectedTab == SelectedTab.LAUNCH_OVERVIEW && tokens && ( + + + + + {selectedTab == SelectedTab.LAUNCH_OVERVIEW && launch && ( <> - + )} - - {selectedTab == SelectedTab.LAUNCH_HOLDERS && ( <> @@ -260,9 +263,9 @@ export const LaunchDetail: React.FC = ({ navigation, ro )} - {selectedTab == SelectedTab.USER_SHARE && shares && ( + {selectedTab == SelectedTab.USER_SHARE && launch?.memecoin_address && ( <> - + )} diff --git a/apps/mobile/src/types/keys.ts b/apps/mobile/src/types/keys.ts index a98bae82..063c8e1a 100644 --- a/apps/mobile/src/types/keys.ts +++ b/apps/mobile/src/types/keys.ts @@ -1,4 +1,4 @@ -import {Uint256} from 'starknet'; +import { Uint256 } from 'starknet'; export interface KeysUser { owner: string; @@ -29,20 +29,10 @@ export interface TokenLaunchInterface { export interface UserShareInterface { - owner: string; - token_address: string; - price: Uint256; - initial_key_price: Uint256; - available_supply: Uint256; - bonding_curve_type?: BondingType; - total_supply: Uint256; - created_at: Uint256; - liquidity_raised: Uint256; - token_holded: Uint256; - is_liquidity_launch: boolean; - quote_token: TokenQuoteBuyKeys; - threshold_liquidity?: Uint256; - slope?: Uint256; + total: number; + total_buy: number; + total_sell: number; + quote_amount: number; } diff --git a/apps/mobile/src/types/tab.ts b/apps/mobile/src/types/tab.ts index 6b9621a7..0fd63182 100644 --- a/apps/mobile/src/types/tab.ts +++ b/apps/mobile/src/types/tab.ts @@ -229,12 +229,6 @@ export const TABS_LAUNCH: {screen?: string; title: string; tab: SelectedTab}[] = screen: 'Holders', tab: SelectedTab.LAUNCH_HOLDERS, }, - - { - title: 'STATS', - screen: 'Stats', - tab: SelectedTab.TOKEN_STATS, - }, { title: 'TX', screen: 'TX', @@ -246,6 +240,11 @@ export const TABS_LAUNCH: {screen?: string; title: string; tab: SelectedTab}[] = screen: 'Share', tab: SelectedTab.USER_SHARE, }, + { + title: 'STATS', + screen: 'Stats', + tab: SelectedTab.TOKEN_STATS, + }, ]; export const TABS_CASHU: {screen?: string; title: string; tab: SelectedTab}[] = [