feat: support withdraw

This commit is contained in:
jiedo
2024-05-16 18:02:57 +08:00
parent a3fd171407
commit dc31351590
6 changed files with 253 additions and 15 deletions

View File

@@ -10,4 +10,5 @@ var (
GlobalNetParams = &chaincfg.MainNetParams
TICKS_ENABLED = ""
ENABLE_SELF_MINT_HEIGHT uint32 = 837090
ENABLE_SWAP_WITHDRAW_HEIGHT uint32 = 847090 // fixme: dummy height
)

View File

@@ -37,21 +37,23 @@ const (
// swap history N
BRC20_HISTORY_MODULE_TYPE_N_INSCRIBE_MODULE uint8 = 6
BRC20_HISTORY_MODULE_TYPE_N_INSCRIBE_WITHDRAW uint8 = 7
BRC20_HISTORY_MODULE_TYPE_N_WITHDRAW_FROM uint8 = 8
BRC20_HISTORY_MODULE_TYPE_N_WITHDRAW_TO uint8 = 9
BRC20_HISTORY_MODULE_TYPE_N_WITHDRAW uint8 = 8
BRC20_HISTORY_MODULE_TYPE_N_WITHDRAW_FROM uint8 = 9
BRC20_HISTORY_MODULE_TYPE_N_WITHDRAW_TO uint8 = 10
BRC20_HISTORY_SWAP_TYPE_N_INSCRIBE_APPROVE uint8 = 10
BRC20_HISTORY_SWAP_TYPE_N_APPROVE uint8 = 11
BRC20_HISTORY_SWAP_TYPE_N_INSCRIBE_APPROVE uint8 = 11
BRC20_HISTORY_SWAP_TYPE_N_APPROVE uint8 = 12
BRC20_HISTORY_SWAP_TYPE_N_INSCRIBE_CONDITIONAL_APPROVE uint8 = 12
BRC20_HISTORY_SWAP_TYPE_N_CONDITIONAL_APPROVE uint8 = 13
BRC20_HISTORY_SWAP_TYPE_N_INSCRIBE_CONDITIONAL_APPROVE uint8 = 13
BRC20_HISTORY_SWAP_TYPE_N_CONDITIONAL_APPROVE uint8 = 14
BRC20_HISTORY_SWAP_TYPE_N_APPROVE_FROM uint8 = 14
BRC20_HISTORY_SWAP_TYPE_N_APPROVE_TO uint8 = 15
BRC20_HISTORY_SWAP_TYPE_N_APPROVE_FROM uint8 = 15
BRC20_HISTORY_SWAP_TYPE_N_APPROVE_TO uint8 = 16
BRC20_HISTORY_SWAP_TYPE_N_INSCRIBE_COMMIT uint8 = 16
BRC20_HISTORY_SWAP_TYPE_N_COMMIT uint8 = 17
BRC20_HISTORY_SWAP_TYPE_N_INSCRIBE_COMMIT uint8 = 17
BRC20_HISTORY_SWAP_TYPE_N_COMMIT uint8 = 18
)
// module op

View File

