@@ -33,6 +33,7 @@ import (
3333 "github.com/code-payments/code-server/pkg/kin"
3434 "github.com/code-payments/code-server/pkg/pointer"
3535 push_lib "github.com/code-payments/code-server/pkg/push"
36+ "github.com/code-payments/code-server/pkg/solana"
3637)
3738
3839var accountTypesToOpen = []commonpb.AccountType {
@@ -218,7 +219,7 @@ func (h *OpenAccountsIntentHandler) AllowCreation(ctx context.Context, intentRec
218219 // Part 4: Validate the individual actions
219220 //
220221
221- err = h .validateActions (initiatiorOwnerAccount , actions )
222+ err = h .validateActions (ctx , initiatiorOwnerAccount , actions )
222223 if err != nil {
223224 return err
224225 }
@@ -239,7 +240,7 @@ func (h *OpenAccountsIntentHandler) AllowCreation(ctx context.Context, intentRec
239240 return validateFeePayments (ctx , h .data , intentRecord , simResult )
240241}
241242
242- func (h * OpenAccountsIntentHandler ) validateActions (initiatiorOwnerAccount * common.Account , actions []* transactionpb.Action ) error {
243+ func (h * OpenAccountsIntentHandler ) validateActions (ctx context. Context , initiatiorOwnerAccount * common.Account , actions []* transactionpb.Action ) error {
243244 expectedActionCount := len (accountTypesToOpen )
244245 if len (actions ) != expectedActionCount {
245246 return newIntentValidationErrorf ("expected %d total actions" , expectedActionCount )
@@ -283,6 +284,10 @@ func (h *OpenAccountsIntentHandler) validateActions(initiatiorOwnerAccount *comm
283284 if ! bytes .Equal (openAction .GetOpenAccount ().Token .Value , expectedVaultAccount .PublicKey ().ToBytes ()) {
284285 return newActionValidationErrorf (openAction , "token must be %s" , expectedVaultAccount .PublicKey ().ToBase58 ())
285286 }
287+
288+ if err := validateTimelockUnlockStateDoesntExist (ctx , h .data , openAction .GetOpenAccount ()); err != nil {
289+ return err
290+ }
286291 }
287292
288293 return nil
@@ -758,6 +763,8 @@ func (h *SendPrivatePaymentIntentHandler) validateActions(
758763 }
759764
760765 err = validateGiftCardAccountOpened (
766+ ctx ,
767+ h .data ,
761768 initiatorOwnerAccount ,
762769 initiatorAccountsByType ,
763770 destination ,
@@ -2137,10 +2144,10 @@ func (h *EstablishRelationshipIntentHandler) AllowCreation(ctx context.Context,
21372144 // Part 8: Validate the individual actions
21382145 //
21392146
2140- return h .validateActions (initiatiorOwnerAccount , actions )
2147+ return h .validateActions (ctx , initiatiorOwnerAccount , actions )
21412148}
21422149
2143- func (h * EstablishRelationshipIntentHandler ) validateActions (initiatiorOwnerAccount * common.Account , actions []* transactionpb.Action ) error {
2150+ func (h * EstablishRelationshipIntentHandler ) validateActions (ctx context. Context , initiatiorOwnerAccount * common.Account , actions []* transactionpb.Action ) error {
21442151 if len (actions ) != 1 {
21452152 return newIntentValidationError ("expected 1 action" )
21462153 }
@@ -2166,6 +2173,10 @@ func (h *EstablishRelationshipIntentHandler) validateActions(initiatiorOwnerAcco
21662173 return newActionValidationErrorf (openAction , "authority cannot be %s" , initiatiorOwnerAccount .PublicKey ().ToBase58 ())
21672174 }
21682175
2176+ if err := validateTimelockUnlockStateDoesntExist (ctx , h .data , openAction .GetOpenAccount ()); err != nil {
2177+ return err
2178+ }
2179+
21692180 return nil
21702181}
21712182
@@ -2476,6 +2487,8 @@ func validateNextTemporaryAccountOpened(
24762487
24772488// Assumes only one gift card account is opened per intent
24782489func validateGiftCardAccountOpened (
2490+ ctx context.Context ,
2491+ data code_data.Provider ,
24792492 initiatorOwnerAccount * common.Account ,
24802493 initiatorAccountsByType map [commonpb.AccountType ][]* common.AccountRecords ,
24812494 expectedGiftCardVault * common.Account ,
@@ -2524,6 +2537,10 @@ func validateGiftCardAccountOpened(
25242537 return newActionValidationErrorf (openAction , "token must be %s" , derivedVaultAccount .PublicKey ().ToBase58 ())
25252538 }
25262539
2540+ if err := validateTimelockUnlockStateDoesntExist (ctx , data , openAction .GetOpenAccount ()); err != nil {
2541+ return err
2542+ }
2543+
25272544 return nil
25282545}
25292546
@@ -2861,6 +2878,28 @@ func validateTipDestination(ctx context.Context, data code_data.Provider, tipped
28612878 return nil
28622879}
28632880
2881+ func validateTimelockUnlockStateDoesntExist (ctx context.Context , data code_data.Provider , openAction * transactionpb.OpenAccountAction ) error {
2882+ authorityAccount , err := common .NewAccountFromProto (openAction .Authority )
2883+ if err != nil {
2884+ return err
2885+ }
2886+
2887+ timelockAccounts , err := authorityAccount .GetTimelockAccounts (common .CodeVmAccount , common .KinMintAccount )
2888+ if err != nil {
2889+ return err
2890+ }
2891+
2892+ _ , err = data .GetBlockchainAccountInfo (ctx , timelockAccounts .Unlock .PublicKey ().ToBase58 (), solana .CommitmentFinalized )
2893+ switch err {
2894+ case nil :
2895+ return newIntentDeniedError ("an account being opened has already initiated an unlock" )
2896+ case solana .ErrNoAccountInfo :
2897+ return nil
2898+ default :
2899+ return err
2900+ }
2901+ }
2902+
28642903func getExpectedTimelockVaultFromProtoAccount (authorityProto * commonpb.SolanaAccountId ) (* common.Account , error ) {
28652904 authorityAccount , err := common .NewAccountFromProto (authorityProto )
28662905 if err != nil {
0 commit comments