diff --git a/API.md b/API.md index 62e3c35b..84ca7a69 100644 --- a/API.md +++ b/API.md @@ -17,6 +17,10 @@ * [Account Order Detail](#account-order-detail) * [Address Deposit](#address-deposit) * [Character Set List](#character-set-list) + * [Account Auction Info](#account-auction-info) + * [Account Auction Price](#account-auction-price) + * [Account Auction OrderStatus](#account-auction-order_status) + * [Account Auction PendingOrders](#account-auction-pending_orders) * [OPERATE API LIST](#operate-api-list) * [Reverse Declare (Deprecated)](#reverse-declare) * [Reverse Redeclare (Deprecated)](#reverse-redeclare) @@ -35,6 +39,7 @@ * [Account Order Pay Hash](#account-order-pay-hash) * [Account Register](#account-register) * [Account Renew](#account-renew) + * [Account Auction Bid](#account-auction-bid) * [NODE RPC](#node-rpc) * [Node Ckb Rpc](#node-ckb-rpc) @@ -313,7 +318,9 @@ curl -X POST http://127.0.0.1:8120/v1/account/mine -d'{"chain_type":1,"address": * 13: unregisterable * 14: sub-account * 15: cross-chain - + * 17: on dutch auction period + * 18: on dutch auction deliver period +* re_registered_time: Time for re registration ```json { "err_no": 0, @@ -333,7 +340,8 @@ curl -X POST http://127.0.0.1:8120/v1/account/mine -d'{"chain_type":1,"address": "base_amount": "3.89", "confirm_proposal_hash": "0xec7bec47a4d3ad467253925a7e097f311e0738d625d55f8b3420cabaaa9b5201", "premium_percentage": "",// for stripe usd premium - "premium_base": "" // for stripe usd premium + "premium_base": "", // for stripe usd premium + "re_registered_time": 1672211096 } } ``` @@ -1935,3 +1943,250 @@ curl -X POST http://127.0.0.1:8119/v1/account/renew -d'{"chain_type":1,"address" curl -X POST http://127.0.0.1:8120/v1/node/ckb/rpc -d'{"jsonrpc":"2.0","id":2976777,"method":"get_blockchain_info","params":[]}' ``` + +#### Account Auction Info + +**Request** + +* path: /account/auction/info + * get dutch auction info of a account +* param: + +```json +{ + "account":"michaeltest1.bit", + "type":"blockchain", + "key_info":{ + "coin_type":"60", + "key":"0xd437b8e9cA16Fce24bF3258760c3567214213C5A" + } +} +``` + +**Response** + +```json +{ + "err_no": 0, + "err_msg": "", + "data": { + "account_id": "", + "account": "", + "bid_status": 2, + "hash": "", + "start_auction_time": 0, + "end_auction_time": 0, + "expired_at": 0, + "account_price": "5", + "base_amount": "0.82" + } +} +``` + +**Usage** + +```curl +curl --location 'http://127.0.0.1:8120/v1/account/auction/info' \ +--header 'Content-Type: application/json' \ +--data '{ + "account":"michaeltest1.bit", + "type":"blockchain", + "key_info":{ + "coin_type":"60", + "key":"0xd437b8e9cA16Fce24bF3258760c3567214213C5A" + } +}' +``` + + +#### Account Auction Price + +**Request** + +* path: /account/auction/price + * get dutch auction price of a account +* param: + +```json +{ + "account":"michaeltest1.bit" +} +``` + +**Response** + +```json +{ + "err_no": 0, + "err_msg": "", + "data": { + "account_price": "5", + "base_amount": "0.82", + "premium_price": 20 + } +} +``` + +**Usage** + +```curl +curl --location 'http://127.0.0.1:8120/v1/account/auction/price' \ +--header 'Content-Type: application/json' \ +--data '{ + "account":"michaeltest1.bit" +}' +``` + +#### Account Auction OrderStatus + +**Request** + +* path: /account/auction/order-status + * get status of a dutch auction order +* param: + +```json +{ + "account":"michaeltest1.bit", + "hash": "0xb9e094dd6fcaa6c68d44233cb5331e63bd966fa86659fc45d30089336021f26e", + "type":"blockchain", + "key_info":{ + "coin_type":"60", + "key":"0xd437b8e9cA16Fce24bF3258760c3567214213C5A" + } +} +``` + +**Response** + +```json +{ + "err_no": 0, + "err_msg": "", + "data": { + "account": "michaeltest1.bit", + "hash": "0xeb4871b7af2ca7129a43c5991c408148abd195eb5699223fad11a712b1e1d584", + "status": 0, + "basic_price": "6", + "premium_price": "100" + } +} +``` + +**Usage** + +```curl +curl --location 'http://127.0.0.1:8120/v1/account/auction/order-status' \ +--header 'Content-Type: application/json' \ +--data '{ + "account":"michaeltest1.bit", + "type":"blockchain", + "key_info":{ + "coin_type":"60", + "key":"0xd437b8e9cA16Fce24bF3258760c3567214213C5A" + } +}' +``` + +#### Account Auction PendingOrders + +**Request** + +* path: /account/auction/pending-order + * get dutch auction order with pending status +* param: + +```json +{ + "type":"blockchain", + "key_info":{ + "coin_type":"60", + "key":"0xd437b8e9cA16Fce24bF3258760c3567214213C5A" + } +} +``` + +**Response** + +```json +{ + "err_no": 0, + "err_msg": "", + "data": [ + { + "account": "michaeltest1.bit", + "outpoint": "0xeb4871b7af2ca7129a43c5991c408148abd195eb5699223fad11a712b1e1d584-0", + "status": 0, + "basic_price": "6", + "premium_price": "100" + } + ] +} +``` + +**Usage** + +```curl +curl --location 'http://127.0.0.1:8120/v1/account/auction/pending-order' \ +--header 'Content-Type: application/json' \ +--data '{ + "type":"blockchain", + "key_info":{ + "coin_type":"60", + "key":"0xd437b8e9cA16Fce24bF3258760c3567214213C5A" + } +}' +``` + +#### Account Auction Bid + +**Request** + +* path: /account/auction/bid + * bid a account during dutch auction period +* param: + +```json +{ + "account":"michaeltest1.bit", + "type":"blockchain", + "key_info":{ + "coin_type":"60", + "key":"0xd437b8e9cA16Fce24bF3258760c3567214213C5A" + } +} +``` + +**Response** + +```json +{ + "err_no": 0, + "err_msg": "", + "data": { + "sign_key": "", + "sign_list": [ + { + "sign_type": 5, + "sign_msg": "" + } + ], + "mm_json": {} + } +} +``` + +**Usage** + +```curl +curl --location 'http://127.0.0.1:8120/v1/account/auction/bid' \ +--header 'Content-Type: application/json' \ +--data '{ + "account":"michaeltest1.bit", + "type":"blockchain", + "key_info":{ + "coin_type":"60", + "key":"0xd437b8e9cA16Fce24bF3258760c3567214213C5A" + } +}' +``` \ No newline at end of file diff --git a/block_parser/action_dutch_auction.go b/block_parser/action_dutch_auction.go new file mode 100644 index 00000000..2b56ceaa --- /dev/null +++ b/block_parser/action_dutch_auction.go @@ -0,0 +1,23 @@ +package block_parser + +import ( + "fmt" + "github.com/dotbitHQ/das-lib/common" +) + +func (b *BlockParser) ActionBidExpiredAccountAuction(req FuncTransactionHandleReq) (resp FuncTransactionHandleResp) { + if isCV, err := isCurrentVersionTx(req.Tx, common.DasContractNameAccountCellType); err != nil { + resp.Err = fmt.Errorf("isCurrentVersion err: %s", err.Error()) + return + } else if !isCV { + return + } + + outpoint := common.OutPoint2String(req.TxHash, 0) + if err := b.DbDao.UpdatePendingStatusToConfirm(req.Action, outpoint, req.BlockNumber, req.BlockTimestamp); err != nil { + resp.Err = fmt.Errorf("UpdatePendingStatusToConfirm err: %s", err.Error()) + return + } + + return +} diff --git a/block_parser/block_parser_handle.go b/block_parser/block_parser_handle.go index f8391e52..de598d20 100644 --- a/block_parser/block_parser_handle.go +++ b/block_parser/block_parser_handle.go @@ -26,6 +26,7 @@ func (b *BlockParser) registerTransactionHandle() { b.mapTransactionHandle[common.DasActionEditRecords] = b.ActionEditRecords b.mapTransactionHandle[common.DasActionEditManager] = b.ActionEditManager b.mapTransactionHandle[common.DasActionTransferAccount] = b.ActionTransferAccount + b.mapTransactionHandle[common.DasActionBidExpiredAccountAuction] = b.ActionBidExpiredAccountAuction // //b.mapTransactionHandle[common.DasActionDeclareReverseRecord] = b.ActionDeclareReverseRecord //b.mapTransactionHandle[common.DasActionRedeclareReverseRecord] = b.ActionRedeclareReverseRecord diff --git a/cmd/main.go b/cmd/main.go index e938c7f1..a61c8d37 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -202,6 +202,7 @@ func runServer(ctx *cli.Context) error { DasCore: dasCore, DasCache: dasCache, TxBuilderBase: txBuilderBase, + ServerScript: serverScript, MapReservedAccounts: builderConfigCell.ConfigCellPreservedAccountMap, MapUnAvailableAccounts: builderConfigCell.ConfigCellUnavailableAccountMap, }) @@ -250,7 +251,7 @@ func initDasCore() (*core.DasCore, *dascache.DasCache, error) { common.DasContractNameBalanceCellType, common.DasContractNameDispatchCellType, common.DasContractNameApplyRegisterCellType, common.DasContractNamePreAccountCellType, common.DasContractNameProposalCellType, common.DasContractNameReverseRecordCellType, common.DasContractNameIncomeCellType, common.DasContractNameAlwaysSuccess, common.DASContractNameEip712LibCellType, - common.DASContractNameSubAccountCellType, common.DasKeyListCellType) + common.DASContractNameSubAccountCellType, common.DasKeyListCellType, common.DasContractNameDpCellType) ops := []core.DasCoreOption{ core.WithClient(ckbClient), core.WithDasContractArgs(env.ContractArgs), diff --git a/config/config.go b/config/config.go index 0619806c..2cad444c 100644 --- a/config/config.go +++ b/config/config.go @@ -76,6 +76,14 @@ type CfgServer struct { UniPayRefundSwitch bool `json:"uni_pay_refund_switch" yaml:"uni_pay_refund_switch"` HedgeUrl string `json:"hedge_url" yaml:"hedge_url"` PrometheusPushGateway string `json:"prometheus_push_gateway" yaml:"prometheus_push_gateway"` + // ConfigCellDPoint.transfer_whitelist + TransferWhitelist string `json:"transfer_whitelist" yaml:"transfer_whitelist"` + TransferWhitelistPrivate string `json:"transfer_whitelist_private" yaml:"transfer_whitelist_private"` + //ConfigCellDPoint.capacity_recycle_whitelist + CapacityWhitelist string `json:"capacity_whitelist" yaml:"capacity_whitelist"` + CapacityWhitelistPrivate string `json:"capacity_whitelist_private" yaml:"capacity_whitelist_private"` + SplitCount int `json:"split_count" yaml:"split_count"` + SplitAmount uint64 `json:"split_amount" yaml:"split_amount"` } `json:"server" yaml:"server"` Origins []string `json:"origins" yaml:"origins"` InviterWhitelist map[string]string `json:"inviter_whitelist" yaml:"inviter_whitelist"` @@ -146,6 +154,8 @@ func GetUnipayAddress(tokenId tables.PayTokenId) string { return Cfg.PayAddressMap["doge"] case tables.TokenIdStripeUSD: return "stripe" + case tables.ToKenIdDidPoint: + return Cfg.PayAddressMap["did_point"] } log.Error("GetUnipayAddress not supported:", tokenId) return "" diff --git a/dao/dao.go b/dao/dao.go index 07800c7e..c6c782d3 100644 --- a/dao/dao.go +++ b/dao/dao.go @@ -34,6 +34,7 @@ func NewGormDB(dbMysql, parserMysql config.DbMysql) (*DbDao, error) { &tables.TableDasOrderTxInfo{}, &tables.TableRegisterPendingInfo{}, &tables.TableCoupon{}, + &tables.TableAuctionOrder{}, ); err != nil { return nil, err } diff --git a/dao/t_auction_order.go b/dao/t_auction_order.go new file mode 100644 index 00000000..5b0c56d1 --- /dev/null +++ b/dao/t_auction_order.go @@ -0,0 +1,31 @@ +package dao + +import ( + "das_register_server/tables" + "fmt" + "github.com/dotbitHQ/das-lib/common" +) + +func (d *DbDao) GetPendingAuctionOrder(chainType common.ChainType, addr string) (list []tables.TableAuctionOrder, err error) { + sql := fmt.Sprintf(`SELECT o.id,o.account,o.outpoint,o.basic_price,o.premium_price,o.order_id,p.status FROM %s o LEFT JOIN %s p ON o.outpoint=p.outpoint WHERE o.chain_type = %d and o.address = "%s" and p.status = 0 order by bid_time desc`, tables.TableNameAuctionOrder, tables.TableNameRegisterPendingInfo, chainType, addr) + err = d.db.Raw(sql).Find(&list).Error + return list, nil +} + +func (d *DbDao) GetAuctionOrderByAccount(account string, createTime int64) (list []tables.TableAuctionOrder, err error) { + sql := fmt.Sprintf(`SELECT o.account,o.outpoint,o.address,o.chain_type,p.status FROM %s o +LEFT JOIN %s p ON o.outpoint=p.outpoint +WHERE o.account= "%s" and p.status != %d and o.created_at > %d `, tables.TableNameAuctionOrder, tables.TableNameRegisterPendingInfo, account, tables.StatusRejected, createTime) + err = d.db.Raw(sql).Find(&list).Error + return list, nil +} + +func (d *DbDao) GetAuctionOrderStatus(chainType common.ChainType, addr, hash string) (list tables.TableAuctionOrder, err error) { + sql := fmt.Sprintf(`SELECT o.id,o.account,o.outpoint,o.basic_price,o.premium_price,o.order_id,p.status FROM %s o LEFT JOIN %s p ON o.outpoint=p.outpoint WHERE p.outpoint="%s" and o.chain_type = %d and o.address = "%s" order by bid_time desc`, tables.TableNameAuctionOrder, tables.TableNameRegisterPendingInfo, fmt.Sprintf("%s-0", hash), chainType, addr) + err = d.db.Raw(sql).First(&list).Error + return list, nil +} + +func (d *DbDao) CreateAuctionOrder(auctionOrder tables.TableAuctionOrder) (err error) { + return d.db.Create(&auctionOrder).Error +} diff --git a/dao/t_das_order_pay_info.go b/dao/t_das_order_pay_info.go index 5aad0fa5..40d4f002 100644 --- a/dao/t_das_order_pay_info.go +++ b/dao/t_das_order_pay_info.go @@ -121,7 +121,7 @@ func (d *DbDao) UpdateRefundStatusToRefundIng(ids []uint64) error { Where("id IN(?) AND `status`=? AND uni_pay_refund_status=?", ids, tables.OrderTxStatusConfirm, tables.UniPayRefundStatusUnRefund). Updates(map[string]interface{}{ - "uni_pay_refund_status": tables.UniPayRefundStatusRefunded, + "uni_pay_refund_status": tables.UniPayRefundStatusRefunding, }).Error } diff --git a/http_server/api_code/api_code.go b/http_server/api_code/api_code.go index 9744a4e2..cf8d6b32 100644 --- a/http_server/api_code/api_code.go +++ b/http_server/api_code/api_code.go @@ -53,24 +53,28 @@ const ( ) const ( - MethodTokenList = "das_tokenList" - MethodConfigInfo = "das_dasConfig" - MethodAccountList = "das_accountList" - MethodAccountMine = "das_myAccounts" - MethodAccountDetail = "das_accountDetail" - MethodAccountRecords = "das_accountParsingRecords" - MethodReverseLatest = "das_reverseLatest" - MethodReverseList = "das_reverseList" - MethodTransactionStatus = "das_transactionStatus" - MethodBalanceInfo = "das_balanceInfo" - MethodTransactionList = "das_transactionList" - MethodRewardsMine = "das_myRewards" - MethodWithdrawList = "das_withdrawList" - MethodAccountSearch = "das_accountSearch" - MethodRegisteringList = "das_registeringAccounts" - MethodOrderDetail = "das_orderDetail" - MethodAddressDeposit = "das_addressDeposit" - MethodCharacterSetList = "das_characterSetList" + MethodTokenList = "das_tokenList" + MethodConfigInfo = "das_dasConfig" + MethodAccountList = "das_accountList" + MethodAccountMine = "das_myAccounts" + MethodAccountDetail = "das_accountDetail" + MethodAccountRecords = "das_accountParsingRecords" + MethodReverseLatest = "das_reverseLatest" + MethodReverseList = "das_reverseList" + MethodTransactionStatus = "das_transactionStatus" + MethodBalanceInfo = "das_balanceInfo" + MethodTransactionList = "das_transactionList" + MethodRewardsMine = "das_myRewards" + MethodWithdrawList = "das_withdrawList" + MethodAccountSearch = "das_accountSearch" + MethodRegisteringList = "das_registeringAccounts" + MethodOrderDetail = "das_orderDetail" + MethodAddressDeposit = "das_addressDeposit" + MethodCharacterSetList = "das_characterSetList" + MethodAuctionInfo = "das_auctionInfo" + MethodAuctionPrice = "das_auctionPrice" + MethodAuctionOrderStatus = "das_auctionOrderStatus" + MethodAuctionPendingOrder = "das_auctionPendingOrder" MethodReverseDeclare = "das_reverseDeclare" MethodReverseRedeclare = "das_reverseRedeclare" @@ -90,6 +94,7 @@ const ( MethodEditScript = "das_editScript" MethodOrderCheckCoupon = "das_checkCoupon" MethodCkbRpc = "das_ckbRpc" + MethodAuctionBid = "das_auctionBid" ) type ApiResp struct { diff --git a/http_server/api_code/monitor.go b/http_server/api_code/monitor.go index 71f87ed2..6cf2feef 100644 --- a/http_server/api_code/monitor.go +++ b/http_server/api_code/monitor.go @@ -3,7 +3,6 @@ package api_code import ( "bytes" "das_register_server/config" - "das_register_server/prometheus" "encoding/json" "fmt" api_code "github.com/dotbitHQ/das-lib/http_api" @@ -42,7 +41,7 @@ func PushLog(url string, req ReqPushLog) { func DoMonitorLog(method string) gin.HandlerFunc { return func(ctx *gin.Context) { - startTime := time.Now() + //startTime := time.Now() //ip := getClientIp(ctx) blw := &bodyWriter{body: bytes.NewBufferString(""), ResponseWriter: ctx.Writer} @@ -71,7 +70,7 @@ func DoMonitorLog(method string) gin.HandlerFunc { if resp.ErrNo == api_code.ApiCodeSuccess { resp.ErrMsg = "" } - prometheus.Tools.Metrics.Api().WithLabelValues(method, fmt.Sprint(statusCode), fmt.Sprint(resp.ErrNo), resp.ErrMsg).Observe(time.Since(startTime).Seconds()) + //prometheus.Tools.Metrics.Api().WithLabelValues(method, fmt.Sprint(statusCode), fmt.Sprint(resp.ErrNo), resp.ErrMsg).Observe(time.Since(startTime).Seconds()) } } diff --git a/http_server/handle/account_detail.go b/http_server/handle/account_detail.go index 48135848..f1fa4823 100644 --- a/http_server/handle/account_detail.go +++ b/http_server/handle/account_detail.go @@ -3,7 +3,6 @@ package handle import ( "bytes" "das_register_server/config" - //"das_register_server/http_server/api_code" "das_register_server/tables" "encoding/binary" @@ -46,6 +45,7 @@ type RespAccountDetail struct { CustomScript string `json:"custom_script"` PremiumPercentage decimal.Decimal `json:"premium_percentage"` PremiumBase decimal.Decimal `json:"premium_base"` + ReRegisterTime uint64 `json:"re_register_time"` } func (h *HttpHandle) RpcAccountDetail(p json.RawMessage, apiResp *api_code.ApiResp) { @@ -136,7 +136,7 @@ func (h *HttpHandle) getAccountPrice(accLen uint8, args, account string, isRenew baseAmount, _ = decimal.NewFromString(fmt.Sprintf("%d", basicCapacity)) decQuote, _ := decimal.NewFromString(fmt.Sprintf("%d", quote)) decUsdRateBase := decimal.NewFromInt(common.UsdRateBase) - baseAmount = baseAmount.Mul(decQuote).DivRound(decUsdRateBase, 2) + baseAmount = baseAmount.Mul(decQuote).DivRound(decUsdRateBase, 6) if isRenew { accountPrice, _ = decimal.NewFromString(fmt.Sprintf("%d", renewPrice)) @@ -148,8 +148,30 @@ func (h *HttpHandle) getAccountPrice(accLen uint8, args, account string, isRenew return } +func (h *HttpHandle) checkDutchAuction(expiredAt, nowTime uint64) (status tables.SearchStatus, reRegisterTime uint64, err error) { + auctionConfig, err := h.GetAuctionConfig(h.dasCore) + if err != nil { + err = fmt.Errorf("GetAuctionConfig err: %s", err.Error()) + return + } + gracePeriodTime := auctionConfig.GracePeriodTime + auctionPeriodTime := auctionConfig.AuctionPeriodTime + deliverPeriodTime := auctionConfig.DeliverPeriodTime + if nowTime-uint64(gracePeriodTime)-uint64(auctionPeriodTime) < expiredAt && expiredAt < nowTime-uint64(gracePeriodTime) { + status = tables.SearchStatusOnDutchAuction + } + + if nowTime-uint64(gracePeriodTime)-uint64(auctionPeriodTime)-uint64(deliverPeriodTime) < expiredAt && expiredAt < nowTime-uint64(gracePeriodTime)-uint64(auctionPeriodTime) { + status = tables.SearchStatusAuctionRecycling + reRegisterTime = expiredAt + uint64(gracePeriodTime+auctionPeriodTime+deliverPeriodTime) + return + } + return +} + func (h *HttpHandle) doAccountDetail(req *ReqAccountDetail, apiResp *api_code.ApiResp) error { var resp RespAccountDetail + var err error resp.Account = req.Account resp.Status = tables.SearchStatusRegisterAble resp.PremiumPercentage = config.Cfg.Stripe.PremiumPercentage @@ -165,8 +187,28 @@ func (h *HttpHandle) doAccountDetail(req *ReqAccountDetail, apiResp *api_code.Ap // check sub account count := strings.Count(req.Account, ".") - if count == 1 && acc.Id > 0 { + //now < expired_at + 90 + 27 => expired_at > now-90-27 + //expired_at+90 < now => expired_at < now - 90 + //now > expired_at+90+27 + //now < expired_at+90+30 + timeCell, err := h.dasCore.GetTimeCell() + if err != nil { + err = fmt.Errorf("GetTimeCell err: %s", err.Error()) + apiResp.ApiRespErr(api_code.ApiCodeError500, "GetTimeCell err") + return err + } + nowTime := uint64(timeCell.Timestamp()) + if status, reRegisterTime, err := h.checkDutchAuction(acc.ExpiredAt, nowTime); err != nil { + apiResp.ApiRespErr(api_code.ApiCodeError500, "checkDutchAuction err") + return fmt.Errorf("checkDutchAuction err: %s", err.Error()) + } else if status != 0 { + resp.Status = status + resp.ReRegisterTime = reRegisterTime + apiResp.ApiRespOK(resp) + return nil + } + accOutpoint := common.String2OutPointStruct(acc.Outpoint) accTx, er := h.dasCore.Client().GetTransaction(h.ctx, accOutpoint.TxHash) if er != nil { @@ -185,14 +227,12 @@ func (h *HttpHandle) doAccountDetail(req *ReqAccountDetail, apiResp *api_code.Ap } // price - var err error resp.BaseAmount, resp.AccountPrice, err = h.getAccountPrice(uint8(accBuilder.AccountChars.Len()), "", req.Account, true) if err != nil { apiResp.ApiRespErr(api_code.ApiCodeError500, "get account price err") return fmt.Errorf("getAccountPrice err: %s", err.Error()) } } - if acc.Id > 0 { resp.Status = acc.FormatAccountStatus() resp.ExpiredAt = int64(acc.ExpiredAt) * 1e3 diff --git a/http_server/handle/auction_bid.go b/http_server/handle/auction_bid.go new file mode 100644 index 00000000..cb216fb6 --- /dev/null +++ b/http_server/handle/auction_bid.go @@ -0,0 +1,445 @@ +package handle + +import ( + "das_register_server/config" + "das_register_server/tables" + "fmt" + "github.com/dotbitHQ/das-lib/common" + "github.com/dotbitHQ/das-lib/core" + "github.com/dotbitHQ/das-lib/http_api" + "github.com/dotbitHQ/das-lib/molecule" + "github.com/dotbitHQ/das-lib/txbuilder" + "github.com/dotbitHQ/das-lib/witness" + "github.com/gin-gonic/gin" + "github.com/nervosnetwork/ckb-sdk-go/address" + "github.com/nervosnetwork/ckb-sdk-go/indexer" + "github.com/nervosnetwork/ckb-sdk-go/types" + "github.com/scorpiotzh/toolib" + "github.com/shopspring/decimal" + "gorm.io/gorm" + "net/http" +) + +type ReqAuctionBid struct { + Account string `json:"account" binding:"required"` + address string + chainType common.ChainType + CoinType string `json:"coin_type"` //default record + core.ChainTypeAddress +} + +type RespAuctionBid struct { + SignInfo +} + +func (h *HttpHandle) AccountAuctionBid(ctx *gin.Context) { + var ( + funcName = "AccountAuctionBid" + clientIp = GetClientIp(ctx) + req ReqAuctionBid + apiResp http_api.ApiResp + err error + ) + + if err := ctx.ShouldBindJSON(&req); err != nil { + log.Error("ShouldBindJSON err: ", err.Error(), funcName, clientIp) + apiResp.ApiRespErr(http_api.ApiCodeParamsInvalid, "params invalid") + ctx.JSON(http.StatusOK, apiResp) + return + } + log.Info("ApiReq:", funcName, clientIp, toolib.JsonString(req)) + + if err = h.doAccountAuctionBid(&req, &apiResp); err != nil { + log.Error("doAccountAuctionBid err:", err.Error(), funcName, clientIp) + } + ctx.JSON(http.StatusOK, apiResp) +} +func (h *HttpHandle) doAccountAuctionBid(req *ReqAuctionBid, apiResp *http_api.ApiResp) (err error) { + var resp RespAuctionBid + addrHex, err := req.FormatChainTypeAddress(config.Cfg.Server.Net, true) + if err != nil { + apiResp.ApiRespErr(http_api.ApiCodeParamsInvalid, "params is invalid: "+err.Error()) + return nil + } + fmt.Println(addrHex.DasAlgorithmId, addrHex.DasSubAlgorithmId, addrHex.AddressHex) + fromLock, _, err := h.dasCore.Daf().HexToScript(*addrHex) + if err != nil { + apiResp.ApiRespErr(http_api.ApiCodeParamsInvalid, "key info is invalid: "+err.Error()) + return nil + } + req.address, req.chainType = addrHex.AddressHex, addrHex.ChainType + + accountId := common.Bytes2Hex(common.GetAccountIdByAccount(req.Account)) + acc, err := h.dbDao.GetAccountInfoByAccountId(accountId) + if err != nil && err != gorm.ErrRecordNotFound { + apiResp.ApiRespErr(http_api.ApiCodeDbError, "search account err") + return fmt.Errorf("SearchAccount err: %s", err.Error()) + } + if acc.Status != tables.AccountStatusNormal { + apiResp.ApiRespErr(http_api.ApiCodeAccountStatusNotNormal, "account is not on dutch auction") + return + } + timeCell, err := h.dasCore.GetTimeCell() + if err != nil { + apiResp.ApiRespErr(http_api.ApiCodeError500, "GetTimeCell err") + return fmt.Errorf("GetTimeCell err: %s", err.Error()) + } + nowTime := timeCell.Timestamp() + + if status, _, err := h.checkDutchAuction(acc.ExpiredAt, uint64(nowTime)); err != nil { + apiResp.ApiRespErr(http_api.ApiCodeError500, "checkDutchAuction err") + return fmt.Errorf("checkDutchAuction err: %s", err.Error()) + } else if status != tables.SearchStatusOnDutchAuction { + apiResp.ApiRespErr(http_api.ApiCodeAuctionAccountNotFound, "This account has not been in dutch auction") + return nil + } + + _, accLen, err := common.GetDotBitAccountLength(req.Account) + if err != nil { + return + } + if accLen == 0 { + err = fmt.Errorf("accLen is 0") + return + } + baseAmount, accountPrice, err := h.getAccountPrice(uint8(accLen), "", req.Account, false) + if err != nil { + apiResp.ApiRespErr(http_api.ApiCodeError500, "get account price err") + return fmt.Errorf("getAccountPrice err: %s", err.Error()) + } + basicPrice := baseAmount.Add(accountPrice) + + log.Info("expiredat: ", int64(acc.ExpiredAt), "nowTime: ", nowTime) + + auctionConfig, err := h.GetAuctionConfig(h.dasCore) + if err != nil { + err = fmt.Errorf("GetAuctionConfig err: %s", err.Error()) + return + } + premiumPrice := decimal.NewFromFloat(common.Premium(int64(acc.ExpiredAt+uint64(auctionConfig.GracePeriodTime)), nowTime)) + amountDP := basicPrice.Add(premiumPrice).Mul(decimal.NewFromInt(common.UsdRateBase)).BigInt().Uint64() + log.Info("baseAmount: ", baseAmount, " accountPrice: ", accountPrice, " basicPrice: ", basicPrice, " premiumPrice: ", premiumPrice, " amountDP: ", amountDP) + + log.Info("GetDpCells:", common.Bytes2Hex(fromLock.Args), amountDP) + _, _, _, err = h.dasCore.GetDpCells(&core.ParamGetDpCells{ + DasCache: h.dasCache, + LockScript: fromLock, + AmountNeed: amountDP, + CurrentBlockNumber: 0, + SearchOrder: indexer.SearchOrderAsc, + }) + + if err != nil { + if err == core.ErrInsufficientFunds { + apiResp.ApiRespErr(http_api.ApiCodeInsufficientBalance, err.Error()) + return + } else { + apiResp.ApiRespErr(http_api.ApiCodeError500, err.Error()) + return fmt.Errorf("dasCore.GetDpCells err: ", err.Error()) + } + } + var reqBuild reqBuildTx + reqBuild.Action = common.DasActionBidExpiredAccountAuction + reqBuild.Account = req.Account + reqBuild.ChainType = req.chainType + reqBuild.Address = req.address + reqBuild.Capacity = 0 + reqBuild.AuctionInfo = AuctionInfo{ + BasicPrice: basicPrice, + PremiumPrice: premiumPrice, + BidTime: nowTime, + } + + // to lock & normal cell lock + if config.Cfg.Server.TransferWhitelist == "" || config.Cfg.Server.CapacityWhitelist == "" { + return fmt.Errorf("TransferWhitelist or CapacityWhitelist is empty") + } + toLock, err := address.Parse(config.Cfg.Server.TransferWhitelist) + if err != nil { + apiResp.ApiRespErr(http_api.ApiCodeError500, err.Error()) + return fmt.Errorf("address.Parse err: %s", err.Error()) + } + + normalCellLock, err := address.Parse(config.Cfg.Server.CapacityWhitelist) + if err != nil { + apiResp.ApiRespErr(http_api.ApiCodeError500, err.Error()) + return fmt.Errorf("address.Parse err: %s", err.Error()) + } + + //default record + var initialRecords []witness.Record + coinType := req.KeyInfo.CoinType + + if addr, err := common.FormatAddressByCoinType(string(coinType), req.address); err == nil { + initialRecords = append(initialRecords, witness.Record{ + Key: string(coinType), + Type: "address", + Label: "", + Value: addr, + TTL: 300, + }) + } else { + log.Error("buildOrderPreRegisterTx FormatAddressByCoinType err: ", err.Error()) + } + + var p auctionBidParams + p.Account = &acc + p.AmountDP = amountDP + p.FromLock = fromLock + p.ToLock = toLock.Script + p.NormalCellLock = normalCellLock.Script + p.TimeCell = timeCell + p.DefaultRecord = initialRecords + txParams, err := h.buildAuctionBidTx(&reqBuild, &p) + if err != nil { + apiResp.ApiRespErr(http_api.ApiCodeError500, "build tx err: "+err.Error()) + return fmt.Errorf("buildEditManagerTx err: %s", err.Error()) + } + if si, err := h.buildTx(&reqBuild, txParams); err != nil { + apiResp.ApiRespErr(http_api.ApiCodeError500, "build tx err: "+err.Error()) + return fmt.Errorf("buildTx: %s", err.Error()) + } else { + resp.SignInfo = *si + } + + apiResp.ApiRespOK(resp) + return nil +} + +type auctionBidParams struct { + Account *tables.TableAccountInfo + DefaultRecord []witness.Record + AmountDP uint64 + FromLock *types.Script + ToLock *types.Script + NormalCellLock *types.Script + TimeCell *core.TimeCell +} + +func (h *HttpHandle) buildAuctionBidTx(req *reqBuildTx, p *auctionBidParams) (*txbuilder.BuildTransactionParams, error) { + var txParams txbuilder.BuildTransactionParams + contractDas, err := core.GetDasContractInfo(common.DasContractNameDispatchCellType) + if err != nil { + return nil, fmt.Errorf("GetDasContractInfo err: %s", err.Error()) + } + balanceContract, err := core.GetDasContractInfo(common.DasContractNameBalanceCellType) + if err != nil { + return nil, fmt.Errorf("GetDasContractInfo err: %s", err.Error()) + } + quoteCell, err := h.dasCore.GetQuoteCell() + if err != nil { + return nil, fmt.Errorf("GetQuoteCell err: %s", err.Error()) + } + + accOutPoint := common.String2OutPointStruct(p.Account.Outpoint) + + // witness account cell + accTx, err := h.dasCore.Client().GetTransaction(h.ctx, accOutPoint.TxHash) + if err != nil { + return nil, fmt.Errorf("GetTransaction err: %s", err.Error()) + } + + builderMap, err := witness.AccountCellDataBuilderMapFromTx(accTx.Transaction, common.DataTypeNew) + if err != nil { + return nil, fmt.Errorf("AccountCellDataBuilderMapFromTx err: %s", err.Error()) + } + builder, ok := builderMap[req.Account] + if !ok { + return nil, fmt.Errorf("builderMap not exist account: %s", req.Account) + } + accCellCapacity := accTx.Transaction.Outputs[builder.Index].Capacity + oldAccOwnerArgs := accTx.Transaction.Outputs[builder.Index].Lock.Args + + actionWitness, err := witness.GenActionDataWitness(common.DasActionBidExpiredAccountAuction, nil) + if err != nil { + return nil, fmt.Errorf("GenActionDataWitness err: %s", err.Error()) + } + txParams.Witnesses = append(txParams.Witnesses, actionWitness) + //-----acc witness + accWitness, accData, err := builder.GenWitness(&witness.AccountCellParam{ + OldIndex: 0, + NewIndex: 0, + Action: common.DasActionBidExpiredAccountAuction, + RegisterAt: uint64(p.TimeCell.Timestamp()), + Records: p.DefaultRecord, + }) + txParams.Witnesses = append(txParams.Witnesses, accWitness) + + //input account cell + txParams.Inputs = append(txParams.Inputs, &types.CellInput{ + PreviousOutput: accOutPoint, + }) + + //output account cell + newOwnerAddrHex := core.DasAddressHex{ + DasAlgorithmId: req.ChainType.ToDasAlgorithmId(true), + AddressHex: req.Address, + IsMulti: false, + ChainType: req.ChainType, + } + lockArgs, err := h.dasCore.Daf().HexToArgs(newOwnerAddrHex, newOwnerAddrHex) + + txParams.Outputs = append(txParams.Outputs, &types.CellOutput{ + Capacity: accTx.Transaction.Outputs[builder.Index].Capacity, + Lock: contractDas.ToScript(lockArgs), + Type: accTx.Transaction.Outputs[builder.Index].Type, + }) + + newExpiredAt := p.TimeCell.Timestamp() + common.OneYearSec + byteExpiredAt := molecule.Go64ToBytes(newExpiredAt) + accData = append(accData, accTx.Transaction.OutputsData[builder.Index][32:]...) + accData1 := accData[:common.ExpireTimeEndIndex-common.ExpireTimeLen] + accData2 := accData[common.ExpireTimeEndIndex:] + newAccData := append(accData1, byteExpiredAt...) + newAccData = append(newAccData, accData2...) + txParams.OutputsData = append(txParams.OutputsData, newAccData) + + //dp + liveCell, totalDP, totalCapacity, err := h.dasCore.GetDpCells(&core.ParamGetDpCells{ + DasCache: h.dasCache, + LockScript: p.FromLock, + AmountNeed: p.AmountDP, + CurrentBlockNumber: 0, + SearchOrder: indexer.SearchOrderAsc, + }) + if err != nil { + return nil, fmt.Errorf("GetDpCells err: %s", err.Error()) + } + + for _, v := range liveCell { + txParams.Inputs = append(txParams.Inputs, &types.CellInput{ + PreviousOutput: v.OutPoint, + }) + } + + // outputs + outputs, outputsData, normalCellCapacity, err := h.dasCore.SplitDPCell(&core.ParamSplitDPCell{ + FromLock: p.FromLock, + ToLock: p.ToLock, + DPLiveCell: liveCell, + DPLiveCellCapacity: totalCapacity, + DPTotalAmount: totalDP, + DPTransferAmount: p.AmountDP, + DPSplitCount: config.Cfg.Server.SplitCount, + DPSplitAmount: config.Cfg.Server.SplitAmount, + NormalCellLock: p.NormalCellLock, + }) + if err != nil { + return nil, fmt.Errorf("SplitDPCell err: %s", err.Error()) + } + for i, _ := range outputs { + txParams.Outputs = append(txParams.Outputs, outputs[i]) + txParams.OutputsData = append(txParams.OutputsData, outputsData[i]) + } + + normalCells, totalNormal, err := h.dasCore.GetBalanceCells(&core.ParamGetBalanceCells{ + DasCache: h.dasCache, + LockScript: h.serverScript, + CapacityNeed: normalCellCapacity + accCellCapacity, + CapacityForChange: common.MinCellOccupiedCkb, + SearchOrder: indexer.SearchOrderAsc, + }) + if err != nil { + return nil, fmt.Errorf("GetBalanceCells err: %s", err.Error()) + } + for _, v := range normalCells { + txParams.Inputs = append(txParams.Inputs, &types.CellInput{ + PreviousOutput: v.OutPoint, + }) + } + + //old owner capacity + txParams.Outputs = append(txParams.Outputs, &types.CellOutput{ + Capacity: accCellCapacity, + Lock: contractDas.ToScript(oldAccOwnerArgs), + Type: balanceContract.ToScript(nil), + }) + txParams.OutputsData = append(txParams.OutputsData, []byte{}) + + log.Info("normalCellCapacity:", normalCellCapacity, common.Bytes2Hex(p.NormalCellLock.Args)) + //change + if change := totalNormal - normalCellCapacity - accCellCapacity; change > 0 { + txParams.Outputs = append(txParams.Outputs, &types.CellOutput{ + Capacity: change, + Lock: h.serverScript, + Type: nil, + }) + txParams.OutputsData = append(txParams.OutputsData, []byte{}) + } + + // cell deps + configCellAcc, err := core.GetDasConfigCellInfo(common.ConfigCellTypeArgsAccount) + if err != nil { + return nil, fmt.Errorf("GetDasConfigCellInfo err: %s", err.Error()) + } + accContract, err := core.GetDasContractInfo(common.DasContractNameAccountCellType) + if err != nil { + return nil, fmt.Errorf("GetDasContractInfo err: %s", err.Error()) + } + configCellMain, err := core.GetDasConfigCellInfo(common.ConfigCellTypeArgsMain) + if err != nil { + return nil, fmt.Errorf("GetDasConfigCellInfo err: %s", err.Error()) + } + configCellDP, err := core.GetDasConfigCellInfo(common.ConfigCellTypeArgsDPoint) + if err != nil { + return nil, fmt.Errorf("GetDasConfigCellInfo err: %s", err.Error()) + } + contractDP, err := core.GetDasContractInfo(common.DasContractNameDpCellType) + if err != nil { + return nil, fmt.Errorf("GetDasContractInfo err: %s", err.Error()) + } + heightCell, err := h.dasCore.GetHeightCell() + if err != nil { + return nil, fmt.Errorf("GetHeightCell err: %s", err.Error()) + } + priceConfig, err := core.GetDasConfigCellInfo(common.ConfigCellTypeArgsPrice) + if err != nil { + return nil, fmt.Errorf("GetDasConfigCellInfo err: %s", err.Error()) + } + txParams.CellDeps = append(txParams.CellDeps, + configCellAcc.ToCellDep(), + priceConfig.ToCellDep(), + configCellMain.ToCellDep(), + configCellDP.ToCellDep(), + contractDP.ToCellDep(), + p.TimeCell.ToCellDep(), + accContract.ToCellDep(), + contractDas.ToCellDep(), + heightCell.ToCellDep(), + quoteCell.ToCellDep(), + ) + return &txParams, nil +} + +type AuctionConfig struct { + GracePeriodTime, AuctionPeriodTime, DeliverPeriodTime uint32 +} + +func (h *HttpHandle) GetAuctionConfig(dasCore *core.DasCore) (res *AuctionConfig, err error) { + builderConfigCell, err := dasCore.ConfigCellDataBuilderByTypeArgs(common.ConfigCellTypeArgsAccount) + if err != nil { + err = fmt.Errorf("ConfigCellDataBuilderByTypeArgs err: %s", err.Error()) + return + } + gracePeriodTime, err := builderConfigCell.ExpirationGracePeriod() + if err != nil { + err = fmt.Errorf("ExpirationGracePeriod err: %s", err.Error()) + return + } + auctionPeriodTime, err := builderConfigCell.ExpirationAuctionPeriod() + if err != nil { + err = fmt.Errorf("ExpirationAuctionPeriod err: %s", err.Error()) + return + } + deliverPeriodTime, err := builderConfigCell.ExpirationDeliverPeriod() + if err != nil { + err = fmt.Errorf("ExpirationDeliverPeriod err: %s", err.Error()) + return + } + res = &AuctionConfig{ + GracePeriodTime: gracePeriodTime, + AuctionPeriodTime: auctionPeriodTime, + DeliverPeriodTime: deliverPeriodTime, + } + return +} diff --git a/http_server/handle/auction_info.go b/http_server/handle/auction_info.go new file mode 100644 index 00000000..867f07ac --- /dev/null +++ b/http_server/handle/auction_info.go @@ -0,0 +1,151 @@ +package handle + +import ( + "das_register_server/config" + "das_register_server/tables" + "fmt" + "github.com/dotbitHQ/das-lib/common" + "github.com/dotbitHQ/das-lib/core" + "github.com/dotbitHQ/das-lib/http_api" + "github.com/gin-gonic/gin" + "github.com/scorpiotzh/toolib" + "github.com/shopspring/decimal" + "gorm.io/gorm" + "net/http" + "time" +) + +type ReqAccountAuctionInfo struct { + Account string `json:"account" binding:"required"` + core.ChainTypeAddress + address string + chainType common.ChainType +} + +type RespAccountAuctionInfo struct { + AccountId string `json:"account_id"` + Account string `json:"account"` + BidStatus tables.BidStatus `json:"bid_status"` + Hash string `json:"hash"` + StartsaleTime uint64 `json:"start_auction_time"` + EndSaleTime uint64 `json:"end_auction_time"` + ExipiredTime uint64 `json:"expired_at"` + AccountPrice decimal.Decimal `json:"account_price"` + BaseAmount decimal.Decimal `json:"base_amount"` +} + +func (h *HttpHandle) GetAccountAuctionInfo(ctx *gin.Context) { + var ( + funcName = "GetAccountAuctionInfo" + clientIp = GetClientIp(ctx) + req ReqAccountAuctionInfo + apiResp http_api.ApiResp + err error + ) + + if err := ctx.ShouldBindJSON(&req); err != nil { + log.Error("ShouldBindJSON err: ", err.Error(), funcName, clientIp) + apiResp.ApiRespErr(http_api.ApiCodeParamsInvalid, "params invalid") + ctx.JSON(http.StatusOK, apiResp) + return + } + log.Info("ApiReq:", funcName, clientIp, toolib.JsonString(req)) + + if err = h.doGetAccountAuctionInfo(&req, &apiResp); err != nil { + log.Error("GetAccountAuctionInfo err:", err.Error(), funcName, clientIp) + } + ctx.JSON(http.StatusOK, apiResp) +} + +func (h *HttpHandle) doGetAccountAuctionInfo(req *ReqAccountAuctionInfo, apiResp *http_api.ApiResp) (err error) { + var resp RespAccountAuctionInfo + var addrHex *core.DasAddressHex + if req.KeyInfo.Key != "" { + addrHex, err = req.FormatChainTypeAddress(config.Cfg.Server.Net, true) + if err != nil { + apiResp.ApiRespErr(http_api.ApiCodeParamsInvalid, "params is invalid: "+err.Error()) + return nil + } + req.address, req.chainType = addrHex.AddressHex, addrHex.ChainType + } + + accountId := common.Bytes2Hex(common.GetAccountIdByAccount(req.Account)) + acc, err := h.dbDao.GetAccountInfoByAccountId(accountId) + if err != nil && err != gorm.ErrRecordNotFound { + apiResp.ApiRespErr(http_api.ApiCodeDbError, "search account err") + return fmt.Errorf("SearchAccount err: %s", err.Error()) + } + if acc.Id == 0 { + apiResp.ApiRespErr(http_api.ApiCodeAccountNotExist, fmt.Sprintf("account [%s] not exist", req.Account)) + return + } + + timeCell, err := h.dasCore.GetTimeCell() + if err != nil { + apiResp.ApiRespErr(http_api.ApiCodeError500, "GetTimeCell err") + return fmt.Errorf("GetTimeCell err: %s", err.Error()) + } + nowTime := timeCell.Timestamp() + if status, _, err := h.checkDutchAuction(acc.ExpiredAt, uint64(nowTime)); err != nil { + apiResp.ApiRespErr(http_api.ApiCodeError500, "checkDutchAuction err") + return fmt.Errorf("checkDutchAuction err: %s", err.Error()) + } else if status != tables.SearchStatusOnDutchAuction { + apiResp.ApiRespErr(http_api.ApiCodeAuctionAccountNotFound, "This account has not been in dutch auction") + return nil + } + + //search bid status of a account + createTime := time.Now().Unix() - 365*86400 + list, err := h.dbDao.GetAuctionOrderByAccount(req.Account, createTime) + if err != nil { + apiResp.ApiRespErr(http_api.ApiCodeDbError, "db error") + return + } + + if addrHex != nil { + if len(list) == 0 { + resp.BidStatus = tables.BidStatusNoOne + } else { + resp.BidStatus = tables.BidStatusByOthers + for _, v := range list { + if v.ChainType == addrHex.ChainType && v.Address == addrHex.AddressHex { + resp.BidStatus = tables.BidStatusByMe + resp.Hash, _ = common.String2OutPoint(v.Outpoint) + } + } + apiResp.ApiRespOK(resp) + return + } + } + + _, accLen, err := common.GetDotBitAccountLength(req.Account) + if err != nil { + return + } + if accLen == 0 { + err = fmt.Errorf("accLen is 0") + return + } + baseAmount, accountPrice, err := h.getAccountPrice(uint8(accLen), "", req.Account, false) + if err != nil { + apiResp.ApiRespErr(http_api.ApiCodeError500, "get account price err") + return fmt.Errorf("getAccountPrice err: %s", err.Error()) + } + auctionConfig, err := h.GetAuctionConfig(h.dasCore) + if err != nil { + err = fmt.Errorf("GetAuctionConfig err: %s", err.Error()) + return + } + gracePeriodTime := auctionConfig.GracePeriodTime + auctionPeriodTime := auctionConfig.AuctionPeriodTime + + resp.AccountId = acc.AccountId + resp.Account = req.Account + resp.StartsaleTime = acc.ExpiredAt + uint64(gracePeriodTime) + resp.EndSaleTime = acc.ExpiredAt + uint64(gracePeriodTime+auctionPeriodTime) + resp.AccountPrice = accountPrice + resp.BaseAmount = baseAmount + resp.ExipiredTime = acc.ExpiredAt + apiResp.ApiRespOK(resp) + return +} diff --git a/http_server/handle/auction_order_status.go b/http_server/handle/auction_order_status.go new file mode 100644 index 00000000..632044c9 --- /dev/null +++ b/http_server/handle/auction_order_status.go @@ -0,0 +1,78 @@ +package handle + +import ( + "das_register_server/config" + "github.com/dotbitHQ/das-lib/common" + "github.com/dotbitHQ/das-lib/core" + "github.com/dotbitHQ/das-lib/http_api" + "github.com/gin-gonic/gin" + "github.com/scorpiotzh/toolib" + "github.com/shopspring/decimal" + "net/http" +) + +type ReqAuctionOrderStatus struct { + Hash string `json:"hash" binding:"required"` + core.ChainTypeAddress + address string + chainType common.ChainType +} +type RepReqGetAuctionOrder struct { + Account string `json:"account"` + Hash string `json:"hash"` + Status int `json:"status"` + BasicPrice decimal.Decimal `json:"basic_price" gorm:"column:basic_price;type:decimal(60,0) NOT NULL DEFAULT '0' COMMENT ''"` + PremiumPrice decimal.Decimal `json:"premium_price" gorm:"column:premium_price;type:decimal(60,0) NOT NULL DEFAULT '0' COMMENT ''"` +} + +func (h *HttpHandle) GetAuctionOrderStatus(ctx *gin.Context) { + var ( + funcName = "GetAuctionOrderStatus" + clientIp = GetClientIp(ctx) + req ReqAuctionOrderStatus + apiResp http_api.ApiResp + err error + ) + + if err := ctx.ShouldBindJSON(&req); err != nil { + log.Error("ShouldBindJSON err: ", err.Error(), funcName, clientIp) + apiResp.ApiRespErr(http_api.ApiCodeParamsInvalid, "params invalid") + ctx.JSON(http.StatusOK, apiResp) + return + } + + log.Info("ApiReq:", funcName, clientIp, toolib.JsonString(req)) + + if err = h.doGetAuctionOrderStatus(&req, &apiResp); err != nil { + log.Error("doGetAuctionOrderStatus err:", err.Error(), funcName, clientIp) + } + ctx.JSON(http.StatusOK, apiResp) +} + +func (h *HttpHandle) doGetAuctionOrderStatus(req *ReqAuctionOrderStatus, apiResp *http_api.ApiResp) (err error) { + var resp RepReqGetAuctionOrder + + addrHex, err := req.FormatChainTypeAddress(config.Cfg.Server.Net, true) + if err != nil { + apiResp.ApiRespErr(http_api.ApiCodeParamsInvalid, "params is invalid: "+err.Error()) + return nil + } + req.address, req.chainType = addrHex.AddressHex, addrHex.ChainType + order, err := h.dbDao.GetAuctionOrderStatus(addrHex.ChainType, addrHex.AddressHex, req.Hash) + if err != nil { + apiResp.ApiRespErr(http_api.ApiCodeDbError, "db error") + return + } + if order.Id == 0 { + apiResp.ApiRespErr(http_api.ApiCodeAuctionOrderNotFound, "order not found") + return + } + + resp.Account = order.Account + resp.PremiumPrice = order.PremiumPrice + resp.BasicPrice = order.BasicPrice + resp.Hash, _ = common.String2OutPoint(order.Outpoint) + resp.Status = order.Status + apiResp.ApiRespOK(resp) + return +} diff --git a/http_server/handle/auction_pending_order.go b/http_server/handle/auction_pending_order.go new file mode 100644 index 00000000..d338cd44 --- /dev/null +++ b/http_server/handle/auction_pending_order.go @@ -0,0 +1,68 @@ +package handle + +import ( + "das_register_server/config" + "github.com/dotbitHQ/das-lib/common" + "github.com/dotbitHQ/das-lib/core" + "github.com/dotbitHQ/das-lib/http_api" + "github.com/gin-gonic/gin" + "github.com/scorpiotzh/toolib" + "net/http" +) + +type ReqGetPendingAuctionOrder struct { + core.ChainTypeAddress + address string + chainType common.ChainType +} + +func (h *HttpHandle) GetPendingAuctionOrder(ctx *gin.Context) { + var ( + funcName = "GetPendingAuctionOrder" + clientIp = GetClientIp(ctx) + req ReqGetPendingAuctionOrder + apiResp http_api.ApiResp + err error + ) + + if err := ctx.ShouldBindJSON(&req); err != nil { + log.Error("ShouldBindJSON err: ", err.Error(), funcName, clientIp) + apiResp.ApiRespErr(http_api.ApiCodeParamsInvalid, "params invalid") + ctx.JSON(http.StatusOK, apiResp) + return + } + + log.Info("ApiReq:", funcName, clientIp, toolib.JsonString(req)) + + if err = h.doGetPendingAuctionOrder(&req, &apiResp); err != nil { + log.Error("doGetPendingAuctionOrder err:", err.Error(), funcName, clientIp) + } + ctx.JSON(http.StatusOK, apiResp) +} + +func (h *HttpHandle) doGetPendingAuctionOrder(req *ReqGetPendingAuctionOrder, apiResp *http_api.ApiResp) (err error) { + resp := make([]RepReqGetAuctionOrder, 0) + addrHex, err := req.FormatChainTypeAddress(config.Cfg.Server.Net, true) + if err != nil { + apiResp.ApiRespErr(http_api.ApiCodeParamsInvalid, "params is invalid: "+err.Error()) + return nil + } + req.address, req.chainType = addrHex.AddressHex, addrHex.ChainType + list, err := h.dbDao.GetPendingAuctionOrder(addrHex.ChainType, addrHex.AddressHex) + if err != nil { + apiResp.ApiRespErr(http_api.ApiCodeDbError, "db error") + return + } + for _, v := range list { + hash, _ := common.String2OutPoint(v.Outpoint) + resp = append(resp, RepReqGetAuctionOrder{ + Account: v.Account, + PremiumPrice: v.PremiumPrice, + BasicPrice: v.BasicPrice, + Hash: hash, + Status: v.Status, + }) + } + apiResp.ApiRespOK(resp) + return +} diff --git a/http_server/handle/auction_price.go b/http_server/handle/auction_price.go new file mode 100644 index 00000000..d57039a7 --- /dev/null +++ b/http_server/handle/auction_price.go @@ -0,0 +1,100 @@ +package handle + +import ( + "das_register_server/tables" + "fmt" + "github.com/dotbitHQ/das-lib/common" + "github.com/dotbitHQ/das-lib/http_api" + "github.com/gin-gonic/gin" + "github.com/scorpiotzh/toolib" + "github.com/shopspring/decimal" + "gorm.io/gorm" + "net/http" +) + +type ReqAuctionPrice struct { + Account string `json:"account" binding:"required"` +} + +type RespAuctionPrice struct { + //BasicPrice decimal.Decimal `json:"basic_price"` + AccountPrice decimal.Decimal `json:"account_price"` + BaseAmount decimal.Decimal `json:"base_amount"` + PremiumPrice decimal.Decimal `json:"premium_price"` +} + +//查询价格 +func (h *HttpHandle) GetAccountAuctionPrice(ctx *gin.Context) { + var ( + funcName = "GetAccountAuctionPrice" + clientIp = GetClientIp(ctx) + req ReqAuctionPrice + apiResp http_api.ApiResp + err error + ) + + if err := ctx.ShouldBindJSON(&req); err != nil { + log.Error("ShouldBindJSON err: ", err.Error(), funcName, clientIp) + apiResp.ApiRespErr(http_api.ApiCodeParamsInvalid, "params invalid") + ctx.JSON(http.StatusOK, apiResp) + return + } + log.Info("ApiReq:", funcName, clientIp, toolib.JsonString(req)) + + if err = h.doGetAccountAuctionPrice(&req, &apiResp); err != nil { + log.Error("doGetAccountAuctionPrice err:", err.Error(), funcName, clientIp) + } + ctx.JSON(http.StatusOK, apiResp) +} +func (h *HttpHandle) doGetAccountAuctionPrice(req *ReqAuctionPrice, apiResp *http_api.ApiResp) (err error) { + var resp RespAuctionPrice + accountId := common.Bytes2Hex(common.GetAccountIdByAccount(req.Account)) + acc, err := h.dbDao.GetAccountInfoByAccountId(accountId) + if err != nil && err != gorm.ErrRecordNotFound { + apiResp.ApiRespErr(http_api.ApiCodeDbError, "search account err") + return fmt.Errorf("SearchAccount err: %s", err.Error()) + } + //nowTime := uint64(time.Now().Unix()) + timeCell, err := h.dasCore.GetTimeCell() + if err != nil { + apiResp.ApiRespErr(http_api.ApiCodeError500, "GetTimeCell err") + + return fmt.Errorf("GetTimeCell err: %s", err.Error()) + } + nowTime := timeCell.Timestamp() + //exp + 90 + 27 +3 + //now > exp+117 exp< now - 117 + //now< exp+90 exp>now -90 + if status, _, err := h.checkDutchAuction(acc.ExpiredAt, uint64(nowTime)); err != nil { + apiResp.ApiRespErr(http_api.ApiCodeError500, "checkDutchAuction err") + return fmt.Errorf("checkDutchAuction err: %s", err.Error()) + } else if status != tables.SearchStatusOnDutchAuction { + apiResp.ApiRespErr(http_api.ApiCodeAuctionAccountNotFound, "This account has not been in dutch auction") + return nil + } + + //计算长度 + _, accLen, err := common.GetDotBitAccountLength(req.Account) + if err != nil { + return + } + if accLen == 0 { + err = fmt.Errorf("accLen is 0") + return + } + baseAmount, accountPrice, err := h.getAccountPrice(uint8(accLen), "", req.Account, false) + if err != nil { + apiResp.ApiRespErr(http_api.ApiCodeError500, "get account price err") + return fmt.Errorf("getAccountPrice err: %s", err.Error()) + } + auctionConfig, err := h.GetAuctionConfig(h.dasCore) + if err != nil { + err = fmt.Errorf("GetAuctionConfig err: %s", err.Error()) + return + } + resp.BaseAmount = baseAmount + resp.AccountPrice = accountPrice + resp.PremiumPrice = decimal.NewFromFloat(common.Premium(int64(acc.ExpiredAt+uint64(auctionConfig.GracePeriodTime)), int64(nowTime))) + apiResp.ApiRespOK(resp) + return +} diff --git a/http_server/handle/common.go b/http_server/handle/common.go index 6d9bd4a2..32b3d4cc 100644 --- a/http_server/handle/common.go +++ b/http_server/handle/common.go @@ -6,6 +6,7 @@ import ( "github.com/dotbitHQ/das-lib/common" "github.com/dotbitHQ/das-lib/txbuilder" "github.com/scorpiotzh/toolib" + "github.com/shopspring/decimal" "time" ) @@ -43,13 +44,20 @@ func (s *SignInfo) SignListString() string { return toolib.JsonString(s.SignList) } +type AuctionInfo struct { + BasicPrice decimal.Decimal `json:"basic_price"` + PremiumPrice decimal.Decimal `json:"premium_price"` + BidTime int64 `json:"bid_time"` +} + type SignInfoCache struct { - ChainType common.ChainType `json:"chain_type"` - Address string `json:"address"` - Action string `json:"action"` - Account string `json:"account"` - Capacity uint64 `json:"capacity"` - BuilderTx *txbuilder.DasTxBuilderTransaction `json:"builder_tx"` + ChainType common.ChainType `json:"chain_type"` + Address string `json:"address"` + Action string `json:"action"` + Account string `json:"account"` + Capacity uint64 `json:"capacity"` + BuilderTx *txbuilder.DasTxBuilderTransaction `json:"builder_tx"` + AuctionInfo AuctionInfo `json:"auction_info"` } func (s *SignInfoCache) SignKey() string { diff --git a/http_server/handle/handle.go b/http_server/handle/handle.go index 913f4b70..cec702d1 100644 --- a/http_server/handle/handle.go +++ b/http_server/handle/handle.go @@ -13,6 +13,7 @@ import ( "github.com/dotbitHQ/das-lib/http_api/logger" "github.com/dotbitHQ/das-lib/txbuilder" "github.com/gin-gonic/gin" + "github.com/nervosnetwork/ckb-sdk-go/types" ) var ( @@ -26,6 +27,7 @@ type HttpHandle struct { dasCore *core.DasCore dasCache *dascache.DasCache txBuilderBase *txbuilder.DasTxBuilderBase + serverScript *types.Script mapReservedAccounts map[string]struct{} mapUnAvailableAccounts map[string]struct{} } @@ -37,6 +39,7 @@ type HttpHandleParams struct { DasCore *core.DasCore DasCache *dascache.DasCache TxBuilderBase *txbuilder.DasTxBuilderBase + ServerScript *types.Script MapReservedAccounts map[string]struct{} MapUnAvailableAccounts map[string]struct{} } @@ -49,6 +52,7 @@ func Initialize(p HttpHandleParams) *HttpHandle { dasCore: p.DasCore, dasCache: p.DasCache, txBuilderBase: p.TxBuilderBase, + serverScript: p.ServerScript, mapReservedAccounts: p.MapReservedAccounts, mapUnAvailableAccounts: p.MapUnAvailableAccounts, } diff --git a/http_server/handle/reverse_declare.go b/http_server/handle/reverse_declare.go index 34a33c89..0181f955 100644 --- a/http_server/handle/reverse_declare.go +++ b/http_server/handle/reverse_declare.go @@ -201,12 +201,13 @@ func (h *HttpHandle) doReverseDeclare(req *ReqReverseDeclare, apiResp *api_code. } type reqBuildTx struct { - Action common.DasAction - ChainType common.ChainType `json:"chain_type"` - Address string `json:"address"` - Account string `json:"account"` - Capacity uint64 `json:"capacity"` - EvmChainId int64 `json:"evm_chain_id"` + Action common.DasAction + ChainType common.ChainType `json:"chain_type"` + Address string `json:"address"` + Account string `json:"account"` + Capacity uint64 `json:"capacity"` + EvmChainId int64 `json:"evm_chain_id"` + AuctionInfo AuctionInfo `json:"auction_info"` } type DeclareParams struct { @@ -216,7 +217,6 @@ type DeclareParams struct { TotalCapacity uint64 DeclareCapacity uint64 FeeCapacity uint64 - //AccountInfo *tables.TableAccountInfo } func (h *HttpHandle) buildTx(req *reqBuildTx, txParams *txbuilder.BuildTransactionParams) (*SignInfo, error) { @@ -241,6 +241,25 @@ func (h *HttpHandle) buildTx(req *reqBuildTx, txParams *txbuilder.BuildTransacti changeCapacity := txBuilder.Transaction.Outputs[0].Capacity - sizeInBlock - 1000 txBuilder.Transaction.Outputs[0].Capacity = changeCapacity log.Info("buildTx:", req.Action, sizeInBlock, changeCapacity) + case common.DasActionBidExpiredAccountAuction: + accTx, err := h.dasCore.Client().GetTransaction(h.ctx, txParams.Inputs[0].PreviousOutput.TxHash) + if err != nil { + return nil, fmt.Errorf("GetTransaction err: %s", err.Error()) + } + accLock := accTx.Transaction.Outputs[txParams.Inputs[0].PreviousOutput.Index].Lock + + dpTx, err := h.dasCore.Client().GetTransaction(h.ctx, txParams.Inputs[1].PreviousOutput.TxHash) + if err != nil { + return nil, fmt.Errorf("GetTransaction err: %s", err.Error()) + } + dpLock := dpTx.Transaction.Outputs[txParams.Inputs[1].PreviousOutput.Index].Lock + if !accLock.Equals(dpLock) { + skipGroups = []int{0} + } + sizeInBlock, _ := txBuilder.Transaction.SizeInBlock() + changeCapacity := txBuilder.Transaction.Outputs[0].Capacity - sizeInBlock - 1000 + txBuilder.Transaction.Outputs[0].Capacity = changeCapacity + log.Info("buildTx:", req.Action, sizeInBlock, changeCapacity) } signList, err := txBuilder.GenerateDigestListFromTx(skipGroups) if err != nil { @@ -269,6 +288,11 @@ func (h *HttpHandle) buildTx(req *reqBuildTx, txParams *txbuilder.BuildTransacti sic.Capacity = req.Capacity sic.BuilderTx = txBuilder.DasTxBuilderTransaction + //dutch auction + if req.Action == common.DasActionBidExpiredAccountAuction { + sic.AuctionInfo = req.AuctionInfo + } + signKey := sic.SignKey() cacheStr := toolib.JsonString(&sic) if err = h.rc.SetSignTxCache(signKey, cacheStr); err != nil { diff --git a/http_server/handle/transaction_send.go b/http_server/handle/transaction_send.go index de9f92e3..0c20d262 100644 --- a/http_server/handle/transaction_send.go +++ b/http_server/handle/transaction_send.go @@ -105,7 +105,7 @@ func (h *HttpHandle) doTransactionSend(req *ReqTransactionSend, apiResp *api_cod apiResp.ApiRespErr(api_code.ApiCodeParamsInvalid, "SignAddress err") return nil } - + log.Info("req.signaddress: ", req.SignAddress) signAddressHex, err := h.dasCore.Daf().NormalToHex(core.DasAddressNormal{ ChainType: common.ChainTypeWebauthn, AddressNormal: req.SignAddress, @@ -187,6 +187,24 @@ func (h *HttpHandle) doTransactionSend(req *ReqTransactionSend, apiResp *api_cod if err = h.dbDao.CreatePending(&pending); err != nil { log.Error("CreatePending err: ", err.Error(), toolib.JsonString(pending)) } + + if sic.Action == common.DasActionBidExpiredAccountAuction { + + auctionOrder := tables.TableAuctionOrder{ + Account: sic.Account, + AccountId: common.Bytes2Hex(common.GetAccountIdByAccount(sic.Account)), + Address: sic.Address, + BasicPrice: sic.AuctionInfo.BasicPrice, + PremiumPrice: sic.AuctionInfo.PremiumPrice, + BidTime: sic.AuctionInfo.BidTime, + ChainType: sic.ChainType, + Outpoint: pending.Outpoint, + } + auctionOrder.CreateOrderId() + if err = h.dbDao.CreateAuctionOrder(auctionOrder); err != nil { + log.Error("CreateAuctionOrder err: ", err.Error(), toolib.JsonString(auctionOrder)) + } + } } } diff --git a/http_server/http_server.go b/http_server/http_server.go index 39f44dcc..46eeb7d6 100644 --- a/http_server/http_server.go +++ b/http_server/http_server.go @@ -10,6 +10,7 @@ import ( "github.com/dotbitHQ/das-lib/http_api/logger" "github.com/dotbitHQ/das-lib/txbuilder" "github.com/gin-gonic/gin" + "github.com/nervosnetwork/ckb-sdk-go/types" "net/http" ) @@ -38,6 +39,7 @@ type HttpServerParams struct { DasCore *core.DasCore DasCache *dascache.DasCache TxBuilderBase *txbuilder.DasTxBuilderBase + ServerScript *types.Script MapReservedAccounts map[string]struct{} MapUnAvailableAccounts map[string]struct{} } @@ -58,6 +60,7 @@ func Initialize(p HttpServerParams) (*HttpServer, error) { TxBuilderBase: p.TxBuilderBase, MapReservedAccounts: p.MapReservedAccounts, MapUnAvailableAccounts: p.MapUnAvailableAccounts, + ServerScript: p.ServerScript, }), rc: p.Rc, } diff --git a/http_server/router.go b/http_server/router.go index 7ebd32e8..5ac12b44 100644 --- a/http_server/router.go +++ b/http_server/router.go @@ -60,6 +60,10 @@ func (h *HttpServer) initRouter() { v1.POST("/account/order/detail", api_code.DoMonitorLog(api_code.MethodOrderDetail), h.h.OrderDetail) v1.POST("/address/deposit", api_code.DoMonitorLog(api_code.MethodAddressDeposit), cacheHandleLong, h.h.AddressDeposit) v1.POST("/character/set/list", api_code.DoMonitorLog(api_code.MethodCharacterSetList), cacheHandleLong, h.h.CharacterSetList) + v1.POST("/account/auction/info", api_code.DoMonitorLog(api_code.MethodAuctionInfo), h.h.GetAccountAuctionInfo) + v1.POST("/account/auction/price", api_code.DoMonitorLog(api_code.MethodAuctionPrice), h.h.GetAccountAuctionPrice) + v1.POST("/account/auction/order-status", api_code.DoMonitorLog(api_code.MethodAuctionOrderStatus), h.h.GetAuctionOrderStatus) + v1.POST("/account/auction/pending-order", api_code.DoMonitorLog(api_code.MethodAuctionPendingOrder), cacheHandleLong, h.h.GetPendingAuctionOrder) // operate //v1.POST("/reverse/declare", api_code.DoMonitorLog(api_code.MethodReverseDeclare), h.h.ReverseDeclare) @@ -79,6 +83,7 @@ func (h *HttpServer) initRouter() { v1.POST("/account/order/pay/hash", api_code.DoMonitorLog(api_code.MethodOrderPayHash), h.h.OrderPayHash) v1.POST("/account/coupon/check", api_code.DoMonitorLog(api_code.MethodOrderCheckCoupon), cacheHandleShort, h.h.CheckCoupon) //v1.POST("/account/edit/script", api_code.DoMonitorLog(api_code.MethodEditScript), h.h.EditScript) + v1.POST("/account/auction/bid", api_code.DoMonitorLog(api_code.MethodAuctionBid), h.h.AccountAuctionBid) // node rpc v1.POST("/node/ckb/rpc", api_code.DoMonitorLog(api_code.MethodCkbRpc), h.h.CkbRpc) diff --git a/tables/t_account_info.go b/tables/t_account_info.go index 32ef79ab..d7f216db 100644 --- a/tables/t_account_info.go +++ b/tables/t_account_info.go @@ -63,6 +63,8 @@ const ( SearchStatusUnAvailableAccount SearchStatus = 13 SearchStatusSubAccountUnRegister SearchStatus = 14 SearchStatusOnCross SearchStatus = 15 + SearchStatusOnDutchAuction SearchStatus = 17 + SearchStatusAuctionRecycling SearchStatus = 18 ) func (t *TableAccountInfo) IsExpired() bool { diff --git a/tables/t_auction_order.go b/tables/t_auction_order.go new file mode 100644 index 00000000..1c47c879 --- /dev/null +++ b/tables/t_auction_order.go @@ -0,0 +1,45 @@ +package tables + +import ( + "github.com/dotbitHQ/das-lib/common" + "github.com/shopspring/decimal" + "time" +) + +const ( + TableNameAuctionOrder = "t_auction_order" +) + +type TableAuctionOrder struct { + Id uint64 `json:"id" gorm:"column:id;primaryKey;type:bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT ''"` + Account string `json:"account" gorm:"column:account;type:varchar(255) DEFAULT NULL"` + AccountId string `json:"account_id" gorm:"column:account_id;type:varchar(255) DEFAULT NULL"` + OrderId string `json:"order_id" gorm:"column:order_id;type:varchar(255) DEFAULT NULL"` + Address string `json:"address" gorm:"column:address;type:varchar(255) DEFAULT NULL"` + ChainType common.ChainType `json:"chain_type" gorm:"column:chain_type;index:k_chain_type_address;type:smallint(6) NOT NULL DEFAULT '0' COMMENT 'order chain type'"` + AlgorithmId common.DasAlgorithmId `json:"algorithm_id" gorm:"column:algorithm_id"` + SubAlgorithmId common.DasSubAlgorithmId `json:"sub_algorithm_id" gorm:"column:sub_algorithm_id"` + BasicPrice decimal.Decimal `json:"basic_price" gorm:"column:basic_price;type:decimal(60,0) NOT NULL DEFAULT '0' COMMENT ''"` + PremiumPrice decimal.Decimal `json:"premium_price" gorm:"column:premium_price;type:decimal(60,0) NOT NULL DEFAULT '0' COMMENT ''"` + Status int `json:"status" ` + BidTime int64 `json:"bid_time" gorm:"column:bid_time;type:bigint NOT NULL DEFAULT '0'"` + Outpoint string `json:"outpoint" gorm:"column:outpoint;type:varchar(255) DEFAULT NULL"` + CreatedAt time.Time `json:"created_at" gorm:"column:created_at;type:timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT ''"` + UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at;type:timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT ''"` +} + +func (t *TableAuctionOrder) TableName() string { + return TableNameAuctionOrder +} + +type BidStatus int + +const ( + BidStatusNoOne BidStatus = 0 //No one is bidding + BidStatusByOthers BidStatus = 1 //Being auctioned by others + BidStatusByMe BidStatus = 2 //Being auctioned by me +) + +func (t *TableAuctionOrder) CreateOrderId() { + t.OrderId = CreateOrderId(1, t.AccountId, common.DasActionBidExpiredAccountAuction, t.ChainType, t.Address, t.BidTime) +} diff --git a/tables/t_das_order_info.go b/tables/t_das_order_info.go index 124f379f..1020121d 100644 --- a/tables/t_das_order_info.go +++ b/tables/t_das_order_info.go @@ -98,6 +98,7 @@ const ( TokenIdErc20USDT PayTokenId = "eth_erc20_usdt" TokenIdTrc20USDT PayTokenId = "tron_trc20_usdt" TokenIdBep20USDT PayTokenId = "bsc_bep20_usdt" + ToKenIdDidPoint PayTokenId = "did_point" ) func (p PayTokenId) IsTokenIdCkbInternal() bool { diff --git a/tables/t_transaction_info.go b/tables/t_transaction_info.go index adb626ca..dc89c55c 100644 --- a/tables/t_transaction_info.go +++ b/tables/t_transaction_info.go @@ -69,6 +69,7 @@ const ( ActionCrossRefund TxAction = 28 ActionCollectSubAccountProfit TxAction = 29 ActionRenewSubAccount TxAction = 30 + ActionAuctionRefundCapacity TxAction = 31 DasActionTransferBalance = "transfer_balance" DasActionBalanceDeposit = "balance_deposit" @@ -145,6 +146,8 @@ func FormatTxAction(action string) TxAction { return ActionCollectSubAccountProfit case common.DasActionRenewSubAccount: return ActionRenewSubAccount + case common.DasActionBidExpiredAccountAuction: + return ActionAuctionRefundCapacity } return ActionUndefined } diff --git a/unipay/refund.go b/unipay/refund.go index 4f7715ca..5b180dd8 100644 --- a/unipay/refund.go +++ b/unipay/refund.go @@ -75,6 +75,9 @@ func (t *ToolUniPay) doRefund() error { PayHash: v.Hash, }) } + if len(req.RefundList) == 0 { + return nil + } _, err = RefundOrder(req) if err != nil {