@@ -11,11 +11,14 @@ import {
1111} from "@solana/spl-token" ;
1212import {
1313 getConfigPDA ,
14+ getFaucetStatePDA ,
1415 getMintPDA ,
1516 getProgram ,
1617 getStakeStatePDA ,
1718 getTokenProgramId ,
1819 getVaultPDA ,
20+ PUBLIC_FAUCET_MAX_BASE_UNITS ,
21+ TOKEN_BASE_UNITS ,
1922} from "../utils/anchor" ;
2023
2124interface StakePanelProps {
@@ -26,9 +29,9 @@ interface StakePanelProps {
2629 * StakePanel component allows users to stake DACIT tokens.
2730 * Connects to the Anchor program and submits stake transactions.
2831 *
29- * NOTE: This panel is wired to the generated Anchor IDL and the active devnet
30- * program address. The staking transaction still assumes the program has been
31- * initialized and the connected wallet already holds DACIT tokens .
32+ * NOTE: This panel is wired to the generated Anchor IDL and active devnet
33+ * program address. It can bootstrap first-time wallets by initializing the
34+ * program, creating the ATA, and using the public faucet when needed .
3235 */
3336export default function StakePanel ( { className = "" } : StakePanelProps ) {
3437 const { connection } = useConnection ( ) ;
@@ -52,13 +55,15 @@ export default function StakePanel({ className = "" }: StakePanelProps) {
5255 throw new Error ( "Connected wallet does not support Anchor transactions." ) ;
5356 }
5457
55- const stakeAmount = BigInt ( Math . trunc ( amount ) ) ;
58+ const normalizedAmount = Number . isFinite ( amount ) ? amount : 0 ;
59+ const stakeAmount = BigInt ( Math . round ( normalizedAmount * TOKEN_BASE_UNITS ) ) ;
5660 if ( stakeAmount <= 0n ) {
57- throw new Error ( "Enter a whole-number DACIT amount greater than zero." ) ;
61+ throw new Error ( "Enter a DACIT amount greater than zero." ) ;
5862 }
5963
6064 const program = getProgram ( wallet as AnchorWallet ) ;
6165 const [ configPda ] = getConfigPDA ( ) ;
66+ const [ faucetStatePda ] = getFaucetStatePDA ( wallet . publicKey ) ;
6267 const [ stakeStatePda ] = getStakeStatePDA ( wallet . publicKey ) ;
6368 const [ mintPda ] = getMintPDA ( ) ;
6469 const [ vaultPda ] = getVaultPDA ( mintPda ) ;
@@ -107,28 +112,46 @@ export default function StakePanel({ className = "" }: StakePanelProps) {
107112 const userAccount = await getAccount ( connection , userAta ) ;
108113 const currentBalance = userAccount . amount ;
109114 if ( currentBalance < stakeAmount ) {
110- if ( ! configAuthority || ! configAuthority . equals ( wallet . publicKey ) ) {
111- throw new Error (
112- `Wallet balance is ${ currentBalance . toString ( ) } DACIT. Connect the authority wallet to mint test tokens or lower the stake amount.`
113- ) ;
114- }
115-
116115 const topUpAmount = stakeAmount - currentBalance ;
117- setStatus ( `Minting ${ topUpAmount . toString ( ) } DACIT from the devnet faucet...` ) ;
118- await program . methods
119- . mintToUser ( new anchor . BN ( topUpAmount . toString ( ) ) )
120- . accounts ( {
121- config : configPda ,
122- mint : mintPda ,
123- userAta,
124- user : wallet . publicKey ,
125- authority : wallet . publicKey ,
126- tokenProgram,
127- systemProgram : anchor . web3 . SystemProgram . programId ,
128- associatedTokenProgram : anchor . utils . token . ASSOCIATED_PROGRAM_ID ,
129- rent : anchor . web3 . SYSVAR_RENT_PUBKEY ,
130- } )
131- . rpc ( ) ;
116+ if ( configAuthority && configAuthority . equals ( wallet . publicKey ) ) {
117+ setStatus ( `Minting ${ ( Number ( topUpAmount ) / TOKEN_BASE_UNITS ) . toFixed ( 9 ) } DACIT from the authority faucet...` ) ;
118+ await program . methods
119+ . mintToUser ( new anchor . BN ( topUpAmount . toString ( ) ) )
120+ . accounts ( {
121+ config : configPda ,
122+ mint : mintPda ,
123+ userAta,
124+ user : wallet . publicKey ,
125+ authority : wallet . publicKey ,
126+ tokenProgram,
127+ systemProgram : anchor . web3 . SystemProgram . programId ,
128+ associatedTokenProgram : anchor . utils . token . ASSOCIATED_PROGRAM_ID ,
129+ rent : anchor . web3 . SYSVAR_RENT_PUBKEY ,
130+ } )
131+ . rpc ( ) ;
132+ } else {
133+ if ( topUpAmount > BigInt ( PUBLIC_FAUCET_MAX_BASE_UNITS ) ) {
134+ throw new Error (
135+ `Need ${ ( Number ( topUpAmount ) / TOKEN_BASE_UNITS ) . toFixed ( 9 ) } DACIT, but the public faucet caps each claim at ${ ( PUBLIC_FAUCET_MAX_BASE_UNITS / TOKEN_BASE_UNITS ) . toFixed ( 9 ) } DACIT. Lower the stake amount or wait for another faucet claim.`
136+ ) ;
137+ }
138+
139+ setStatus ( `Claiming ${ ( Number ( topUpAmount ) / TOKEN_BASE_UNITS ) . toFixed ( 9 ) } DACIT from the public faucet...` ) ;
140+ await program . methods
141+ . claimFaucet ( new anchor . BN ( topUpAmount . toString ( ) ) )
142+ . accounts ( {
143+ config : configPda ,
144+ mint : mintPda ,
145+ faucetState : faucetStatePda ,
146+ user : wallet . publicKey ,
147+ userAta,
148+ tokenProgram,
149+ systemProgram : anchor . web3 . SystemProgram . programId ,
150+ associatedTokenProgram : anchor . utils . token . ASSOCIATED_PROGRAM_ID ,
151+ rent : anchor . web3 . SYSVAR_RENT_PUBKEY ,
152+ } )
153+ . rpc ( ) ;
154+ }
132155 }
133156
134157 setStatus ( "Submitting stake transaction..." ) ;
@@ -148,7 +171,7 @@ export default function StakePanel({ className = "" }: StakePanelProps) {
148171 } )
149172 . rpc ( ) ;
150173
151- alert ( `Successfully staked ${ amount } DACIT tokens!` ) ;
174+ alert ( `Successfully staked ${ normalizedAmount } DACIT tokens!` ) ;
152175 setAmount ( 0 ) ;
153176 setStatus ( null ) ;
154177 } catch ( err ) {
@@ -188,6 +211,7 @@ export default function StakePanel({ className = "" }: StakePanelProps) {
188211 type = "number"
189212 value = { amount }
190213 min = { 0 }
214+ step = "0.000000001"
191215 onChange = { ( e ) => setAmount ( Number ( e . target . value ) ) }
192216 className = "w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
193217 placeholder = "Enter amount"
@@ -215,8 +239,8 @@ export default function StakePanel({ className = "" }: StakePanelProps) {
215239
216240 { wallet . publicKey && (
217241 < p className = "text-xs text-gray-500 text-center" >
218- First run will initialize the program, create your ATA, and mint DACIT if this wallet
219- is the configured authority .
242+ First run will initialize the program, create your ATA, and claim up to 100 DACIT
243+ from the public faucet when needed .
220244 </ p >
221245 ) }
222246 </ div >
0 commit comments