public async Task <APIResponse> Confirm([FromBody] ConfirmModel model) { // validate if (BaseValidableModel.IsInvalid(model, out var errFields)) { return(APIResponse.BadRequest(errFields)); } var user = (DAL.Models.Identity.User)null; var agent = GetUserAgentInfo(); var userLocale = GetUserLocale(); // check token if (!await JWT.IsValid( appConfig: AppConfig, jwtToken: model.Token, expectedAudience: JwtAudience.Cabinet, expectedArea: Common.JwtArea.Registration, validStamp: async(jwt, id) => { user = await UserManager.FindByNameAsync(id); return(""); } ) || user == null) { return(APIResponse.BadRequest(nameof(model.Token), "Invalid token")); } if (!user.EmailConfirmed) { user.EmailConfirmed = true; await DbContext.SaveChangesAsync(); } return(APIResponse.Success()); }
public async Task <APIResponse> NewPassword([FromBody] NewPasswordModel model) { // validate if (BaseValidableModel.IsInvalid(model, out var errFields)) { return(APIResponse.BadRequest(errFields)); } var user = (DAL.Models.Identity.User)null; var audience = GetCurrentAudience(); var agent = GetUserAgentInfo(); var userLocale = GetUserLocale(); // check token if (!await Core.Tokens.JWT.IsValid( appConfig: AppConfig, jwtToken: model.Token, expectedAudience: JwtAudience.Cabinet, expectedArea: JwtArea.RestorePassword, validStamp: async(jwt, id) => { user = await UserManager.FindByNameAsync(id); return(""); } ) || user == null) { return(APIResponse.BadRequest(nameof(model.Token), "Invalid token")); } await UserManager.RemovePasswordAsync(user); await UserManager.AddPasswordAsync(user, model.Password); if (audience != null) { UserAccount.GenerateJwtSalt(user, audience.Value); await DbContext.SaveChangesAsync(); } // notification await EmailComposer.FromTemplate(await TemplateProvider.GetEmailTemplate(EmailTemplate.PasswordChanged, userLocale)) .Initiator(agent.Ip, agent.Agent, DateTime.UtcNow) .Send(user.Email, user.UserName, EmailQueue) ; // activity var userActivity = CoreLogic.User.CreateUserActivity( user: user, type: Common.UserActivityType.Password, comment: "Password changed", ip: agent.Ip, agent: agent.Agent, locale: userLocale ); DbContext.UserActivity.Add(userActivity); await DbContext.SaveChangesAsync(); return(APIResponse.Success()); }
public async Task <APIResponse> Estimate([FromBody] EstimateModel model) { if (BaseValidableModel.IsInvalid(model, out var errFields)) { return(APIResponse.BadRequest(errFields)); } var exchangeCurrency = FiatCurrency.Usd; TradableCurrency?cryptoToken = null; // try parse fiat currency if (Enum.TryParse(model.Currency, true, out FiatCurrency fc)) { exchangeCurrency = fc; } // or crypto currency else if (Enum.TryParse(model.Currency, true, out TradableCurrency cc)) { cryptoToken = cc; } else { return(APIResponse.BadRequest(nameof(model.Currency), "Invalid format")); } // try parse amount if (!BigInteger.TryParse(model.Amount, out var inputAmount) || inputAmount < 1 || (cryptoToken == null && !model.Reversed && inputAmount > long.MaxValue)) { return(APIResponse.BadRequest(nameof(model.Amount), "Invalid amount")); } // --- var userOrNull = await GetUserFromDb(); var rcfg = RuntimeConfigHolder.Clone(); // get user limits var limits = cryptoToken != null ? DepositLimits(rcfg, cryptoToken.Value) : await DepositLimits(rcfg, DbContext, userOrNull?.Id, exchangeCurrency) ; // estimate var estimation = await Estimation(rcfg, inputAmount, cryptoToken, exchangeCurrency, model.Reversed, 0, limits.Min, limits.Max); if (!estimation.TradingAllowed) { return(APIResponse.BadRequest(APIErrorCode.TradingNotAllowed)); } if (estimation.IsLimitExceeded) { return(APIResponse.BadRequest(APIErrorCode.TradingExchangeLimit, estimation.View.Limits)); } return(APIResponse.Success(estimation.View)); }
public async Task <APIResponse> ChangePassword([FromBody] ChangePasswordModel model) { // validate if (BaseValidableModel.IsInvalid(model, out var errFields)) { return(APIResponse.BadRequest(errFields)); } var user = await GetUserFromDb(); var agent = GetUserAgentInfo(); var userLocale = GetUserLocale(); // first check tfa if (user.TwoFactorEnabled && !Core.Tokens.GoogleAuthenticator.Validate(model.TfaCode, user.TfaSecret)) { return(APIResponse.BadRequest(nameof(model.TfaCode), "Invalid 2fa code")); } // check current password if (await UserManager.HasPasswordAsync(user) && (model.Current == null || !await UserManager.CheckPasswordAsync(user, model.Current))) { return(APIResponse.BadRequest(nameof(model.Current), "Invalid current password")); } // set new password await UserManager.RemovePasswordAsync(user); await UserManager.AddPasswordAsync(user, model.New); // notification await EmailComposer.FromTemplate(await TemplateProvider.GetEmailTemplate(EmailTemplate.PasswordChanged, userLocale)) .Initiator(agent.Ip, agent.Agent, DateTime.UtcNow) .Send(user.Email, user.UserName, EmailQueue) ; // activity var userActivity = CoreLogic.User.CreateUserActivity( user: user, type: Common.UserActivityType.Password, comment: "Password changed", ip: agent.Ip, agent: agent.Agent, locale: userLocale ); DbContext.UserActivity.Add(userActivity); await DbContext.SaveChangesAsync(); return(APIResponse.Success( new ChangePasswordView() { } )); }
public async Task <APIResponse> Tfa([FromBody] TfaModel model) { // validate if (BaseValidableModel.IsInvalid(model, out var errFields)) { return(APIResponse.BadRequest(errFields)); } var audience = GetCurrentAudience(); if (audience == null) { return(APIResponse.BadRequest(APIErrorCode.Unauthorized)); } var user = await GetUserFromDb(); if (user != null && user.TwoFactorEnabled) { // locked out if (await UserManager.IsLockedOutAsync(user)) { return(APIResponse.BadRequest(APIErrorCode.AccountLocked, "Too many unsuccessful attempts. Account is locked, try to sign in later")); } // by code if (GoogleAuthenticator.Validate(model.Code, user.TfaSecret)) { return(OnSignInResultCheck( services: HttpContext.RequestServices, result: Microsoft.AspNetCore.Identity.SignInResult.Success, user: user, audience: audience.Value, tfaRequired: false )); } // +1 failed login DbContext.Attach <DAL.Models.Identity.User>(user); await UserManager.AccessFailedAsync(user); } return(APIResponse.BadRequest(nameof(model.Code), "Invalid code")); }
public async Task <APIResponse> VerificationEdit([FromBody] VerificationEditModel model) { // validate if (BaseValidableModel.IsInvalid(model, out var errFields)) { return(APIResponse.BadRequest(errFields)); } var user = await GetUserFromDb(); var userTier = CoreLogic.User.GetTier(user); if (userTier == UserTier.Tier1 && !CoreLogic.User.HasKycVerification(user.UserVerification)) { // format phone number var phoneFormatted = Common.TextFormatter.NormalizePhoneNumber(model.PhoneNumber); // parse dob var dob = DateTime.ParseExact(model.Dob, "dd.MM.yyyy", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); { user.UserVerification.FirstName = model.FirstName.Limit(64); user.UserVerification.MiddleName = model.MiddleName?.Limit(64); user.UserVerification.LastName = model.LastName.Limit(64); user.UserVerification.DoB = dob; user.UserVerification.PhoneNumber = phoneFormatted.Limit(32); user.UserVerification.Country = Common.Countries.GetNameByAlpha2(model.Country); user.UserVerification.CountryCode = model.Country.ToUpper(); user.UserVerification.State = model.State.Limit(256); user.UserVerification.City = model.City.Limit(256); user.UserVerification.PostalCode = model.PostalCode.Limit(16); user.UserVerification.Street = model.Street.Limit(256); user.UserVerification.Apartment = model.Apartment?.Limit(128); user.UserVerification.TimeUserChanged = DateTime.UtcNow; } await DbContext.SaveChangesAsync(); } return(APIResponse.Success(MakeVerificationView(user))); }
public async Task <APIResponse> Confirm([FromBody] ConfirmModel model) { if (BaseValidableModel.IsInvalid(model, out var errFields)) { return(APIResponse.BadRequest(errFields)); } var user = await GetUserFromDb(); var userLocale = GetUserLocale(); var agent = GetUserAgentInfo(); // --- var request = await ( from r in DbContext.BuyGoldEth where r.Status == BuySellGoldRequestStatus.Unconfirmed && r.Id == model.RequestId && r.UserId == user.Id && (DateTime.UtcNow - r.TimeCreated).TotalHours < 1.0 select r ) .AsTracking() .FirstOrDefaultAsync() ; // request not exists if (request == null) { return(APIResponse.BadRequest(nameof(model.RequestId), "Invalid id")); } request.Status = BuySellGoldRequestStatus.Confirmed; await DbContext.SaveChangesAsync(); return(APIResponse.Success( new ConfirmView() { } )); }
public async Task <APIResponse> Register([FromBody] RegisterModel model) { // validate if (BaseValidableModel.IsInvalid(model, out var errFields)) { return(APIResponse.BadRequest(errFields)); } var agent = GetUserAgentInfo(); var userLocale = GetUserLocale(); // captcha if (!HostingEnvironment.IsDevelopment()) { if (!await Core.Recaptcha.Verify(AppConfig.Services.Recaptcha.SecretKey, model.Captcha, agent.Ip)) { return(APIResponse.BadRequest(nameof(model.Captcha), "Failed to validate captcha")); } } var result = await Core.UserAccount.CreateUserAccount(HttpContext.RequestServices, model.Email, model.Password); if (result.User != null) { // confirmation token var token = Core.Tokens.JWT.CreateSecurityToken( appConfig: AppConfig, entityId: result.User.UserName, audience: JwtAudience.Cabinet, securityStamp: "", area: Common.JwtArea.Registration, validFor: TimeSpan.FromDays(2) ); var callbackUrl = this.MakeAppLink(JwtAudience.Cabinet, fragment: AppConfig.Apps.Cabinet.RouteSignUpConfirmation.Replace(":token", token)); // email confirmation await EmailComposer.FromTemplate(await TemplateProvider.GetEmailTemplate(EmailTemplate.EmailConfirmation, userLocale)) .Link(callbackUrl) .Send(model.Email, "", EmailQueue) ; // auth token return(APIResponse.Success( new Models.API.v1.User.UserModels.AuthenticateView() { Token = JWT.CreateAuthToken( appConfig: AppConfig, user: result.User, audience: JwtAudience.Cabinet, area: JwtArea.Authorized ), TfaRequired = false, } )); } else { if (result.IsUsernameExists || result.IsEmailExists) { return(APIResponse.BadRequest(APIErrorCode.AccountEmailTaken, "Email is already taken")); } } throw new Exception("Registration failed"); }
public async Task <APIResponse> VerificationKycStart([FromBody] VerificationKycStartModel model) { // validate if (BaseValidableModel.IsInvalid(model, out var errFields)) { return(APIResponse.BadRequest(errFields)); } var user = await GetUserFromDb(); var userTier = CoreLogic.User.GetTier(user); // tos not signed, didn't fill personal data, has kyc already if ( !CoreLogic.User.HasTosSigned(user.UserVerification) || !CoreLogic.User.HasFilledPersonalData(user.UserVerification) || CoreLogic.User.HasKycVerification(user.UserVerification) ) { return(APIResponse.BadRequest(APIErrorCode.AccountNotVerified)); } // check previous verification attempt var status = MakeVerificationView(user); if (status.IsKycPending) { return(APIResponse.BadRequest(APIErrorCode.RateLimit)); } // --- // new kyc ticket var ticket = new KycTicket() { UserId = user.Id, ReferenceId = Guid.NewGuid().ToString("N"), Method = "general", FirstName = user.UserVerification.FirstName, LastName = user.UserVerification.LastName, DoB = user.UserVerification.DoB.Value, CountryCode = user.UserVerification.CountryCode, PhoneNumber = user.UserVerification.PhoneNumber, TimeCreated = DateTime.UtcNow, }; DbContext.KycShuftiProTicket.Add(ticket); await DbContext.SaveChangesAsync(); // set last ticket user.UserVerification.LastKycTicket = ticket; await DbContext.SaveChangesAsync(); // new redirect var kycUser = new CoreLogic.Services.KYC.UserData() { FirstName = ticket.FirstName, LastName = ticket.LastName, CountryCode = ticket.CountryCode, LanguageCode = GetUserLocale().ToString().ToUpper(), DoB = ticket.DoB, PhoneNumber = ticket.PhoneNumber, Email = user.Email, }; var callbackUrl = Url.Link("CallbackShuftiPro", new { /*secret = AppConfig.Services.ShuftiPro.CallbackSecret*/ }); var userTempRedirectUrl = Url.Link("CallbackRedirect", new { to = System.Web.HttpUtility.UrlEncode(model.Redirect) }); var kycRedirect = await KycExternalProvider.GetRedirect( kycUser, ticket.ReferenceId, userTempRedirectUrl, callbackUrl ); Logger.Verbose($"{user.UserName} got kyc redirect to {kycRedirect} with callback to {callbackUrl} and middle redirect to {userTempRedirectUrl}"); return(APIResponse.Success(new VerificationKycStartView() { TicketId = ticket.Id.ToString(), Redirect = kycRedirect, })); }
public async Task <APIResponse> Password([FromBody] RestoreModel model) { // validate if (BaseValidableModel.IsInvalid(model, out var errFields)) { return(APIResponse.BadRequest(errFields)); } var agent = GetUserAgentInfo(); var userLocale = GetUserLocale(); // captcha if (!HostingEnvironment.IsDevelopment()) { if (!await Core.Recaptcha.Verify(AppConfig.Services.Recaptcha.SecretKey, model.Captcha, agent.Ip)) { return(APIResponse.BadRequest(nameof(model.Captcha), "Failed to validate captcha")); } } // try find user var user = await UserManager.FindByEmailAsync(model.Email); if (user == null || !(await UserManager.IsEmailConfirmedAsync(user))) { return(APIResponse.Success()); } // confirmation token var token = Core.Tokens.JWT.CreateSecurityToken( appConfig: AppConfig, entityId: user.UserName, audience: JwtAudience.Cabinet, area: Common.JwtArea.RestorePassword, securityStamp: "", validFor: TimeSpan.FromHours(24) ); var callbackUrl = this.MakeAppLink(JwtAudience.Cabinet, fragment: AppConfig.Apps.Cabinet.RoutePasswordRestoration.Replace(":token", token)); // restoration email await EmailComposer.FromTemplate(await TemplateProvider.GetEmailTemplate(EmailTemplate.PasswordRestoration, userLocale)) .Link(callbackUrl) .Initiator(agent.Ip, agent.Agent, DateTime.UtcNow) .Send(model.Email, user.UserName, EmailQueue) ; // activity var userActivity = CoreLogic.User.CreateUserActivity( user: user, type: Common.UserActivityType.Password, comment: "Password restoration requested", ip: agent.Ip, agent: agent.Agent, locale: userLocale ); DbContext.UserActivity.Add(userActivity); await DbContext.SaveChangesAsync(); return(APIResponse.Success()); }
public async Task <APIResponse> Confirm([FromBody] ConfirmModel model) { // validate if (BaseValidableModel.IsInvalid(model, out var errFields)) { return(APIResponse.BadRequest(errFields)); } var user = await GetUserFromDb(); var userTier = CoreLogic.User.GetTier(user); var userLocale = GetUserLocale(); var agent = GetUserAgentInfo(); if (userTier < UserTier.Tier2) { return(APIResponse.BadRequest(APIErrorCode.AccountNotVerified)); } // --- var request = await ( from r in DbContext.SellGoldEth where r.Status == BuySellGoldRequestStatus.Unconfirmed && r.Id == model.RequestId && r.UserId == user.Id && (DateTime.UtcNow - r.TimeCreated).TotalHours < 1.0 select r ) .Include(_ => _.RelFinHistory) .AsTracking() .FirstOrDefaultAsync() ; // request not exists if (request == null) { return(APIResponse.BadRequest(nameof(model.RequestId), "Invalid id")); } // charge using (var scope = HttpContext.RequestServices.CreateScope()) { if (!await CoreLogic.Finance.SumusWallet.Charge(scope.ServiceProvider, request.UserId, request.GoldAmount, SumusToken.Gold)) { // mark request failed request.Status = BuySellGoldRequestStatus.Failed; if (request.RelFinHistory != null) { request.RelFinHistory.Status = UserFinHistoryStatus.Failed; request.RelFinHistory.Comment = "Not enough GOLD"; } await DbContext.SaveChangesAsync(); return(APIResponse.BadRequest(APIErrorCode.TradingNotAllowed)); } else { // mark request for processing request.Status = BuySellGoldRequestStatus.Confirmed; if (request.RelFinHistory != null) { request.RelFinHistory.Status = UserFinHistoryStatus.Processing; } await DbContext.SaveChangesAsync(); return(APIResponse.Success( new ConfirmView() { } )); } } }
public async Task <APIResponse> AssetEth([FromBody] AssetEthModel model) { // validate if (BaseValidableModel.IsInvalid(model, out var errFields)) { return(APIResponse.BadRequest(errFields)); } // try parse amount if (!BigInteger.TryParse(model.Amount, out var inputAmount) || inputAmount < 1) { return(APIResponse.BadRequest(nameof(model.Amount), "Invalid amount")); } // try parse fiat currency var exchangeCurrency = FiatCurrency.Usd; if (Enum.TryParse(model.Currency, true, out FiatCurrency fc)) { exchangeCurrency = fc; } // --- var rcfg = RuntimeConfigHolder.Clone(); var user = await GetUserFromDb(); var userTier = CoreLogic.User.GetTier(user); var agent = GetUserAgentInfo(); if (userTier < UserTier.Tier2) { return(APIResponse.BadRequest(APIErrorCode.AccountNotVerified)); } // --- if (!rcfg.Gold.AllowTradingEth) { return(APIResponse.BadRequest(APIErrorCode.TradingNotAllowed)); } var limits = WithdrawalLimits(rcfg, TradableCurrency.Eth); var estimation = await Estimation(rcfg, inputAmount, TradableCurrency.Eth, exchangeCurrency, model.EthAddress, model.Reversed, limits.Min, limits.Max); if (!estimation.TradingAllowed || estimation.ResultCurrencyAmount < 1) { return(APIResponse.BadRequest(APIErrorCode.TradingNotAllowed)); } if (estimation.IsLimitExceeded) { return(APIResponse.BadRequest(APIErrorCode.TradingExchangeLimit, estimation.View.Limits)); } // limit gold amount to max available if (estimation.ResultGoldAmount.FromSumus() > user.UserSumusWallet.BalanceGold) { estimation = await Estimation(rcfg, user.UserSumusWallet.BalanceGold.ToSumus(), TradableCurrency.Eth, exchangeCurrency, model.EthAddress, false, limits.Min, limits.Max); if (!estimation.TradingAllowed || estimation.ResultCurrencyAmount < 1) { return(APIResponse.BadRequest(APIErrorCode.TradingNotAllowed)); } if (estimation.IsLimitExceeded) { return(APIResponse.BadRequest(APIErrorCode.TradingExchangeLimit, estimation.View.Limits)); } } var timeNow = DateTime.UtcNow; // history var finHistory = new DAL.Models.UserFinHistory() { Status = UserFinHistoryStatus.Unconfirmed, Type = UserFinHistoryType.GoldSell, Source = "GOLD", SourceAmount = TextFormatter.FormatTokenAmountFixed(estimation.ResultGoldAmount, TokensPrecision.Sumus), Destination = "ETH", DestinationAmount = TextFormatter.FormatTokenAmountFixed(estimation.ResultCurrencyAmount, TokensPrecision.Ethereum), Comment = "", TimeCreated = timeNow, UserId = user.Id, }; DbContext.UserFinHistory.Add(finHistory); await DbContext.SaveChangesAsync(); // request var request = new DAL.Models.SellGoldEth() { Status = BuySellGoldRequestStatus.Unconfirmed, GoldAmount = estimation.ResultGoldAmount.FromSumus(), Destination = model.EthAddress, EthAmount = estimation.ResultCurrencyAmount.FromSumus(), ExchangeCurrency = exchangeCurrency, GoldRateCents = estimation.CentsPerGoldRate, EthRateCents = estimation.CentsPerAssetRate, TimeCreated = timeNow, RelFinHistoryId = finHistory.Id, UserId = user.Id, }; // add and save DbContext.SellGoldEth.Add(request); await DbContext.SaveChangesAsync(); var assetPerGold = CoreLogic.Finance.Estimation.AssetPerGold(TradableCurrency.Eth, estimation.CentsPerAssetRate, estimation.CentsPerGoldRate); return(APIResponse.Success( new AssetEthView() { RequestId = request.Id, EthRate = estimation.CentsPerAssetRate / 100d, GoldRate = estimation.CentsPerGoldRate / 100d, Currency = exchangeCurrency.ToString().ToUpper(), EthPerGoldRate = assetPerGold.ToString(), Estimation = estimation.View, } )); }
public async Task <APIResponse> Authenticate([FromBody] AuthenticateModel model) { var notFoundDesc = "Account not found"; // validate if (BaseValidableModel.IsInvalid(model, out var errFields)) { return(APIResponse.BadRequest(APIErrorCode.AccountNotFound, notFoundDesc)); } var agent = GetUserAgentInfo(); var userLocale = GetUserLocale(); var user = await UserManager.FindByNameAsync(model.Username) ?? await UserManager.FindByEmailAsync(model.Username); if (user != null) { bool isLockedOut = false; // locked out if (user.LockoutEnd != null && user.LockoutEnd.Value.UtcDateTime > DateTime.UtcNow) { // unlock before this check isLockedOut = true; user.LockoutEnd = null; } // get audience JwtAudience audience = JwtAudience.Cabinet; if (!string.IsNullOrWhiteSpace(model.Audience)) { if (Enum.TryParse(model.Audience, true, out JwtAudience aud)) { audience = aud; } } // load options await DbContext.Entry(user).Reference(_ => _.UserOptions).LoadAsync(); var sres = OnSignInResultCheck( services: HttpContext.RequestServices, result: await SignInManager.CheckPasswordSignInAsync(user, model.Password, lockoutOnFailure: true), audience: audience, user: user, tfaRequired: user.TwoFactorEnabled ); if (sres != null) { // successful result if (sres.GetHttpStatusCode() == System.Net.HttpStatusCode.OK && sres.GetErrorCode() == null) { // notification await EmailComposer.FromTemplate(await TemplateProvider.GetEmailTemplate(EmailTemplate.SignedIn, userLocale)) .ReplaceBodyTag("IP", agent.Ip) .Initiator(agent.Ip, agent.Agent, DateTime.UtcNow) .Send(user.Email, user.UserName, EmailQueue) ; // activity var userActivity = CoreLogic.User.CreateUserActivity( user: user, type: Common.UserActivityType.Auth, comment: "Signed in with password", ip: agent.Ip, agent: agent.Agent, locale: userLocale ); DbContext.UserActivity.Add(userActivity); await DbContext.SaveChangesAsync(); } return(sres); } // was locked before if (isLockedOut) { await UserManager.SetLockoutEndDateAsync(user, (DateTimeOffset)DateTime.UtcNow.AddMinutes(60)); return(APIResponse.BadRequest(APIErrorCode.AccountLocked, "Too many unsuccessful attempts. Account is locked, try to sign in later")); } } return(APIResponse.BadRequest(APIErrorCode.AccountNotFound, notFoundDesc)); }
public async Task <APIResponse> LiteWallet([FromBody] LiteWalletModel model) { // validate if (BaseValidableModel.IsInvalid(model, out var errFields)) { return(APIResponse.BadRequest(errFields)); } var user = await GetUserFromDb(); var userTier = CoreLogic.User.GetTier(user); var userLocale = GetUserLocale(); var agent = GetUserAgentInfo(); if (userTier < UserTier.Tier2) { return(APIResponse.BadRequest(APIErrorCode.AccountNotVerified)); } if (model.Amount <= 0.001m || user.UserSumusWallet.BalanceGold < model.Amount) { return(APIResponse.BadRequest(APIErrorCode.TradingNotAllowed)); } // --- var timeNow = DateTime.UtcNow; // charge using (var scope = HttpContext.RequestServices.CreateScope()) { if (await CoreLogic.Finance.SumusWallet.Charge(scope.ServiceProvider, user.Id, model.Amount, SumusToken.Gold)) { try { var finHistory = new UserFinHistory() { Status = UserFinHistoryStatus.Processing, Type = UserFinHistoryType.GoldWithdraw, Source = "GOLD", SourceAmount = TextFormatter.FormatTokenAmountFixed(model.Amount), Destination = "", DestinationAmount = "", Comment = "", TimeCreated = timeNow, UserId = user.Id, }; DbContext.UserFinHistory.Add(finHistory); await DbContext.SaveChangesAsync(); var request = new WithdrawGold() { Status = EmissionRequestStatus.Requested, SumAddress = model.SumusAddress, Amount = model.Amount, TimeCreated = timeNow, UserId = user.Id, RelFinHistoryId = finHistory.Id, }; DbContext.WithdrawGold.Add(request); await DbContext.SaveChangesAsync(); // mint-sender service { var reply = await Bus.Request( MintSender.Subject.Sender.Request.Send, new MintSender.Sender.Request.Send() { Id = request.Id.ToString(), Amount = model.Amount.ToString("F18"), PublicKey = model.SumusAddress, Service = "core_gold_withdrawer", Token = "GOLD", }, MintSender.Sender.Request.SendReply.Parser, 3000 ); if (!reply.Success) { throw new Exception(reply.Error); } } return(APIResponse.Success( new LiteWalletView() { } )); } catch (Exception e) { try { await CoreLogic.Finance.SumusWallet.Refill(scope.ServiceProvider, user.Id, model.Amount, SumusToken.Gold); } catch { } Logger.Error(e, $"Failed to withdraw user {model.Amount} GOLD"); return(APIResponse.GeneralInternalFailure(e)); } } else { return(APIResponse.BadRequest(APIErrorCode.TradingNotAllowed)); } } }
public async Task <APIResponse> TFAEdit([FromBody] TfaEditModel model) { // validate if (BaseValidableModel.IsInvalid(model, out var errFields)) { return(APIResponse.BadRequest(nameof(model.Code), "Invalid 2fa code")); } var user = await GetUserFromDb(); var agent = GetUserAgentInfo(); var userLocale = GetUserLocale(); var makeChange = user.TwoFactorEnabled != model.Enable; if (makeChange) { if (!Core.Tokens.GoogleAuthenticator.Validate(model.Code, user.TfaSecret)) { return(APIResponse.BadRequest(nameof(model.Code), "Invalid 2fa code")); } user.TwoFactorEnabled = model.Enable; } user.UserOptions.InitialTfaQuest = true; await DbContext.SaveChangesAsync(); // notify if (makeChange) { if (model.Enable) { // notification await EmailComposer.FromTemplate(await TemplateProvider.GetEmailTemplate(EmailTemplate.TfaEnabled, userLocale)) .Initiator(agent.Ip, agent.Agent, DateTime.UtcNow) .Send(user.Email, user.UserName, EmailQueue) ; // activity var userActivity = CoreLogic.User.CreateUserActivity( user: user, type: Common.UserActivityType.Settings, comment: "Two factor authentication enabled", ip: agent.Ip, agent: agent.Agent, locale: userLocale ); DbContext.UserActivity.Add(userActivity); await DbContext.SaveChangesAsync(); } else { // notification await EmailComposer.FromTemplate(await TemplateProvider.GetEmailTemplate(EmailTemplate.TfaDisabled, userLocale)) .Initiator(agent.Ip, agent.Agent, DateTime.UtcNow) .Send(user.Email, user.UserName, EmailQueue) ; // activity var userActivity = CoreLogic.User.CreateUserActivity( user: user, type: Common.UserActivityType.Settings, comment: "Two factor authentication disabled", ip: agent.Ip, agent: agent.Agent, locale: userLocale ); DbContext.UserActivity.Add(userActivity); await DbContext.SaveChangesAsync(); } } return(APIResponse.Success(MakeTFASetupView(user))); }
public async Task <APIResponse> AssetEth([FromBody] AssetEthModel model) { // validate if (BaseValidableModel.IsInvalid(model, out var errFields)) { return(APIResponse.BadRequest(errFields)); } // try parse amount if (!BigInteger.TryParse(model.Amount, out var inputAmount) || inputAmount < 1) { return(APIResponse.BadRequest(nameof(model.Amount), "Invalid amount")); } // try parse fiat currency var exchangeCurrency = FiatCurrency.Usd; if (Enum.TryParse(model.Currency, true, out FiatCurrency fc)) { exchangeCurrency = fc; } // --- var rcfg = RuntimeConfigHolder.Clone(); var user = await GetUserFromDb(); // --- if (!rcfg.Gold.AllowTradingEth) { return(APIResponse.BadRequest(APIErrorCode.TradingNotAllowed)); } var limits = DepositLimits(rcfg, TradableCurrency.Eth); // estimation var estimation = await Estimation(rcfg, inputAmount, TradableCurrency.Eth, exchangeCurrency, model.Reversed, 0d, limits.Min, limits.Max); if (!estimation.TradingAllowed || estimation.ResultCurrencyAmount < 1) { return(APIResponse.BadRequest(APIErrorCode.TradingNotAllowed)); } if (estimation.IsLimitExceeded) { return(APIResponse.BadRequest(APIErrorCode.TradingExchangeLimit, estimation.View.Limits)); } var timeNow = DateTime.UtcNow; // request var request = new DAL.Models.BuyGoldEth() { Status = BuySellGoldRequestStatus.Unconfirmed, ExchangeCurrency = exchangeCurrency, GoldRateCents = estimation.CentsPerGoldRate, EthRateCents = estimation.CentsPerAssetRate, TimeCreated = timeNow, UserId = user.Id, }; DbContext.BuyGoldEth.Add(request); await DbContext.SaveChangesAsync(); // get a token from eth2gold service var contractToken = ""; { try { var reply = await Bus.Request( Eth2Gold.Subject.Request.OrderCreate, new Eth2Gold.Request.OrderCreate() { ExternalID = (ulong)request.Id, }, Eth2Gold.Request.OrderCreateReply.Parser ); if (reply.ResultCase == Eth2Gold.Request.OrderCreateReply.ResultOneofCase.Token) { if (reply.Token.ToByteArray().Length != 32) { throw new Exception($"token is length of {reply.Token.ToByteArray().Length}"); } contractToken = BitConverter.ToString(reply.Token.ToByteArray()).Replace("-", string.Empty); } else { throw new Exception(reply.Error); } } catch (Exception e) { Logger.Error(e, "Failed to get token from eth2gold service"); return(APIResponse.Failure(APIErrorCode.InternalServerError)); } } var assetPerGold = CoreLogic.Finance.Estimation.AssetPerGold(TradableCurrency.Eth, estimation.CentsPerAssetRate, estimation.CentsPerGoldRate); return(APIResponse.Success( new AssetEthView() { RequestId = request.Id, EthRate = estimation.CentsPerAssetRate / 100d, GoldRate = estimation.CentsPerGoldRate / 100d, EthPerGoldRate = assetPerGold.ToString(), Currency = exchangeCurrency.ToString().ToUpper(), Expires = ((DateTimeOffset)timeNow.AddHours(1)).ToUnixTimeSeconds(), Estimation = estimation.View, ContractToken = contractToken, } )); }