@@ -83,6 +83,19 @@ func (g *BRC20ModuleIndexer) ProcessUpdateLatestBRC20Loop(brc20Datas, brc20Datas
break
}
// module withdraw
if withdrawInfo, isInvalid := g.GetWithdrawInfoByKey(data.CreateIdxKey); withdrawInfo != nil {
g.InscriptionsWithdrawRemoveMap[data.CreateIdxKey] = data.Height
g.Durty = true
if err := g.ProcessWithdraw(data, withdrawInfo, isInvalid); err != nil {
log.Printf("process withdraw move failed: %s", err)
} else {
g.Durty = true
}
break
}
// module commit
if commitFrom, isInvalid := g.GetCommitInfoByKey(data.CreateIdxKey); commitFrom != nil {
g.InscriptionsCommitRemoveMap[data.CreateIdxKey] = data.Height

View File

@@ -4,13 +4,159 @@ import (
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"log"
"strings"
"github.com/unisat-wallet/libbrc20-indexer/conf"
"github.com/unisat-wallet/libbrc20-indexer/constant"
"github.com/unisat-wallet/libbrc20-indexer/decimal"
"github.com/unisat-wallet/libbrc20-indexer/model"
"github.com/unisat-wallet/libbrc20-indexer/utils"
)
func (g *BRC20ModuleIndexer) GetWithdrawInfoByKey(createIdxKey string) (
withdrawInfo *model.InscriptionBRC20SwapInfo, isInvalid bool) {
var ok bool
// withdraw
withdrawInfo, ok = g.InscriptionsValidWithdrawMap[createIdxKey]
if !ok {
withdrawInfo, ok = g.InscriptionsInvalidWithdrawMap[createIdxKey]
if !ok {
withdrawInfo = nil
}
isInvalid = true
}
return withdrawInfo, isInvalid
}
func (g *BRC20ModuleIndexer) ProcessWithdraw(data *model.InscriptionBRC20Data, withdrawInfo *model.InscriptionBRC20SwapInfo, isInvalid bool) error {
// ticker
uniqueLowerTicker := strings.ToLower(withdrawInfo.Tick)
tokenInfo, ok := g.InscriptionsTickerInfoMap[uniqueLowerTicker]
if !ok {
log.Printf("ProcessWithdraw send withdraw, but ticker invalid. txid: %s",
utils.HashString([]byte(data.TxId)),
)
return errors.New("transfer, invalid ticker")
}
moduleInfo, ok := g.ModulesInfoMap[withdrawInfo.Module]
if !ok {
log.Printf("ProcessBRC20Withdraw send withdraw, but ticker invalid. txid: %s",
hex.EncodeToString(utils.ReverseBytes([]byte(data.TxId))),
)
return errors.New("withdraw, module invalid")
}
// global history fixme
// if g.EnableHistory {
// historyObj := model.NewBRC20History(constant.BRC20_HISTORY_MODULE_TYPE_N_WITHDRAW, !isInvalid, true, withdrawInfo, nil, data)
// history := g.UpdateHistoryHeightAndGetHistoryIndex(historyObj)
// tokenInfo.History = append(tokenInfo.History, history)
// // tokenInfo.HistoryWithdraw = append(tokenInfo.HistoryTransfer, history)
// if !isInvalid {
// // all history
// g.AllHistory = append(g.AllHistory, history)
// }
// }
// from
// get user's tokens to update
fromUserTokens, ok := moduleInfo.UsersTokenBalanceDataMap[string(withdrawInfo.Data.PkScript)]
if !ok {
log.Printf("ProcessBRC20Withdraw send from user missing. height: %d, txidx: %d",
data.Height,
data.TxIdx,
)
return errors.New("withdraw, send from user missing")
}
// get tokenBalance to update
fromTokenBalance, ok := fromUserTokens[uniqueLowerTicker]
if !ok {
log.Printf("ProcessBRC20Withdraw send from ticker missing. height: %d, txidx: %d",
data.Height,
data.TxIdx,
)
return errors.New("withdraw, send from ticker missing")
}
// Cross-check whether the withdraw-inscription exists.
if _, ok := fromTokenBalance.ValidWithdrawMap[data.CreateIdxKey]; !ok {
log.Printf("ProcessBRC20Withdraw send from withdraw missing(dup withdraw?). height: %d, txidx: %d",
data.Height,
data.TxIdx,
)
return errors.New("withdraw, send from withdraw missing(dup)")
}
// to address
receiverPkScript := string(data.PkScript)
if data.Satoshi == 0 {
receiverPkScript = string(withdrawInfo.Data.PkScript)
data.PkScript = receiverPkScript
}
// global history
historyData := &model.BRC20SwapHistoryWithdrawData{
Tick: withdrawInfo.Tick,
Amount: withdrawInfo.Amount.String(),
}
history := model.NewBRC20ModuleHistory(true, constant.BRC20_HISTORY_MODULE_TYPE_N_WITHDRAW, withdrawInfo.Data, data, historyData, !isInvalid)
moduleInfo.History = append(moduleInfo.History, history)
if isInvalid {
// from invalid history
fromHistory := model.NewBRC20ModuleHistory(true, constant.BRC20_HISTORY_MODULE_TYPE_N_WITHDRAW_FROM, withdrawInfo.Data, data, nil, false)
fromTokenBalance.History = append(fromTokenBalance.History, fromHistory)
return nil
}
// set from
fromTokenBalance.UpdateHeight = data.Height
fromTokenBalance.WithdrawableBalance = fromTokenBalance.WithdrawableBalance.Sub(withdrawInfo.Amount)
delete(fromTokenBalance.ValidWithdrawMap, data.CreateIdxKey)
fromHistory := model.NewBRC20ModuleHistory(true, constant.BRC20_HISTORY_MODULE_TYPE_N_WITHDRAW_FROM, withdrawInfo.Data, data, nil, true)
fromTokenBalance.History = append(fromTokenBalance.History, fromHistory)
// to
tokenBalance := g.GetUserTokenBalance(withdrawInfo.Tick, receiverPkScript)
// set to
tokenBalance.UpdateHeight = data.Height
if data.BlockTime > 0 {
tokenBalance.AvailableBalanceSafe = tokenBalance.AvailableBalanceSafe.Add(withdrawInfo.Amount)
}
tokenBalance.AvailableBalance = tokenBalance.AvailableBalance.Add(withdrawInfo.Amount)
// burn
if len(receiverPkScript) == 1 && []byte(receiverPkScript)[0] == 0x6a {
tokenInfo.Deploy.Burned = tokenInfo.Deploy.Burned.Add(withdrawInfo.Amount)
}
// fixme: add user module history
// if g.EnableHistory {
// historyObj := model.NewBRC20History(constant.BRC20_HISTORY_TYPE_N_RECEIVE, true, true, withdrawInfo, tokenBalance, data)
// toHistory := g.UpdateHistoryHeightAndGetHistoryIndex(historyObj)
// tokenBalance.History = append(tokenBalance.History, toHistory)
// tokenBalance.HistoryReceive = append(tokenBalance.HistoryReceive, toHistory)
// userHistoryTo := g.GetBRC20HistoryByUser(receiverPkScript)
// userHistoryTo.History = append(userHistoryTo.History, toHistory)
// }
// toHistory := model.NewBRC20ModuleHistory(true, constant.BRC20_HISTORY_MODULE_TYPE_N_WITHDRAW_TO, withdrawInfo.Data, data, nil, true)
// tokenBalance.History = append(tokenBalance.History, toHistory)
////////////////////////////////////////////////////////////////
// withdraw to a module, is NOT deposit
return nil
}
func (g *BRC20ModuleIndexer) ProcessInscribeWithdraw(data *model.InscriptionBRC20Data) error {
var body model.InscriptionBRC20ModuleWithdrawContent
if err := json.Unmarshal(data.ContentBody, &body); err != nil {
@@ -25,10 +171,79 @@ func (g *BRC20ModuleIndexer) ProcessInscribeWithdraw(data *model.InscriptionBRC2
return errors.New("module id invalid")
}
if _, ok := g.ModulesInfoMap[body.Module]; !ok { // invalid module
moduleInfo, ok := g.ModulesInfoMap[body.Module]
if !ok { // invalid module
return errors.New("module invalid")
}
// black module
return nil
if data.Height < conf.ENABLE_SWAP_WITHDRAW_HEIGHT {
return errors.New("module withdraw disable")
}
if len(body.Tick) != 4 && len(body.Tick) != 5 {
return errors.New("tick invalid")
}
uniqueLowerTicker := strings.ToLower(body.Tick)
tokenInfo, ok := g.InscriptionsTickerInfoMap[uniqueLowerTicker]
if !ok {
return errors.New("tick not exist")
}
tinfo := tokenInfo.Deploy
amt, err := decimal.NewDecimalFromString(body.Amount, int(tinfo.Decimal))
if err != nil {
return errors.New(fmt.Sprintf("withdraw amount invalid: %s", body.Amount))
}
if amt.Sign() <= 0 || amt.Cmp(tinfo.Max) > 0 {
return errors.New("amount out of range")
}
balanceWithdraw := decimal.NewDecimalCopy(amt)
// Unify ticker case
body.Tick = tokenInfo.Ticker
// Set up withdraw data for subsequent use.
withdrawInfo := &model.InscriptionBRC20SwapInfo{
Data: data,
}
withdrawInfo.Module = body.Module
withdrawInfo.Tick = tokenInfo.Ticker
withdrawInfo.Amount = balanceWithdraw
// global history
historyData := &model.BRC20SwapHistoryWithdrawData{
Tick: withdrawInfo.Tick,
Amount: withdrawInfo.Amount.String(),
}
history := model.NewBRC20ModuleHistory(false, constant.BRC20_HISTORY_MODULE_TYPE_N_INSCRIBE_WITHDRAW, data, data, historyData, true)
moduleInfo.History = append(moduleInfo.History, history)
// Check if the module balance is sufficient to withdraw
moduleTokenBalance := moduleInfo.GetUserTokenBalance(withdrawInfo.Tick, data.PkScript)
// available > amt
if moduleTokenBalance.AvailableBalance.Cmp(balanceWithdraw) < 0 { // invalid
history.Valid = false
g.InscriptionsInvalidWithdrawMap[data.CreateIdxKey] = withdrawInfo
} else {
history.Valid = true
// The available balance here needs to be directly deducted and transferred to WithdrawableBalance.
moduleTokenBalance.AvailableBalanceSafe = moduleTokenBalance.AvailableBalanceSafe.Sub(balanceWithdraw)
moduleTokenBalance.AvailableBalance = moduleTokenBalance.AvailableBalance.Sub(balanceWithdraw)
moduleTokenBalance.WithdrawableBalance = moduleTokenBalance.WithdrawableBalance.Add(balanceWithdraw)
// Update personal withdraw lookup table ValidWithdrawMap
if moduleTokenBalance.ValidWithdrawMap == nil {
moduleTokenBalance.ValidWithdrawMap = make(map[string]*model.InscriptionBRC20Data, 1)
}
moduleTokenBalance.ValidWithdrawMap[data.CreateIdxKey] = data
moduleTokenBalance.UpdateHeight = data.Height
// Update global withdraw lookup table
g.InscriptionsValidWithdrawMap[data.CreateIdxKey] = withdrawInfo
// g.InscriptionsValidBRC20DataMap[data.CreateIdxKey] = withdrawInfo.Data // fixme
}
return nil
}

View File

@@ -50,13 +50,19 @@ func NewBRC20ModuleHistory(isTransfer bool, historyType uint8, from, to *Inscrip
return history
}
// history
// withdraw history
type BRC20SwapHistoryWithdrawData struct {
Tick string `json:"tick"`
Amount string `json:"amount"` // current amt
}
// approve history
type BRC20SwapHistoryApproveData struct {
Tick string `json:"tick"`
Amount string `json:"amount"` // current amt
}
// history
// cond approve history
type BRC20SwapHistoryCondApproveData struct {
Tick string `json:"tick"`
Amount string `json:"amount"` // current amt

View File

@@ -178,6 +178,7 @@ type BRC20TokenInfo struct {
HistoryMint []uint32
HistoryInscribeTransfer []uint32
HistoryTransfer []uint32
HistoryWithdraw []uint32 // fixme
}
type InscriptionBRC20TransferInfo struct {