1
+ import * as crypto from "crypto" ;
2
+
1
3
import { AnchorProvider , BN , Program } from "@coral-xyz/anchor" ;
4
+ import {
5
+ getTokenOwnerRecordAddress ,
6
+ PROGRAM_VERSION_V2 ,
7
+ withCreateTokenOwnerRecord ,
8
+ } from "@solana/spl-governance" ;
2
9
import {
3
10
type Account ,
4
11
createTransferInstruction ,
@@ -9,10 +16,12 @@ import type { AnchorWallet } from "@solana/wallet-adapter-react";
9
16
import {
10
17
Connection ,
11
18
PublicKey ,
19
+ SystemProgram ,
12
20
Transaction ,
13
21
TransactionInstruction ,
14
22
} from "@solana/web3.js" ;
15
23
24
+ import { GOVERNANCE_ADDRESS , POSITIONS_ACCOUNT_SIZE } from "./constants" ;
16
25
import {
17
26
getConfigAddress ,
18
27
getPoolConfigAddress ,
@@ -334,6 +343,99 @@ export class PythStakingClient {
334
343
return sendTransaction ( instructions , this . connection , this . wallet ) ;
335
344
}
336
345
346
+ public async hasGovernanceRecord ( config : GlobalConfig ) : Promise < boolean > {
347
+ const tokenOwnerRecordAddress = await getTokenOwnerRecordAddress (
348
+ GOVERNANCE_ADDRESS ,
349
+ config . pythGovernanceRealm ,
350
+ config . pythTokenMint ,
351
+ this . wallet . publicKey ,
352
+ ) ;
353
+ const voterAccountInfo =
354
+ await this . stakingProgram . provider . connection . getAccountInfo (
355
+ tokenOwnerRecordAddress ,
356
+ ) ;
357
+
358
+ return Boolean ( voterAccountInfo ) ;
359
+ }
360
+
361
+ public async createStakeAccountAndDeposit ( amount : bigint ) {
362
+ const globalConfig = await this . getGlobalConfig ( ) ;
363
+
364
+ const senderTokenAccount = await getAssociatedTokenAddress (
365
+ globalConfig . pythTokenMint ,
366
+ this . wallet . publicKey ,
367
+ ) ;
368
+
369
+ const nonce = crypto . randomBytes ( 16 ) . toString ( "hex" ) ;
370
+ const stakeAccountPositions = await PublicKey . createWithSeed (
371
+ this . wallet . publicKey ,
372
+ nonce ,
373
+ this . stakingProgram . programId ,
374
+ ) ;
375
+
376
+ const minimumBalance =
377
+ await this . stakingProgram . provider . connection . getMinimumBalanceForRentExemption (
378
+ POSITIONS_ACCOUNT_SIZE ,
379
+ ) ;
380
+
381
+ const instructions = [ ] ;
382
+
383
+ instructions . push (
384
+ SystemProgram . createAccountWithSeed ( {
385
+ fromPubkey : this . wallet . publicKey ,
386
+ newAccountPubkey : stakeAccountPositions ,
387
+ basePubkey : this . wallet . publicKey ,
388
+ seed : nonce ,
389
+ lamports : minimumBalance ,
390
+ space : POSITIONS_ACCOUNT_SIZE ,
391
+ programId : this . stakingProgram . programId ,
392
+ } ) ,
393
+ await this . stakingProgram . methods
394
+ . createStakeAccount ( this . wallet . publicKey , { fullyVested : { } } )
395
+ . accounts ( {
396
+ stakeAccountPositions,
397
+ } )
398
+ . instruction ( ) ,
399
+ await this . stakingProgram . methods
400
+ . createVoterRecord ( )
401
+ . accounts ( {
402
+ stakeAccountPositions,
403
+ } )
404
+ . instruction ( ) ,
405
+ ) ;
406
+
407
+ if ( ! ( await this . hasGovernanceRecord ( globalConfig ) ) ) {
408
+ await withCreateTokenOwnerRecord (
409
+ instructions ,
410
+ GOVERNANCE_ADDRESS ,
411
+ PROGRAM_VERSION_V2 ,
412
+ globalConfig . pythGovernanceRealm ,
413
+ this . wallet . publicKey ,
414
+ globalConfig . pythTokenMint ,
415
+ this . wallet . publicKey ,
416
+ ) ;
417
+ }
418
+
419
+ instructions . push (
420
+ await this . stakingProgram . methods
421
+ . joinDaoLlc ( globalConfig . agreementHash )
422
+ . accounts ( {
423
+ stakeAccountPositions,
424
+ } )
425
+ . instruction ( ) ,
426
+ createTransferInstruction (
427
+ senderTokenAccount ,
428
+ getStakeAccountCustodyAddress ( stakeAccountPositions ) ,
429
+ this . wallet . publicKey ,
430
+ amount ,
431
+ ) ,
432
+ ) ;
433
+
434
+ await sendTransaction ( instructions , this . connection , this . wallet ) ;
435
+
436
+ return stakeAccountPositions ;
437
+ }
438
+
337
439
public async depositTokensToStakeAccountCustody (
338
440
stakeAccountPositions : PublicKey ,
339
441
amount : bigint ,
0 commit comments