@@ -10,6 +10,7 @@ import {
1010 TOKEN_ADDRESSES
1111} from "@/lib/soroban" ;
1212import { hasValidPrecision , validateAmountInput } from "@/utils/amount" ;
13+ import { isValidStellarPublicKey } from "@/lib/stellar" ;
1314import { toast } from "react-hot-toast" ;
1415import { useRouter } from "next/navigation" ;
1516import Link from "next/link" ;
@@ -38,13 +39,30 @@ export default function CreateStreamPage() {
3839 return ;
3940 }
4041
42+ // Validate recipient
43+ if ( ! formData . recipient . trim ( ) ) {
44+ toast . error ( "Recipient address is required" ) ;
45+ return ;
46+ }
47+ if ( ! isValidStellarPublicKey ( formData . recipient ) ) {
48+ toast . error ( "Invalid Stellar public key format" ) ;
49+ return ;
50+ }
51+
4152 // Validate amount
4253 const validationError = validateAmountInput ( formData . amount , TOKEN_DECIMALS ) ;
4354 if ( validationError ) {
4455 toast . error ( validationError ) ;
4556 return ;
4657 }
4758
59+ // Validate duration
60+ const durationNum = parseFloat ( formData . duration ) ;
61+ if ( isNaN ( durationNum ) || durationNum <= 0 ) {
62+ toast . error ( "Duration must be a positive number" ) ;
63+ return ;
64+ }
65+
4866 setLoading ( true ) ;
4967 setTxState ( "signing" ) ;
5068
@@ -94,6 +112,16 @@ export default function CreateStreamPage() {
94112 ? validateAmountInput ( formData . amount , TOKEN_DECIMALS )
95113 : null ;
96114
115+ const recipientError = formData . recipient
116+ ? ( ! isValidStellarPublicKey ( formData . recipient ) ? "Invalid Stellar public key format" : null )
117+ : null ;
118+
119+ const durationError = formData . duration
120+ ? ( isNaN ( Number ( formData . duration ) ) || Number ( formData . duration ) <= 0
121+ ? "Duration must be a positive number"
122+ : null )
123+ : null ;
124+
97125 return (
98126 < div className = "container mx-auto max-w-2xl px-4 py-12" >
99127 < Link
@@ -118,11 +146,16 @@ export default function CreateStreamPage() {
118146 < input
119147 type = "text"
120148 placeholder = "G..."
121- className = "w-full rounded-xl border border-slate-800 bg-slate-900/50 p-4 outline-none focus:border-accent transition-colors"
149+ className = { `w-full rounded-xl border ${
150+ recipientError ? "border-red-500 focus:border-red-500" : "border-slate-800 focus:border-accent"
151+ } bg-slate-900/50 p-4 outline-none transition-colors`}
122152 value = { formData . recipient }
123153 onChange = { ( e ) => setFormData ( { ...formData , recipient : e . target . value } ) }
124154 required
125155 />
156+ { recipientError && (
157+ < p className = "text-xs text-red-400 mt-1" > { recipientError } </ p >
158+ ) }
126159 </ div >
127160
128161 < div className = "grid grid-cols-2 gap-4" >
@@ -150,7 +183,9 @@ export default function CreateStreamPage() {
150183 type = "text"
151184 inputMode = "decimal"
152185 placeholder = "0.00"
153- className = "w-full rounded-xl border border-slate-800 bg-slate-900/50 p-4 outline-none focus:border-accent transition-colors"
186+ className = { `w-full rounded-xl border ${
187+ amountError ? "border-red-500 focus:border-red-500" : "border-slate-800 focus:border-accent"
188+ } bg-slate-900/50 p-4 outline-none transition-colors`}
154189 value = { formData . amount }
155190 onChange = { ( e ) => {
156191 const newValue = e . target . value ;
@@ -176,33 +211,40 @@ export default function CreateStreamPage() {
176211 < input
177212 type = "number"
178213 placeholder = "30"
179- className = "w-full rounded-xl border border-slate-800 bg-slate-900/50 p-4 outline-none focus:border-accent transition-colors"
214+ className = { `w-full rounded-xl border ${
215+ durationError ? "border-red-500 focus:border-red-500" : "border-slate-800 focus:border-accent"
216+ } bg-slate-900/50 p-4 outline-none transition-colors`}
180217 value = { formData . duration }
181218 onChange = { ( e ) => setFormData ( { ...formData , duration : e . target . value } ) }
182219 required
183220 />
221+ { durationError && (
222+ < p className = "text-xs text-red-400 mt-1" > { durationError } </ p >
223+ ) }
184224 </ div >
185225
186226 < div className = "rounded-2xl bg-accent/5 p-6 space-y-4" >
187227 < div className = "flex justify-between items-center text-sm" >
188228 < span className = "text-slate-400" > Streaming Rate</ span >
189229 < span className = "font-mono font-medium text-accent" >
190- { formData . amount && formData . duration
230+ { formData . amount && formData . duration && Number ( formData . duration ) > 0
191231 ? ( Number ( formData . amount ) / ( Number ( formData . duration ) * 86400 ) ) . toFixed ( 8 )
192232 : "0.00000000" } { formData . token } /sec
193233 </ span >
194234 </ div >
195235 < div className = "flex justify-between items-center text-sm" >
196236 < span className = "text-slate-400" > Estimated End Date</ span >
197237 < span className = "font-medium" >
198- { new Date ( nowTimestamp + Number ( formData . duration || 0 ) * 86400000 ) . toLocaleDateString ( ) }
238+ { formData . duration && Number ( formData . duration ) > 0
239+ ? new Date ( nowTimestamp + Number ( formData . duration ) * 86400000 ) . toLocaleDateString ( )
240+ : "—" }
199241 </ span >
200242 </ div >
201243 </ div >
202244
203245 < button
204246 type = "submit"
205- disabled = { loading || status !== "connected" }
247+ disabled = { loading || status !== "connected" || ! ! amountError || ! ! recipientError || ! ! durationError }
206248 className = "w-full rounded-xl bg-accent py-4 text-lg font-bold text-background transition-all hover:opacity-90 disabled:opacity-50 active:scale-[0.98]"
207249 >
208250 { getButtonText ( ) }
0 commit comments