How to use Swap method of main Package

Best Mock code snippet using main.Swap

swap.go

Source:swap.go Github

copy

Full Screen

...18 "github.com/bcskill/eth-main-side-swap/common"19 "github.com/bcskill/eth-main-side-swap/model"20 "github.com/bcskill/eth-main-side-swap/util"21)22// NewSwapEngine returns the swapEngine instance23func NewSwapEngine(db *gorm.DB, cfg *util.Config, sideClient, mainClient *ethclient.Client) (*SwapEngine, error) {24 pairs := make([]model.SwapPair, 0)25 db.Find(&pairs)26 swapPairInstances, err := buildSwapPairInstance(pairs)27 if err != nil {28 return nil, err29 }30 sideContractAddrToMainContractAddr := make(map[ethcom.Address]ethcom.Address)31 mainContractAddrToSideContractAddr := make(map[ethcom.Address]ethcom.Address)32 for _, token := range pairs {33 mainContractAddrToSideContractAddr[ethcom.HexToAddress(token.MainChainErc20Addr)] = ethcom.HexToAddress(token.SideChainErc20Addr)34 sideContractAddrToMainContractAddr[ethcom.HexToAddress(token.SideChainErc20Addr)] = ethcom.HexToAddress(token.MainChainErc20Addr)35 }36 keyConfig, err := GetKeyConfig(cfg)37 if err != nil {38 return nil, err39 }40 sidePrivateKey, _, err := BuildKeys(keyConfig.SidePrivateKey)41 if err != nil {42 return nil, err43 }44 mainPrivateKey, _, err := BuildKeys(keyConfig.MainPrivateKey)45 if err != nil {46 return nil, err47 }48 sideChainID, err := sideClient.ChainID(context.Background())49 if err != nil {50 return nil, err51 }52 mainChainID, err := mainClient.ChainID(context.Background())53 if err != nil {54 return nil, err55 }56 MainSwapAgentABI, err := abi.JSON(strings.NewReader(sabi.MainSwapAgentABI))57 if err != nil {58 return nil, err59 }60 SideSwapAgentABI, err := abi.JSON(strings.NewReader(sabi.SideSwapAgentABI))61 if err != nil {62 return nil, err63 }64 swapEngine := &SwapEngine{65 db: db,66 config: cfg,67 hmacCKey: keyConfig.HMACKey,68 mainPrivateKey: mainPrivateKey,69 sidePrivateKey: sidePrivateKey,70 sideClient: sideClient,71 mainClient: mainClient,72 sideChainID: sideChainID.Int64(),73 mainChainID: mainChainID.Int64(),74 swapPairsFromERC20Addr: swapPairInstances,75 side20ToMain20: sideContractAddrToMainContractAddr,76 main20ToSide20: mainContractAddrToSideContractAddr,77 mainSwapAgentABI: &MainSwapAgentABI,78 sideSwapAgentABI: &SideSwapAgentABI,79 mainSwapAgent: ethcom.HexToAddress(cfg.ChainConfig.MainSwapAgentAddr),80 sideSwapAgent: ethcom.HexToAddress(cfg.ChainConfig.SideSwapAgentAddr),81 }82 return swapEngine, nil83}84func (engine *SwapEngine) Start() {85 go engine.monitorSwapRequestDaemon()86 go engine.confirmSwapRequestDaemon()87 go engine.swapInstanceDaemon(SwapMain2Side)88 go engine.swapInstanceDaemon(SwapSide2Main)89 go engine.trackSwapTxDaemon()90 go engine.retryFailedSwapsDaemon()91 go engine.trackRetrySwapTxDaemon()92}93func (engine *SwapEngine) monitorSwapRequestDaemon() {94 for {95 swapStartTxLogs := make([]model.SwapStartTxLog, 0)96 engine.db.Where("phase = ?", model.SeenRequest).Order("height asc").Limit(BatchSize).Find(&swapStartTxLogs)97 if len(swapStartTxLogs) == 0 {98 time.Sleep(SleepTime * time.Second)99 continue100 }101 for _, swapEventLog := range swapStartTxLogs {102 swap := engine.createSwap(&swapEventLog)103 writeDBErr := func() error {104 tx := engine.db.Begin()105 if err := tx.Error; err != nil {106 return err107 }108 if err := engine.insertSwap(tx, swap); err != nil {109 tx.Rollback()110 return err111 }112 tx.Model(model.SwapStartTxLog{}).Where("tx_hash = ?", swap.StartTxHash).Updates(113 map[string]interface{}{114 "phase": model.ConfirmRequest,115 "update_time": time.Now().Unix(),116 })117 return tx.Commit().Error118 }()119 if writeDBErr != nil {120 util.Logger.Errorf("write db error: %s", writeDBErr.Error())121 util.SendTelegramMessage(fmt.Sprintf("write db error: %s", writeDBErr.Error()))122 }123 }124 }125}126func (engine *SwapEngine) getSwapHMAC(swap *model.Swap) string {127 material := fmt.Sprintf("%s#%s#%s#%s#%s#%s#%s#%d#%s#%s#%s",128 swap.Status, swap.Sponsor, swap.SourceChainErc20Addr, swap.TargetChainErc20Addr, swap.TargetChainToAddr, swap.Symbol, swap.Amount, swap.Decimals, swap.Direction, swap.StartTxHash, swap.FillTxHash)129 mac := hmac.New(sha256.New, []byte(engine.hmacCKey))130 mac.Write([]byte(material))131 return hex.EncodeToString(mac.Sum(nil))132}133func (engine *SwapEngine) verifySwap(swap *model.Swap) bool {134 return swap.RecordHash == engine.getSwapHMAC(swap)135}136func (engine *SwapEngine) insertSwap(tx *gorm.DB, swap *model.Swap) error {137 swap.RecordHash = engine.getSwapHMAC(swap)138 return tx.Create(swap).Error139}140func (engine *SwapEngine) updateSwap(tx *gorm.DB, swap *model.Swap) {141 swap.RecordHash = engine.getSwapHMAC(swap)142 tx.Save(swap)143}144func (engine *SwapEngine) createSwap(txEventLog *model.SwapStartTxLog) *model.Swap {145 sponsor := txEventLog.Sponsor146 amount := txEventLog.Amount147 swapStartTxHash := txEventLog.TxHash148 swapDirection := SwapMain2Side149 if txEventLog.Chain == common.ChainSide {150 swapDirection = SwapSide2Main151 }152 var mainSourceErc20Addr ethcom.Address153 var sideSourceErc20Addr ethcom.Address154 var targetChainErc20Addr ethcom.Address155 var ok bool156 decimals := 0157 var symbol string158 swapStatus := SwapQuoteRejected159 err := func() error {160 if txEventLog.Chain == common.ChainMain {161 mainSourceErc20Addr = ethcom.HexToAddress(txEventLog.SourceChainErc20Addr)162 if sideSourceErc20Addr, ok = engine.main20ToSide20[ethcom.HexToAddress(txEventLog.SourceChainErc20Addr)]; !ok {163 return fmt.Errorf("unsupported main token contract address: %s", txEventLog.SourceChainErc20Addr)164 }165 targetChainErc20Addr = sideSourceErc20Addr166 } else {167 sideSourceErc20Addr = ethcom.HexToAddress(txEventLog.SourceChainErc20Addr)168 if mainSourceErc20Addr, ok = engine.side20ToMain20[ethcom.HexToAddress(txEventLog.SourceChainErc20Addr)]; !ok {169 return fmt.Errorf("unsupported side token contract address: %s", txEventLog.SourceChainErc20Addr)170 }171 targetChainErc20Addr = mainSourceErc20Addr172 }173 pairInstance, err := engine.GetSwapPairInstance(mainSourceErc20Addr)174 if err != nil {175 return fmt.Errorf("failed to get swap pair for target erc20 %s, error %s", targetChainErc20Addr.String(), err.Error())176 }177 decimals = pairInstance.Decimals178 symbol = pairInstance.Symbol179 swapAmount := big.NewInt(0)180 _, ok = swapAmount.SetString(txEventLog.Amount, 10)181 if !ok {182 return fmt.Errorf("unrecongnized swap amount: %s", txEventLog.Amount)183 }184 if swapAmount.Cmp(pairInstance.LowBound) < 0 || swapAmount.Cmp(pairInstance.UpperBound) > 0 {185 return fmt.Errorf("swap amount is out of bound, expected bound [%s, %s]", pairInstance.LowBound.String(), pairInstance.UpperBound.String())186 }187 swapStatus = SwapTokenReceived188 return nil189 }()190 log := ""191 if err != nil {192 log = err.Error()193 }194 swap := &model.Swap{195 Status: swapStatus,196 Sponsor: sponsor,197 SourceChainErc20Addr: ethcom.HexToAddress(txEventLog.SourceChainErc20Addr).String(),198 TargetChainErc20Addr: targetChainErc20Addr.String(),199 TargetChainToAddr: ethcom.HexToAddress(txEventLog.TargetChainToAddr).String(),200 Symbol: symbol,201 Amount: amount,202 Decimals: decimals,203 Direction: swapDirection,204 StartTxHash: swapStartTxHash,205 FillTxHash: "",206 Log: log,207 }208 return swap209}210func (engine *SwapEngine) confirmSwapRequestDaemon() {211 for {212 txEventLogs := make([]model.SwapStartTxLog, 0)213 engine.db.Where("status = ? and phase = ?", model.TxStatusConfirmed, model.ConfirmRequest).214 Order("height asc").Limit(BatchSize).Find(&txEventLogs)215 if len(txEventLogs) == 0 {216 time.Sleep(SleepTime * time.Second)217 continue218 }219 util.Logger.Debugf("found %d confirmed event logs", len(txEventLogs))220 for _, txEventLog := range txEventLogs {221 writeDBErr := func() error {222 tx := engine.db.Begin()223 if err := tx.Error; err != nil {224 return err225 }226 swap, err := engine.getSwapByStartTxHash(tx, txEventLog.TxHash)227 if err != nil {228 util.Logger.Errorf("verify hmac of swap failed: %s", txEventLog.TxHash)229 util.SendTelegramMessage(fmt.Sprintf("Urgent alert: verify hmac of swap failed: %s", txEventLog.TxHash))230 return err231 }232 if swap.Status == SwapTokenReceived {233 swap.Status = SwapConfirmed234 engine.updateSwap(tx, swap)235 }236 tx.Model(model.SwapStartTxLog{}).Where("id = ?", txEventLog.Id).Updates(237 map[string]interface{}{238 "phase": model.AckRequest,239 "update_time": time.Now().Unix(),240 })241 return tx.Commit().Error242 }()243 if writeDBErr != nil {244 util.Logger.Errorf("write db error: %s", writeDBErr.Error())245 util.SendTelegramMessage(fmt.Sprintf("write db error: %s", writeDBErr.Error()))246 }247 }248 }249}250func (engine *SwapEngine) swapInstanceDaemon(direction common.SwapDirection) {251 util.Logger.Infof("start swap daemon, direction %s", direction)252 for {253 swaps := make([]model.Swap, 0)254 engine.db.Where("status in (?) and direction = ?", []common.SwapStatus{SwapConfirmed, SwapSending}, direction).Order("id asc").Limit(BatchSize).Find(&swaps)255 if len(swaps) == 0 {256 time.Sleep(SwapSleepSecond * time.Second)257 continue258 }259 util.Logger.Debugf("found %d confirmed swap requests", len(swaps))260 for _, swap := range swaps {261 var swapPairInstance *SwapPairIns262 var mainSourceErc20Addr ethcom.Address263 var err error264 retryCheckErr := func() error {265 if !engine.verifySwap(&swap) {266 return fmt.Errorf("verify hmac of swap failed: %s", swap.StartTxHash)267 }268 if swap.Direction == SwapMain2Side {269 mainSourceErc20Addr = ethcom.HexToAddress(swap.SourceChainErc20Addr)270 } else {271 mainSourceErc20Addr = ethcom.HexToAddress(swap.TargetChainErc20Addr)272 }273 swapPairInstance, err = engine.GetSwapPairInstance(mainSourceErc20Addr)274 if err != nil {275 return fmt.Errorf("swap instance for target erc20 %s doesn't exist, skip this swap", swap.TargetChainErc20Addr)276 }277 return nil278 }()279 if retryCheckErr != nil {280 writeDBErr := func() error {281 tx := engine.db.Begin()282 if err := tx.Error; err != nil {283 return err284 }285 swap.Status = SwapQuoteRejected286 swap.Log = retryCheckErr.Error()287 engine.updateSwap(tx, &swap)288 return tx.Commit().Error289 }()290 if writeDBErr != nil {291 util.Logger.Errorf("write db error: %s", writeDBErr.Error())292 util.SendTelegramMessage(fmt.Sprintf("write db error: %s", writeDBErr.Error()))293 }294 continue295 }296 skip, writeDBErr := func() (bool, error) {297 isSkip := false298 tx := engine.db.Begin()299 if err := tx.Error; err != nil {300 return false, err301 }302 if swap.Status == SwapSending {303 var swapTx model.SwapFillTx304 engine.db.Where("start_swap_tx_hash = ?", swap.StartTxHash).First(&swapTx)305 if swapTx.FillSwapTxHash == "" {306 util.Logger.Infof("retry swap, start tx hash %s, symbol %s, amount %s, direction %s",307 swap.StartTxHash, swap.Symbol, swap.Amount, swap.Direction)308 swap.Status = SwapConfirmed309 engine.updateSwap(tx, &swap)310 } else {311 util.Logger.Infof("swap tx is built successfully, but the swap tx status is uncertain, just mark the swap and swap tx status as sent, swap ID %d", swap.ID)312 tx.Model(model.SwapFillTx{}).Where("fill_swap_tx_hash = ?", swapTx.FillSwapTxHash).Updates(313 map[string]interface{}{314 "status": model.FillTxSent,315 "updated_at": time.Now().Unix(),316 })317 swap.Status = SwapSent318 swap.FillTxHash = swapTx.FillSwapTxHash319 engine.updateSwap(tx, &swap)320 isSkip = true321 }322 } else {323 swap.Status = SwapSending324 engine.updateSwap(tx, &swap)325 }326 return isSkip, tx.Commit().Error327 }()328 if writeDBErr != nil {329 util.Logger.Errorf("write db error: %s", writeDBErr.Error())330 util.SendTelegramMessage(fmt.Sprintf("write db error: %s", writeDBErr.Error()))331 continue332 }333 if skip {334 util.Logger.Debugf("skip this swap, start tx hash %s", swap.StartTxHash)335 continue336 }337 util.Logger.Infof("Swap token direction %s, sponsor: %s, SourceChainErc20Addr: %s, TargetChainErc20Addr: %s, TargetChainToAddr: %s, amount %s, decimals %d", direction, swap.Sponsor, swap.SourceChainErc20Addr, swap.TargetChainErc20Addr, swap.TargetChainToAddr, swap.Amount, swap.Decimals)338 swapTx, swapErr := engine.doSwap(&swap, swapPairInstance)339 writeDBErr = func() error {340 tx := engine.db.Begin()341 if err := tx.Error; err != nil {342 return err343 }344 if swapErr != nil {345 util.Logger.Errorf("do swap failed: %s, start hash %s", swapErr.Error(), swap.StartTxHash)346 util.SendTelegramMessage(fmt.Sprintf("do swap failed: %s, start hash %s", swapErr.Error(), swap.StartTxHash))347 if swapErr.Error() == core.ErrReplaceUnderpriced.Error() {348 //delete the fill swap tx349 tx.Where("fill_swap_tx_hash = ?", swapTx.FillSwapTxHash).Delete(model.SwapFillTx{})350 // retry this swap351 swap.Status = SwapConfirmed352 swap.Log = fmt.Sprintf("do swap failure: %s", swapErr.Error())353 engine.updateSwap(tx, &swap)354 } else {355 fillTxHash := ""356 if swapTx != nil {357 tx.Model(model.SwapFillTx{}).Where("fill_swap_tx_hash = ?", swapTx.FillSwapTxHash).Updates(358 map[string]interface{}{359 "status": model.FillTxFailed,360 "updated_at": time.Now().Unix(),361 })362 fillTxHash = swapTx.FillSwapTxHash363 }364 swap.Status = SwapSendFailed365 swap.FillTxHash = fillTxHash366 swap.Log = fmt.Sprintf("do swap failure: %s", swapErr.Error())367 engine.updateSwap(tx, &swap)368 }369 } else {370 tx.Model(model.SwapFillTx{}).Where("fill_swap_tx_hash = ?", swapTx.FillSwapTxHash).Updates(371 map[string]interface{}{372 "status": model.FillTxSent,373 "updated_at": time.Now().Unix(),374 })375 swap.Status = SwapSent376 swap.FillTxHash = swapTx.FillSwapTxHash377 engine.updateSwap(tx, &swap)378 }379 return tx.Commit().Error380 }()381 if writeDBErr != nil {382 util.Logger.Errorf("write db error: %s", writeDBErr.Error())383 util.SendTelegramMessage(fmt.Sprintf("write db error: %s", writeDBErr.Error()))384 }385 if swap.Direction == SwapMain2Side {386 time.Sleep(time.Duration(engine.config.ChainConfig.SideWaitMilliSecBetweenSwaps) * time.Millisecond)387 } else {388 time.Sleep(time.Duration(engine.config.ChainConfig.MainWaitMilliSecBetweenSwaps) * time.Millisecond)389 }390 }391 }392}393func (engine *SwapEngine) doSwap(swap *model.Swap, swapPairInstance *SwapPairIns) (*model.SwapFillTx, error) {394 amount := big.NewInt(0)395 _, ok := amount.SetString(swap.Amount, 10)396 if !ok {397 return nil, fmt.Errorf("invalid swap amount: %s", swap.Amount)398 }399 if swap.Direction == SwapMain2Side {400 sideClientMutex.Lock()401 defer sideClientMutex.Unlock()402 data, err := abiEncodeFillMain2SideSwap(ethcom.HexToHash(swap.StartTxHash), ethcom.HexToAddress(swap.SourceChainErc20Addr), ethcom.HexToAddress(swap.TargetChainToAddr), amount, engine.sideSwapAgentABI)403 if err != nil {404 return nil, err405 }406 signedTx, err := buildSignedTransaction(engine.sideSwapAgent, engine.sideClient, data, engine.sidePrivateKey)407 if err != nil {408 return nil, err409 }410 block, err := engine.sideClient.BlockByNumber(context.Background(), nil)411 if err != nil {412 util.Logger.Debugf("Side, query block failed: %s", err.Error())413 return nil, err414 }415 swapTx := &model.SwapFillTx{416 Direction: SwapMain2Side,417 StartSwapTxHash: swap.StartTxHash,418 FillSwapTxHash: signedTx.Hash().String(),419 GasPrice: signedTx.GasPrice().String(),420 Status: model.FillTxCreated,421 TrackRetryHeight:block.Number().Int64(),422 }423 err = engine.insertSwapTxToDB(swapTx)424 if err != nil {425 return nil, err426 }427 err = engine.sideClient.SendTransaction(context.Background(), signedTx)428 if err != nil {429 util.Logger.Errorf("broadcast tx to Side error: %s", err.Error())430 return nil, err431 }432 util.Logger.Infof("Send transaction to Side, %s/%s", engine.config.ChainConfig.SideExplorerUrl, signedTx.Hash().String())433 return swapTx, nil434 } else {435 mainClientMutex.Lock()436 defer mainClientMutex.Unlock()437 data, err := abiEncodeFillSide2MainSwap(ethcom.HexToHash(swap.StartTxHash), ethcom.HexToAddress(swap.SourceChainErc20Addr), ethcom.HexToAddress(swap.TargetChainToAddr), amount, engine.mainSwapAgentABI)438 signedTx, err := buildSignedTransaction(engine.mainSwapAgent, engine.mainClient, data, engine.mainPrivateKey)439 if err != nil {440 return nil, err441 }442 block, err := engine.mainClient.BlockByNumber(context.Background(), nil)443 if err != nil {444 util.Logger.Debugf("Side, query block failed: %s", err.Error())445 return nil, err446 }447 swapTx := &model.SwapFillTx{448 Direction: SwapSide2Main,449 StartSwapTxHash: swap.StartTxHash,450 GasPrice: signedTx.GasPrice().String(),451 FillSwapTxHash: signedTx.Hash().String(),452 Status: model.FillTxCreated,453 TrackRetryHeight:block.Number().Int64(),454 }455 err = engine.insertSwapTxToDB(swapTx)456 if err != nil {457 return nil, err458 }459 err = engine.mainClient.SendTransaction(context.Background(), signedTx)460 if err != nil {461 util.Logger.Errorf("broadcast tx to Main error: %s", err.Error())462 return nil, err463 } else {464 util.Logger.Infof("Send transaction to Main, %s/%s", engine.config.ChainConfig.MainExplorerUrl, signedTx.Hash().String())465 }466 return swapTx, nil467 }468}469func (engine *SwapEngine) trackSwapTxDaemon() {470 go func() {471 for {472 time.Sleep(SleepTime * time.Second)473 swapTxs := make([]model.SwapFillTx, 0)474 engine.db.Where("status = ? and track_retry_counter >= ?", model.FillTxSent, engine.config.ChainConfig.MainMaxTrackRetry).475 Order("id asc").Limit(TrackSentTxBatchSize).Find(&swapTxs)476 if len(swapTxs) > 0 {477 util.Logger.Infof("%d fill tx are missing, mark these swaps as failed", len(swapTxs))478 }479 for _, swapTx := range swapTxs {480 chainName := "Main"481 maxRetry := engine.config.ChainConfig.MainMaxTrackRetry482 if swapTx.Direction == SwapMain2Side {483 chainName = "Side"484 maxRetry = engine.config.ChainConfig.SideMaxTrackRetry485 }486 util.Logger.Errorf("The fill tx is sent, however, after %d seconds its status is still uncertain. Mark tx as missing and mark swap as failed, chain %s, fill hash %s", SleepTime*maxRetry, chainName, swapTx.StartSwapTxHash)487 util.SendTelegramMessage(fmt.Sprintf("The fill tx is sent, however, after %d seconds its status is still uncertain. Mark tx as missing and mark swap as failed, chain %s, start hash %s", SleepTime*maxRetry, chainName, swapTx.StartSwapTxHash))488 writeDBErr := func() error {489 tx := engine.db.Begin()490 if err := tx.Error; err != nil {491 return err492 }493 tx.Model(model.SwapFillTx{}).Where("id = ?", swapTx.ID).Updates(494 map[string]interface{}{495 "status": model.FillTxMissing,496 "updated_at": time.Now().Unix(),497 })498 swap, err := engine.getSwapByStartTxHash(tx, swapTx.StartSwapTxHash)499 if err != nil {500 tx.Rollback()501 return err502 }503 swap.Status = SwapSendFailed504 swap.Log = fmt.Sprintf("track fill tx for more than %d times, the fill tx status is still uncertain", maxRetry)505 engine.updateSwap(tx, swap)506 return tx.Commit().Error507 }()508 if writeDBErr != nil {509 util.Logger.Errorf("write db error: %s", writeDBErr.Error())510 util.SendTelegramMessage(fmt.Sprintf("write db error: %s", writeDBErr.Error()))511 }512 }513 }514 }()515 go func() {516 for {517 time.Sleep(SleepTime * time.Second)518 mainSwapTxs := make([]model.SwapFillTx, 0)519 engine.db.Where("status = ? and direction = ? and track_retry_counter < ?", model.FillTxSent, SwapSide2Main, engine.config.ChainConfig.MainMaxTrackRetry).520 Order("id asc").Limit(TrackSentTxBatchSize).Find(&mainSwapTxs)521 sideSwapTxs := make([]model.SwapFillTx, 0)522 engine.db.Where("status = ? and direction = ? and track_retry_counter < ?", model.FillTxSent, SwapMain2Side, engine.config.ChainConfig.SideMaxTrackRetry).523 Order("id asc").Limit(TrackSentTxBatchSize).Find(&sideSwapTxs)524 swapTxs := append(mainSwapTxs, sideSwapTxs...)525 if len(swapTxs) > 0 {526 util.Logger.Debugf("Track %d non-finalized swap txs", len(swapTxs))527 }528 for _, swapTx := range swapTxs {529 gasPrice := big.NewInt(0)530 gasPrice.SetString(swapTx.GasPrice, 10)531 var client *ethclient.Client532 var chainName string533 var lostRangeNum int64534 var confirmNum int64535 if swapTx.Direction == SwapSide2Main {536 client = engine.mainClient537 chainName = "Main"538 lostRangeNum = engine.config.ChainConfig.MainLostRangeNum539 confirmNum = engine.config.ChainConfig.MainConfirmNum540 } else {541 client = engine.sideClient542 chainName = "Side"543 lostRangeNum = engine.config.ChainConfig.SideLostRangeNum544 confirmNum = engine.config.ChainConfig.SideConfirmNum545 }546 var txRecipient *types.Receipt547 txMissed := false548 var currentBlockHeight int64549 queryTxStatusErr := func() error {550 block, err := client.BlockByNumber(context.Background(), nil)551 if err != nil {552 util.Logger.Debugf("%s, query block failed: %s", chainName, err.Error())553 return err554 }555 currentBlockHeight = block.Number().Int64()556 txRecipient, err = client.TransactionReceipt(context.Background(), ethcom.HexToHash(swapTx.FillSwapTxHash))557 if err != nil {558 if strings.EqualFold(err.Error(), TransactionReceiptMissed) {559 swapFoundTxs := make([]model.SwapFillTx, 0)560 engine.db.Where("fill_swap_tx_hash = ?", swapTx.FillSwapTxHash).Find(&swapFoundTxs)561 if len(swapFoundTxs) == 0 {562 return fmt.Errorf("%s, swap_fill_txs data lost fill_swap_tx_hash: %s", chainName, swapTx.FillSwapTxHash)563 }564 if currentBlockHeight > (swapFoundTxs[0].TrackRetryHeight + lostRangeNum) {565 txMissed = true566 util.Logger.Debugf("%s, transaction missed, txHash: %s", chainName, swapTx.FillSwapTxHash)567 return nil568 } else {569 util.Logger.Debugf("%s, No transaction found, continue to wait", chainName)570 return err571 }572 } else {573 util.Logger.Debugf("%s, query tx failed: %s", chainName, err.Error())574 return err575 }576 }577 if currentBlockHeight < (txRecipient.BlockNumber.Int64() + confirmNum) {578 return fmt.Errorf("%s, swap tx is still not finalized", chainName)579 }580 return nil581 }()582 writeDBErr := func() error {583 tx := engine.db.Begin()584 if err := tx.Error; err != nil {585 return err586 }587 if queryTxStatusErr != nil {588 tx.Model(model.SwapFillTx{}).Where("id = ?", swapTx.ID).Updates(589 map[string]interface{}{590 "track_retry_counter": gorm.Expr("track_retry_counter + 1"),591 "updated_at": time.Now().Unix(),592 })593 } else {594 if txMissed {595 util.Logger.Infof(fmt.Sprintf("fill swap tx is missed, chain %s, fillTxHash: %s", chainName, swapTx.FillSwapTxHash))596 util.SendTelegramMessage(fmt.Sprintf("fill swap tx is missed, chain %s, fillTxHash: %s", chainName, swapTx.FillSwapTxHash))597 tx.Model(model.SwapFillTx{}).Where("id = ?", swapTx.ID).Updates(598 map[string]interface{}{599 "status": model.FillTxFailed,600 "height": currentBlockHeight,601 "updated_at": time.Now().Unix(),602 })603 swap, err := engine.getSwapByStartTxHash(tx, swapTx.StartSwapTxHash)604 if err != nil {605 tx.Rollback()606 return err607 }608 swap.Status = SwapSendFailed609 swap.Log = "Transaction lost"610 engine.updateSwap(tx, swap)611 } else {612 txFee := big.NewInt(1).Mul(gasPrice, big.NewInt(int64(txRecipient.GasUsed))).String()613 if txRecipient.Status == TxFailedStatus {614 util.Logger.Infof(fmt.Sprintf("fill swap tx is failed, chain %s, txHash: %s", chainName, txRecipient.TxHash.String()))615 util.SendTelegramMessage(fmt.Sprintf("fill swap tx is failed, chain %s, txHash: %s", chainName, txRecipient.TxHash.String()))616 tx.Model(model.SwapFillTx{}).Where("id = ?", swapTx.ID).Updates(617 map[string]interface{}{618 "status": model.FillTxFailed,619 "height": txRecipient.BlockNumber.Int64(),620 "consumed_fee_amount": txFee,621 "updated_at": time.Now().Unix(),622 })623 swap, err := engine.getSwapByStartTxHash(tx, swapTx.StartSwapTxHash)624 if err != nil {625 tx.Rollback()626 return err627 }628 swap.Status = SwapSendFailed629 swap.Log = "fill tx is failed"630 engine.updateSwap(tx, swap)631 } else {632 util.Logger.Infof(fmt.Sprintf("fill swap tx is success, chain %s, txHash: %s", chainName, txRecipient.TxHash.String()))633 tx.Model(model.SwapFillTx{}).Where("id = ?", swapTx.ID).Updates(634 map[string]interface{}{635 "status": model.FillTxSuccess,636 "height": txRecipient.BlockNumber.Int64(),637 "consumed_fee_amount": txFee,638 "updated_at": time.Now().Unix(),639 })640 swap, err := engine.getSwapByStartTxHash(tx, swapTx.StartSwapTxHash)641 if err != nil {642 tx.Rollback()643 return err644 }645 swap.Status = SwapSuccess646 engine.updateSwap(tx, swap)647 }648 }649 }650 return tx.Commit().Error651 }()652 if writeDBErr != nil {653 util.Logger.Errorf("update db failure: %s", writeDBErr.Error())654 util.SendTelegramMessage(fmt.Sprintf("Upgent alert: update db failure: %s", writeDBErr.Error()))655 }656 }657 }658 }()659}660func (engine *SwapEngine) getSwapByStartTxHash(tx *gorm.DB, txHash string) (*model.Swap, error) {661 swap := model.Swap{}662 err := tx.Where("start_tx_hash = ?", txHash).First(&swap).Error663 if err != nil {664 return nil, err665 }666 if !engine.verifySwap(&swap) {667 return nil, fmt.Errorf("hmac verification failure")668 }669 return &swap, nil670}671func (engine *SwapEngine) insertSwapTxToDB(data *model.SwapFillTx) error {672 tx := engine.db.Begin()673 if err := tx.Error; err != nil {674 return err675 }676 if err := tx.Create(data).Error; err != nil {677 tx.Rollback()678 return err679 }680 return tx.Commit().Error681}682func (engine *SwapEngine) AddSwapPairInstance(swapPair *model.SwapPair) error {683 lowBound := big.NewInt(0)684 _, ok := lowBound.SetString(swapPair.LowBound, 10)685 if !ok {686 return fmt.Errorf("invalid lowBound amount: %s", swapPair.LowBound)687 }688 upperBound := big.NewInt(0)689 _, ok = upperBound.SetString(swapPair.UpperBound, 10)690 if !ok {691 return fmt.Errorf("invalid upperBound amount: %s", swapPair.LowBound)692 }693 engine.mutex.Lock()694 defer engine.mutex.Unlock()695 engine.swapPairsFromERC20Addr[ethcom.HexToAddress(swapPair.MainChainErc20Addr)] = &SwapPairIns{696 Symbol: swapPair.Symbol,697 Name: swapPair.Name,698 Decimals: swapPair.Decimals,699 LowBound: lowBound,700 UpperBound: upperBound,701 MainChainErc20Addr: ethcom.HexToAddress(swapPair.MainChainErc20Addr),702 SideChainErc20Addr: ethcom.HexToAddress(swapPair.SideChainErc20Addr),703 }704 engine.main20ToSide20[ethcom.HexToAddress(swapPair.MainChainErc20Addr)] = ethcom.HexToAddress(swapPair.SideChainErc20Addr)705 engine.side20ToMain20[ethcom.HexToAddress(swapPair.SideChainErc20Addr)] = ethcom.HexToAddress(swapPair.MainChainErc20Addr)706 util.Logger.Infof("Create new swap pair, symbol %s, main chain address %s, side chain address %s", swapPair.Symbol, swapPair.MainChainErc20Addr,swapPair.SideChainErc20Addr)707 return nil708}709func (engine *SwapEngine) GetSwapPairInstance(erc20Addr ethcom.Address) (*SwapPairIns, error) {710 engine.mutex.RLock()711 defer engine.mutex.RUnlock()712 tokenInstance, ok := engine.swapPairsFromERC20Addr[erc20Addr]713 if !ok {714 return nil, fmt.Errorf("swap instance doesn't exist")715 }716 return tokenInstance, nil717}718func (engine *SwapEngine) UpdateSwapInstance(swapPair *model.SwapPair) {719 engine.mutex.Lock()720 defer engine.mutex.Unlock()721 sideTokenAddr := ethcom.HexToAddress(swapPair.SideChainErc20Addr)722 tokenInstance, ok := engine.swapPairsFromERC20Addr[sideTokenAddr]723 if !ok {724 return725 }726 if !swapPair.Available {727 delete(engine.swapPairsFromERC20Addr, sideTokenAddr)728 return729 }730 upperBound := big.NewInt(0)731 _, ok = upperBound.SetString(swapPair.UpperBound, 10)732 tokenInstance.UpperBound = upperBound...

