import { PER_ITEM_LIMIT , SERVER_URL } from '../constants' ; import { ClassSortType , NFT , NFTDetail , NFTTransaction , PaginationOptions , ProductState , SearchOptions , SearchResponse , SearchType , SpecialCategories , Transaction , TransactionLogResponse , UnsignedTransaction , } from '../models' ; import { Issuer , IssuerInfo , IssuerTokenClassResult , FollowerResponse , IssuersResponse , } from '../models/issuer' ; import { SpecialAssets } from '../models/special-assets' ; import { Notifications } from '../models/banner' ; import { ClassList , FollowClassList , Tag , TokenClass , } from '../models/class-list' ; import axios , { AxiosInstance , AxiosResponse } from 'axios' ; import { Transaction as PwTransaction , transformers , Reader , SerializeWitnessArgs , WitnessArgs , normalizers , } from '@lay2/pw-core' ; import { rawTransactionToPWTransaction } from '../pw/toPwTransaction' ; import { Auth , User , UserResponse } from '../models/user' ; import { WxSignConfig } from '../models/wx' ; import { GetHolderByTokenClassUuidResponse } from '../models/holder' ; import { MyRedeemEvents , RedeemDetailModel , RedeemEvents , RedeemListType , RedeemParams , RedeemResultResponse , RewardDetailResponse , } from '../models/redeem' ; import { ClaimResult } from '../models/claim' ; import { OrderDetail , OrdersResponse , OrderState , PlaceOrderProps , } from '../models/order' ; import { RoutePath } from '../routes' ; import { RankingListResponse } from '../models/rank' ; import { PaymentChannel } from '../hooks/useOrder' ; import { OpenRedEnvelopeResponse , RedEnvelopeRecords , RedEnvelopeResponse , } from '../models/red-envelope' ; import { isPwTransaction } from '../utils' ; function randomid ( length = 10 ) { let result = '' ; const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' ; const charactersLength = characters . length ; for ( let i = 0 ; i < length ; i ++ ) { result += characters . charAt ( Math . floor ( Math . random ( ) * charactersLength ) ) ; } return result ; } async function writeFormData ( object , prefix , formData , filetype ? ) { const keys = Object . keys ( object ) ; for ( const key of keys ) { let data = object [ key ] ; if ( key === 'avatar' ) { try { data = await fetch ( data ) . then ( async ( d ) => await d . blob ( ) ) ; } catch ( error ) { console . log ( error ) ; } } if ( key === 'avatar' ) { const ext = filetype ?. startsWith ( 'svg' ) ? 'svg' : filetype ; formData . append ( `[${ prefix }]${ key }` , data , `${ randomid ( ) }.${ ext ?? 'jpeg' }` ) ; } else { formData . append ( `[${ prefix }]${ key }` , data ) ; } } return formData ; } export class ServerWalletAPI { private readonly address : string ; private readonly axios : AxiosInstance ; private readonly orderCallbackURL = `${ location . origin }${ RoutePath . OrderSuccess }` ; constructor ( address ) { this . address = address ; this . axios = axios . create ( { baseURL : SERVER_URL } ) ; } async getNFTs ( page , options ? ) { return await this . axios . get ( `/holder_tokens/${ options ?. address ?? this . address }` , { params : { page , limit : PER_ITEM_LIMIT , exclude_banned : options ?. exclude_banned , } , } ) ; } async getNFTDetail ( uuid , auth ? ) { const params = { include_submitting : true , } ; if ( this . address ) { params . address = this . address ; } const headers = { } ; if ( auth ) { headers . auth = JSON . stringify ( auth ) ; } return await this . axios . get < NFTDetail > ( `/tokens/${ uuid }` , { params , headers , } ) ; } async getNFTDetailByClassUuidAndTid ( uuid , tid , options ? ) { const params = { include_submitting : true , } ; if ( this . address ) { params . address = this . address ; } const headers = { } ; if ( options ?. auth ) { headers . auth = JSON . stringify ( options . auth ) ; } return await this . axios . get < NFTDetail > ( `/token_classes/${ uuid }/tokens/${ tid }` , { params , headers , } ) ; } async getTransactions ( page ) { return await this . axios . get ( `/holder_transactions/${ this . address }` , { params : { page , source : 'wallet' , limit : PER_ITEM_LIMIT , } , } ) ; } async toggleLike ( uuid , like , auth ) { const url = `/token_classes/${ uuid }/toggle_likes/${ this . address }` ; return await this . axios . put ( url , { auth , } , { headers : { auth : JSON . stringify ( auth ) } , } ) ; } async submitAddress ( uuid , auth ) { const url = `/address_packages/${ uuid }/items` ; return await axios . post ( `${ SERVER_URL }${ url }` . replace ( '/wallet/' , '/saas/' ) , { auth , address : this . address , } , { headers : { auth : JSON . stringify ( auth ) , } , } ) ; } async detectAddress ( uuid ) { const url = `/address_packages/${ uuid }` ; return await axios . get ( `${ SERVER_URL }${ url }` . replace ( '/wallet/' , '/saas/' ) ) ; } async getClassListByTagId ( uuid , page , sortType ) { const params = { page , limit : PER_ITEM_LIMIT , } ; if ( sortType === ClassSortType . Likes ) { params . sort = 'likes' ; params . order = 'desc' ; } if ( sortType === ClassSortType . Recommend ) { params . sort = 'recommended' ; params . order = 'desc' ; } if ( sortType === ClassSortType . OnSale ) { params . sort = sortType ; params . order = 'desc' ; } if ( this . address ) { params . address = this . address ; } return await this . axios . get ( `/tags/${ uuid }/token_classes` , { params , } ) ; } async getUserLikesClassList ( page , options ? ) { const params = { page , limit : PER_ITEM_LIMIT , } ; return await this . axios . get ( `/liked_token_classes/${ options ?. address ?? this . address }` , { params , } ) ; } async getTags ( ) { return await this . axios . get ( '/tags' ) ; } async getRegion ( latitude , longitude ) { return await this . axios . get ( '/regions' , { params : { latitude , longitude , } , } ) ; } async getTokenClass ( uuid , auth ? ) { const params = { } ; if ( this . address ) { params . address = this . address ; } const headers = { } ; if ( auth ) { headers . auth = JSON . stringify ( auth ) ; } return await this . axios . get ( `/token_classes/${ uuid }` , { params , headers , } ) ; } async getTransferNftTransaction ( uuid , toAddress , isUnipass = true ) { const { data } = await this . axios . get < any , AxiosResponse < UnsignedTransaction > > ( '/token_ckb_transactions/new' , { params : { token_uuid : uuid , from_address : this . address , to_address : toAddress , } , } ) ; const tx = await rawTransactionToPWTransaction ( data . unsigned_tx , isUnipass ) ; return { tx , uuid : data . token_ckb_transaction_uuid , } ; } async setProfile ( user , options ? ) { const fd = new FormData ( ) ; await writeFormData ( user , 'user' , fd , options ?. ext ) ; const headers = { 'Content-Type' : 'multipart/form-data' , } ; if ( options ?. auth ) { await writeFormData ( options ?. auth as any , 'auth' , fd ) ; headers . auth = JSON . stringify ( options ?. auth ) ; } const { data } = await this . axios . put ( `/users/${ this . address }` , fd , { headers , } ) ; return data ; } async getProfile ( address ? ) { try { const { data } = await this . axios . get ( `/users/${ address ?? this . address }` ) ; return data ; } catch ( error ) { return Object . create ( null ) ; } } async transfer ( uuid , tx , toAddress , sig ? ) { const rawTx = isPwTransaction ( tx ) ? ( transformers . TransformTransaction ( tx ) as RPC . RawTransaction ) : tx ; if ( sig ) { const witnessArgs = { lock : sig , input_type : '' , output_type : '' , } ; const witness = new Reader ( SerializeWitnessArgs ( normalizers . NormalizeWitnessArgs ( witnessArgs ) ) ) . serializeJson ( ) ; rawTx . witnesses [ 0 ] = witness ; } const data = { signed_tx : JSON . stringify ( rawTx ) , token_uuid : uuid , from_address : this . address , to_address : toAddress , } ; return await this . axios . post ( '/token_ckb_transactions' , data ) ; } async getSpecialAssets ( ) { return await this . axios . get ( '/special_categories' ) ; } async getCollectionDetail ( uuid ) { return await this . axios . get ( `/special_categories/${ uuid }` ) ; } async getRecommendIssuers ( ) { return await this . axios . get ( '/recommended_issuers' , { params : { address : this . address , } , } ) ; } async getRecommendClasses ( ) { const params = { } ; if ( this . address ) { params . address = this . address ; } return await this . axios . get < TokenClass [ ] > ( '/recommended_classes' , { params , } ) ; } async getCollection ( uuid , page ) { const params = { page , limit : PER_ITEM_LIMIT , } ; if ( this . address ) { params . address = this . address ; } return await this . axios . get < ClassList > ( `/special_categories/${ uuid }/token_classes` , { params , } ) ; } async getNotifications ( ) { return await this . axios . get ( '/notifications' ) ; } async getClaimStatus ( uuid ) { return await this . axios . get ( `/token_claim_codes/${ uuid }` ) ; } async claim ( uuid ) { return await this . axios . post ( '/token_claim_codes' , { to_address : this . address , code : uuid , } ) ; } async toggleFollow ( uuid , auth ) { return await this . axios . put ( `/issuers/${ uuid }/toggle_follows/${ this . address }` , { auth , } , { headers : { auth : JSON . stringify ( auth ) , } , } ) ; } async getFollowIssuers ( options ? ) { const page = options ?. page ?? 0 ; const limit = options ?. limit ?? PER_ITEM_LIMIT ; const params = { page , limit , } ; const headers = { } ; if ( options ?. auth ) { headers . auth = JSON . stringify ( options ?. auth ) ; } return await this . axios . get ( `/followed_issuers/${ options ?. address ?? this . address }` , { headers , params , } ) ; } async getFollowTokenClasses ( auth , page , sortType ) { const params = { page , limit : PER_ITEM_LIMIT , } ; if ( sortType === ClassSortType . OnSale ) { params . sort = ClassSortType . OnSale ; } if ( sortType === ClassSortType . Latest ) { params . sort = ClassSortType . Latest ; } if ( sortType === ClassSortType . Likes ) { params . sort = ClassSortType . Likes ; } return await this . axios . get ( `/followed_token_classes/${ this . address }` , { headers : { auth : JSON . stringify ( auth ) , } , params , } ) ; } async getIssuerInfo ( uuid ) { return await this . axios . get < IssuerInfo > ( `/issuers/${ uuid }` , { params : { address : this . address , } , } ) ; } async getAllRedeemEvents ( page , type ) { const params = { page , limit : PER_ITEM_LIMIT , type , } ; if ( this . address ) { params . wallet_address = this . address ; } return await this . axios . get ( '/redemption_events' , { params , } ) ; } async getMyRedeemEvents ( page , type ) { const params = { page , limit : PER_ITEM_LIMIT , type , } ; if ( this . address ) { params . wallet_address = this . address ; } return await this . axios . get ( '/redemption_records' , { params , } ) ; } async getRedeemDetail ( uuid ) { return await this . axios . get ( `/redemption_events/${ uuid }` , { params : { uuid , wallet_address : this . address , } , } ) ; } async getRedeemPrize ( uuid ) { return await this . axios . get ( `/redemption_records/${ uuid }` ) ; } async getRedeemTransaction ( uuid , isUnipass = true ) { const { data } = await this . axios . get ( `/redemption_events/${ uuid }/records/new` , { params : { uuid , wallet_address : this . address , } , } ) ; const tx = await rawTransactionToPWTransaction ( data . unsigned_tx , isUnipass ) ; return { tx , uuid : data . redemption_event_uuid , } ; } async redeem ( { uuid , tx , customData , sig , } ) { const rawTx = transformers . TransformTransaction ( tx ) as any ; if ( sig ) { const witnessArgs = { lock : sig , input_type : '' , output_type : '' , } ; const witness = new Reader ( SerializeWitnessArgs ( normalizers . NormalizeWitnessArgs ( witnessArgs ) ) ) . serializeJson ( ) ; rawTx . witnesses [ 0 ] = witness ; } const data = { signed_tx : JSON . stringify ( rawTx ) , ... customData , wallet_address : this . address , } ; return await this . axios . post ( `/redemption_events/${ uuid }/records` , data ) ; } async getIssuerTokenClass ( uuid , productState = 'product_state' , options ? ) { const limit = options ?. limit ?? PER_ITEM_LIMIT ; const page = options ?. page ?? 0 ; return await this . axios . get < IssuerTokenClassResult > ( `/issuers/${ uuid }/token_classes` , { params : { address : this . address , product_state : productState , limit , page , } , } ) ; } async getWechatSignature ( config ) { return await this . axios . get ( `/mini_program_signers?nonce_str=${ config . nonce_str }&url=${ encodeURIComponent ( config . url ) }&timestamp=${ config . timestamp }` ) ; } async getHolderByTokenClassUuid ( uuid , options ? ) { const limit = options ?. limit ?? PER_ITEM_LIMIT ; const page = options ?. page ?? 0 ; return await this . axios . get < GetHolderByTokenClassUuidResponse > ( `/token_classes/${ uuid }/holders` , { params : { limit , page , } , } ) ; } async submitOrder ( auth ) { return await this . axios . post ( '/token_order_uuid' , { auth , } , { headers : { auth : JSON . stringify ( auth ) , } , } ) ; } async getOrders ( page , auth , orderState ? ) { const params = { page , limit : PER_ITEM_LIMIT , address : this . address , } ; if ( orderState ) { params . state = orderState ; } const headers = { } ; if ( auth ) { headers . auth = JSON . stringify ( auth ) ; } return await this . axios . get ( '/token_orders' , { params , headers , } ) ; } async placeOrder ( props , auth ) { const headers = { auth : JSON . stringify ( auth ) , } ; let callbackUrl = this . orderCallbackURL ; if ( props . channel === PaymentChannel . WechatMobile ) { callbackUrl = `${ location . origin }${ RoutePath . OrderStatus }/${ props . uuid as string }` ; } return await this . axios . post ( '/token_orders' , { token_order : { ... props , callback_url : callbackUrl , cancel_url : location . href , } , auth , } , { headers , } ) ; } async getTokenClassTransactions ( uuid , options ? ) { const limit = options ?. limit ?? PER_ITEM_LIMIT ; const page = options ?. page ?? 0 ; return await this . axios . get < TransactionLogResponse > ( `/token_classes/${ uuid }/token_ckb_transactions` , { params : { limit , page , } , } ) ; } async getOrderDetail ( uuid , auth ) { const headers = { } ; headers . auth = JSON . stringify ( auth ) ; return await this . axios . get ( `/token_orders/${ uuid }` , { params : { uuid , address : this . address , } , headers , } ) ; } async continuePlaceOrder ( uuid , channel , auth ) { const headers = { } ; if ( auth ) { headers . auth = JSON . stringify ( auth ) ; } let callbackUrl = this . orderCallbackURL ; if ( channel === PaymentChannel . WechatMobile ) { callbackUrl = `${ location . origin }${ RoutePath . OrderStatus }/${ uuid }?redirect_from=${ encodeURIComponent ( location . pathname ) }` ; } return await this . axios . put ( `/token_orders/${ uuid }` , { channel , address : this . address , callback_url : callbackUrl , cancel_url : location . href , } , { params : { channel , address : this . address , callback_url : callbackUrl , cancel_url : location . href , } , headers , } ) ; } async deleteOrder ( uuid , auth ) { const headers = { } ; if ( auth ) { headers . auth = JSON . stringify ( auth ) ; } return await this . axios . delete ( `/token_orders/${ uuid }` , { headers , } ) ; } async getTokenTransactions ( uuid , options ? ) { const limit = options ?. limit ?? PER_ITEM_LIMIT ; const page = options ?. page ?? 0 ; return await this . axios . get < TransactionLogResponse > ( `/tokens/${ uuid }/token_ckb_transactions` , { params : { limit , page , } , } ) ; } async getWechatOauthUrl ( auth ) { return await this . axios . get < { oauth_url : string ; } > ( '/wechat_auths' , { params : { mibao_url : location . href , } , headers : { auth : JSON . stringify ( auth ) , } , } ) ; } async getRankingList < O extends { uuid ? : string ; } > ( options ? ) { const uuid = options ?. uuid ; return await this . axios . get < RankingListResponse < O > > ( '/ranking_lists' + ( uuid ? `/${ uuid }` : '' ) ) ; } async getUrlBase64 ( url ) { return await this . axios . get < { result : string ; } > ( '/image_forwardings' , { params : { url : encodeURI ( url ) , type : 'base64' , } , } ) ; } async getRedEnvelopeEvent ( uuid , options ? ) { return await this . axios . get < RedEnvelopeResponse > ( `/redpack_events/${ uuid }` , { params : { wallet_address : options ?. address , } , } ) ; } async getRedEnvelopeRecords ( uuid , options ? ) { return await this . axios . get < RedEnvelopeRecords > ( `/redpack_events/${ uuid }/records` , { params : { limit : PER_ITEM_LIMIT , ... options , } , } ) ; } async openRedEnvelopeEvent ( uuid , address , auth , options ? ) { return await this . axios . post < OpenRedEnvelopeResponse > ( `/redpack_events/${ uuid }/records` , { wallet_address : address , user_input : options ?. input , } , { headers : { auth : JSON . stringify ( auth ) , } , } ) ; } async search < T extends SearchType > ( keyword , type , options ? ) { return await this . axios . get < SearchResponse < T > > ( '/searches' , { params : { keyword , type , limit : PER_ITEM_LIMIT , ... options , } , } ) ; } }