Skip to content

Commit 9b8ed57

Browse files
authored
fix create launch hook (#519)
1 parent a10745d commit 9b8ed57

File tree

6 files changed

+283
-21
lines changed

6 files changed

+283
-21
lines changed

apps/mobile/src/hooks/launchpad/useCreateToken.ts

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import {LAUNCHPAD_ADDRESS} from 'common';
2-
import {AccountInterface, cairo, CairoCustomEnum, CallData, constants} from 'starknet';
1+
import { LAUNCHPAD_ADDRESS } from 'common';
2+
import { AccountInterface, cairo, CairoCustomEnum, CallData, constants } from 'starknet';
33

44
// import { LAUNCHPAD_ADDRESS, UNRUGGABLE_FACTORY_ADDRESS } from "../../constants/contracts";
5-
import {formatFloatToUint256} from '../../utils/format';
6-
import {BondingType} from '../../types/keys';
5+
import { formatFloatToUint256 } from '../../utils/format';
6+
import { BondingType } from '../../types/keys';
77
import { byteArray } from 'starknet';
88

99
export type DeployTokenFormValues = {
@@ -14,6 +14,8 @@ export type DeployTokenFormValues = {
1414
contract_address_salt: string | undefined;
1515
is_unruggable?: boolean;
1616
bonding_type?: BondingType;
17+
creator_fee_percent?: number;
18+
creator_fee_destination?: string;
1719
};
1820

1921
export const useCreateToken = () => {
@@ -29,14 +31,14 @@ export const useCreateToken = () => {
2931

3032
let initial_supply = formatFloatToUint256(data?.initialSupply ?? 100_000_000);
3133
console.log('initial supply', initial_supply);
32-
34+
;
3335
// if(Number.isNaN(initial_supply) && Number.isInteger(data?.initialSupply)){
3436
// initial_supply = cairo.uint256(data?.initialSupply)
3537
// }
3638
console.log('initial supply', initial_supply);
3739

38-
const nameByteArray= byteArray.byteArrayFromString(data.name ?? 'LFG')
39-
const symbolByteArray= byteArray.byteArrayFromString(data.symbol ?? 'LFG')
40+
const nameByteArray = byteArray.byteArrayFromString(data.name ?? 'LFG')
41+
const symbolByteArray = byteArray.byteArrayFromString(data.symbol ?? 'LFG')
4042
console.log("byteArray.byteArrayFromString(data.name ?? 'LFG'),", nameByteArray)
4143
console.log("byteArray.byteArrayFromString(data.symbol ?? 'LFG'),", symbolByteArray)
4244
const deployCall = {
@@ -56,6 +58,7 @@ export const useCreateToken = () => {
5658
// contract_address_salt: new Date().getTime(),
5759
// is_unruggable: false,
5860
is_unruggable: cairo.felt(String(data?.is_unruggable ?? false)),
61+
5962
// bonding_type:bondingEnum
6063
// contract_address_salt:CONTRACT_ADDRESS_SALT_DEFAULT + Math.random() + Math.random() / 1000
6164
// contract_address_salt:cairo.felt(Math.random())
@@ -86,7 +89,7 @@ export const useCreateToken = () => {
8689
const initial_supply = formatFloatToUint256(data?.initialSupply ?? 100_000_000);
8790

8891
// let bondingEnum = new CairoCustomEnum({Exponential: 1});
89-
let bondingEnum = new CairoCustomEnum({Linear: {}});
92+
let bondingEnum = new CairoCustomEnum({ Linear: {} });
9093
// let bondingEnum = new CairoCustomEnum({Linear: 0});
9194
// let bondingEnum = new CairoCustomEnum({Exponential: {}});
9295
console.log('[DEBUG] bondingEnum', bondingEnum);
@@ -96,19 +99,24 @@ export const useCreateToken = () => {
9699
if (data.bonding_type === BondingType.Linear) {
97100
console.log('[DEBUG] bondingEnum linear', data.bonding_type);
98101
// bondingEnum = new CairoCustomEnum({Linear: 0});
99-
bondingEnum = new CairoCustomEnum({Linear: {}});
102+
bondingEnum = new CairoCustomEnum({ Linear: {} });
100103
} else if (data.bonding_type === BondingType.Exponential) {
101104
console.log('[DEBUG] bondingEnum exp', data.bonding_type);
102105
// bondingEnum = new CairoCustomEnum({Exponential: 1});
103106
// bondingEnum = new CairoCustomEnum({Exponential: 3});
104-
bondingEnum = new CairoCustomEnum({Exponential: {}});
107+
bondingEnum = new CairoCustomEnum({ Exponential: {} });
105108
}
106109
}
107110
console.log('[DEBUG] bondingEnum updt', bondingEnum);
108111

109-
const nameByteArray= byteArray.byteArrayFromString(data.name ?? 'LFG')
110-
const symbolByteArray= byteArray.byteArrayFromString(data.symbol ?? 'LFG')
111-
112+
let creator_fee_percent = formatFloatToUint256(data?.creator_fee_percent ?? 0);
113+
console.log('creator fee percent', creator_fee_percent);
114+
115+
let creator_fee_destination = cairo.felt(data?.creator_fee_destination ?? account?.address)
116+
117+
const nameByteArray = byteArray.byteArrayFromString(data.name ?? 'LFG')
118+
const symbolByteArray = byteArray.byteArrayFromString(data.symbol ?? 'LFG')
119+
112120
console.log("byteArray.byteArrayFromString(data.name ?? 'LFG'),", nameByteArray)
113121
console.log("byteArray.byteArrayFromString(data.symbol ?? 'LFG'),", symbolByteArray)
114122
console.log('initial supply', initial_supply);
@@ -128,6 +136,8 @@ export const useCreateToken = () => {
128136
// is_unruggable: data?.is_unruggable
129137
is_unruggable: cairo.felt(String(data?.is_unruggable ?? false)),
130138
bonding_type: bondingEnum,
139+
creator_fee_percent: creator_fee_percent,
140+
creator_fee_destination: creator_fee_destination,
131141
}),
132142
};
133143

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
import { NDKEvent } from '@nostr-dev-kit/ndk';
2+
import { useAccount } from '@starknet-react/core';
3+
import { useQueryClient } from '@tanstack/react-query';
4+
import { useProfile } from 'afk_nostr_sdk';
5+
// import { useAuth } from '../../store/auth';
6+
import { useAuth } from 'afk_nostr_sdk';
7+
import { Formik, FormikProps } from 'formik';
8+
import { useMemo, useRef, useState } from 'react';
9+
import { ScrollView, TextInput, View } from 'react-native';
10+
11+
import { Button, SquareInput, Text } from '../../components';
12+
import { useStyles, useWaitConnection, useWindowDimensions } from '../../hooks';
13+
import { DeployTokenFormValues, useCreateToken } from '../../hooks/launchpad/useCreateToken';
14+
import { useToast, useWalletModal } from '../../hooks/modals';
15+
import stylesheet from '../../screens/CreateChannel/styles';
16+
import { TipSuccessModalProps } from '../TipSuccessModal';
17+
import { Picker } from '@react-native-picker/picker';
18+
import { BondingType } from '../../types/keys';
19+
import { numericValue } from '../../utils/format';
20+
import { useTokenCreatedModal } from '../../hooks/modals/useTokenCreateModal';
21+
import { LoadingSpinner } from '../../components/Loading';
22+
import { byteArray } from 'starknet';
23+
enum TypeCreate {
24+
LAUNCH,
25+
CREATE,
26+
CREATE_AND_LAUNCH,
27+
}
28+
export type FormTokenCreatedProps = {
29+
event?: NDKEvent;
30+
starknetAddress?: string;
31+
hide?: () => void;
32+
showSuccess?: (props: TipSuccessModalProps) => void;
33+
hideSuccess?: () => void;
34+
};
35+
36+
type FormValues = DeployTokenFormValues;
37+
export const FormLaunchToken: React.FC<FormTokenCreatedProps> = () => {
38+
const [loading, setLoading] = useState(false)
39+
const formikRef = useRef<FormikProps<FormValues>>(null);
40+
const { hide: hideTokenCreateModal } = useTokenCreatedModal();
41+
const walletModal = useWalletModal();
42+
const styles = useStyles(stylesheet);
43+
const publicKey = useAuth((state) => state.publicKey);
44+
const profile = useProfile({ publicKey });
45+
const queryClient = useQueryClient();
46+
const { showToast } = useToast();
47+
const account = useAccount();
48+
const waitConnection = useWaitConnection();
49+
const { deployToken, deployTokenAndLaunch } = useCreateToken();
50+
51+
const [type, setType] = useState(TypeCreate.CREATE);
52+
const initialFormValues: FormValues = {
53+
name: '',
54+
symbol: '',
55+
bonding_type: BondingType.Linear,
56+
// ticker: '',
57+
initialSupply: undefined,
58+
contract_address_salt: new Date().getTime()?.toString(),
59+
recipient: account?.address,
60+
is_unruggable:false
61+
};
62+
63+
const onSubmitPress = (type: TypeCreate) => {
64+
setType(type);
65+
formikRef.current?.handleSubmit();
66+
};
67+
68+
const validateForm = (values: FormValues) => {
69+
const errors = {} as Partial<FormValues>;
70+
// TODO: Do validation
71+
72+
return errors;
73+
};
74+
75+
const onFormSubmit = async (values: FormValues) => {
76+
try {
77+
console.log('onFormSubmit deploy');
78+
if (!account.address) {
79+
walletModal.show();
80+
const result = await waitConnection();
81+
if (!result) return;
82+
}
83+
84+
if (!account || !account?.account) return;
85+
console.log('test deploy');
86+
87+
if (!values?.symbol) {
88+
return showToast({ type: 'info', title: 'Add symbol' });
89+
} else if (!values?.name) {
90+
return showToast({ type: 'info', title: 'Add name' });
91+
}
92+
else if (!values?.initialSupply) {
93+
return showToast({ type: 'info', title: 'Initial supply required' });
94+
}
95+
96+
let tx;
97+
setLoading(true)
98+
if (type == TypeCreate.CREATE) {
99+
const data: DeployTokenFormValues = {
100+
recipient: account?.address,
101+
// name: byteArray.byteArrayFromString(values.name),
102+
name:values.name,
103+
symbol: values.symbol,
104+
initialSupply: values?.initialSupply,
105+
contract_address_salt: values.contract_address_salt,
106+
is_unruggable: values.is_unruggable ?? false,
107+
};
108+
109+
tx = await deployToken(account?.account, data).catch(err => {
110+
showToast({ type: 'error', title: err?.message || "Something went wrong" });
111+
setLoading(false)
112+
});
113+
114+
115+
} else {
116+
const data: DeployTokenFormValues = {
117+
recipient: account?.address,
118+
name: values.name,
119+
symbol: values.symbol,
120+
initialSupply: values?.initialSupply,
121+
contract_address_salt: values.contract_address_salt,
122+
is_unruggable: values.is_unruggable ?? false,
123+
bonding_type: values.bonding_type,
124+
};
125+
tx = await deployTokenAndLaunch(account?.account, data).catch(err => {
126+
// showToast({ type: 'error', title: err?.message || "Something went wrong" });
127+
showToast({ type: 'error', title: "Something went wrong when deploy token and launch", description: err?.message || "Something went wrong" });
128+
129+
setLoading(false)
130+
});
131+
}
132+
133+
if (tx) {
134+
showToast({ type: 'success', title: 'Token launch created successfully' });
135+
hideTokenCreateModal?.()
136+
setLoading(false)
137+
}
138+
} catch (error) {
139+
140+
showToast({ type: 'error', title: 'Failed to create token and launch' });
141+
setLoading(false)
142+
}
143+
144+
};
145+
146+
if (profile.isLoading) return null;
147+
148+
const dimensions = useWindowDimensions();
149+
const isDesktop = useMemo(() => {
150+
return dimensions.width >= 1024;
151+
}, [dimensions]);
152+
153+
return (
154+
<ScrollView
155+
automaticallyAdjustKeyboardInsets
156+
style={styles.container}
157+
contentContainerStyle={
158+
isDesktop ? styles.contentContainerDesktop : styles.contentContainerMobile
159+
}
160+
>
161+
<Formik
162+
innerRef={formikRef}
163+
initialValues={initialFormValues}
164+
onSubmit={onFormSubmit}
165+
validate={validateForm}
166+
>
167+
{({ handleChange, handleBlur, values, errors, setFieldValue }) => (
168+
<View style={styles.form}>
169+
<View style={styles.inputContainer}>
170+
<Text style={styles.inputLabel}>Name</Text>
171+
<TextInput
172+
value={values.name}
173+
onChangeText={handleChange('name')}
174+
onBlur={handleBlur('name')}
175+
placeholder="AFK Token"
176+
style={styles.input}
177+
/>
178+
</View>
179+
180+
<View style={styles.inputContainer}>
181+
<Text style={styles.inputLabel}>Symbol</Text>
182+
<TextInput
183+
value={values.symbol}
184+
onChangeText={handleChange('symbol')}
185+
onBlur={handleBlur('symbol')}
186+
placeholder="AFK"
187+
style={styles.input}
188+
/>
189+
</View>
190+
191+
<View style={styles.inputContainer}>
192+
<Text style={styles.inputLabel}>Total Supply</Text>
193+
<TextInput
194+
// type="number"
195+
value={values.initialSupply?.toString()}
196+
onChangeText={(text) => setFieldValue("initialSupply", numericValue(text))}
197+
onBlur={handleBlur('initialSupply')}
198+
placeholder="100000"
199+
inputMode="numeric"
200+
keyboardType="numeric"
201+
style={styles.input}
202+
203+
204+
/>
205+
</View>
206+
207+
<View style={styles.inputContainer}>
208+
<Text style={styles.inputLabel}>Bonding Type</Text>
209+
<Picker
210+
selectedValue={values.bonding_type}
211+
onValueChange={(itemValue) => {
212+
formikRef.current?.setFieldValue('bonding_type', Number(itemValue));
213+
}}
214+
style={styles.input}
215+
>
216+
{Object.keys(BondingType)
217+
.filter((key) => isNaN(Number(key)))
218+
.map((bonding) => (
219+
<Picker.Item
220+
key={bonding}
221+
label={bonding}
222+
value={BondingType[bonding as keyof typeof BondingType]}
223+
/>
224+
))}
225+
</Picker>
226+
</View>
227+
228+
<Button disabled={loading} variant="primary" onPress={() => onSubmitPress(TypeCreate.CREATE)}>
229+
Create
230+
{loading && type == TypeCreate.CREATE &&
231+
<LoadingSpinner size={14} />
232+
}
233+
</Button>
234+
235+
<Button disabled={loading} variant="primary" onPress={() => onSubmitPress(TypeCreate.CREATE_AND_LAUNCH)}>
236+
Create & Launch
237+
{loading && type !== TypeCreate.CREATE &&
238+
<LoadingSpinner size={14} />
239+
}
240+
</Button>
241+
242+
<View style={styles.gap} />
243+
</View>
244+
)}
245+
</Formik>
246+
</ScrollView>
247+
);
248+
};

apps/mobile/src/modules/LaunchTokenPump/FormLaunchToken.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,10 @@ export const FormLaunchToken: React.FC<FormTokenCreatedProps> = () => {
5757
initialSupply: undefined,
5858
contract_address_salt: new Date().getTime()?.toString(),
5959
recipient: account?.address,
60-
is_unruggable:false
60+
is_unruggable:false,
61+
creator_fee_percent: 0,
62+
creator_fee_destination: account?.address,
63+
6164
};
6265

6366
const onSubmitPress = (type: TypeCreate) => {

onchain/cairo/launchpad/src/launchpad/launchpad.cairo

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ pub mod LaunchpadMarketplace {
9494
const SLIPPAGE_THRESHOLD: u256 = 100; //1%
9595

9696
// TODO Used in V2 and be choose by user
97+
const ZERO_FEE_AMOUNT: u256 = 0; //1%
9798
const MIN_FEE_CREATOR: u256 = 100; //1%
9899
const MID_FEE_CREATOR: u256 = 1000; //10%
99100
const MAX_FEE_CREATOR: u256 = 5000; //50%
@@ -1390,7 +1391,7 @@ pub mod LaunchpadMarketplace {
13901391
// let creator_fee_percent = self.creator_fee_percent.read();
13911392

13921393
assert(
1393-
creator_fee_percent <= MAX_FEE_CREATOR && creator_fee_percent >= MIN_FEE_CREATOR,
1394+
creator_fee_percent <= MAX_FEE_CREATOR && creator_fee_percent >= ZERO_FEE_AMOUNT,
13941395
errors::CREATOR_FEE_OUT_OF_BOUNDS
13951396
);
13961397

packages/common/src/contracts.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,9 @@ export const LAUNCHPAD_ADDRESS = {
5656

5757
// [constants.StarknetChainId.SN_SEPOLIA]:
5858
// "0x2b4d93fc565381d1911f3f449f615de050b72d297fc95d89dda0301d7d35a37",
59+
// [constants.StarknetChainId.SN_SEPOLIA]: "0x7fbf067657772a454c302354a19e07ce0a920736e2e3b7ca605d813723db883"
5960
[constants.StarknetChainId.SN_SEPOLIA]: "0x7fbf067657772a454c302354a19e07ce0a920736e2e3b7ca605d813723db883"
60-
61+
// [constants.StarknetChainId.SN_SEPOLIA]: "0x1001bff43b6e171161be4d6c9025d0839caa745e199ccf720449cc5bf89166"
6162
};
6263

6364
export const ESCROW_ADDRESS = {

0 commit comments

Comments
 (0)