@@ -54,21 +54,35 @@ func (p *runtime) markSwapSubmitting(ctx context.Context, record *swap.Record) e
5454 return p .data .SaveSwap (ctx , record )
5555}
5656
57- func (p * runtime ) markSwapFinalized (ctx context.Context , record * swap.Record ) error {
57+ func (p * runtime ) markSwapFinalized (ctx context.Context , swapRecord * swap.Record ) error {
58+ destinationCurrencyMetadataRecord , err := p .data .GetCurrencyMetadata (ctx , swapRecord .ToMint )
59+ if err != nil {
60+ return err
61+ }
62+ if destinationCurrencyMetadataRecord .State != currency .MetadataStateExecutingInitialPurchase {
63+ return errors .New ("unexpected currency metadata state" )
64+ }
65+
5866 return p .data .ExecuteInTx (ctx , sql .LevelDefault , func (ctx context.Context ) error {
59- err := p .validateSwapState (record , swap .StateSubmitting )
67+ err := p .validateSwapState (swapRecord , swap .StateSubmitting )
6068 if err != nil {
6169 return err
6270 }
6371
64- err = p .markNonceReleasedDueToSubmittedTransaction (ctx , record )
72+ err = p .markNonceReleasedDueToSubmittedTransaction (ctx , swapRecord )
6573 if err != nil {
6674 return err
6775 }
6876
69- record .TransactionBlob = nil
70- record .State = swap .StateFinalized
71- return p .data .SaveSwap (ctx , record )
77+ destinationCurrencyMetadataRecord .State = currency .MetadataStateCompletingInitialization
78+ err = p .data .SaveCurrencyMetadata (ctx , destinationCurrencyMetadataRecord )
79+ if err != nil {
80+ return err
81+ }
82+
83+ swapRecord .TransactionBlob = nil
84+ swapRecord .State = swap .StateFinalized
85+ return p .data .SaveSwap (ctx , swapRecord )
7286 })
7387}
7488
@@ -134,45 +148,70 @@ func (p *runtime) submitTransaction(ctx context.Context, record *swap.Record) er
134148 return nil
135149}
136150
137- func (p * runtime ) updateBalancesForFinalizedSwap (ctx context.Context , swapRecord * swap.Record , tokenBalances * solana.TransactionTokenBalances ) (uint64 , error ) {
151+ func (p * runtime ) maybeUpdateBalancesForFinalizedSwap (ctx context.Context , swapRecord * swap.Record , tokenBalances * solana.TransactionTokenBalances ) (uint64 , bool , error ) {
138152 owner , err := common .NewAccountFromPublicKeyString (swapRecord .Owner )
139153 if err != nil {
140- return 0 , err
154+ return 0 , false , err
141155 }
142156
143157 fromMint , err := common .NewAccountFromPublicKeyString (swapRecord .FromMint )
144158 if err != nil {
145- return 0 , err
159+ return 0 , false , err
146160 }
147161
148162 toMint , err := common .NewAccountFromPublicKeyString (swapRecord .ToMint )
149163 if err != nil {
150- return 0 , err
164+ return 0 , false , err
151165 }
152166
153167 if ! common .IsCoreMintUsdStableCoin () {
154- return 0 , errors .New ("core mint is not a usd stable coin" )
168+ return 0 , false , errors .New ("core mint is not a usd stable coin" )
155169 }
156170 if ! common .IsCoreMint (fromMint ) && ! common .IsCoreMint (toMint ) {
157- return 0 , errors .New ("core mint must be involved in swap" )
171+ return 0 , false , errors .New ("core mint must be involved in swap" )
172+ }
173+
174+ destinationCurrencyMetadataRecord , err := p .data .GetCurrencyMetadata (ctx , swapRecord .ToMint )
175+ if err != nil {
176+ return 0 , false , err
177+ }
178+ if destinationCurrencyMetadataRecord .State != currency .MetadataStateAvailable {
179+ currencyAccounts , err := common .GetLaunchpadCurrencyAccounts (destinationCurrencyMetadataRecord )
180+ if err != nil {
181+ return 0 , false , err
182+ }
183+
184+ deltaQuarksOutOfVault , err := transaction_util .GetDeltaQuarksFromTokenBalances (currencyAccounts .VaultMint , tokenBalances )
185+ if err != nil {
186+ return 0 , false , nil
187+ }
188+
189+ if deltaQuarksOutOfVault >= 0 {
190+ return 0 , false , errors .New ("delta quarks into destination vm omnibus is not negative" )
191+ }
192+
193+ // This swap is initializing the VM and the funds will be deposited
194+ // after memory accounts become available. Balances should only be
195+ // reflected after finalized deposit into a VTA.
196+ return uint64 (- deltaQuarksOutOfVault ), true , nil
158197 }
159198
160199 destinationVmConfig , err := common .GetVmConfigForMint (ctx , p .data , toMint )
161200 if err != nil {
162- return 0 , err
201+ return 0 , false , err
163202 }
164203
165204 ownerDestinationTimelockVault , err := owner .ToTimelockVault (destinationVmConfig )
166205 if err != nil {
167- return 0 , err
206+ return 0 , false , err
168207 }
169208
170209 deltaQuarksIntoOmnibus , err := transaction_util .GetDeltaQuarksFromTokenBalances (destinationVmConfig .Omnibus , tokenBalances )
171210 if err != nil {
172- return 0 , err
211+ return 0 , false , err
173212 }
174213 if deltaQuarksIntoOmnibus <= 0 {
175- return 0 , errors .New ("delta quarks into destination vm omnibus is not positive" )
214+ return 0 , false , errors .New ("delta quarks into destination vm omnibus is not positive" )
176215 }
177216
178217 var exchangeCurrency currency_lib.Code
@@ -182,11 +221,11 @@ func (p *runtime) updateBalancesForFinalizedSwap(ctx context.Context, swapRecord
182221 case swap .FundingSourceSubmitIntent :
183222 fundingIntentRecord , err := p .data .GetIntent (ctx , swapRecord .FundingId )
184223 if err != nil {
185- return 0 , err
224+ return 0 , false , err
186225 }
187226
188227 if fundingIntentRecord .IntentType != intent .SendPublicPayment {
189- return 0 , errors .New ("unexpected intent type" )
228+ return 0 , false , errors .New ("unexpected intent type" )
190229 }
191230
192231 exchangeCurrency = fundingIntentRecord .SendPublicPaymentMetadata .ExchangeCurrency
@@ -196,7 +235,7 @@ func (p *runtime) updateBalancesForFinalizedSwap(ctx context.Context, swapRecord
196235 if common .IsCoreMint (toMint ) {
197236 usdMarketValue , err := currency_util .CalculateUsdMarketValueFromTokenAmount (ctx , p .data , common .CoreMintAccount , uint64 (deltaQuarksIntoOmnibus ), time .Now ())
198237 if err != nil {
199- return 0 , err
238+ return 0 , false , err
200239 }
201240
202241 usdMarketValueWithoutFees , _ = new (big.Float ).Quo (
@@ -212,22 +251,22 @@ func (p *runtime) updateBalancesForFinalizedSwap(ctx context.Context, swapRecord
212251 fundingIntentRecord .SendPublicPaymentMetadata .UsdMarketValue = usdMarketValueWithoutFees
213252 err = p .data .SaveIntent (ctx , fundingIntentRecord )
214253 if err != nil {
215- return 0 , err
254+ return 0 , false , err
216255 }
217256 }
218257 case swap .FundingSourceExternalWallet :
219258 if ! common .IsCoreMint (fromMint ) {
220- return 0 , errors .New ("unexpected source mint" )
259+ return 0 , false , errors .New ("unexpected source mint" )
221260 }
222261
223262 exchangeCurrency = currency_lib .USD
224263 usdMarketValueWithoutFees , err = currency_util .CalculateUsdMarketValueFromTokenAmount (ctx , p .data , common .CoreMintAccount , swapRecord .Amount , time .Now ())
225264 if err != nil {
226- return 0 , err
265+ return 0 , false , err
227266 }
228267 nativeAmountWithoutFees = usdMarketValueWithoutFees
229268 default :
230- return 0 , errors .New ("unsupported funding source" )
269+ return 0 , false , errors .New ("unsupported funding source" )
231270 }
232271
233272 nativeAmount := nativeAmountWithoutFees
@@ -287,12 +326,12 @@ func (p *runtime) updateBalancesForFinalizedSwap(ctx context.Context, swapRecord
287326 return p .data .SaveExternalDeposit (ctx , externalDepositRecord )
288327 })
289328 if err != nil {
290- return 0 , err
329+ return 0 , false , err
291330 }
292- return uint64 (deltaQuarksIntoOmnibus ), nil
331+ return uint64 (deltaQuarksIntoOmnibus ), false , nil
293332}
294333
295- func (p * runtime ) notifySwapFinalized (ctx context.Context , swapRecord * swap.Record ) error {
334+ func (p * runtime ) notifySwapFinalized (ctx context.Context , swapRecord * swap.Record , isMintInit bool ) error {
296335 owner , err := common .NewAccountFromPublicKeyString (swapRecord .Owner )
297336 if err != nil {
298337 return err
@@ -358,7 +397,7 @@ func (p *runtime) notifySwapFinalized(ctx context.Context, swapRecord *swap.Reco
358397 ).Float64 ()
359398 }
360399
361- return p .integration .OnSwapFinalized (ctx , owner , isBuy , targetMint , targetCurrencyMetadataRecord .Name , currencyCode , valueReceived )
400+ return p .integration .OnSwapFinalized (ctx , owner , isBuy , targetMint , targetCurrencyMetadataRecord .Name , currencyCode , valueReceived , isMintInit )
362401}
363402
364403func (p * runtime ) markNonceReleasedDueToSubmittedTransaction (ctx context.Context , record * swap.Record ) error {
@@ -505,6 +544,16 @@ func (p *runtime) ensureSwapDestinationIsInitialized(ctx context.Context, record
505544 return err
506545 }
507546
547+ destinationCurrencyMetadataRecord , err := p .data .GetCurrencyMetadata (ctx , record .ToMint )
548+ if err != nil {
549+ return err
550+ }
551+ if destinationCurrencyMetadataRecord .State != currency .MetadataStateAvailable {
552+ // This swap is initializing the VM and the funds will be deposited
553+ // after memory accounts become available.
554+ return nil
555+ }
556+
508557 owner , err := common .NewAccountFromPublicKeyString (record .Owner )
509558 if err != nil {
510559 return err
0 commit comments