Full Screen

Full Screen

memory.go

Source:memory.go Github

copy

Full Screen

...26 }27 return ret28}29func isMemorySet(r *configs.Resources) bool {30 return r.MemoryReservation != 0 || r.Memory != 0 || r.MemorySwap != 031}32func setMemory(dirPath string, r *configs.Resources) error {33 if !isMemorySet(r) {34 return nil35 }36 swap, err := cgroups.ConvertMemorySwapToCgroupV2Value(r.MemorySwap, r.Memory)37 if err != nil {38 return err39 }40 swapStr := numToStr(swap)41 if swapStr == "" && swap == 0 && r.MemorySwap > 0 {42 // memory and memorySwap set to the same value -- disable swap43 swapStr = "0"44 }45 // never write empty string to `memory.swap.max`, it means set to 0.46 if swapStr != "" {47 if err := cgroups.WriteFile(dirPath, "memory.swap.max", swapStr); err != nil {48 return err49 }50 }51 if val := numToStr(r.Memory); val != "" {52 if err := cgroups.WriteFile(dirPath, "memory.max", val); err != nil {53 return err54 }55 }56 // cgroup.Resources.KernelMemory is ignored57 if val := numToStr(r.MemoryReservation); val != "" {58 if err := cgroups.WriteFile(dirPath, "memory.low", val); err != nil {59 return err60 }61 }62 return nil63}64func statMemory(dirPath string, stats *cgroups.Stats) error {65 const file = "memory.stat"66 statsFile, err := cgroups.OpenFile(dirPath, file, os.O_RDONLY)67 if err != nil {68 return err69 }70 defer statsFile.Close()71 sc := bufio.NewScanner(statsFile)72 for sc.Scan() {73 t, v, err := fscommon.ParseKeyValue(sc.Text())74 if err != nil {75 return &parseError{Path: dirPath, File: file, Err: err}76 }77 stats.MemoryStats.Stats[t] = v78 }79 if err := sc.Err(); err != nil {80 return &parseError{Path: dirPath, File: file, Err: err}81 }82 stats.MemoryStats.Cache = stats.MemoryStats.Stats["file"]83 // Unlike cgroup v1 which has memory.use_hierarchy binary knob,84 // cgroup v2 is always hierarchical.85 stats.MemoryStats.UseHierarchy = true86 memoryUsage, err := getMemoryDataV2(dirPath, "")87 if err != nil {88 if errors.Is(err, unix.ENOENT) && dirPath == UnifiedMountpoint {89 // The root cgroup does not have memory.{current,max}90 // so emulate those using data from /proc/meminfo.91 return statsFromMeminfo(stats)92 }93 return err94 }95 stats.MemoryStats.Usage = memoryUsage96 swapUsage, err := getMemoryDataV2(dirPath, "swap")97 if err != nil {98 return err99 }100 // As cgroup v1 reports SwapUsage values as mem+swap combined,101 // while in cgroup v2 swap values do not include memory,102 // report combined mem+swap for v1 compatibility.103 swapUsage.Usage += memoryUsage.Usage104 if swapUsage.Limit != math.MaxUint64 {105 swapUsage.Limit += memoryUsage.Limit106 }107 stats.MemoryStats.SwapUsage = swapUsage108 return nil109}110func getMemoryDataV2(path, name string) (cgroups.MemoryData, error) {111 memoryData := cgroups.MemoryData{}112 moduleName := "memory"113 if name != "" {114 moduleName = "memory." + name115 }116 usage := moduleName + ".current"117 limit := moduleName + ".max"118 value, err := fscommon.GetCgroupParamUint(path, usage)119 if err != nil {120 if name != "" && os.IsNotExist(err) {121 // Ignore EEXIST as there's no swap accounting122 // if kernel CONFIG_MEMCG_SWAP is not set or123 // swapaccount=0 kernel boot parameter is given.124 return cgroups.MemoryData{}, nil125 }126 return cgroups.MemoryData{}, err127 }128 memoryData.Usage = value129 value, err = fscommon.GetCgroupParamUint(path, limit)130 if err != nil {131 return cgroups.MemoryData{}, err132 }133 memoryData.Limit = value134 return memoryData, nil135}136func statsFromMeminfo(stats *cgroups.Stats) error {137 const file = "/proc/meminfo"138 f, err := os.Open(file)139 if err != nil {140 return err141 }142 defer f.Close()143 // Fields we are interested in.144 var (145 swap_free uint64146 swap_total uint64147 main_total uint64148 main_free uint64149 )150 mem := map[string]*uint64{151 "SwapFree": &swap_free,152 "SwapTotal": &swap_total,153 "MemTotal": &main_total,154 "MemFree": &main_free,155 }156 found := 0157 sc := bufio.NewScanner(f)158 for sc.Scan() {159 parts := strings.SplitN(sc.Text(), ":", 3)160 if len(parts) != 2 {161 // Should not happen.162 continue163 }164 k := parts[0]165 p, ok := mem[k]166 if !ok {167 // Unknown field -- not interested.168 continue169 }170 vStr := strings.TrimSpace(strings.TrimSuffix(parts[1], " kB"))171 *p, err = strconv.ParseUint(vStr, 10, 64)172 if err != nil {173 return &parseError{File: file, Err: errors.New("bad value for " + k)}174 }175 found++176 if found == len(mem) {177 // Got everything we need -- skip the rest.178 break179 }180 }181 if err := sc.Err(); err != nil {182 return &parseError{Path: "", File: file, Err: err}183 }184 stats.MemoryStats.SwapUsage.Usage = (swap_total - swap_free) * 1024185 stats.MemoryStats.SwapUsage.Limit = math.MaxUint64186 stats.MemoryStats.Usage.Usage = (main_total - main_free) * 1024187 stats.MemoryStats.Usage.Limit = math.MaxUint64188 return nil189}...

Full Screen

Full Screen

free_test.go

Source:free_test.go Github

copy

Full Screen

...10MemFree: 721716 kB11MemAvailable: 2774100 kB12Buffers: 244880 kB13Cached: 3462124 kB14SwapTotal: 8265724 kB15SwapFree: 8264956 kB16SReclaimable: 179852 kB`)17 m, err := meminfoFromBytes(input)18 if err != nil {19 t.Fatal(err)20 }21 if m["MemFree"] != 721716 {22 t.Fatalf("MemFree: got %v, want 721716", m["MemFree"])23 }24 if m["MemAvailable"] != 2774100 {25 t.Fatalf("MemAvailable: got %v, want 2774100", m["MemAvailable"])26 }27 if m["Buffers"] != 244880 {28 t.Fatalf("Buffers: got %v, want 244880", m["Buffers"])29 }30 if m["Cached"] != 3462124 {31 t.Fatalf("Cached: got %v, want 3462124", m["Cached"])32 }33 if m["SwapTotal"] != 8265724 {34 t.Fatalf("SwapTotal: got %v, want 8265724", m["SwapTotal"])35 }36 if m["SwapFree"] != 8264956 {37 t.Fatalf("SwapFree: got %v, want 8264956", m["SwapFree"])38 }39 if m["SReclaimable"] != 179852 {40 t.Fatalf("SReclaimable: got %v, want 179852", m["SReclaimable"])41 }42}43func TestPrintSwap(t *testing.T) {44 input := []byte(`MemTotal: 8052976 kB45MemFree: 721716 kB46MemAvailable: 2774100 kB47Buffers: 244880 kB48Cached: 3462124 kB49SwapTotal: 8265724 kB50SwapFree: 8264956 kB51SReclaimable: 179852 kB`)52 m, err := meminfoFromBytes(input)53 if err != nil {54 t.Fatal(err)55 }56 si, err := getSwapInfo(m, &FreeConfig{Unit: KB})57 if err != nil {58 t.Fatal(err)59 }60 if si.Total != 8464101376 {61 t.Fatalf("Swap.Total: got %v, want 8464101376", si.Total)62 }63 if si.Used != 786432 {64 t.Fatalf("Swap.Used: got %v, want 786432", si.Used)65 }66 if si.Free != 8463314944 {67 t.Fatalf("Swap.Free: got %v, want 8463314944", si.Free)68 }69}70func TestPrintSwapMissingFields(t *testing.T) {71 input := []byte(`MemTotal: 8052976 kB72MemFree: 721716 kB73MemAvailable: 2774100 kB74Buffers: 244880 kB75Cached: 3462124 kB76SwapTotal: 8265724 kB77SReclaimable: 179852 kB`)78 m, err := meminfoFromBytes(input)79 if err != nil {80 t.Fatal(err)81 }82 _, err = getSwapInfo(m, &FreeConfig{Unit: KB})83 // should error out for the missing field84 if err == nil {85 t.Fatal("printSwap: got no error when expecting one")86 }87}88func TestPrintMem(t *testing.T) {89 input := []byte(`MemTotal: 8052976 kB90MemFree: 721716 kB91MemAvailable: 2774100 kB92Buffers: 244880 kB93Cached: 3462124 kB94Shmem: 1617788 kB95SwapTotal: 8265724 kB96SwapFree: 8264956 kB97SReclaimable: 179852 kB`)98 m, err := meminfoFromBytes(input)99 if err != nil {100 t.Fatal(err)101 }102 mmi, err := getMainMemInfo(m, &FreeConfig{Unit: KB})103 if err != nil {104 t.Fatal(err)105 }106 if mmi.Total != 8246247424 {107 t.Fatalf("MainMem.Total: got %v, want 8246247424", mmi.Total)108 }109 if mmi.Free != 739037184 {110 t.Fatalf("MainMem.Free: got %v, want 739037184", mmi.Free)111 }112 if mmi.Used != 3527069696 {113 t.Fatalf("MainMem.Used: got %v, want 3527069696", mmi.Used)114 }115 if mmi.Shared != 1656614912 {116 t.Fatalf("MainMem.Shared: got %v, want 1656614912", mmi.Shared)117 }118 if mmi.Cached != 3729383424 {119 t.Fatalf("MainMem.Cached: got %v, want 3729383424", mmi.Cached)120 }121 if mmi.Buffers != 250757120 {122 t.Fatalf("MainMem.Buffers: got %v, want 250757120", mmi.Buffers)123 }124 if mmi.Available != 2840678400 {125 t.Fatalf("MainMem.Available: got %v, want 2840678400", mmi.Available)126 }127}128func TestPrintMemMissingFields(t *testing.T) {129 input := []byte(`MemTotal: 8052976 kB130MemFree: 721716 kB131MemAvailable: 2774100 kB132Buffers: 244880 kB133SwapTotal: 8265724 kB134SwapFree: 8264956 kB135SReclaimable: 179852 kB`)136 m, err := meminfoFromBytes(input)137 if err != nil {138 t.Fatal(err)139 }140 _, err = getMainMemInfo(m, &FreeConfig{Unit: KB})141 // should error out for the missing field142 if err == nil {143 t.Fatal("printMem: got no error when expecting one")144 }145}...

Full Screen

Full Screen

mem.go

Source:mem.go Github

copy

Full Screen

...17 }18 self.Label = "Memory Usage"19 self.Zoom = zoom20 self.Data["Main"] = []float64{0}21 self.Data["Swap"] = []float64{0}22 self.update()23 ticker := time.NewTicker(self.interval)24 go func() {25 for range ticker.C {26 self.update()27 }28 }()29 return self30}31func (self *Mem) update() {32 main, err := psMem.VirtualMemory()33 if err != nil {34 log.Println(err)35 return36 }37 swap, err := psMem.SwapMemory()38 if err != nil {39 log.Println(err)40 return41 }42 self.Data["Main"] = append(self.Data["Main"], main.UsedPercent)43 if len(self.Data["Main"]) > MEMHISTMAX {44 self.Data["Main"] = self.Data["Main"][len(self.Data["Main"])-MEMHISTMAX+60:]45 }46 self.Data["Swap"] = append(self.Data["Swap"], swap.UsedPercent)47 if len(self.Data["Swap"]) > MEMHISTMAX {48 self.Data["Swap"] = self.Data["Swap"][len(self.Data["Swap"])-MEMHISTMAX+60:]49 }50}...

Full Screen

Full Screen

Swap

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 fmt.Print("Enter a: ")4 fmt.Scan(&a)5 fmt.Print("Enter b: ")6 fmt.Scan(&b)7 fmt.Println("Before swapping")8 fmt.Println("a =", a)9 fmt.Println("b =", b)10 Swap(&a, &b)11 fmt.Println("After swapping")12 fmt.Println("a =", a)13 fmt.Println("b =", b)14}15func Swap(a *int, b *int) {16}

Full Screen

Full Screen

Swap

Using AI Code Generation

copy

Full Screen

1import "fmt"2func main() {3 fmt.Println("Before Swapping")4 fmt.Println("a = ", a)5 fmt.Println("b = ", b)6 fmt.Println("After Swapping")7 Swap(a, b)8}9import "fmt"10func Swap(x, y int) {11 fmt.Println("x = ", x)12 fmt.Println("y = ", y)13}14import "fmt"15func main() {16 fmt.Println("Before Swapping")17 fmt.Println("a = ", a)18 fmt.Println("b = ", b)19 fmt.Println("After Swapping")20 Swap(&a, &b)21}22import "fmt"23func Swap(x, y *int) {24 fmt.Println("x = ", *x)25 fmt.Println("y = ", *y)26}

Full Screen

Full Screen

Swap

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 fmt.Println("Enter the value of a")4 fmt.Scanln(&a)5 fmt.Println("Enter the value of b")6 fmt.Scanln(&b)7 fmt.Printf("Before swapping a = %d and b = %d", a, b)8 fmt.Printf("9}10import (11func main() {12 fmt.Println("Enter the value of a")13 fmt.Scanln(&a)14 fmt.Println("Enter the value of b")15 fmt.Scanln(&b)16 fmt.Printf("Before swapping a = %d and b = %d", a, b)17 a, b = swap(a, b)18 fmt.Printf("19}20func swap(a, b int) (int, int) {21}22import (23func main() {24 fmt.Println("Enter the value of a")25 fmt.Scanln(&a)26 fmt.Println("Enter the value of b")27 fmt.Scanln(&b)28 fmt.Printf("Before swapping a = %d and b = %d", a, b)29 a, b = swap(a, b)30 fmt.Printf("31}32func swap(a, b int) (int, int) {33}

Full Screen

Full Screen

Swap

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 fmt.Print("Enter the first number: ")4 fmt.Scan(&a)5 fmt.Print("Enter the second number: ")6 fmt.Scan(&b)7 fmt.Println("Before swapping")8 fmt.Println("a = ", a)9 fmt.Println("b = ", b)10 Swap(&a, &b)11 fmt.Println("After swapping")12 fmt.Println("a = ", a)13 fmt.Println("b = ", b)14}15func Swap(x *int, y *int) {16}

Full Screen

Full Screen

Swap

Using AI Code Generation

copy

Full Screen

1import "fmt"2func main() {3 fmt.Println("Before swapping a =", a, "b =", b)4 Swap(a, b)5 fmt.Println("After swapping a =", a, "b =", b)6}7import "fmt"8func Swap(a, b int) {9 fmt.Println("Inside swapping a =", a, "b =", b)10}11import "fmt"12func main() {13 fmt.Println("Before swapping a =", a, "b =", b)14 main.Swap(a, b)15 fmt.Println("After swapping a =", a, "b =", b)16}17import "fmt"18func Swap(a, b int) {19 fmt.Println("Inside swapping a =", a, "b =", b)20}21import "fmt"22func main() {23 fmt.Println("Before swapping a =", a, "b =", b)24 swap(a, b)25 fmt.Println("After swapping a =", a, "b =", b)26}27func swap(a, b int) {28 fmt.Println("Inside swapping a =", a, "b =", b)29}

Full Screen

Full Screen

Swap

Using AI Code Generation

copy

Full Screen

1import "fmt"2func main() {3 fmt.Println("Before swap a =", a, "b =", b)4 Swap(a, b)5 fmt.Println("After swap a =", a, "b =", b)6}7func Swap(x, y int) {8 fmt.Println("Inside swap x =", x, "y =", y)9}

Full Screen

Full Screen

Swap

Using AI Code Generation

copy

Full Screen

1import "fmt"2func main() {3 fmt.Println("Enter the two numbers to be swapped")4 fmt.Scanln(&a, &b)5 fmt.Println("Before swapping")6 fmt.Println("a = ", a, "b = ", b)7 fmt.Println("After swapping")8 fmt.Println("a = ", a, "b = ", b)9}

Full Screen

Full Screen

Swap

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 p := person{4 }5 p.Swap()6}7import (8type person struct {9}10func (p *person) Swap() {11 fmt.Println(p.name, p.age)12}

Full Screen

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful