Skip to content

Commit c669c00

Browse files
committed
Swap PDA flows in SubmitIntent and Sequencer
1 parent 944ddcb commit c669c00

File tree

3 files changed

+96
-9
lines changed

3 files changed

+96
-9
lines changed

pkg/code/async/sequencer/fulfillment_handler.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -256,11 +256,17 @@ func (h *NoPrivacyTransferWithAuthorityFulfillmentHandler) CanSubmitToBlockchain
256256
if err != nil {
257257
return false, err
258258
}
259-
hasCreateOnSendFee, err := h.data.HasFeeAction(ctx, fulfillmentRecord.Intent, transactionpb.FeePaymentAction_CREATE_ON_SEND_WITHDRAWAL)
259+
isCreateOnSend, err := h.data.HasFeeAction(ctx, fulfillmentRecord.Intent, transactionpb.FeePaymentAction_CREATE_ON_SEND_WITHDRAWAL)
260260
if err != nil {
261261
return false, err
262262
}
263-
if isInternalTransfer || !hasCreateOnSendFee {
263+
if !isInternalTransfer && !isCreateOnSend {
264+
isCreateOnSend, err = isExternalWithdrawToVmSwapPda(ctx, h.data, fulfillmentRecord.Intent, destinationAccount)
265+
if err != nil {
266+
return false, err
267+
}
268+
}
269+
if isInternalTransfer || !isCreateOnSend {
264270
isDestinationAccountCreated, err := isAccountInitialized(ctx, h.data, *fulfillmentRecord.Destination)
265271
if err != nil {
266272
return false, err
@@ -410,16 +416,25 @@ func (h *NoPrivacyTransferWithAuthorityFulfillmentHandler) MakeOnDemandTransacti
410416
*actionRecord.Quantity,
411417
)
412418
} else {
419+
// The Fee payment can be an external transfer, but we know the account
420+
// already exists and doesn't need an idempotent create instruction
413421
isFeePayment := actionRecord.FeeType != nil
414422

423+
// Create-on-send is enabled if there was a fee, or it is to an approved
424+
// destination for subsidization.
415425
var isCreateOnSend bool
416-
// The Fee payment can be an external transfer, but we know the account
417-
// already exists and doesn't need an idempotent create instruction
418426
if !isFeePayment {
419427
isCreateOnSend, err = h.data.HasFeeAction(ctx, fulfillmentRecord.Intent, transactionpb.FeePaymentAction_CREATE_ON_SEND_WITHDRAWAL)
420428
if err != nil {
421429
return nil, nil, err
422430
}
431+
432+
if !isCreateOnSend {
433+
isCreateOnSend, err = isExternalWithdrawToVmSwapPda(ctx, h.data, fulfillmentRecord.Intent, destinationToken)
434+
if err != nil {
435+
return nil, nil, err
436+
}
437+
}
423438
}
424439

425440
var destinationOwner *common.Account

pkg/code/async/sequencer/vm.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package async_sequencer
22

33
import (
4+
"bytes"
45
"context"
56
"sync"
67

@@ -12,6 +13,7 @@ import (
1213
code_data "github.com/code-payments/code-server/pkg/code/data"
1314
"github.com/code-payments/code-server/pkg/code/data/cvm/ram"
1415
"github.com/code-payments/code-server/pkg/code/data/cvm/storage"
16+
"github.com/code-payments/code-server/pkg/code/data/intent"
1517
"github.com/code-payments/code-server/pkg/code/data/timelock"
1618
"github.com/code-payments/code-server/pkg/solana/cvm"
1719
)
@@ -152,3 +154,43 @@ func isInternalVmTransfer(ctx context.Context, data code_data.Provider, destinat
152154
return false, err
153155
}
154156
}
157+
158+
func isExternalWithdrawToVmSwapPda(ctx context.Context, data code_data.Provider, intentId string, destinationAta *common.Account) (bool, error) {
159+
intentRecord, err := data.GetIntent(ctx, intentId)
160+
if err != nil {
161+
return false, err
162+
}
163+
164+
if intentRecord.IntentType != intent.SendPublicPayment {
165+
return false, nil
166+
}
167+
168+
if !intentRecord.SendPublicPaymentMetadata.IsWithdrawal {
169+
return false, nil
170+
}
171+
172+
owner, err := common.NewAccountFromPublicKeyString(intentRecord.InitiatorOwnerAccount)
173+
if err != nil {
174+
return false, err
175+
}
176+
177+
mint, err := common.NewAccountFromPublicKeyString(intentRecord.MintAccount)
178+
if err != nil {
179+
return false, err
180+
}
181+
182+
vmConfig, err := common.GetVmConfigForMint(ctx, data, mint)
183+
if err != nil {
184+
return false, err
185+
}
186+
187+
vmSwapAccounts, err := owner.GetVmSwapAccounts(vmConfig)
188+
if err != nil {
189+
return false, err
190+
}
191+
192+
if !bytes.Equal(destinationAta.PublicKey().ToBytes(), vmSwapAccounts.Ata.PublicKey().ToBytes()) {
193+
return false, nil
194+
}
195+
return vmSwapAccounts.Pda.PublicKey().ToBase58() == intentRecord.SendPublicPaymentMetadata.DestinationOwnerAccount, nil
196+
}

pkg/code/server/transaction/intent_handler.go

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)