包含 webapp(Next.js 用户端)、webapp-back(Go 后端)、 antdesign(管理后台)、landingpage(营销落地页)、 数据库 SQL 和配置文件。
126 lines
3.9 KiB
Go
126 lines
3.9 KiB
Go
package holders
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"time"
|
|
|
|
"github.com/gothinkster/golang-gin-realworld-example-app/common"
|
|
)
|
|
|
|
// Asset represents a product/asset in the database
|
|
type Asset struct {
|
|
ID int64 `gorm:"column:id"`
|
|
AssetCode string `gorm:"column:asset_code"`
|
|
Name string `gorm:"column:name"`
|
|
TokenRole string `gorm:"column:token_role"`
|
|
ChainID int `gorm:"column:chain_id"`
|
|
ContractAddress string `gorm:"column:contract_address"`
|
|
DeployBlock *uint64 `gorm:"column:deploy_block"`
|
|
}
|
|
|
|
|
|
const zeroAddress = "0x0000000000000000000000000000000000000000"
|
|
|
|
// LoadConfigFromDB loads contract addresses and deploy blocks from database based on chain ID
|
|
func LoadConfigFromDB(chainID int64) (Config, error) {
|
|
db := common.GetDB()
|
|
|
|
log.Printf("📚 [Scanner] 从数据库加载配置 - Chain ID: %d", chainID)
|
|
|
|
switch chainID {
|
|
case 97, 421614:
|
|
// supported
|
|
default:
|
|
return Config{}, fmt.Errorf("unsupported chain ID: %d", chainID)
|
|
}
|
|
|
|
// Load YT assets by token_role filtered by chain_id
|
|
var assets []Asset
|
|
if err := db.Table("assets").
|
|
Where("token_role = ? AND chain_id = ? AND is_active = ?", "yt_token", chainID, true).
|
|
Find(&assets).Error; err != nil {
|
|
return Config{}, fmt.Errorf("failed to load assets: %w", err)
|
|
}
|
|
|
|
ytVaults := make([]VaultConfig, 0, len(assets))
|
|
for _, asset := range assets {
|
|
if asset.ContractAddress == "" || asset.ContractAddress == zeroAddress {
|
|
log.Printf("⚠️ [Scanner] 跳过 %s (地址未配置)", asset.AssetCode)
|
|
continue
|
|
}
|
|
if asset.DeployBlock == nil || *asset.DeployBlock == 0 {
|
|
log.Printf("⚠️ [Scanner] 跳过 %s (deploy_block 未配置)", asset.AssetCode)
|
|
continue
|
|
}
|
|
|
|
ytVaults = append(ytVaults, VaultConfig{
|
|
Name: asset.AssetCode,
|
|
Address: asset.ContractAddress,
|
|
DeployBlock: *asset.DeployBlock,
|
|
})
|
|
log.Printf(" ✓ %s: %s (部署区块: %d)", asset.AssetCode, asset.ContractAddress, *asset.DeployBlock)
|
|
}
|
|
log.Printf("✅ [Scanner] 加载了 %d 个 YT Vault", len(ytVaults))
|
|
|
|
// Load YTLPToken address from system_contracts
|
|
var ytLPContract struct {
|
|
Address string `gorm:"column:address"`
|
|
DeployBlock *uint64 `gorm:"column:deploy_block"`
|
|
}
|
|
ytLPAddress := ""
|
|
var ytLPDeployBlock uint64
|
|
err := db.Table("system_contracts").
|
|
Where("name = ? AND chain_id = ? AND is_active = ?", "YTLPToken", chainID, 1).
|
|
Select("address, deploy_block").
|
|
First(&ytLPContract).Error
|
|
if err != nil {
|
|
log.Printf("⚠️ [Scanner] 未找到 YTLPToken 配置")
|
|
} else if ytLPContract.Address == "" || ytLPContract.Address == zeroAddress {
|
|
log.Printf("⚠️ [Scanner] 跳过 ytLP (地址未配置)")
|
|
} else if ytLPContract.DeployBlock == nil || *ytLPContract.DeployBlock == 0 {
|
|
log.Printf("⚠️ [Scanner] 跳过 ytLP (deploy_block 未配置)")
|
|
} else {
|
|
ytLPAddress = ytLPContract.Address
|
|
ytLPDeployBlock = *ytLPContract.DeployBlock
|
|
log.Printf("✅ [Scanner] ytLP: %s (部署区块: %d)", ytLPAddress, ytLPDeployBlock)
|
|
}
|
|
|
|
rpcURL := getRPCURLForChain(chainID)
|
|
|
|
config := Config{
|
|
ChainID: int(chainID),
|
|
RPCURL: rpcURL,
|
|
YTVaults: ytVaults,
|
|
YTLPAddress: ytLPAddress,
|
|
DeploymentBlocks: DeploymentBlocks{
|
|
YTLP: ytLPDeployBlock,
|
|
},
|
|
PollInterval: 30 * time.Second,
|
|
BatchSize: 9999,
|
|
}
|
|
|
|
log.Printf("📊 [Scanner] 配置加载完成: YT Vaults=%d, ytLP=%s, RPC=%s",
|
|
len(config.YTVaults), config.YTLPAddress, config.RPCURL)
|
|
|
|
return config, nil
|
|
}
|
|
|
|
// getRPCURLForChain returns the RPC URL for the given chain ID
|
|
func getRPCURLForChain(chainID int64) string {
|
|
switch chainID {
|
|
case 421614:
|
|
return "https://api.zan.top/node/v1/arb/sepolia/baf84c429d284bb5b676cb8c9ca21c07"
|
|
case 97:
|
|
return "https://api.zan.top/node/v1/bsc/testnet/baf84c429d284bb5b676cb8c9ca21c07"
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
// TableName sets the table name for GORM
|
|
func (Asset) TableName() string {
|
|
return "assets"
|
|
}
|
|
|