common.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. package common
  2. import (
  3. "crypto/rand"
  4. "crypto/tls"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "math"
  9. "math/big"
  10. "time"
  11. "github.com/dgrijalva/jwt-go"
  12. "github.com/ethereum/go-ethereum/common"
  13. "github.com/gin-gonic/gin"
  14. "github.com/metarare/metarare_api/contracts/operator"
  15. "github.com/metarare/metarare_api/helpers"
  16. "github.com/metarare/metarare_api/helpers/gauth"
  17. "github.com/metarare/metarare_api/models"
  18. "github.com/spf13/viper"
  19. gomail "gopkg.in/mail.v2"
  20. "gorm.io/gorm"
  21. )
  22. var (
  23. EMAIL_HOST string = "smtp.mailplug.co.kr"
  24. EMAIL_PORT int = 465
  25. EMAIL_SUBJECT string = "Certification Code\n"
  26. EMAIL_MIME string = "MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n"
  27. )
  28. type Email struct {
  29. From string `json:"from,omitempty"`
  30. Password string `json:"password,omitempty"`
  31. ToList string `json:"toList,omitempty"`
  32. Host string `json:"host,omitempty"`
  33. Port string `json:"port,omitempty"`
  34. Subject string `json:"subject,omitempty"`
  35. Mime string `json:"mime,omitempty"`
  36. Message string `json:"message,omitempty"`
  37. }
  38. type Router struct {
  39. Engine *gin.Engine
  40. Port string
  41. Version *gin.RouterGroup
  42. Db DataBases
  43. Env *viper.Viper
  44. }
  45. type DataBases struct {
  46. MasterDB *gorm.DB
  47. ReadDB *gorm.DB
  48. }
  49. type JsonObject map[string]interface{}
  50. type AuthContainer struct {
  51. AccessToken string `json:"access_token,omitempty"`
  52. }
  53. type SwagStruct struct {
  54. Json string `json:"json"`
  55. }
  56. type FILTER_KEY string
  57. var Decimal13 = int64(math.Pow(10, 13)) // 10^13
  58. type MakeHashData struct {
  59. OwnerAddress string `json:"owner_address"`
  60. CollectionAddress string `json:"collection_address"`
  61. TokenType string `json:"token_type"`
  62. TokenID int64 `json:"token_id"`
  63. Currency string `json:"currency"`
  64. Price float64 `json:"price"`
  65. Index int64 `json:"index"`
  66. TotalIndex int64 `json:"total_index"`
  67. CreationTax int64 `json:"creation_tax"`
  68. TreasuryTax float64 `json:"treasury_tax"`
  69. TreasuryAddress string `json:"treasury_address"`
  70. TargetPrivateKey string `json:"target_private_key"`
  71. PrevTreasuryTax int64 `json:"prev_treasury_tax"`
  72. }
  73. const (
  74. NETWORK_ETH = FILTER_KEY("eth")
  75. NETWORK_BSC = FILTER_KEY("bsc")
  76. NETWORK_MR = FILTER_KEY("mr")
  77. )
  78. const (
  79. CATEGORY_ALL = FILTER_KEY("all")
  80. CATEGORY_ART = FILTER_KEY("art")
  81. CATEGORY_GAME = FILTER_KEY("game")
  82. )
  83. const (
  84. SALETYPE_FIXED = FILTER_KEY("fixed")
  85. SALETYPE_BID = FILTER_KEY("bid")
  86. SALETYPE_TIME = FILTER_KEY("time")
  87. )
  88. const (
  89. CURRENCY_ETH = FILTER_KEY("eth")
  90. CURRENCY_MR = FILTER_KEY("mr")
  91. CURRENCY_MF = FILTER_KEY("mf")
  92. )
  93. const (
  94. CURRENCY_AMOUNT_START = FILTER_KEY("currency_amount_strart")
  95. CURRENCY_AMOUNT_END = FILTER_KEY("currency_amount_end")
  96. )
  97. var NETWORKS = []FILTER_KEY{NETWORK_ETH, NETWORK_BSC, NETWORK_MR}
  98. var CATEGORIES = []FILTER_KEY{CATEGORY_ALL, CATEGORY_ART, CATEGORY_GAME}
  99. var SALETYPES = []FILTER_KEY{SALETYPE_FIXED, SALETYPE_BID, SALETYPE_TIME}
  100. var CURRENCIES = []FILTER_KEY{CURRENCY_ETH, CURRENCY_MF, CURRENCY_MR}
  101. var (
  102. tt256 = BigPow(2, 256)
  103. tt256m1 = new(big.Int).Sub(tt256, big.NewInt(1))
  104. MaxBig256 = new(big.Int).Set(tt256m1)
  105. )
  106. type Filter struct {
  107. Network string `json:"network,omitempty"`
  108. Category string `json:"category,omitempty"`
  109. CollectionName string `json:"collection_name,omitempty"`
  110. SaleType string `json:"sale_type,omitempty"`
  111. Currency string `json:"currency,omitempty"`
  112. CurrencyAmountStart string `json:"currency_amount_start,omitempty"`
  113. CurrencyAmountEnd string `json:"currency_amount_end,omitempty"`
  114. SaleStatus string `json:"sale_status,omitempty"` // for 개인페이지의 판매중
  115. Offset int `json:"offset,omitempty" default:"-1"`
  116. Limit int `json:"limit,omitempty" default:"-1"`
  117. TokenName string `json:"token_name,omitempty"`
  118. }
  119. type ExpItem struct { // exploreItem
  120. CollectionID uint64 `json:"collection_id"`
  121. CollectionThumbnailImage string `json:"collection_thumbnail_image"`
  122. CollectionName string `json:"collection_name"`
  123. IsOfficialCollection bool `json:"is_official_collection"`
  124. UserID uint64 `json:"user_id"`
  125. IsArtist bool `json:"is_artist"`
  126. OwnerThumbnailImage string `json:"owner_thumbnail_image"`
  127. OwnerName string `json:"owner_name"`
  128. TokenID uint64 `json:"token_id"`
  129. ContentURL string `json:"content_url"`
  130. ContentTitle string `json:"content_title"`
  131. LastestPrice string `json:"lastest_price"`
  132. LastestCurrency string `json:"lastest_currency"`
  133. ItemIndex int `json:"item_index"`
  134. TotalCount int `json:"total_count"`
  135. SaleID uint64 `json:"sale_id"`
  136. SaleType string `json:"sale_type"`
  137. SaleStatus string `json:"sale_status"`
  138. FixedPrice string `json:"fixed_price"`
  139. CurrentCurrency string `json:"current_currency"`
  140. CurrentPrice string `json:"current_price"`
  141. BidCount int `json:"bid_count"`
  142. Likes int `json:"likes"`
  143. IsLike bool `json:"is_like"`
  144. StartPrice string `json:"start_price"`
  145. EndAt time.Time `json:"end_at"`
  146. CreatedAt time.Time `json:"created_at"`
  147. }
  148. type CollectionItem struct {
  149. CollectionID uint64 `json:"collection_id"`
  150. CoverImage string `json:"cover_image"`
  151. ThumbnailImage string `json:"thumbnail_image"`
  152. IsOfficial bool `json:"is_official"`
  153. CollectionName string `json:"collection_name"`
  154. UserID uint64 `json:"user_id"`
  155. OwnerName string `json:"owner_name"`
  156. TotalVolume float32 `json:"total_volume"`
  157. TotalItem int `json:"total_item"`
  158. }
  159. type ActivityItem struct {
  160. LogRelationID uint64 `json:"log_relation_id"`
  161. TokenContentURL string `json:"token_content_url"`
  162. TokenName string `json:"token_name"`
  163. LogType string `json:"log_type"`
  164. SaleUID string `json:"sale_uid"`
  165. ToAddress string `json:"to_address"`
  166. FromAddress string `json:"from_address"`
  167. Price string `json:"price"`
  168. Currency string `json:"currency"`
  169. SaleID uint64 `json:"sale_id"`
  170. TokenID uint64 `json:"token_id"`
  171. IsCancel bool `json:"is_cancel"`
  172. Tx string `json:"tx"`
  173. CreatedAt time.Time `json:"created_at"`
  174. FromUserProfile string `json:"from_user_profile"`
  175. FromUserName string `json:"from_user_name"`
  176. FromUserIsArtist bool `json:"from_user_is_artist"`
  177. ToUserProfile string `json:"to_user_profile"`
  178. ToUserName string `json:"to_user_name"`
  179. ToUserIsArtist bool `json:"to_user_is_artist"`
  180. }
  181. type BidItem struct {
  182. UserID uint64
  183. UserThumbnailImage string
  184. UserName string
  185. IsCancel string
  186. ActionType string
  187. }
  188. type ArtistItem struct {
  189. ID uint64
  190. UserID uint64
  191. Team string
  192. Category string
  193. ThumbnailImage string
  194. }
  195. type TopSales struct {
  196. UserId uint64 `json:"artist_id"`
  197. ThumbnailImage string `json:"thumbnail_image"`
  198. CoverImage string `json:"cover_image"`
  199. Name string `json:"name"`
  200. Category string `json:"category"`
  201. AmountSales float64 `json:"amounts_sale"`
  202. }
  203. type SearchExpItems struct {
  204. Result []ExpItem
  205. Suggestion []CollectionItem
  206. }
  207. // SECTION jwt session
  208. func MakeClaims(userID int64) (JsonObject, JsonObject) {
  209. standardClaims := JsonObject{
  210. "iss": "metarare.com",
  211. "aud": gauth.User,
  212. }
  213. customClaims := JsonObject{
  214. gauth.Pack("user_id"): gauth.Pack(fmt.Sprintf("%d", userID)),
  215. }
  216. return standardClaims, customClaims
  217. }
  218. func MakeSession(standardClaims JsonObject, customClaims JsonObject) (error, AuthContainer) {
  219. currentTimestamp := time.Now()
  220. claims := jwt.MapClaims{}
  221. claims["sub"] = "access_key"
  222. claims["iat"] = currentTimestamp.Unix()
  223. claims["exp"] = currentTimestamp.Add(24 * time.Hour).Format(time.RFC3339)
  224. for k, v := range standardClaims {
  225. claims[k] = v
  226. }
  227. for k, v := range customClaims {
  228. claims[k] = v
  229. }
  230. token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
  231. tokenString, err := token.SignedString([]byte(gauth.ObtainJWTKey()))
  232. fmt.Println(tokenString, err)
  233. return nil, AuthContainer{AccessToken: tokenString}
  234. }
  235. func MakeJwtToken(userID uint64) AuthContainer {
  236. standardClaims, customClaims := MakeClaims(int64(userID))
  237. _, authContainer := MakeSession(standardClaims, customClaims)
  238. return authContainer
  239. }
  240. // SECTION GenerateRandomHexString
  241. func GenerateRandomString(max int, prefix string) string {
  242. var table = [...]byte{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}
  243. b := make([]byte, max)
  244. n, err := io.ReadAtLeast(rand.Reader, b, max)
  245. if n != max {
  246. panic(err)
  247. }
  248. for i := 0; i < len(b); i++ {
  249. b[i] = table[int(b[i])%len(table)]
  250. }
  251. return prefix + string(b)
  252. }
  253. func GenerateRandomNumber(max int) string {
  254. var table = [...]byte{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}
  255. b := make([]byte, max)
  256. n, err := io.ReadAtLeast(rand.Reader, b, max)
  257. if n != max {
  258. panic(err)
  259. }
  260. for i := 0; i < len(b); i++ {
  261. b[i] = table[int(b[i])%len(table)]
  262. }
  263. return string(b)
  264. }
  265. func GenerateUserUID(db *gorm.DB, prefix string) string {
  266. UID := GenerateRandomString(32, prefix)
  267. var count int64
  268. for {
  269. db.Table("user").Where("uid = ?", UID).Count(&count)
  270. if count == 0 {
  271. break
  272. }
  273. UID = GenerateRandomString(32, prefix)
  274. }
  275. return string(UID)
  276. }
  277. func GenerateSaleUID(db *gorm.DB, prefix string) string {
  278. UID := GenerateRandomString(32, prefix)
  279. var count int64
  280. for {
  281. db.Table("sale").Where("uid = ?", UID).Count(&count)
  282. if count == 0 {
  283. break
  284. }
  285. UID = GenerateRandomString(32, prefix)
  286. }
  287. return string(UID)
  288. }
  289. func GenerateTokenUID(db *gorm.DB, prefix string) string {
  290. UID := GenerateRandomString(32, prefix)
  291. var count int64
  292. for {
  293. db.Table("token").Where("uid = ?", UID).Count(&count)
  294. if count == 0 {
  295. break
  296. }
  297. UID = GenerateRandomString(32, prefix)
  298. }
  299. return string(UID)
  300. }
  301. func GenerateUserName(db *gorm.DB) string {
  302. var name string
  303. var randName = [...]string{"가온해", "겨루", "여우별", "바람꽃", "이플", "소솜", "감또개", "옅구름", "뾰롱뾰롱", "나비잠", "도르레", "미쁘다", "산들림", "소록소록", "도담도담", "가온누리", "이쁘동이", "아슬라", "아리아", "개밥바라기", "다솜", "로운", "나르샤", "미리내", "하늬바람",
  304. "붙박이별", "그루잠", "여우비", "곰다시", "시나브로", "곰살굳다", "여낙낙하다", "달보드레", "눈바래다", "함초롬하다", "돋을별", "아련나래", "늘품", "아름드리", "넨다하다", "눅진하다", "안다미로", "초련", "닻별", "예그리나", "가시버시", "별하", "누리보듬", "비나리", "씨밀레"}
  305. idx, err := rand.Int(rand.Reader, big.NewInt(50))
  306. if err != nil {
  307. panic(err)
  308. }
  309. number, err := rand.Int(rand.Reader, big.NewInt(1000000))
  310. if err != nil {
  311. panic(err)
  312. }
  313. name = randName[idx.Int64()] + "_" + number.String()
  314. return name
  315. }
  316. // NOTE x 통화 업데이트
  317. func UpdateCurrency(db *gorm.DB) (error, models.CurrencyPrice) {
  318. currencyPrice := models.CurrencyPrice{}
  319. err := db.Find(&currencyPrice).Error
  320. if errors.Is(err, gorm.ErrRecordNotFound) {
  321. return err, currencyPrice
  322. }
  323. t := time.Now().UTC().In(time.FixedZone("KST", 9*60*60))
  324. compare := currencyPrice.UpdatedAt.Add(time.Hour * 1)
  325. if t.After(compare) {
  326. eth, mf1, mr := ObtainCurrencyPrice()
  327. if err := db.Model(&currencyPrice).Updates(models.CurrencyPrice{Eth: eth, Mf: mf1, Mr: mr, UpdatedAt: time.Now().UTC()}).Error; err != nil {
  328. return err, currencyPrice
  329. }
  330. }
  331. return nil, currencyPrice
  332. }
  333. func ConvertToId(db *gorm.DB, name string) (uint64, error) {
  334. _up := models.UserProfile{}
  335. err := db.Where("name = ?", name).Find(&_up).Error
  336. if errors.Is(err, gorm.ErrRecordNotFound) {
  337. return 0, err
  338. }
  339. return _up.UserID, nil
  340. }
  341. func ConvertToCollectionId(db *gorm.DB, name string) (uint64, error) {
  342. _cp := models.CollectionProfile{}
  343. err := db.Where("name = ?", name).Find(&_cp).Error
  344. if errors.Is(err, gorm.ErrRecordNotFound) {
  345. return 0, err
  346. }
  347. return _cp.CollectionID, nil
  348. }
  349. // NOTE x chekc duplicate value
  350. func DuplicateValue(db *gorm.DB, kind string, name string) (bool, error) {
  351. type Check struct {
  352. Name string `json:"name,omitempty"`
  353. }
  354. _check := Check{}
  355. var _table string
  356. if kind == "user" {
  357. _table = "user_profile"
  358. } else {
  359. _table = "collection_profile"
  360. }
  361. err := db.Select("name").Table(_table).Where("name = ?", name).Find(&_check).Error
  362. if err != nil {
  363. return true, err
  364. } else if _check.Name != "" {
  365. return true, nil
  366. }
  367. return false, nil
  368. }
  369. // NOTE x make sining
  370. func MakeSining(db *gorm.DB, info MakeHashData) (operator.MetaRareOperatorNFTVoucher, error) {
  371. _v := operator.MetaRareOperatorNFTVoucher{
  372. Signer: common.HexToAddress(info.OwnerAddress),
  373. TokenAddress: common.HexToAddress(info.CollectionAddress),
  374. TokenId: big.NewInt(info.TokenID),
  375. CreationTax: big.NewInt(info.CreationTax),
  376. Uri: "https://nftinfo.meta-rare.net/",
  377. TreasuryAddress: common.HexToAddress(info.TreasuryAddress),
  378. TreasuryTax: big.NewInt(info.PrevTreasuryTax),
  379. }
  380. if info.TokenType == "erc721" {
  381. _v.TokenType = big.NewInt(0)
  382. _v.Balance = big.NewInt(0)
  383. _v.TotalBalance = big.NewInt(0)
  384. } else {
  385. _v.TokenType = big.NewInt(1)
  386. _v.Balance = big.NewInt(info.Index)
  387. _v.TotalBalance = big.NewInt(info.TotalIndex)
  388. }
  389. if info.Currency == "eth" {
  390. _v.AssetAddress = common.HexToAddress("0x0000000000000000000000000000000000000000")
  391. } else if info.Currency == "mf" {
  392. _v.AssetAddress = common.HexToAddress(helpers.Addr_metaFinance)
  393. } else if info.Currency == "mr" {
  394. _v.AssetAddress = common.HexToAddress(helpers.Addr_metaRare)
  395. }
  396. // setting := models.Setting{}
  397. // _err := db.Find(&setting).Error
  398. // if errors.Is(_err, gorm.ErrRecordNotFound) {
  399. // return operator.MetaRareOperatorNFTVoucher{}, _err
  400. // }
  401. // _v.TreasuryTax = big.NewInt(int64(setting.Commission) * 100)
  402. // _v.TreasuryTax = big.NewInt(int64(info.PrevTreasuryTax))
  403. // _v.TreasuryAddress = common.HexToAddress(setting.TreasuryAddress)
  404. _v.Price = big.NewInt(10).Mul(big.NewInt(int64(info.Price*100000)), big.NewInt(Decimal13))
  405. return _v, nil
  406. }
  407. func BigPow(a, b int64) *big.Int {
  408. r := big.NewInt(a)
  409. return r.Exp(r, big.NewInt(b), nil)
  410. }
  411. // SECTION db trasaction
  412. func DBTransaction(db *gorm.DB) {
  413. if r := recover(); r != nil {
  414. db.Rollback()
  415. }
  416. }
  417. /*
  418. # NOTE x 2차 인증 이메일 발송
  419. */
  420. func SendCertificationCode(recipientList string, email string, password string) (string, error) {
  421. headers := make(map[string]string)
  422. headers["From"] = email
  423. headers["To"] = recipientList
  424. headers["Subject"] = EMAIL_SUBJECT
  425. message := ""
  426. for k, v := range headers {
  427. message += fmt.Sprintf("%s: %s\r", k, v)
  428. }
  429. _code := GenerateRandomNumber(6)
  430. msg := convertToEmailForm(_code)
  431. message += "\r" + msg
  432. body := (msg)
  433. m := gomail.NewMessage()
  434. // Set E-Mail sender
  435. m.SetHeader("From", email)
  436. // Set E-Mail receivers
  437. m.SetHeader("To", recipientList)
  438. // Set E-Mail subject
  439. m.SetHeader("Subject", EMAIL_SUBJECT)
  440. // Set E-Mail body. You can set plain text or html with text/html
  441. m.SetBody("text/html", fmt.Sprintf(body))
  442. // Settings for SMTP server
  443. d := gomail.NewDialer(EMAIL_HOST, EMAIL_PORT, email, password)
  444. // This is only needed when SSL/TLS certificate is not valid on server.
  445. // In production this should be set to false.
  446. d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
  447. // Now send E-Mail
  448. if err := d.DialAndSend(m); err != nil {
  449. fmt.Println(err)
  450. panic(err)
  451. }
  452. return _code, nil
  453. }
  454. func convertToEmailForm(certificationCode string) string {
  455. convertForm := `<!DOCTYPE html>
  456. <html lang="en">
  457. <head>
  458. <meta charset="UTF-8">
  459. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  460. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  461. <title>Document</title>
  462. </head>
  463. <body>
  464. <div style="display: flex; justify-content: center; align-items: center; flex-direction: column; padding-top : 150px; padding-bottom: 150px; margin-left: 150px;" >
  465. <div style="border: 1px solid #eeee; width: 800px; background: #FFFFFF 0% 0% no-repeat padding-box; position:relative;">
  466. <div style="width: 100%; height: 20px; background: transparent linear-gradient(90deg, #F948A0 0%, #789CBB 70%, #16DCCF 100%) 0% 0% no-repeat padding-box;"></div>
  467. <div style="margin-top : 40px; margin-left: 30px;">
  468. <img src="https://beta-metarare-image.s3.ap-northeast-2.amazonaws.com/logo.png" />
  469. </div>
  470. <div style="margin-right:auto; margin-left: auto; margin-top: 150px; margin-bottom: 100px; height: 56px; width: 500px; text-align: center; color: #000000; font: var(--unnamed-font-style-normal) normal var(--unnamed-font-weight-normal) 20px/32px D-DIN Exp; font: normal normal normal 25px/37px D-DIN Exp;letter-spacing: -0.6px;">출금을 위한 인증코드입니다.<br>인증코드 입력 창에 아래 내용을 입력해주세요.</div>
  471. <div style="display: flex; text-align: center;
  472. justify-content: center;
  473. flex-direction: column;
  474. margin-right:auto;
  475. margin-left: auto;
  476. background: #F5F5F5 0% 0% no-repeat padding-box;
  477. border: 1px solid #F5F5F5;
  478. border-radius: 28px;
  479. width: 350px;
  480. height: 50px;
  481. opacity: 1;
  482. font: var(--unnamed-font-style-normal) normal var(--unnamed-font-weight-bold) var(--unnamed-font-size-14)/15px PT Mono; letter-spacing: var(--unnamed-character-spacing--0-42); color: var(--unnamed-color-000000); font: normal normal bold 16px/15px PT Mono; letter-spacing: -0.42px; color: #000000; opacity: 1;">
  483. <p style="padding-left: 150px">` + certificationCode + `</p>
  484. </div>
  485. <div style="width: 500px;
  486. margin-left: auto;
  487. margin-right: auto;
  488. margin-top: 50px;
  489. margin-bottom: 50px;
  490. text-align: center;
  491. letter-spacing: -0.39px;
  492. color: #22BCB2;
  493. opacity: 1;
  494. letter-spacing: var(--unnamed-character-spacing--0-39);
  495. text-align: center;
  496. font: normal normal normal 20px/34px D-DIN Exp;
  497. color: #22BCB2;">"본 인증코드는 본인인증 및 출금을 위한 용도이며,<br>제 3자에게 공유 및 유출되었을 시 책임지지 않습니다.
  498. </div>
  499. <div style="
  500. width: 800px;
  501. background: #F5F5F5 0% 0% no-repeat padding-box;
  502. opacity: 1;
  503. height: 180px;
  504. justify-content: center;
  505. flex-direction: column;
  506. text-align: center;
  507. font: var(--unnamed-font-style-normal) normal var(--unnamed-font-weight-normal) var(--unnamed-font-size-12)/var(--unnamed-line-spacing-14) var(--unnamed-font-family-pretendard-variable);
  508. letter-spacing: var(--unnamed-character-spacing--0-36);
  509. color: var(--unnamed-color-888888);
  510. text-align: center;
  511. font: normal normal normal 16px/8px Pretendard Variable;
  512. letter-spacing: -0.36px;
  513. color: #888888;
  514. opacity: 1;">
  515. <p style="padding-top:85px;">@ MetaRare. Inc. All rights reserved.</p>
  516. </div>
  517. </div>
  518. </div>
  519. </body>
  520. </html>`
  521. return convertForm
  522. }