@@ -19,6 +19,11 @@ import (
1919 "github.com/code-payments/code-server/pkg/code/data/swap"
2020 "github.com/code-payments/code-server/pkg/code/data/transaction"
2121 transaction_util "github.com/code-payments/code-server/pkg/code/transaction"
22+ "github.com/code-payments/code-server/pkg/solana"
23+ compute_budget "github.com/code-payments/code-server/pkg/solana/computebudget"
24+ "github.com/code-payments/code-server/pkg/solana/cvm"
25+ "github.com/code-payments/code-server/pkg/solana/memo"
26+ "github.com/code-payments/code-server/pkg/solana/system"
2227)
2328
2429func (p * service ) validateSwapState (record * swap.Record , states ... swap.State ) error {
@@ -74,25 +79,77 @@ func (p *service) markSwapFailed(ctx context.Context, record *swap.Record) error
7479 })
7580}
7681
77- func (p * service ) markSwapCancelled (ctx context.Context , record * swap.Record ) error {
82+ func (p * service ) markSwapCancelling (ctx context.Context , record * swap.Record , txn * solana. Transaction ) error {
7883 return p .data .ExecuteInTx (ctx , sql .LevelDefault , func (ctx context.Context ) error {
79- err := p .validateSwapState (record , swap .StateCreated )
84+ err := p .validateSwapState (record , swap .StateFunded )
8085 if err != nil {
8186 return err
8287 }
8388
84- err = p .markNonceAvailableDueToCancelledSwap (ctx , record )
89+ txnSignature := base58 .Encode (txn .Signature ())
90+
91+ err = transaction_util .UpdateNonceSignature (ctx , p .data , record .Nonce , record .ProofSignature , txnSignature )
8592 if err != nil {
8693 return err
8794 }
8895
96+ record .TransactionSignature = & txnSignature
97+ record .TransactionBlob = txn .Marshal ()
98+ record .State = swap .StateCancelling
99+ return p .data .SaveSwap (ctx , record )
100+ })
101+ }
102+
103+ func (p * service ) markSwapCancelled (ctx context.Context , record * swap.Record ) error {
104+ return p .data .ExecuteInTx (ctx , sql .LevelDefault , func (ctx context.Context ) error {
105+ err := p .validateSwapState (record , swap .StateCreated , swap .StateCancelling )
106+ if err != nil {
107+ return err
108+ }
109+
110+ switch record .State {
111+ case swap .StateCreated :
112+ err = p .markNonceAvailableDueToCancelledSwap (ctx , record )
113+ if err != nil {
114+ return err
115+ }
116+ case swap .StateCancelling :
117+ err = p .markNonceReleasedDueToSubmittedTransaction (ctx , record )
118+ if err != nil {
119+ return err
120+ }
121+ }
122+
123+ record .TransactionBlob = nil
89124 record .State = swap .StateCancelled
90125 return p .data .SaveSwap (ctx , record )
91126 })
92127}
93128
94- // todo: commonalities between this and geyser external deposit logic
95- func (p * service ) updateBalances (ctx context.Context , record * swap.Record ) error {
129+ func (p * service ) submitTransaction (ctx context.Context , record * swap.Record ) error {
130+ err := p .validateSwapState (record , swap .StateSubmitting , swap .StateCancelling )
131+ if err != nil {
132+ return err
133+ }
134+
135+ var txn solana.Transaction
136+ err = txn .Unmarshal (record .TransactionBlob )
137+ if err != nil {
138+ return errors .Wrap (err , "error unmarshalling transaction" )
139+ }
140+
141+ if base58 .Encode (txn .Signature ()) != * record .TransactionSignature {
142+ return errors .New ("unexpected transaction signature" )
143+ }
144+
145+ _ , err = p .data .SubmitBlockchainTransaction (ctx , & txn )
146+ if err != nil {
147+ return errors .Wrap (err , "error submitting transaction" )
148+ }
149+ return nil
150+ }
151+
152+ func (p * service ) updateBalancesForFinalizedSwap (ctx context.Context , record * swap.Record ) error {
96153 owner , err := common .NewAccountFromPublicKeyString (record .Owner )
97154 if err != nil {
98155 return err
@@ -171,6 +228,85 @@ func (p *service) updateBalances(ctx context.Context, record *swap.Record) error
171228 })
172229}
173230
231+ func (p * service ) updateBalancesForCancelledSwap (ctx context.Context , record * swap.Record ) error {
232+ owner , err := common .NewAccountFromPublicKeyString (record .Owner )
233+ if err != nil {
234+ return err
235+ }
236+
237+ fromMint , err := common .NewAccountFromPublicKeyString (record .FromMint )
238+ if err != nil {
239+ return err
240+ }
241+
242+ soureVmConfig , err := common .GetVmConfigForMint (ctx , p .data , fromMint )
243+ if err != nil {
244+ return err
245+ }
246+
247+ ownerSourceTimelockVault , err := owner .ToTimelockVault (soureVmConfig )
248+ if err != nil {
249+ return err
250+ }
251+
252+ tokenBalances , err := p .data .GetBlockchainTransactionTokenBalances (ctx , * record .TransactionSignature )
253+ if err != nil {
254+ return err
255+ }
256+
257+ deltaQuarksIntoOmnibus , err := transaction_util .GetDeltaQuarksFromTokenBalances (soureVmConfig .Omnibus , tokenBalances )
258+ if err != nil {
259+ return err
260+ }
261+ if deltaQuarksIntoOmnibus <= 0 {
262+ return errors .New ("delta quarks into destination vm omnibus is not positive" )
263+ }
264+
265+ usdMarketValue , _ , err := currency_util .CalculateUsdMarketValue (ctx , p .data , fromMint , uint64 (deltaQuarksIntoOmnibus ), time .Now ())
266+ if err != nil {
267+ return err
268+ }
269+
270+ return p .data .ExecuteInTx (ctx , sql .LevelDefault , func (ctx context.Context ) error {
271+ // For transaction history
272+ intentRecord := & intent.Record {
273+ IntentId : getSwapDepositIntentID (* record .TransactionSignature , ownerSourceTimelockVault ),
274+ IntentType : intent .ExternalDeposit ,
275+
276+ MintAccount : fromMint .PublicKey ().ToBase58 (),
277+
278+ InitiatorOwnerAccount : owner .PublicKey ().ToBase58 (),
279+
280+ ExternalDepositMetadata : & intent.ExternalDepositMetadata {
281+ DestinationTokenAccount : ownerSourceTimelockVault .PublicKey ().ToBase58 (),
282+ Quantity : uint64 (deltaQuarksIntoOmnibus ),
283+ UsdMarketValue : usdMarketValue ,
284+ },
285+
286+ State : intent .StateConfirmed ,
287+ CreatedAt : time .Now (),
288+ }
289+ err = p .data .SaveIntent (ctx , intentRecord )
290+ if err != nil {
291+ return err
292+ }
293+
294+ // For tracking in cached balances
295+ externalDepositRecord := & deposit.Record {
296+ Signature : * record .TransactionSignature ,
297+ Destination : ownerSourceTimelockVault .PublicKey ().ToBase58 (),
298+ Amount : uint64 (deltaQuarksIntoOmnibus ),
299+ UsdMarketValue : usdMarketValue ,
300+
301+ Slot : tokenBalances .Slot ,
302+ ConfirmationState : transaction .ConfirmationFinalized ,
303+
304+ CreatedAt : time .Now (),
305+ }
306+ return p .data .SaveExternalDeposit (ctx , externalDepositRecord )
307+ })
308+ }
309+
174310func (p * service ) notifySwapFinalized (ctx context.Context , swapRecord * swap.Record ) error {
175311 owner , err := common .NewAccountFromPublicKeyString (swapRecord .Owner )
176312 if err != nil {
@@ -199,8 +335,95 @@ func (p *service) notifySwapFinalized(ctx context.Context, swapRecord *swap.Reco
199335 return p .integration .OnSwapFinalized (ctx , owner , toMint , currencyName , fundingIntentRecord .SendPublicPaymentMetadata .ExchangeCurrency , fundingIntentRecord .SendPublicPaymentMetadata .NativeAmount )
200336}
201337
338+ // todo: put this in transaction utility package
339+ func (p * service ) getCancellationTransaction (ctx context.Context , record * swap.Record ) (* solana.Transaction , error ) {
340+ owner , err := common .NewAccountFromPublicKeyString (record .Owner )
341+ if err != nil {
342+ return nil , err
343+ }
344+
345+ fromMint , err := common .NewAccountFromPublicKeyString (record .FromMint )
346+ if err != nil {
347+ return nil , err
348+ }
349+
350+ nonce , err := common .NewAccountFromPublicKeyString (record .Nonce )
351+ if err != nil {
352+ return nil , err
353+ }
354+
355+ decodedBlockhash , err := base58 .Decode (record .Blockhash )
356+ if err != nil {
357+ return nil , err
358+ }
359+
360+ sourceVmConfig , err := common .GetVmConfigForMint (ctx , p .data , fromMint )
361+ if err != nil {
362+ return nil , err
363+ }
364+
365+ sourceOwnerVmSwapPdaAccounts , err := owner .GetVmSwapAccounts (sourceVmConfig )
366+ if err != nil {
367+ return nil , err
368+ }
369+
370+ memoryAccount , memoryIndex , err := common .GetVirtualTimelockAccountLocationInMemory (ctx , p .vmIndexerClient , sourceVmConfig .Vm , owner )
371+ if err != nil {
372+ return nil , err
373+ }
374+
375+ txn := solana .NewLegacyTransaction (
376+ common .GetSubsidizer ().PublicKey ().ToBytes (),
377+ compute_budget .SetComputeUnitLimit (200_000 ), // todo: optimize this
378+ compute_budget .SetComputeUnitPrice (1_000 ),
379+ memo .Instruction ("cancel_swap_v0" ),
380+ system .AdvanceNonce (nonce .PublicKey ().ToBytes (), common .GetSubsidizer ().PublicKey ().ToBytes ()),
381+ cvm .NewCancelSwapInstruction (
382+ & cvm.CancelSwapInstructionAccounts {
383+ VmAuthority : sourceVmConfig .Authority .PublicKey ().ToBytes (),
384+ Vm : sourceVmConfig .Vm .PublicKey ().ToBytes (),
385+ VmMemory : memoryAccount .PublicKey ().ToBytes (),
386+ Swapper : owner .PublicKey ().ToBytes (),
387+ SwapPda : sourceOwnerVmSwapPdaAccounts .Pda .PublicKey ().ToBytes (),
388+ SwapAta : sourceOwnerVmSwapPdaAccounts .Ata .PublicKey ().ToBytes (),
389+ VmOmnibus : sourceVmConfig .Omnibus .PublicKey ().ToBytes (),
390+ },
391+ & cvm.CancelSwapInstructionArgs {
392+ AccountIndex : memoryIndex ,
393+ Amount : record .Amount ,
394+ Bump : sourceOwnerVmSwapPdaAccounts .PdaBump ,
395+ },
396+ ),
397+ cvm .NewCloseSwapAccountIfEmptyInstruction (
398+ & cvm.CloseSwapAccountIfEmptyInstructionAccounts {
399+ VmAuthority : sourceVmConfig .Authority .PublicKey ().ToBytes (),
400+ Vm : sourceVmConfig .Vm .PublicKey ().ToBytes (),
401+ Swapper : owner .PublicKey ().ToBytes (),
402+ SwapPda : sourceOwnerVmSwapPdaAccounts .Pda .PublicKey ().ToBytes (),
403+ SwapAta : sourceOwnerVmSwapPdaAccounts .Ata .PublicKey ().ToBytes (),
404+ Destination : common .GetSubsidizer ().PublicKey ().ToBytes (),
405+ },
406+ & cvm.CloseSwapAccountIfEmptyInstructionArgs {
407+ Bump : sourceOwnerVmSwapPdaAccounts .PdaBump ,
408+ },
409+ ),
410+ )
411+
412+ txn .SetBlockhash (solana .Blockhash (decodedBlockhash ))
413+
414+ err = txn .Sign (
415+ common .GetSubsidizer ().PrivateKey ().ToBytes (),
416+ sourceVmConfig .Authority .PrivateKey ().ToBytes (),
417+ )
418+ if err != nil {
419+ return nil , err
420+ }
421+
422+ return & txn , nil
423+ }
424+
202425func (p * service ) markNonceReleasedDueToSubmittedTransaction (ctx context.Context , record * swap.Record ) error {
203- err := p .validateSwapState (record , swap .StateSubmitting )
426+ err := p .validateSwapState (record , swap .StateSubmitting , swap . StateCancelling )
204427 if err != nil {
205428 return err
206429 }
0 commit comments