package arbitrage import ( "github.com/ethereum/go-ethereum/arbitrage/api" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/shopspring/decimal" "math/big" ) // FundMovements 池子资金动向 type FundMovements struct { LpHash string Fee int64 InIndex uint64 InToken string InReserve *big.Int OutIndex uint64 OutToken string OutReserve *big.Int } // Path 交易路径 type Path struct { InTokenHash string OutTokenHash string LoanLp string LoanIndex int64 LoanTokenHash string Level int64 Profit decimal.Decimal Sim Simulation FmList []FundMovements } func GenerateMapping(lpList []api.V2Lp) map[string][]api.V2Lp { mapping := make(map[string][]api.V2Lp) for _, lp := range lpList { mapping[lp.Token0] = append(mapping[lp.Token0], lp) mapping[lp.Token1] = append(mapping[lp.Token1], lp) } return mapping } func LpListFilter(lpList []api.V2Lp, unfilteredMapping map[string][]api.V2Lp) []api.V2Lp { var filteredLpList []api.V2Lp for _, lp := range lpList { isToken0NoCorrelation := len(unfilteredMapping[lp.Token0]) <= 1 isToken1NoCorrelation := len(unfilteredMapping[lp.Token1]) <= 1 // 有任意一个token是孤儿token(与其他lp没有关系),则该lp是无效lp if isToken0NoCorrelation || isToken1NoCorrelation { continue } filteredLpList = append(filteredLpList, lp) } return filteredLpList } func GeneratePathListCore(finalMapping map[string][]api.V2Lp, level int64, maxLevel int64, path Path) []Path { var pathList []Path // 1. path闭环判断 firstFm := path.FmList[0] lastFm := path.FmList[len(path.FmList)-1] if firstFm.InToken == lastFm.OutToken { path.Level = level - 1 path.InTokenHash = firstFm.InToken path.OutTokenHash = lastFm.OutToken pathList = append(pathList, path) return pathList } // 2. level溢出判断 if maxLevel < level { return pathList } // 3. 不闭环或不溢出,继续递归 relatedLpList := finalMapping[lastFm.OutToken] for _, lp := range relatedLpList { // 3.1 lp去重(用过的lp不再用) isLpUsed := false for _, fm := range path.FmList { if lp.Hash == fm.LpHash { isLpUsed = true } } if isLpUsed { continue } newPath := path // 3.2 构造FundMovements,必须要 if lastFm.OutToken == lp.Token0 { newPath.FmList = append(path.FmList, FundMovements{ LpHash: lp.Hash, Fee: lp.Fee, InIndex: 0, InToken: lp.Token0, InReserve: lp.R0, OutIndex: 1, OutToken: lp.Token1, OutReserve: lp.R1, }) } else if lastFm.OutToken == lp.Token1 { newPath.FmList = append(path.FmList, FundMovements{ LpHash: lp.Hash, Fee: lp.Fee, InIndex: 1, InToken: lp.Token1, InReserve: lp.R1, OutIndex: 0, OutToken: lp.Token0, OutReserve: lp.R0, }) } else { log.Error("path生成有错误,请重新debug查询。", "lp hash", lp.Hash) continue } newPathList := GeneratePathListCore(finalMapping, level+1, maxLevel, newPath) pathList = append(pathList, newPathList...) } return pathList } func GeneratePathList(finalLpList []api.V2Lp, finalMapping map[string][]api.V2Lp, maxLevel int64) []Path { var pathList []Path NowLevel := int64(1) for _, lp := range finalLpList { pathA := Path{ Level: NowLevel, LoanTokenHash: lp.Token1, LoanLp: lp.Hash, LoanIndex: 1, FmList: []FundMovements{ { LpHash: lp.Hash, Fee: lp.Fee, InIndex: 0, InToken: lp.Token0, OutReserve: lp.R0, OutIndex: 1, OutToken: lp.Token1, InReserve: lp.R1, }, }, } pathListA := GeneratePathListCore(finalMapping, NowLevel+1, maxLevel, pathA) pathB := Path{ Level: NowLevel, LoanTokenHash: lp.Token0, LoanLp: lp.Hash, LoanIndex: 0, FmList: []FundMovements{ { LpHash: lp.Hash, Fee: lp.Fee, InIndex: 1, InToken: lp.Token1, InReserve: lp.R1, OutIndex: 0, OutToken: lp.Token0, OutReserve: lp.R0, }, }, } pathListB := GeneratePathListCore(finalMapping, NowLevel+1, maxLevel, pathB) pathList = append(pathList, pathListA...) pathList = append(pathList, pathListB...) } return pathList } func ParseLpToFinalLp(lpList []api.V2Lp) []api.V2Lp { // 1. 生成token->relatedLpList的映射结构,token为tokenHash,lpList为与该token相关的所有池子。 // // 生成这个映射结构有两个目的: // a. 基础过滤无效lp(主要是过滤与其他lp无关的lp) // b. 之后可以用于生成path unfilteredMapping := GenerateMapping(lpList) // 2. 剔除len(lpList)<=1的映射;第二层意思:如果某个token只有一个lp,那么,该lp不属于有效池子;用于生成有效lp finalLpList := LpListFilter(lpList, unfilteredMapping) return finalLpList } func ParseLpListToPathList(finalLpList []api.V2Lp, maxLevel int64) []Path { // 1. 重新生成映射结构,形成最终的映射结构(token->relatedLpList) finalMapping := GenerateMapping(finalLpList) // 2. 根据资金流概念生成path pathList := GeneratePathList(finalLpList, finalMapping, maxLevel) return pathList } func ParseLpListToLpHashList(lpList []api.V2Lp) []string { var rst []string for _, lp := range lpList { rst = append(rst, lp.Hash) } return rst } func (h *HistoryArbitrage) GetLpList() []api.V2Lp { rst, err := h.javaApi.V2LpListByChainIdAndPaginate(api.V2LpListByChainIdAndPaginateRequest{ ChainId: params.CoreChainConfig.ChainID.Uint64(), PageNumber: 1, PageSize: MaxLpLength, AuthObj: api.GenerateAuth(), }) if err != nil { HistoryError("Get lp list error.", err.Error()) return nil } if !rst.State { HistoryError("Get lp list java error.", err.Error()) return nil } return rst.Data } func PutR0R1ToLpList(lpList *[]api.V2Lp, balanceMapping map[string][2]*big.Int) { for i := range *lpList { lp := &((*lpList)[i]) reserves := balanceMapping[lp.Hash] lp.R0 = reserves[0] lp.R1 = reserves[1] } }