@@ -21,6 +21,7 @@ import (
2121 "github.com/code-payments/code-server/pkg/code/data/account"
2222 "github.com/code-payments/code-server/pkg/code/data/action"
2323 "github.com/code-payments/code-server/pkg/code/data/intent"
24+ "github.com/code-payments/code-server/pkg/code/data/timelock"
2425 currency_lib "github.com/code-payments/code-server/pkg/currency"
2526 "github.com/code-payments/code-server/pkg/solana"
2627)
@@ -725,7 +726,7 @@ func (h *SendPublicPaymentIntentHandler) validateActions(
725726 }
726727
727728 // Check whether the destination account is a token account of the same mint that's
728- // been created on the blockchain. If not, a fee is required
729+ // been created on the blockchain. If not, a fee is likely required
729730 err = validateExternalTokenAccountWithinIntent (ctx , h .data , destination , intentMint )
730731 switch err {
731732 case nil :
@@ -734,13 +735,42 @@ func (h *SendPublicPaymentIntentHandler) validateActions(
734735 return err
735736 }
736737
737- if ! simResult .HasAnyFeePayments () {
738- return NewIntentValidationErrorf ("%s fee payment is required" , transactionpb .FeePaymentAction_CREATE_ON_SEND_WITHDRAWAL .String ())
739- }
740-
741738 if metadata .DestinationOwner == nil {
742739 return NewIntentValidationError ("destination owner account is required to derive ata" )
743740 }
741+
742+ destinationOwner , err := common .NewAccountFromProto (metadata .DestinationOwner )
743+ if err != nil {
744+ return err
745+ }
746+
747+ var isVmSwapPda bool
748+ vmSwapPdaTimelockRecord , err := h .data .GetTimelockBySwapPda (ctx , destinationOwner .PublicKey ().ToBase58 ())
749+ if err == nil {
750+ isVmSwapPda = true
751+ } else if err != timelock .ErrTimelockNotFound {
752+ return err
753+ }
754+
755+ if isVmSwapPda {
756+ accountInfoRecord , err := h .data .GetAccountInfoByTokenAddress (ctx , vmSwapPdaTimelockRecord .VaultAddress )
757+ if err != nil {
758+ return err
759+ }
760+
761+ if accountInfoRecord .OwnerAccount != initiatorOwnerAccount .PublicKey ().ToBase58 () {
762+ return NewIntentDeniedError ("can only send to your own vm swap pda" )
763+ }
764+
765+ if accountInfoRecord .MintAccount != intentMint .PublicKey ().ToBase58 () {
766+ return NewIntentValidationError ("vm swap pda is for a different mint" )
767+ }
768+ }
769+
770+ // Only VM swap PDAs are subsidized by server
771+ if ! simResult .HasAnyFeePayments () && ! isVmSwapPda {
772+ return NewIntentValidationErrorf ("%s fee payment is required" , transactionpb .FeePaymentAction_CREATE_ON_SEND_WITHDRAWAL .String ())
773+ }
744774 }
745775
746776 return nil
0 commit comments