@@ -22,8 +22,11 @@ import (
2222
2323 "github.com/code-payments/code-server/pkg/code/common"
2424 currency_util "github.com/code-payments/code-server/pkg/code/currency"
25+ code_data "github.com/code-payments/code-server/pkg/code/data"
2526 "github.com/code-payments/code-server/pkg/code/data/deposit"
27+ "github.com/code-payments/code-server/pkg/code/data/fulfillment"
2628 "github.com/code-payments/code-server/pkg/code/data/intent"
29+ "github.com/code-payments/code-server/pkg/code/data/timelock"
2730 "github.com/code-payments/code-server/pkg/code/data/transaction"
2831 transaction_util "github.com/code-payments/code-server/pkg/code/transaction"
2932 "github.com/code-payments/code-server/pkg/grpc/client"
@@ -143,6 +146,46 @@ func (s *transactionServer) Swap(streamer transactionpb.Transaction_SwapServer)
143146 return handleSwapError (streamer , err )
144147 }
145148
149+ ownerDestinationTimelockVault , err := owner .ToTimelockVault (destinationVmConfig )
150+ if err != nil {
151+ log .WithError (err ).Warn ("failure getting owner destination timelock accounts" )
152+ return handleSwapError (streamer , err )
153+ }
154+ ownerDestinationTimelockRecord , err := s .data .GetTimelockByVault (ctx , ownerDestinationTimelockVault .PublicKey ().ToBase58 ())
155+ if err == timelock .ErrTimelockNotFound {
156+ return handleSwapError (streamer , NewSwapValidationError ("destination timelock vault account not opened" ))
157+ } else if err != nil {
158+ log .WithError (err ).Warn ("failure getting destination timelock record" )
159+ return handleSwapError (streamer , err )
160+ }
161+
162+ //
163+ // Section: On-demand account creation
164+ //
165+
166+ // todo: commonalities here and in geyser external deposit flow
167+ // todo: this will live somewhere else once we add the state management system
168+ if ! ownerDestinationTimelockRecord .ExistsOnBlockchain () {
169+ err = ensureVirtualTimelockAccountIsInitialzed (ctx , s .data , ownerDestinationTimelockRecord )
170+ if err != nil {
171+ log .WithError (err ).Warn ("failure scheduling destination timelock account initialization" )
172+ return handleSwapError (streamer , err )
173+ }
174+
175+ for range 60 {
176+ time .Sleep (time .Second )
177+
178+ _ , _ , err = common .GetVirtualTimelockAccountLocationInMemory (ctx , s .vmIndexerClient , destinationVmConfig .Vm , owner )
179+ if err == nil {
180+ break
181+ }
182+ }
183+ if err != nil {
184+ log .WithError (err ).Warn ("timed out waiting for destination timelock account initialization" )
185+ return handleSwapError (streamer , err )
186+ }
187+ }
188+
146189 //
147190 // Section: Transaction construction
148191 //
@@ -330,20 +373,15 @@ func (s *transactionServer) Swap(streamer transactionpb.Transaction_SwapServer)
330373 // Section: Balance and transaction history
331374 //
332375
333- // todo: This will live somewhere else once we add the state management system
376+ // todo: commonalities here and in geyser external deposit flow
377+ // todo: this will live somewhere else once we add the state management system
334378 var waitForBalanceUpdate sync.WaitGroup
335379 waitForBalanceUpdate .Add (1 )
336380 go func () {
337381 defer waitForBalanceUpdate .Done ()
338382
339383 ctx := context .Background ()
340384
341- ownerDestinationTimelockVault , err := owner .ToTimelockVault (destinationVmConfig )
342- if err != nil {
343- log .WithError (err ).Warn ("failure getting owner destination timelock accounts" )
344- return
345- }
346-
347385 var tokenBalances * solana.TransactionTokenBalances
348386 for range 30 {
349387 time .Sleep (time .Second )
@@ -490,12 +528,38 @@ func (s *transactionServer) boundedSwapRecv(ctx context.Context, streamer transa
490528 }
491529}
492530
493- type SwapServerParameters struct {
494- ComputeUnitLimit uint32
495- ComputeUnitPrice uint64
496- MemoValue string
497- MemoryAccount * common.Account
498- MemoryIndex uint16
531+ func ensureVirtualTimelockAccountIsInitialzed (ctx context.Context , data code_data.Provider , timelockRecord * timelock.Record ) error {
532+ if ! timelockRecord .ExistsOnBlockchain () {
533+ initializeFulfillmentRecord , err := data .GetFirstSchedulableFulfillmentByAddressAsSource (ctx , timelockRecord .VaultAddress )
534+ if err != nil {
535+ return err
536+ }
537+
538+ if initializeFulfillmentRecord .FulfillmentType != fulfillment .InitializeLockedTimelockAccount {
539+ return errors .New ("expected an initialize locked timelock account fulfillment" )
540+ }
541+
542+ return markFulfillmentAsActivelyScheduled (ctx , data , initializeFulfillmentRecord )
543+ }
544+
545+ return nil
546+ }
547+
548+ func markFulfillmentAsActivelyScheduled (ctx context.Context , data code_data.Provider , fulfillmentRecord * fulfillment.Record ) error {
549+ if fulfillmentRecord .Id == 0 {
550+ return nil
551+ }
552+
553+ if ! fulfillmentRecord .DisableActiveScheduling {
554+ return nil
555+ }
556+
557+ if fulfillmentRecord .State != fulfillment .StateUnknown {
558+ return nil
559+ }
560+
561+ fulfillmentRecord .DisableActiveScheduling = false
562+ return data .UpdateFulfillment (ctx , fulfillmentRecord )
499563}
500564
501565func getDeltaQuarksFromTokenBalances (tokenAccount * common.Account , tokenBalances * solana.TransactionTokenBalances ) (int64 , error ) {
0 commit comments