diff --git a/abis/ethereumabi/usdc.go b/abis/ethereumabi/usdc.go new file mode 100644 index 0000000..5bb0674 --- /dev/null +++ b/abis/ethereumabi/usdc.go @@ -0,0 +1,9 @@ +package ethereumabi + +import "go-admin/abis" + +var USDCErc20 = abis.TokenABI{ + Address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + TestAddress: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238", + Decimals: 6, +} diff --git a/abis/token.go b/abis/token.go new file mode 100644 index 0000000..70a3f22 --- /dev/null +++ b/abis/token.go @@ -0,0 +1,10 @@ +package abis + +type TokenABI struct { + //token 合约地址 + Address string + //测试网地址 + TestAddress string + //token 最小单位位数 + Decimals int +} diff --git a/app/admin/apis/wm_transfer.go b/app/admin/apis/wm_transfer.go new file mode 100644 index 0000000..fb18ac1 --- /dev/null +++ b/app/admin/apis/wm_transfer.go @@ -0,0 +1,193 @@ +package apis + +import ( + "fmt" + + "github.com/gin-gonic/gin" + "github.com/go-admin-team/go-admin-core/sdk/api" + "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user" + _ "github.com/go-admin-team/go-admin-core/sdk/pkg/response" + + "go-admin/app/admin/models" + "go-admin/app/admin/service" + "go-admin/app/admin/service/dto" + "go-admin/common/actions" +) + +type WmTransfer struct { + api.Api +} + +// GetPage 获取批量转账任务列表 +// @Summary 获取批量转账任务列表 +// @Description 获取批量转账任务列表 +// @Tags 批量转账任务 +// @Param type query int64 false "类型 0-百分比 1-实际金额" +// @Param transferType query int64 false "转账类型 0-批量转出 1-批量汇总" +// @Param pageSize query int false "页条数" +// @Param pageIndex query int false "页码" +// @Success 200 {object} response.Response{data=response.Page{list=[]models.WmTransfer}} "{"code": 200, "data": [...]}" +// @Router /api/v1/wm-transfer [get] +// @Security Bearer +func (e WmTransfer) GetPage(c *gin.Context) { + req := dto.WmTransferGetPageReq{} + s := service.WmTransfer{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, err.Error()) + return + } + + p := actions.GetPermissionFromContext(c) + list := make([]models.WmTransfer, 0) + var count int64 + + err = s.GetPage(&req, p, &list, &count) + if err != nil { + e.Error(500, err, fmt.Sprintf("获取批量转账任务失败,\r\n失败信息 %s", err.Error())) + return + } + + e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功") +} + +// Get 获取批量转账任务 +// @Summary 获取批量转账任务 +// @Description 获取批量转账任务 +// @Tags 批量转账任务 +// @Param id path int false "id" +// @Success 200 {object} response.Response{data=models.WmTransfer} "{"code": 200, "data": [...]}" +// @Router /api/v1/wm-transfer/{id} [get] +// @Security Bearer +func (e WmTransfer) Get(c *gin.Context) { + req := dto.WmTransferGetReq{} + s := service.WmTransfer{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, err.Error()) + return + } + var object models.WmTransfer + + p := actions.GetPermissionFromContext(c) + err = s.Get(&req, p, &object) + if err != nil { + e.Error(500, err, fmt.Sprintf("获取批量转账任务失败,\r\n失败信息 %s", err.Error())) + return + } + + e.OK( object, "查询成功") +} + +// Insert 创建批量转账任务 +// @Summary 创建批量转账任务 +// @Description 创建批量转账任务 +// @Tags 批量转账任务 +// @Accept application/json +// @Product application/json +// @Param data body dto.WmTransferInsertReq true "data" +// @Success 200 {object} response.Response "{"code": 200, "message": "添加成功"}" +// @Router /api/v1/wm-transfer [post] +// @Security Bearer +func (e WmTransfer) Insert(c *gin.Context) { + req := dto.WmTransferInsertReq{} + s := service.WmTransfer{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, err.Error()) + return + } + // 设置创建人 + req.SetCreateBy(user.GetUserId(c)) + + err = s.Insert(&req) + if err != nil { + e.Error(500, err, fmt.Sprintf("创建批量转账任务失败,\r\n失败信息 %s", err.Error())) + return + } + + e.OK(req.GetId(), "创建成功") +} + +// Update 修改批量转账任务 +// @Summary 修改批量转账任务 +// @Description 修改批量转账任务 +// @Tags 批量转账任务 +// @Accept application/json +// @Product application/json +// @Param id path int true "id" +// @Param data body dto.WmTransferUpdateReq true "body" +// @Success 200 {object} response.Response "{"code": 200, "message": "修改成功"}" +// @Router /api/v1/wm-transfer/{id} [put] +// @Security Bearer +func (e WmTransfer) Update(c *gin.Context) { + req := dto.WmTransferUpdateReq{} + s := service.WmTransfer{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, err.Error()) + return + } + req.SetUpdateBy(user.GetUserId(c)) + p := actions.GetPermissionFromContext(c) + + err = s.Update(&req, p) + if err != nil { + e.Error(500, err, fmt.Sprintf("修改批量转账任务失败,\r\n失败信息 %s", err.Error())) + return + } + e.OK( req.GetId(), "修改成功") +} + +// Delete 删除批量转账任务 +// @Summary 删除批量转账任务 +// @Description 删除批量转账任务 +// @Tags 批量转账任务 +// @Param data body dto.WmTransferDeleteReq true "body" +// @Success 200 {object} response.Response "{"code": 200, "message": "删除成功"}" +// @Router /api/v1/wm-transfer [delete] +// @Security Bearer +func (e WmTransfer) Delete(c *gin.Context) { + s := service.WmTransfer{} + req := dto.WmTransferDeleteReq{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, err.Error()) + return + } + + // req.SetUpdateBy(user.GetUserId(c)) + p := actions.GetPermissionFromContext(c) + + err = s.Remove(&req, p) + if err != nil { + e.Error(500, err, fmt.Sprintf("删除批量转账任务失败,\r\n失败信息 %s", err.Error())) + return + } + e.OK( req.GetId(), "删除成功") +} diff --git a/app/admin/apis/wm_transfer_item.go b/app/admin/apis/wm_transfer_item.go new file mode 100644 index 0000000..d917177 --- /dev/null +++ b/app/admin/apis/wm_transfer_item.go @@ -0,0 +1,191 @@ +package apis + +import ( + "fmt" + + "github.com/gin-gonic/gin" + "github.com/go-admin-team/go-admin-core/sdk/api" + "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user" + _ "github.com/go-admin-team/go-admin-core/sdk/pkg/response" + + "go-admin/app/admin/models" + "go-admin/app/admin/service" + "go-admin/app/admin/service/dto" + "go-admin/common/actions" +) + +type WmTransferItem struct { + api.Api +} + +// GetPage 获取批量转账明细列表 +// @Summary 获取批量转账明细列表 +// @Description 获取批量转账明细列表 +// @Tags 批量转账明细 +// @Param pageSize query int false "页条数" +// @Param pageIndex query int false "页码" +// @Success 200 {object} response.Response{data=response.Page{list=[]models.WmTransferItem}} "{"code": 200, "data": [...]}" +// @Router /api/v1/wm-transfer-item [get] +// @Security Bearer +func (e WmTransferItem) GetPage(c *gin.Context) { + req := dto.WmTransferItemGetPageReq{} + s := service.WmTransferItem{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, err.Error()) + return + } + + p := actions.GetPermissionFromContext(c) + list := make([]models.WmTransferItem, 0) + var count int64 + + err = s.GetPage(&req, p, &list, &count) + if err != nil { + e.Error(500, err, fmt.Sprintf("获取批量转账明细失败,\r\n失败信息 %s", err.Error())) + return + } + + e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功") +} + +// Get 获取批量转账明细 +// @Summary 获取批量转账明细 +// @Description 获取批量转账明细 +// @Tags 批量转账明细 +// @Param id path int false "id" +// @Success 200 {object} response.Response{data=models.WmTransferItem} "{"code": 200, "data": [...]}" +// @Router /api/v1/wm-transfer-item/{id} [get] +// @Security Bearer +func (e WmTransferItem) Get(c *gin.Context) { + req := dto.WmTransferItemGetReq{} + s := service.WmTransferItem{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, err.Error()) + return + } + var object models.WmTransferItem + + p := actions.GetPermissionFromContext(c) + err = s.Get(&req, p, &object) + if err != nil { + e.Error(500, err, fmt.Sprintf("获取批量转账明细失败,\r\n失败信息 %s", err.Error())) + return + } + + e.OK( object, "查询成功") +} + +// Insert 创建批量转账明细 +// @Summary 创建批量转账明细 +// @Description 创建批量转账明细 +// @Tags 批量转账明细 +// @Accept application/json +// @Product application/json +// @Param data body dto.WmTransferItemInsertReq true "data" +// @Success 200 {object} response.Response "{"code": 200, "message": "添加成功"}" +// @Router /api/v1/wm-transfer-item [post] +// @Security Bearer +func (e WmTransferItem) Insert(c *gin.Context) { + req := dto.WmTransferItemInsertReq{} + s := service.WmTransferItem{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, err.Error()) + return + } + // 设置创建人 + req.SetCreateBy(user.GetUserId(c)) + + err = s.Insert(&req) + if err != nil { + e.Error(500, err, fmt.Sprintf("创建批量转账明细失败,\r\n失败信息 %s", err.Error())) + return + } + + e.OK(req.GetId(), "创建成功") +} + +// Update 修改批量转账明细 +// @Summary 修改批量转账明细 +// @Description 修改批量转账明细 +// @Tags 批量转账明细 +// @Accept application/json +// @Product application/json +// @Param id path int true "id" +// @Param data body dto.WmTransferItemUpdateReq true "body" +// @Success 200 {object} response.Response "{"code": 200, "message": "修改成功"}" +// @Router /api/v1/wm-transfer-item/{id} [put] +// @Security Bearer +func (e WmTransferItem) Update(c *gin.Context) { + req := dto.WmTransferItemUpdateReq{} + s := service.WmTransferItem{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, err.Error()) + return + } + req.SetUpdateBy(user.GetUserId(c)) + p := actions.GetPermissionFromContext(c) + + err = s.Update(&req, p) + if err != nil { + e.Error(500, err, fmt.Sprintf("修改批量转账明细失败,\r\n失败信息 %s", err.Error())) + return + } + e.OK( req.GetId(), "修改成功") +} + +// Delete 删除批量转账明细 +// @Summary 删除批量转账明细 +// @Description 删除批量转账明细 +// @Tags 批量转账明细 +// @Param data body dto.WmTransferItemDeleteReq true "body" +// @Success 200 {object} response.Response "{"code": 200, "message": "删除成功"}" +// @Router /api/v1/wm-transfer-item [delete] +// @Security Bearer +func (e WmTransferItem) Delete(c *gin.Context) { + s := service.WmTransferItem{} + req := dto.WmTransferItemDeleteReq{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, err.Error()) + return + } + + // req.SetUpdateBy(user.GetUserId(c)) + p := actions.GetPermissionFromContext(c) + + err = s.Remove(&req, p) + if err != nil { + e.Error(500, err, fmt.Sprintf("删除批量转账明细失败,\r\n失败信息 %s", err.Error())) + return + } + e.OK( req.GetId(), "删除成功") +} diff --git a/app/admin/apis/wm_wallet_info.go b/app/admin/apis/wm_wallet_info.go new file mode 100644 index 0000000..24e5a91 --- /dev/null +++ b/app/admin/apis/wm_wallet_info.go @@ -0,0 +1,154 @@ +package apis + +import ( + "fmt" + "strings" + + "github.com/gin-gonic/gin" + "github.com/go-admin-team/go-admin-core/sdk/api" + "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user" + _ "github.com/go-admin-team/go-admin-core/sdk/pkg/response" + + "go-admin/app/admin/models" + "go-admin/app/admin/service" + "go-admin/app/admin/service/dto" + "go-admin/common/actions" +) + +type WmWalletInfo struct { + api.Api +} + +// GetPage 获取钱包信息列表 +// @Summary 获取钱包信息列表 +// @Description 获取钱包信息列表 +// @Tags 钱包信息 +// @Param privateKey query string false "钱包私钥" +// @Param pageSize query int false "页条数" +// @Param pageIndex query int false "页码" +// @Success 200 {object} response.Response{data=response.Page{list=[]models.WmWalletInfo}} "{"code": 200, "data": [...]}" +// @Router /api/v1/wm-wallet-info [get] +// @Security Bearer +func (e WmWalletInfo) GetPage(c *gin.Context) { + req := dto.WmWalletInfoGetPageReq{} + s := service.WmWalletInfo{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, err.Error()) + return + } + + p := actions.GetPermissionFromContext(c) + list := make([]models.WmWalletInfo, 0) + var count int64 + + err = s.GetPage(&req, p, &list, &count) + if err != nil { + e.Error(500, err, fmt.Sprintf("获取钱包信息失败,\r\n失败信息 %s", err.Error())) + return + } + + e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功") +} + +// Get 获取钱包信息 +// @Summary 获取钱包信息 +// @Description 获取钱包信息 +// @Tags 钱包信息 +// @Param id path int false "id" +// @Success 200 {object} response.Response{data=models.WmWalletInfo} "{"code": 200, "data": [...]}" +// @Router /api/v1/wm-wallet-info/{id} [get] +// @Security Bearer +func (e WmWalletInfo) Get(c *gin.Context) { + req := dto.WmWalletInfoGetReq{} + s := service.WmWalletInfo{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, err.Error()) + return + } + var object models.WmWalletInfo + + p := actions.GetPermissionFromContext(c) + err = s.Get(&req, p, &object) + if err != nil { + e.Error(500, err, fmt.Sprintf("获取钱包信息失败,\r\n失败信息 %s", err.Error())) + return + } + + e.OK(object, "查询成功") +} + +// Insert 创建钱包信息 +// @Summary 创建钱包信息 +// @Description 创建钱包信息 +// @Tags 钱包信息 +// @Accept application/json +// @Product application/json +// @Param data body dto.WmWalletInfoInsertReq true "data" +// @Success 200 {object} response.Response "{"code": 200, "message": "添加成功"}" +// @Router /api/v1/wm-wallet-info [post] +// @Security Bearer +func (e WmWalletInfo) Insert(c *gin.Context) { + req := dto.WmWalletInfoBatchInsertReq{} + s := service.WmWalletInfo{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, err.Error()) + return + } + // 设置创建人 + req.SetCreateBy(user.GetUserId(c)) + + errs := s.Insert(&req) + + e.OK(strings.Join(errs, "
"), "创建成功") +} + +// Delete 删除钱包信息 +// @Summary 删除钱包信息 +// @Description 删除钱包信息 +// @Tags 钱包信息 +// @Param data body dto.WmWalletInfoDeleteReq true "body" +// @Success 200 {object} response.Response "{"code": 200, "message": "删除成功"}" +// @Router /api/v1/wm-wallet-info [delete] +// @Security Bearer +func (e WmWalletInfo) Delete(c *gin.Context) { + s := service.WmWalletInfo{} + req := dto.WmWalletInfoDeleteReq{} + err := e.MakeContext(c). + MakeOrm(). + Bind(&req). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, err.Error()) + return + } + + // req.SetUpdateBy(user.GetUserId(c)) + p := actions.GetPermissionFromContext(c) + + err = s.Remove(&req, p) + if err != nil { + e.Error(500, err, fmt.Sprintf("删除钱包信息失败,\r\n失败信息 %s", err.Error())) + return + } + e.OK(req.GetId(), "删除成功") +} diff --git a/app/admin/models/wm_transfer.go b/app/admin/models/wm_transfer.go new file mode 100644 index 0000000..6c6a863 --- /dev/null +++ b/app/admin/models/wm_transfer.go @@ -0,0 +1,31 @@ +package models + +import ( + + "go-admin/common/models" + +) + +type WmTransfer struct { + models.Model + + Type int64 `json:"type" gorm:"type:tinyint;comment:类型 0-百分比 1-实际金额"` + TransferType int64 `json:"transferType" gorm:"type:tinyint;comment:转账类型 0-批量转出 1-批量汇总"` + Status int64 `json:"status" gorm:"type:tinyint;comment:状态"` + Remark string `json:"remark" gorm:"type:varchar(255);comment:备注信息"` + models.ModelTime + models.ControlBy +} + +func (WmTransfer) TableName() string { + return "wm_transfer" +} + +func (e *WmTransfer) Generate() models.ActiveRecord { + o := *e + return &o +} + +func (e *WmTransfer) GetId() interface{} { + return e.Id +} \ No newline at end of file diff --git a/app/admin/models/wm_transfer_item.go b/app/admin/models/wm_transfer_item.go new file mode 100644 index 0000000..315e7d0 --- /dev/null +++ b/app/admin/models/wm_transfer_item.go @@ -0,0 +1,34 @@ +package models + +import ( + + "go-admin/common/models" + +) + +type WmTransferItem struct { + models.Model + + TokenAddress string `json:"tokenAddress" gorm:"type:varchar(50);comment:代币地址"` + FromAddress string `json:"fromAddress" gorm:"type:varchar(50);comment:来源地址"` + ToAddress string `json:"toAddress" gorm:"type:varchar(50);comment:目标地址"` + Amount string `json:"amount" gorm:"type:decimal(18,8);comment:代币数量"` + Type string `json:"type" gorm:"type:tinyint;comment:类型 0-主账号百分比 1-实际数量"` + TypeValue string `json:"typeValue" gorm:"type:decimal(18,8);comment:操作类型值"` + PrivateKey string `json:"privateKey" gorm:"type:varchar(255);comment:私钥"` + models.ModelTime + models.ControlBy +} + +func (WmTransferItem) TableName() string { + return "wm_transfer_item" +} + +func (e *WmTransferItem) Generate() models.ActiveRecord { + o := *e + return &o +} + +func (e *WmTransferItem) GetId() interface{} { + return e.Id +} \ No newline at end of file diff --git a/app/admin/models/wm_wallet_info.go b/app/admin/models/wm_wallet_info.go new file mode 100644 index 0000000..5041a68 --- /dev/null +++ b/app/admin/models/wm_wallet_info.go @@ -0,0 +1,30 @@ +package models + +import ( + "go-admin/common/models" + + "github.com/shopspring/decimal" +) + +type WmWalletInfo struct { + models.Model + + PrivateKey string `json:"privateKey" gorm:"type:varchar(50);comment:钱包私钥"` + Address string `json:"address" gorm:"type:varchar(50);comment:钱包地址"` + UsdcAmount decimal.Decimal `json:"usdcAmount" gorm:"type:decimal(18,8);comment:USDC余额"` + models.ModelTime + models.ControlBy +} + +func (WmWalletInfo) TableName() string { + return "wm_wallet_info" +} + +func (e *WmWalletInfo) Generate() models.ActiveRecord { + o := *e + return &o +} + +func (e *WmWalletInfo) GetId() interface{} { + return e.Id +} diff --git a/app/admin/router/wm_transfer.go b/app/admin/router/wm_transfer.go new file mode 100644 index 0000000..a0a773b --- /dev/null +++ b/app/admin/router/wm_transfer.go @@ -0,0 +1,27 @@ +package router + +import ( + "github.com/gin-gonic/gin" + jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth" + + "go-admin/app/admin/apis" + "go-admin/common/middleware" + "go-admin/common/actions" +) + +func init() { + routerCheckRole = append(routerCheckRole, registerWmTransferRouter) +} + +// registerWmTransferRouter +func registerWmTransferRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) { + api := apis.WmTransfer{} + r := v1.Group("/wm-transfer").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) + { + r.GET("", actions.PermissionAction(), api.GetPage) + r.GET("/:id", actions.PermissionAction(), api.Get) + r.POST("", api.Insert) + r.PUT("/:id", actions.PermissionAction(), api.Update) + r.DELETE("", api.Delete) + } +} \ No newline at end of file diff --git a/app/admin/router/wm_transfer_item.go b/app/admin/router/wm_transfer_item.go new file mode 100644 index 0000000..c0399b1 --- /dev/null +++ b/app/admin/router/wm_transfer_item.go @@ -0,0 +1,27 @@ +package router + +import ( + "github.com/gin-gonic/gin" + jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth" + + "go-admin/app/admin/apis" + "go-admin/common/middleware" + "go-admin/common/actions" +) + +func init() { + routerCheckRole = append(routerCheckRole, registerWmTransferItemRouter) +} + +// registerWmTransferItemRouter +func registerWmTransferItemRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) { + api := apis.WmTransferItem{} + r := v1.Group("/wm-transfer-item").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) + { + r.GET("", actions.PermissionAction(), api.GetPage) + r.GET("/:id", actions.PermissionAction(), api.Get) + r.POST("", api.Insert) + r.PUT("/:id", actions.PermissionAction(), api.Update) + r.DELETE("", api.Delete) + } +} \ No newline at end of file diff --git a/app/admin/router/wm_wallet_info.go b/app/admin/router/wm_wallet_info.go new file mode 100644 index 0000000..e900fc7 --- /dev/null +++ b/app/admin/router/wm_wallet_info.go @@ -0,0 +1,27 @@ +package router + +import ( + "github.com/gin-gonic/gin" + jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth" + + "go-admin/app/admin/apis" + "go-admin/common/actions" + "go-admin/common/middleware" +) + +func init() { + routerCheckRole = append(routerCheckRole, registerWmWalletInfoRouter) +} + +// registerWmWalletInfoRouter +func registerWmWalletInfoRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) { + api := apis.WmWalletInfo{} + r := v1.Group("/wm-wallet-info").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) + { + r.GET("", actions.PermissionAction(), api.GetPage) + r.GET("/:id", actions.PermissionAction(), api.Get) + r.POST("", api.Insert) + // r.PUT("/:id", actions.PermissionAction(), api.Update) + r.DELETE("", api.Delete) + } +} diff --git a/app/admin/service/dto/wm_transfer.go b/app/admin/service/dto/wm_transfer.go new file mode 100644 index 0000000..2723396 --- /dev/null +++ b/app/admin/service/dto/wm_transfer.go @@ -0,0 +1,98 @@ +package dto + +import ( + + "go-admin/app/admin/models" + "go-admin/common/dto" + common "go-admin/common/models" +) + +type WmTransferGetPageReq struct { + dto.Pagination `search:"-"` + Type int64 `form:"type" search:"type:exact;column:type;table:wm_transfer" comment:"类型 0-百分比 1-实际金额"` + TransferType int64 `form:"transferType" search:"type:exact;column:transfer_type;table:wm_transfer" comment:"转账类型 0-批量转出 1-批量汇总"` + WmTransferOrder +} + +type WmTransferOrder struct { + Id string `form:"idOrder" search:"type:order;column:id;table:wm_transfer"` + Type string `form:"typeOrder" search:"type:order;column:type;table:wm_transfer"` + TransferType string `form:"transferTypeOrder" search:"type:order;column:transfer_type;table:wm_transfer"` + Status string `form:"statusOrder" search:"type:order;column:status;table:wm_transfer"` + Remark string `form:"remarkOrder" search:"type:order;column:remark;table:wm_transfer"` + CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:wm_transfer"` + UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:wm_transfer"` + CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:wm_transfer"` + UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:wm_transfer"` + DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:wm_transfer"` + +} + +func (m *WmTransferGetPageReq) GetNeedSearch() interface{} { + return *m +} + +type WmTransferInsertReq struct { + Id int `json:"-" comment:"主键id"` // 主键id + Type int64 `json:"type" comment:"类型 0-百分比 1-实际金额"` + TransferType int64 `json:"transferType" comment:"转账类型 0-批量转出 1-批量汇总"` + Status int64 `json:"status" comment:"状态"` + Remark string `json:"remark" comment:"备注信息"` + common.ControlBy +} + +func (s *WmTransferInsertReq) Generate(model *models.WmTransfer) { + if s.Id == 0 { + model.Model = common.Model{ Id: s.Id } + } + model.Type = s.Type + model.TransferType = s.TransferType + model.Status = s.Status + model.Remark = s.Remark + model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的 +} + +func (s *WmTransferInsertReq) GetId() interface{} { + return s.Id +} + +type WmTransferUpdateReq struct { + Id int `uri:"id" comment:"主键id"` // 主键id + Type int64 `json:"type" comment:"类型 0-百分比 1-实际金额"` + TransferType int64 `json:"transferType" comment:"转账类型 0-批量转出 1-批量汇总"` + Status int64 `json:"status" comment:"状态"` + Remark string `json:"remark" comment:"备注信息"` + common.ControlBy +} + +func (s *WmTransferUpdateReq) Generate(model *models.WmTransfer) { + if s.Id == 0 { + model.Model = common.Model{ Id: s.Id } + } + model.Type = s.Type + model.TransferType = s.TransferType + model.Status = s.Status + model.Remark = s.Remark + model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的 +} + +func (s *WmTransferUpdateReq) GetId() interface{} { + return s.Id +} + +// WmTransferGetReq 功能获取请求参数 +type WmTransferGetReq struct { + Id int `uri:"id"` +} +func (s *WmTransferGetReq) GetId() interface{} { + return s.Id +} + +// WmTransferDeleteReq 功能删除请求参数 +type WmTransferDeleteReq struct { + Ids []int `json:"ids"` +} + +func (s *WmTransferDeleteReq) GetId() interface{} { + return s.Ids +} diff --git a/app/admin/service/dto/wm_transfer_item.go b/app/admin/service/dto/wm_transfer_item.go new file mode 100644 index 0000000..402b69c --- /dev/null +++ b/app/admin/service/dto/wm_transfer_item.go @@ -0,0 +1,111 @@ +package dto + +import ( + + "go-admin/app/admin/models" + "go-admin/common/dto" + common "go-admin/common/models" +) + +type WmTransferItemGetPageReq struct { + dto.Pagination `search:"-"` + WmTransferItemOrder +} + +type WmTransferItemOrder struct { + Id string `form:"idOrder" search:"type:order;column:id;table:wm_transfer_item"` + TokenAddress string `form:"tokenAddressOrder" search:"type:order;column:token_address;table:wm_transfer_item"` + FromAddress string `form:"fromAddressOrder" search:"type:order;column:from_address;table:wm_transfer_item"` + ToAddress string `form:"toAddressOrder" search:"type:order;column:to_address;table:wm_transfer_item"` + Amount string `form:"amountOrder" search:"type:order;column:amount;table:wm_transfer_item"` + Type string `form:"typeOrder" search:"type:order;column:type;table:wm_transfer_item"` + TypeValue string `form:"typeValueOrder" search:"type:order;column:type_value;table:wm_transfer_item"` + PrivateKey string `form:"privateKeyOrder" search:"type:order;column:private_key;table:wm_transfer_item"` + CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:wm_transfer_item"` + UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:wm_transfer_item"` + CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:wm_transfer_item"` + UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:wm_transfer_item"` + DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:wm_transfer_item"` + +} + +func (m *WmTransferItemGetPageReq) GetNeedSearch() interface{} { + return *m +} + +type WmTransferItemInsertReq struct { + Id int `json:"-" comment:"主键id"` // 主键id + TokenAddress string `json:"tokenAddress" comment:"代币地址"` + FromAddress string `json:"fromAddress" comment:"来源地址"` + ToAddress string `json:"toAddress" comment:"目标地址"` + Amount string `json:"amount" comment:"代币数量"` + Type string `json:"type" comment:"类型 0-主账号百分比 1-实际数量"` + TypeValue string `json:"typeValue" comment:"操作类型值"` + PrivateKey string `json:"privateKey" comment:"私钥"` + common.ControlBy +} + +func (s *WmTransferItemInsertReq) Generate(model *models.WmTransferItem) { + if s.Id == 0 { + model.Model = common.Model{ Id: s.Id } + } + model.TokenAddress = s.TokenAddress + model.FromAddress = s.FromAddress + model.ToAddress = s.ToAddress + model.Amount = s.Amount + model.Type = s.Type + model.TypeValue = s.TypeValue + model.PrivateKey = s.PrivateKey + model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的 +} + +func (s *WmTransferItemInsertReq) GetId() interface{} { + return s.Id +} + +type WmTransferItemUpdateReq struct { + Id int `uri:"id" comment:"主键id"` // 主键id + TokenAddress string `json:"tokenAddress" comment:"代币地址"` + FromAddress string `json:"fromAddress" comment:"来源地址"` + ToAddress string `json:"toAddress" comment:"目标地址"` + Amount string `json:"amount" comment:"代币数量"` + Type string `json:"type" comment:"类型 0-主账号百分比 1-实际数量"` + TypeValue string `json:"typeValue" comment:"操作类型值"` + PrivateKey string `json:"privateKey" comment:"私钥"` + common.ControlBy +} + +func (s *WmTransferItemUpdateReq) Generate(model *models.WmTransferItem) { + if s.Id == 0 { + model.Model = common.Model{ Id: s.Id } + } + model.TokenAddress = s.TokenAddress + model.FromAddress = s.FromAddress + model.ToAddress = s.ToAddress + model.Amount = s.Amount + model.Type = s.Type + model.TypeValue = s.TypeValue + model.PrivateKey = s.PrivateKey + model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的 +} + +func (s *WmTransferItemUpdateReq) GetId() interface{} { + return s.Id +} + +// WmTransferItemGetReq 功能获取请求参数 +type WmTransferItemGetReq struct { + Id int `uri:"id"` +} +func (s *WmTransferItemGetReq) GetId() interface{} { + return s.Id +} + +// WmTransferItemDeleteReq 功能删除请求参数 +type WmTransferItemDeleteReq struct { + Ids []int `json:"ids"` +} + +func (s *WmTransferItemDeleteReq) GetId() interface{} { + return s.Ids +} diff --git a/app/admin/service/dto/wm_wallet_info.go b/app/admin/service/dto/wm_wallet_info.go new file mode 100644 index 0000000..ae21b21 --- /dev/null +++ b/app/admin/service/dto/wm_wallet_info.go @@ -0,0 +1,105 @@ +package dto + +import ( + "errors" + "go-admin/app/admin/models" + "go-admin/common/dto" + common "go-admin/common/models" + "strings" +) + +type WmWalletInfoGetPageReq struct { + dto.Pagination `search:"-"` + PrivateKey string `form:"privateKey" search:"type:exact;column:private_key;table:wm_wallet_info" comment:"钱包私钥"` + WmWalletInfoOrder +} + +type WmWalletInfoOrder struct { + Id string `form:"idOrder" search:"type:order;column:id;table:wm_wallet_info"` + PrivateKey string `form:"privateKeyOrder" search:"type:order;column:private_key;table:wm_wallet_info"` + Address string `form:"addressOrder" search:"type:order;column:address;table:wm_wallet_info"` + CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:wm_wallet_info"` + UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:wm_wallet_info"` + CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:wm_wallet_info"` + UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:wm_wallet_info"` + DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:wm_wallet_info"` +} + +func (m *WmWalletInfoGetPageReq) GetNeedSearch() interface{} { + return *m +} + +type WmWalletInfoBatchInsertReq struct { + Keys string `json:"keys"` + common.ControlBy +} + +func (s *WmWalletInfoBatchInsertReq) Valid() error { + s.Keys = strings.ReplaceAll(s.Keys, " ", "") + s.Keys = strings.ReplaceAll(s.Keys, ",", "\n") + s.Keys = strings.ReplaceAll(s.Keys, ",", "\n") + + if s.Keys == "" { + return errors.New("请至少输入一条数据") + } + + return nil +} + +type WmWalletInfoInsertReq struct { + Id int `json:"-" comment:"主键Id"` // 主键Id + PrivateKey string `json:"privateKey" comment:"钱包私钥"` + Address string `json:"address" comment:"钱包地址"` + common.ControlBy +} + +func (s *WmWalletInfoInsertReq) Generate(model *models.WmWalletInfo) { + if s.Id == 0 { + model.Model = common.Model{Id: s.Id} + } + model.PrivateKey = s.PrivateKey + model.Address = s.Address + model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的 +} + +func (s *WmWalletInfoInsertReq) GetId() interface{} { + return s.Id +} + +type WmWalletInfoUpdateReq struct { + Id int `uri:"id" comment:"主键Id"` // 主键Id + PrivateKey string `json:"privateKey" comment:"钱包私钥"` + Address string `json:"address" comment:"钱包地址"` + common.ControlBy +} + +func (s *WmWalletInfoUpdateReq) Generate(model *models.WmWalletInfo) { + if s.Id == 0 { + model.Model = common.Model{Id: s.Id} + } + model.PrivateKey = s.PrivateKey + model.Address = s.Address + model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的 +} + +func (s *WmWalletInfoUpdateReq) GetId() interface{} { + return s.Id +} + +// WmWalletInfoGetReq 功能获取请求参数 +type WmWalletInfoGetReq struct { + Id int `uri:"id"` +} + +func (s *WmWalletInfoGetReq) GetId() interface{} { + return s.Id +} + +// WmWalletInfoDeleteReq 功能删除请求参数 +type WmWalletInfoDeleteReq struct { + Ids []int `json:"ids"` +} + +func (s *WmWalletInfoDeleteReq) GetId() interface{} { + return s.Ids +} diff --git a/app/admin/service/wm_transfer.go b/app/admin/service/wm_transfer.go new file mode 100644 index 0000000..1db97d7 --- /dev/null +++ b/app/admin/service/wm_transfer.go @@ -0,0 +1,109 @@ +package service + +import ( + "errors" + + "github.com/go-admin-team/go-admin-core/sdk/service" + "gorm.io/gorm" + + "go-admin/app/admin/models" + "go-admin/app/admin/service/dto" + "go-admin/common/actions" + cDto "go-admin/common/dto" +) + +type WmTransfer struct { + service.Service +} + +// GetPage 获取WmTransfer列表 +func (e *WmTransfer) GetPage(c *dto.WmTransferGetPageReq, p *actions.DataPermission, list *[]models.WmTransfer, count *int64) error { + var err error + var data models.WmTransfer + + err = e.Orm.Model(&data). + Scopes( + cDto.MakeCondition(c.GetNeedSearch()), + cDto.Paginate(c.GetPageSize(), c.GetPageIndex()), + actions.Permission(data.TableName(), p), + ). + Find(list).Limit(-1).Offset(-1). + Count(count).Error + if err != nil { + e.Log.Errorf("WmTransferService GetPage error:%s \r\n", err) + return err + } + return nil +} + +// Get 获取WmTransfer对象 +func (e *WmTransfer) Get(d *dto.WmTransferGetReq, p *actions.DataPermission, model *models.WmTransfer) error { + var data models.WmTransfer + + err := e.Orm.Model(&data). + Scopes( + actions.Permission(data.TableName(), p), + ). + First(model, d.GetId()).Error + if err != nil && errors.Is(err, gorm.ErrRecordNotFound) { + err = errors.New("查看对象不存在或无权查看") + e.Log.Errorf("Service GetWmTransfer error:%s \r\n", err) + return err + } + if err != nil { + e.Log.Errorf("db error:%s", err) + return err + } + return nil +} + +// Insert 创建WmTransfer对象 +func (e *WmTransfer) Insert(c *dto.WmTransferInsertReq) error { + var err error + var data models.WmTransfer + c.Generate(&data) + err = e.Orm.Create(&data).Error + if err != nil { + e.Log.Errorf("WmTransferService Insert error:%s \r\n", err) + return err + } + return nil +} + +// Update 修改WmTransfer对象 +func (e *WmTransfer) Update(c *dto.WmTransferUpdateReq, p *actions.DataPermission) error { + var err error + var data = models.WmTransfer{} + e.Orm.Scopes( + actions.Permission(data.TableName(), p), + ).First(&data, c.GetId()) + c.Generate(&data) + + db := e.Orm.Save(&data) + if err = db.Error; err != nil { + e.Log.Errorf("WmTransferService Save error:%s \r\n", err) + return err + } + if db.RowsAffected == 0 { + return errors.New("无权更新该数据") + } + return nil +} + +// Remove 删除WmTransfer +func (e *WmTransfer) Remove(d *dto.WmTransferDeleteReq, p *actions.DataPermission) error { + var data models.WmTransfer + + db := e.Orm.Model(&data). + Scopes( + actions.Permission(data.TableName(), p), + ).Delete(&data, d.GetId()) + if err := db.Error; err != nil { + e.Log.Errorf("Service RemoveWmTransfer error:%s \r\n", err) + return err + } + if db.RowsAffected == 0 { + return errors.New("无权删除该数据") + } + return nil +} diff --git a/app/admin/service/wm_transfer_item.go b/app/admin/service/wm_transfer_item.go new file mode 100644 index 0000000..2ee9540 --- /dev/null +++ b/app/admin/service/wm_transfer_item.go @@ -0,0 +1,109 @@ +package service + +import ( + "errors" + + "github.com/go-admin-team/go-admin-core/sdk/service" + "gorm.io/gorm" + + "go-admin/app/admin/models" + "go-admin/app/admin/service/dto" + "go-admin/common/actions" + cDto "go-admin/common/dto" +) + +type WmTransferItem struct { + service.Service +} + +// GetPage 获取WmTransferItem列表 +func (e *WmTransferItem) GetPage(c *dto.WmTransferItemGetPageReq, p *actions.DataPermission, list *[]models.WmTransferItem, count *int64) error { + var err error + var data models.WmTransferItem + + err = e.Orm.Model(&data). + Scopes( + cDto.MakeCondition(c.GetNeedSearch()), + cDto.Paginate(c.GetPageSize(), c.GetPageIndex()), + actions.Permission(data.TableName(), p), + ). + Find(list).Limit(-1).Offset(-1). + Count(count).Error + if err != nil { + e.Log.Errorf("WmTransferItemService GetPage error:%s \r\n", err) + return err + } + return nil +} + +// Get 获取WmTransferItem对象 +func (e *WmTransferItem) Get(d *dto.WmTransferItemGetReq, p *actions.DataPermission, model *models.WmTransferItem) error { + var data models.WmTransferItem + + err := e.Orm.Model(&data). + Scopes( + actions.Permission(data.TableName(), p), + ). + First(model, d.GetId()).Error + if err != nil && errors.Is(err, gorm.ErrRecordNotFound) { + err = errors.New("查看对象不存在或无权查看") + e.Log.Errorf("Service GetWmTransferItem error:%s \r\n", err) + return err + } + if err != nil { + e.Log.Errorf("db error:%s", err) + return err + } + return nil +} + +// Insert 创建WmTransferItem对象 +func (e *WmTransferItem) Insert(c *dto.WmTransferItemInsertReq) error { + var err error + var data models.WmTransferItem + c.Generate(&data) + err = e.Orm.Create(&data).Error + if err != nil { + e.Log.Errorf("WmTransferItemService Insert error:%s \r\n", err) + return err + } + return nil +} + +// Update 修改WmTransferItem对象 +func (e *WmTransferItem) Update(c *dto.WmTransferItemUpdateReq, p *actions.DataPermission) error { + var err error + var data = models.WmTransferItem{} + e.Orm.Scopes( + actions.Permission(data.TableName(), p), + ).First(&data, c.GetId()) + c.Generate(&data) + + db := e.Orm.Save(&data) + if err = db.Error; err != nil { + e.Log.Errorf("WmTransferItemService Save error:%s \r\n", err) + return err + } + if db.RowsAffected == 0 { + return errors.New("无权更新该数据") + } + return nil +} + +// Remove 删除WmTransferItem +func (e *WmTransferItem) Remove(d *dto.WmTransferItemDeleteReq, p *actions.DataPermission) error { + var data models.WmTransferItem + + db := e.Orm.Model(&data). + Scopes( + actions.Permission(data.TableName(), p), + ).Delete(&data, d.GetId()) + if err := db.Error; err != nil { + e.Log.Errorf("Service RemoveWmTransferItem error:%s \r\n", err) + return err + } + if db.RowsAffected == 0 { + return errors.New("无权删除该数据") + } + return nil +} diff --git a/app/admin/service/wm_wallet_info.go b/app/admin/service/wm_wallet_info.go new file mode 100644 index 0000000..7fa2574 --- /dev/null +++ b/app/admin/service/wm_wallet_info.go @@ -0,0 +1,174 @@ +package service + +import ( + "errors" + "strings" + + "github.com/go-admin-team/go-admin-core/sdk/service" + "gorm.io/gorm" + + "go-admin/abis/ethereumabi" + "go-admin/app/admin/models" + "go-admin/app/admin/service/dto" + "go-admin/common/actions" + cDto "go-admin/common/dto" + "go-admin/config" + "go-admin/utils/aeshelper" + "go-admin/utils/ethbalanceofhelper" + "go-admin/utils/ethtransferhelper" + "go-admin/utils/stringhelper" +) + +type WmWalletInfo struct { + service.Service +} + +// GetPage 获取WmWalletInfo列表 +func (e *WmWalletInfo) GetPage(c *dto.WmWalletInfoGetPageReq, p *actions.DataPermission, list *[]models.WmWalletInfo, count *int64) error { + var err error + var data models.WmWalletInfo + + err = e.Orm.Model(&data). + Scopes( + cDto.MakeCondition(c.GetNeedSearch()), + cDto.Paginate(c.GetPageSize(), c.GetPageIndex()), + actions.Permission(data.TableName(), p), + ). + Find(list).Limit(-1).Offset(-1). + Count(count).Error + if err != nil { + e.Log.Errorf("WmWalletInfoService GetPage error:%s \r\n", err) + return err + } + + for i := 0; i < len(*list); i++ { + (*list)[i].PrivateKey = stringhelper.DesensitizeWalletAddress(aeshelper.AesEcbDecrypt((*list)[i].PrivateKey)) + } + + return nil +} + +// Get 获取WmWalletInfo对象 +func (e *WmWalletInfo) Get(d *dto.WmWalletInfoGetReq, p *actions.DataPermission, model *models.WmWalletInfo) error { + var data models.WmWalletInfo + + err := e.Orm.Model(&data). + Scopes( + actions.Permission(data.TableName(), p), + ). + First(model, d.GetId()).Error + if err != nil && errors.Is(err, gorm.ErrRecordNotFound) { + err = errors.New("查看对象不存在或无权查看") + e.Log.Errorf("Service GetWmWalletInfo error:%s \r\n", err) + return err + } + if err != nil { + e.Log.Errorf("db error:%s", err) + return err + } + return nil +} + +// Insert 创建WmWalletInfo对象 +func (e *WmWalletInfo) Insert(c *dto.WmWalletInfoBatchInsertReq) []string { + errs := []string{} + var count int64 + keys := strings.Split(c.Keys, "\n") + + for _, key := range keys { + if key == "" { + continue + } + + var data models.WmWalletInfo + data.PrivateKey = aeshelper.AesEcbEncrypt(key) + _, address, _ := ethtransferhelper.GetAddressFromPrivateKey(key) + data.Address = address.String() + + if err := e.Orm.Model(data).Where("private_key =?", data.PrivateKey).Count(&count).Error; err != nil { + e.Log.Errorf("db error:%s", err) + } + + if count == 0 { + if err := e.Orm.Create(&data).Error; err != nil { + e.Log.Errorf("db error:%s", err) + errs = append(errs, key) + } + } + } + + return errs +} + +// Update 修改WmWalletInfo对象 +func (e *WmWalletInfo) Update(c *dto.WmWalletInfoUpdateReq, p *actions.DataPermission) error { + var err error + var data = models.WmWalletInfo{} + e.Orm.Scopes( + actions.Permission(data.TableName(), p), + ).First(&data, c.GetId()) + c.Generate(&data) + + db := e.Orm.Save(&data) + if err = db.Error; err != nil { + e.Log.Errorf("WmWalletInfoService Save error:%s \r\n", err) + return err + } + if db.RowsAffected == 0 { + return errors.New("无权更新该数据") + } + return nil +} + +// Remove 删除WmWalletInfo +func (e *WmWalletInfo) Remove(d *dto.WmWalletInfoDeleteReq, p *actions.DataPermission) error { + var data models.WmWalletInfo + + db := e.Orm.Model(&data). + Scopes( + actions.Permission(data.TableName(), p), + ).Delete(&data, d.GetId()) + if err := db.Error; err != nil { + e.Log.Errorf("Service RemoveWmWalletInfo error:%s \r\n", err) + return err + } + if db.RowsAffected == 0 { + return errors.New("无权删除该数据") + } + return nil +} + +// ScheduledTask 定时任务 +func (e *WmWalletInfo) ScheduledTask() error { + var datas []models.WmWalletInfo + + if err := e.Orm.Model(models.WmWalletInfo{}).Find(&datas).Error; err != nil { + e.Log.Errorf("db error:%s", err) + return err + } + + client, err := ethbalanceofhelper.EthClientWithProxy(config.ExtConfig.ApiEndpoint, config.ExtConfig.ProxyUrl) + + if err != nil { + e.Log.Errorf("ethclient error:%s", err) + return err + } + + for i := range datas { + amount, err := ethbalanceofhelper.GetERC20Balance(client, ethereumabi.USDCErc20, datas[i].Address) + + if err != nil { + e.Log.Errorf("GetERC20Balance error:%s", err) + continue + } + + datas[i].UsdcAmount = amount + + if err := e.Orm.Model(&datas[i]).Update("usdc_amount", amount).Error; err != nil { + e.Log.Errorf("db error:%s", err) + continue + } + } + + return nil +} diff --git a/app/jobs/transfer_job.go b/app/jobs/transfer_job.go new file mode 100644 index 0000000..69b5cf1 --- /dev/null +++ b/app/jobs/transfer_job.go @@ -0,0 +1,31 @@ +package jobs + +import ( + "go-admin/app/admin/service" + + "github.com/go-admin-team/go-admin-core/logger" + "github.com/go-admin-team/go-admin-core/sdk" + "gorm.io/gorm" +) + +type TransferJob struct{} + +// 定期转账 +func (t TransferJob) Exec(arg interface{}) error { + walletService := service.WmWalletInfo{} + walletService.Orm = getDefaultDb() + walletService.Log = logger.NewHelper(logger.DefaultLogger) + + return walletService.ScheduledTask() +} + +func getDefaultDb() *gorm.DB { + dbs := sdk.Runtime.GetDb() + var db *gorm.DB + + for _, item := range dbs { + db = item + break + } + return db +} diff --git a/app/jobs/transfer_job_test.go b/app/jobs/transfer_job_test.go new file mode 100644 index 0000000..c54548d --- /dev/null +++ b/app/jobs/transfer_job_test.go @@ -0,0 +1,23 @@ +package jobs + +import ( + "go-admin/config" + "testing" + + "github.com/go-admin-team/go-admin-core/sdk" + "gorm.io/driver/mysql" + "gorm.io/gorm" +) + +func TestTransferJob(t *testing.T) { + dsn := "root:123456@tcp(127.0.0.1:3306)/eth_transfer?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms" + db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{}) + sdk.Runtime.SetDb("default", db) + + config.ExtConfig.ApiEndpoint = "https://stylish-cool-fire.ethereum-sepolia.quiknode.pro/17572db4c091accfa5dc6faa0c60a805e5173459" + config.ExtConfig.ProxyUrl = "http://127.0.0.1:7890" + + job := TransferJob{} + + job.Exec(nil) +} diff --git a/config/extend.go b/config/extend.go index bd06e03..3fa110d 100644 --- a/config/extend.go +++ b/config/extend.go @@ -3,12 +3,17 @@ package config var ExtConfig Extend // Extend 扩展配置 -// extend: -// demo: -// name: demo-name +// +// extend: +// demo: +// name: demo-name +// // 使用方法: config.ExtConfig......即可!! type Extend struct { - AMap AMap // 这里配置对应配置文件的结构即可 + AMap AMap // 这里配置对应配置文件的结构即可 + + ApiEndpoint string //rpc api地址 + ProxyUrl string //代理地址 } type AMap struct { diff --git a/config/settings.yml b/config/settings.yml index 8e7ed87..e3ad81b 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -5,7 +5,7 @@ settings: # 服务器ip,默认使用 0.0.0.0 host: 0.0.0.0 # 服务名称 - name: testApp + name: ethTransfer # 端口号 port: 8000 # 服务端口号 readtimeout: 1 @@ -23,7 +23,7 @@ settings: enableddb: false jwt: # token 密钥,生产环境时及的修改 - secret: go-admin + secret: eth-transfer-admin # token 过期时间 单位:秒 timeout: 3600 database: @@ -31,7 +31,7 @@ settings: # sqlserver: sqlserver://用户名:密码@地址?database=数据库名 driver: mysql # 数据库连接字符串 mysql 缺省信息 charset=utf8&parseTime=True&loc=Local&timeout=1000ms - source: user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8&parseTime=True&loc=Local&timeout=1000ms + source: root:123456@tcp(127.0.0.1:3306)/eth_transfer?charset=utf8&parseTime=True&loc=Local&timeout=1000ms # databases: # 'locaohost:8000': # driver: mysql @@ -42,17 +42,19 @@ settings: # - user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8&parseTime=True&loc=Local&timeout=1000ms gen: # 代码生成读取的数据库名称 - dbname: dbname + dbname: eth_transfer # 代码生成是使用前端代码存放位置,需要指定到src文件夹,相对路径 frontpath: ../go-admin-ui/src extend: # 扩展项使用说明 demo: name: data + + apiEndpoint: https://stylish-cool-fire.ethereum-sepolia.quiknode.pro/17572db4c091accfa5dc6faa0c60a805e5173459 cache: -# redis: -# addr: 127.0.0.1:6379 -# password: xxxxxx -# db: 2 + redis: + addr: 127.0.0.1:6379 + password: '' + db: 15 # key存在即可 memory: '' queue: diff --git a/go.mod b/go.mod index 2a36339..7b84b0f 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module go-admin -go 1.18 +go 1.23.0 + +toolchain go1.24.2 require ( github.com/alibaba/sentinel-golang v1.0.4 @@ -9,6 +11,7 @@ require ( github.com/bitly/go-simplejson v0.5.0 github.com/bytedance/go-tagexpr/v2 v2.7.12 github.com/casbin/casbin/v2 v2.51.2 + github.com/ethereum/go-ethereum v1.15.11 github.com/gin-gonic/gin v1.9.1 github.com/go-admin-team/go-admin-core v1.4.1-0.20220809101213-21187928f7d9 github.com/go-admin-team/go-admin-core/sdk v1.4.1-0.20220809101213-21187928f7d9 @@ -17,16 +20,16 @@ require ( github.com/mssola/user_agent v0.5.2 github.com/opentracing/opentracing-go v1.1.0 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.11.1 + github.com/prometheus/client_golang v1.12.0 github.com/qiniu/go-sdk/v7 v7.11.1 github.com/robfig/cron/v3 v3.0.1 github.com/shirou/gopsutil/v3 v3.22.1 - github.com/spf13/cobra v1.0.0 + github.com/spf13/cobra v1.8.1 github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a github.com/swaggo/gin-swagger v1.5.0 github.com/swaggo/swag v1.8.3 github.com/unrolled/secure v1.0.8 - golang.org/x/crypto v0.9.0 + golang.org/x/crypto v0.38.0 gorm.io/driver/mysql v1.3.5 gorm.io/driver/postgres v1.3.8 gorm.io/driver/sqlite v1.3.6 @@ -38,26 +41,41 @@ require ( github.com/BurntSushi/toml v0.3.1 // indirect github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect github.com/KyleBanks/depth v1.2.1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/StackExchange/wmi v1.2.1 // indirect github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/bits-and-blooms/bitset v1.20.0 // indirect github.com/bsm/redislock v0.5.0 // indirect + github.com/bytedance/sonic v1.9.1 // indirect github.com/casbin/redis-watcher/v2 v2.0.0-20220614104201-0e70bf2be930 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chanxuehong/rand v0.0.0-20201110082127-2f19a1bdd973 // indirect github.com/chanxuehong/wechat v0.0.0-20201110083048-0180211b69fd // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/consensys/bavard v0.1.27 // indirect + github.com/consensys/gnark-crypto v0.16.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect + github.com/crate-crypto/go-eth-kzg v1.3.0 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/denisenkom/go-mssqldb v0.12.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/fatih/color v1.9.0 // indirect - github.com/fsnotify/fsnotify v1.4.9 // indirect + github.com/ethereum/c-kzg-4844/v2 v2.1.0 // indirect + github.com/ethereum/go-verkle v0.2.2 // indirect + github.com/fatih/color v1.16.0 // indirect + github.com/forgoer/openssl v1.6.0 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/git-chglog/git-chglog v0.0.0-20190611050339-63a4e637021f // indirect github.com/go-admin-team/go-admin-core/plugins/logger/zap v0.0.0-20210610020726-2db73adb505d // indirect github.com/go-admin-team/gorm-adapter/v3 v3.7.8-0.20220809100335-eaf9f67b3d21 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.6 // indirect github.com/go-openapi/spec v0.20.4 // indirect @@ -68,17 +86,19 @@ require ( github.com/go-redis/redis/v7 v7.4.0 // indirect github.com/go-redis/redis/v8 v8.11.5 // indirect github.com/go-sql-driver/mysql v1.6.0 // indirect - github.com/golang-jwt/jwt/v4 v4.4.2 // indirect + github.com/goccy/go-json v0.10.4 // indirect + github.com/golang-jwt/jwt/v4 v4.5.1 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect - github.com/golang/protobuf v1.5.0 // indirect - github.com/golang/snappy v0.0.1 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/henrylee2cn/ameda v1.4.10 // indirect github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8 // indirect + github.com/holiman/uint256 v1.3.2 // indirect github.com/imdario/mergo v0.3.9 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgconn v1.12.1 // indirect github.com/jackc/pgio v1.0.0 // indirect @@ -92,52 +112,60 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mailru/easyjson v0.7.6 // indirect - github.com/mattn/go-colorable v0.1.7 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-sqlite3 v1.14.12 // indirect github.com/mattn/goveralls v0.0.2 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mojocn/base64Captcha v1.3.1 // indirect github.com/nsqio/go-nsq v1.0.8 // indirect github.com/nyaruka/phonenumbers v1.0.55 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.26.0 // indirect - github.com/prometheus/procfs v0.6.0 // indirect + github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a // indirect + github.com/prometheus/common v0.32.1 // indirect + github.com/prometheus/procfs v0.7.3 // indirect github.com/robinjoseph08/redisqueue/v2 v2.1.0 // indirect - github.com/russross/blackfriday/v2 v2.0.1 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shamsher31/goimgext v1.0.0 // indirect - github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect + github.com/shopspring/decimal v1.4.0 // indirect github.com/spf13/cast v1.3.1 // indirect - github.com/spf13/pflag v1.0.3 // indirect - github.com/tklauser/go-sysconf v0.3.9 // indirect - github.com/tklauser/numcpus v0.3.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/supranational/blst v0.3.14 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect github.com/tsuyoshiwada/go-gitcmd v0.0.0-20180205145712-5f1f5f9475df // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect github.com/urfave/cli v1.22.1 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect go.uber.org/atomic v1.6.0 // indirect go.uber.org/multierr v1.5.0 // indirect go.uber.org/zap v1.15.0 // indirect + golang.org/x/arch v0.3.0 // indirect golang.org/x/image v0.1.0 // indirect - golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect - golang.org/x/net v0.10.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect - golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect - golang.org/x/tools v0.6.0 // indirect - google.golang.org/protobuf v1.30.0 // indirect + golang.org/x/net v0.36.0 // indirect + golang.org/x/sync v0.14.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/text v0.25.0 // indirect + golang.org/x/time v0.9.0 // indirect + golang.org/x/tools v0.29.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/AlecAivazis/survey.v1 v1.8.5 // indirect gopkg.in/kyokomi/emoji.v1 v1.5.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect gorm.io/plugin/dbresolver v1.2.2 // indirect + rsc.io/tmplfunc v0.0.3 // indirect ) //replace ( diff --git a/static/abis/usdc.abi b/static/abis/usdc.abi new file mode 100644 index 0000000..df96206 --- /dev/null +++ b/static/abis/usdc.abi @@ -0,0 +1 @@ +[{"constant":false,"inputs":[{"name":"newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newImplementation","type":"address"},{"name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"implementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newAdmin","type":"address"}],"name":"changeAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"admin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_implementation","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"previousAdmin","type":"address"},{"indexed":false,"name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"implementation","type":"address"}],"name":"Upgraded","type":"event"}] \ No newline at end of file diff --git a/static/abis/usdc.go b/static/abis/usdc.go new file mode 100644 index 0000000..cc3d424 --- /dev/null +++ b/static/abis/usdc.go @@ -0,0 +1,596 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package usdc + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// UsdcMetaData contains all meta data concerning the Usdc contract. +var UsdcMetaData = &bind.MetaData{ + ABI: "[{\"constant\":false,\"inputs\":[{\"name\":\"newImplementation\",\"type\":\"address\"}],\"name\":\"upgradeTo\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"newImplementation\",\"type\":\"address\"},{\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"upgradeToAndCall\",\"outputs\":[],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"implementation\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"changeAdmin\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"_implementation\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"previousAdmin\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"}]", +} + +// UsdcABI is the input ABI used to generate the binding from. +// Deprecated: Use UsdcMetaData.ABI instead. +var UsdcABI = UsdcMetaData.ABI + +// Usdc is an auto generated Go binding around an Ethereum contract. +type Usdc struct { + UsdcCaller // Read-only binding to the contract + UsdcTransactor // Write-only binding to the contract + UsdcFilterer // Log filterer for contract events +} + +// UsdcCaller is an auto generated read-only Go binding around an Ethereum contract. +type UsdcCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// UsdcTransactor is an auto generated write-only Go binding around an Ethereum contract. +type UsdcTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// UsdcFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type UsdcFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// UsdcSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type UsdcSession struct { + Contract *Usdc // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// UsdcCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type UsdcCallerSession struct { + Contract *UsdcCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// UsdcTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type UsdcTransactorSession struct { + Contract *UsdcTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// UsdcRaw is an auto generated low-level Go binding around an Ethereum contract. +type UsdcRaw struct { + Contract *Usdc // Generic contract binding to access the raw methods on +} + +// UsdcCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type UsdcCallerRaw struct { + Contract *UsdcCaller // Generic read-only contract binding to access the raw methods on +} + +// UsdcTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type UsdcTransactorRaw struct { + Contract *UsdcTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewUsdc creates a new instance of Usdc, bound to a specific deployed contract. +func NewUsdc(address common.Address, backend bind.ContractBackend) (*Usdc, error) { + contract, err := bindUsdc(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Usdc{UsdcCaller: UsdcCaller{contract: contract}, UsdcTransactor: UsdcTransactor{contract: contract}, UsdcFilterer: UsdcFilterer{contract: contract}}, nil +} + +// NewUsdcCaller creates a new read-only instance of Usdc, bound to a specific deployed contract. +func NewUsdcCaller(address common.Address, caller bind.ContractCaller) (*UsdcCaller, error) { + contract, err := bindUsdc(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &UsdcCaller{contract: contract}, nil +} + +// NewUsdcTransactor creates a new write-only instance of Usdc, bound to a specific deployed contract. +func NewUsdcTransactor(address common.Address, transactor bind.ContractTransactor) (*UsdcTransactor, error) { + contract, err := bindUsdc(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &UsdcTransactor{contract: contract}, nil +} + +// NewUsdcFilterer creates a new log filterer instance of Usdc, bound to a specific deployed contract. +func NewUsdcFilterer(address common.Address, filterer bind.ContractFilterer) (*UsdcFilterer, error) { + contract, err := bindUsdc(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &UsdcFilterer{contract: contract}, nil +} + +// bindUsdc binds a generic wrapper to an already deployed contract. +func bindUsdc(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := UsdcMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Usdc *UsdcRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Usdc.Contract.UsdcCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Usdc *UsdcRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Usdc.Contract.UsdcTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Usdc *UsdcRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Usdc.Contract.UsdcTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Usdc *UsdcCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Usdc.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Usdc *UsdcTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Usdc.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Usdc *UsdcTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Usdc.Contract.contract.Transact(opts, method, params...) +} + +// Admin is a free data retrieval call binding the contract method 0xf851a440. +// +// Solidity: function admin() view returns(address) +func (_Usdc *UsdcCaller) Admin(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Usdc.contract.Call(opts, &out, "admin") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Admin is a free data retrieval call binding the contract method 0xf851a440. +// +// Solidity: function admin() view returns(address) +func (_Usdc *UsdcSession) Admin() (common.Address, error) { + return _Usdc.Contract.Admin(&_Usdc.CallOpts) +} + +// Admin is a free data retrieval call binding the contract method 0xf851a440. +// +// Solidity: function admin() view returns(address) +func (_Usdc *UsdcCallerSession) Admin() (common.Address, error) { + return _Usdc.Contract.Admin(&_Usdc.CallOpts) +} + +// Implementation is a free data retrieval call binding the contract method 0x5c60da1b. +// +// Solidity: function implementation() view returns(address) +func (_Usdc *UsdcCaller) Implementation(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Usdc.contract.Call(opts, &out, "implementation") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Implementation is a free data retrieval call binding the contract method 0x5c60da1b. +// +// Solidity: function implementation() view returns(address) +func (_Usdc *UsdcSession) Implementation() (common.Address, error) { + return _Usdc.Contract.Implementation(&_Usdc.CallOpts) +} + +// Implementation is a free data retrieval call binding the contract method 0x5c60da1b. +// +// Solidity: function implementation() view returns(address) +func (_Usdc *UsdcCallerSession) Implementation() (common.Address, error) { + return _Usdc.Contract.Implementation(&_Usdc.CallOpts) +} + +// ChangeAdmin is a paid mutator transaction binding the contract method 0x8f283970. +// +// Solidity: function changeAdmin(address newAdmin) returns() +func (_Usdc *UsdcTransactor) ChangeAdmin(opts *bind.TransactOpts, newAdmin common.Address) (*types.Transaction, error) { + return _Usdc.contract.Transact(opts, "changeAdmin", newAdmin) +} + +// ChangeAdmin is a paid mutator transaction binding the contract method 0x8f283970. +// +// Solidity: function changeAdmin(address newAdmin) returns() +func (_Usdc *UsdcSession) ChangeAdmin(newAdmin common.Address) (*types.Transaction, error) { + return _Usdc.Contract.ChangeAdmin(&_Usdc.TransactOpts, newAdmin) +} + +// ChangeAdmin is a paid mutator transaction binding the contract method 0x8f283970. +// +// Solidity: function changeAdmin(address newAdmin) returns() +func (_Usdc *UsdcTransactorSession) ChangeAdmin(newAdmin common.Address) (*types.Transaction, error) { + return _Usdc.Contract.ChangeAdmin(&_Usdc.TransactOpts, newAdmin) +} + +// UpgradeTo is a paid mutator transaction binding the contract method 0x3659cfe6. +// +// Solidity: function upgradeTo(address newImplementation) returns() +func (_Usdc *UsdcTransactor) UpgradeTo(opts *bind.TransactOpts, newImplementation common.Address) (*types.Transaction, error) { + return _Usdc.contract.Transact(opts, "upgradeTo", newImplementation) +} + +// UpgradeTo is a paid mutator transaction binding the contract method 0x3659cfe6. +// +// Solidity: function upgradeTo(address newImplementation) returns() +func (_Usdc *UsdcSession) UpgradeTo(newImplementation common.Address) (*types.Transaction, error) { + return _Usdc.Contract.UpgradeTo(&_Usdc.TransactOpts, newImplementation) +} + +// UpgradeTo is a paid mutator transaction binding the contract method 0x3659cfe6. +// +// Solidity: function upgradeTo(address newImplementation) returns() +func (_Usdc *UsdcTransactorSession) UpgradeTo(newImplementation common.Address) (*types.Transaction, error) { + return _Usdc.Contract.UpgradeTo(&_Usdc.TransactOpts, newImplementation) +} + +// UpgradeToAndCall is a paid mutator transaction binding the contract method 0x4f1ef286. +// +// Solidity: function upgradeToAndCall(address newImplementation, bytes data) payable returns() +func (_Usdc *UsdcTransactor) UpgradeToAndCall(opts *bind.TransactOpts, newImplementation common.Address, data []byte) (*types.Transaction, error) { + return _Usdc.contract.Transact(opts, "upgradeToAndCall", newImplementation, data) +} + +// UpgradeToAndCall is a paid mutator transaction binding the contract method 0x4f1ef286. +// +// Solidity: function upgradeToAndCall(address newImplementation, bytes data) payable returns() +func (_Usdc *UsdcSession) UpgradeToAndCall(newImplementation common.Address, data []byte) (*types.Transaction, error) { + return _Usdc.Contract.UpgradeToAndCall(&_Usdc.TransactOpts, newImplementation, data) +} + +// UpgradeToAndCall is a paid mutator transaction binding the contract method 0x4f1ef286. +// +// Solidity: function upgradeToAndCall(address newImplementation, bytes data) payable returns() +func (_Usdc *UsdcTransactorSession) UpgradeToAndCall(newImplementation common.Address, data []byte) (*types.Transaction, error) { + return _Usdc.Contract.UpgradeToAndCall(&_Usdc.TransactOpts, newImplementation, data) +} + +// Fallback is a paid mutator transaction binding the contract fallback function. +// +// Solidity: fallback() payable returns() +func (_Usdc *UsdcTransactor) Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) { + return _Usdc.contract.RawTransact(opts, calldata) +} + +// Fallback is a paid mutator transaction binding the contract fallback function. +// +// Solidity: fallback() payable returns() +func (_Usdc *UsdcSession) Fallback(calldata []byte) (*types.Transaction, error) { + return _Usdc.Contract.Fallback(&_Usdc.TransactOpts, calldata) +} + +// Fallback is a paid mutator transaction binding the contract fallback function. +// +// Solidity: fallback() payable returns() +func (_Usdc *UsdcTransactorSession) Fallback(calldata []byte) (*types.Transaction, error) { + return _Usdc.Contract.Fallback(&_Usdc.TransactOpts, calldata) +} + +// UsdcAdminChangedIterator is returned from FilterAdminChanged and is used to iterate over the raw logs and unpacked data for AdminChanged events raised by the Usdc contract. +type UsdcAdminChangedIterator struct { + Event *UsdcAdminChanged // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *UsdcAdminChangedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(UsdcAdminChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(UsdcAdminChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *UsdcAdminChangedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *UsdcAdminChangedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// UsdcAdminChanged represents a AdminChanged event raised by the Usdc contract. +type UsdcAdminChanged struct { + PreviousAdmin common.Address + NewAdmin common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterAdminChanged is a free log retrieval operation binding the contract event 0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f. +// +// Solidity: event AdminChanged(address previousAdmin, address newAdmin) +func (_Usdc *UsdcFilterer) FilterAdminChanged(opts *bind.FilterOpts) (*UsdcAdminChangedIterator, error) { + + logs, sub, err := _Usdc.contract.FilterLogs(opts, "AdminChanged") + if err != nil { + return nil, err + } + return &UsdcAdminChangedIterator{contract: _Usdc.contract, event: "AdminChanged", logs: logs, sub: sub}, nil +} + +// WatchAdminChanged is a free log subscription operation binding the contract event 0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f. +// +// Solidity: event AdminChanged(address previousAdmin, address newAdmin) +func (_Usdc *UsdcFilterer) WatchAdminChanged(opts *bind.WatchOpts, sink chan<- *UsdcAdminChanged) (event.Subscription, error) { + + logs, sub, err := _Usdc.contract.WatchLogs(opts, "AdminChanged") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(UsdcAdminChanged) + if err := _Usdc.contract.UnpackLog(event, "AdminChanged", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseAdminChanged is a log parse operation binding the contract event 0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f. +// +// Solidity: event AdminChanged(address previousAdmin, address newAdmin) +func (_Usdc *UsdcFilterer) ParseAdminChanged(log types.Log) (*UsdcAdminChanged, error) { + event := new(UsdcAdminChanged) + if err := _Usdc.contract.UnpackLog(event, "AdminChanged", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// UsdcUpgradedIterator is returned from FilterUpgraded and is used to iterate over the raw logs and unpacked data for Upgraded events raised by the Usdc contract. +type UsdcUpgradedIterator struct { + Event *UsdcUpgraded // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *UsdcUpgradedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(UsdcUpgraded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(UsdcUpgraded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *UsdcUpgradedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *UsdcUpgradedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// UsdcUpgraded represents a Upgraded event raised by the Usdc contract. +type UsdcUpgraded struct { + Implementation common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterUpgraded is a free log retrieval operation binding the contract event 0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b. +// +// Solidity: event Upgraded(address implementation) +func (_Usdc *UsdcFilterer) FilterUpgraded(opts *bind.FilterOpts) (*UsdcUpgradedIterator, error) { + + logs, sub, err := _Usdc.contract.FilterLogs(opts, "Upgraded") + if err != nil { + return nil, err + } + return &UsdcUpgradedIterator{contract: _Usdc.contract, event: "Upgraded", logs: logs, sub: sub}, nil +} + +// WatchUpgraded is a free log subscription operation binding the contract event 0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b. +// +// Solidity: event Upgraded(address implementation) +func (_Usdc *UsdcFilterer) WatchUpgraded(opts *bind.WatchOpts, sink chan<- *UsdcUpgraded) (event.Subscription, error) { + + logs, sub, err := _Usdc.contract.WatchLogs(opts, "Upgraded") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(UsdcUpgraded) + if err := _Usdc.contract.UnpackLog(event, "Upgraded", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseUpgraded is a log parse operation binding the contract event 0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b. +// +// Solidity: event Upgraded(address implementation) +func (_Usdc *UsdcFilterer) ParseUpgraded(log types.Log) (*UsdcUpgraded, error) { + event := new(UsdcUpgraded) + if err := _Usdc.contract.UnpackLog(event, "Upgraded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/utils/aeshelper/aes256.go b/utils/aeshelper/aes256.go new file mode 100644 index 0000000..726882f --- /dev/null +++ b/utils/aeshelper/aes256.go @@ -0,0 +1,88 @@ +package aeshelper + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/md5" + "crypto/rand" + "encoding/base64" + "io" +) + +// Encrypt text with the passphrase +func Encrypt(text string, passphrase string) string { + salt := make([]byte, 8) + if _, err := io.ReadFull(rand.Reader, salt); err != nil { + panic(err.Error()) + } + + key, iv := DeriveKeyAndIv(passphrase, string(salt)) + + block, err := aes.NewCipher([]byte(key)) + if err != nil { + panic(err) + } + + pad := PKCS7Padding([]byte(text), block.BlockSize()) + ecb := cipher.NewCBCEncrypter(block, []byte(iv)) + encrypted := make([]byte, len(pad)) + ecb.CryptBlocks(encrypted, pad) + + return base64.StdEncoding.EncodeToString([]byte("Salted__" + string(salt) + string(encrypted))) +} + +// Decrypt encrypted text with the passphrase +func Decrypt(encrypted string, passphrase string) string { + ct, _ := base64.StdEncoding.DecodeString(encrypted) + if len(ct) < 16 || string(ct[:8]) != "Salted__" { + return "" + } + + salt := ct[8:16] + ct = ct[16:] + key, iv := DeriveKeyAndIv(passphrase, string(salt)) + + block, err := aes.NewCipher([]byte(key)) + if err != nil { + panic(err) + } + + cbc := cipher.NewCBCDecrypter(block, []byte(iv)) + dst := make([]byte, len(ct)) + cbc.CryptBlocks(dst, ct) + + return string(PKCS7Trimming(dst)) +} + +// PKCS7Padding PKCS7Padding +func PKCS7Padding(ciphertext []byte, blockSize int) []byte { + padding := blockSize - len(ciphertext)%blockSize + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(ciphertext, padtext...) +} + +// PKCS7Trimming PKCS7Trimming +func PKCS7Trimming(encrypt []byte) []byte { + padding := encrypt[len(encrypt)-1] + return encrypt[:len(encrypt)-int(padding)] +} + +// DeriveKeyAndIv DeriveKeyAndIv +func DeriveKeyAndIv(passphrase string, salt string) (string, string) { + salted := "" + dI := "" + + for len(salted) < 48 { + md := md5.New() + md.Write([]byte(dI + passphrase + salt)) + dM := md.Sum(nil) + dI = string(dM[:16]) + salted = salted + dI + } + + key := salted[0:32] + iv := salted[32:48] + + return key, iv +} diff --git a/utils/aeshelper/aeshelper.go b/utils/aeshelper/aeshelper.go new file mode 100644 index 0000000..819dbe6 --- /dev/null +++ b/utils/aeshelper/aeshelper.go @@ -0,0 +1,108 @@ +package aeshelper + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "encoding/base64" + "encoding/hex" + + "github.com/forgoer/openssl" +) + +const ( + sKey = "ptQJqRKyICCTeo6w" // "dde4b1f8a9e6b814" + ivParameter = "O3vZvOJSxQDP9hKT" // "dde4b1f8a9e6b814" + +) + +// PswEncrypt 加密 +func PswEncrypt(src string) (string, error) { + key := []byte(sKey) + iv := []byte(ivParameter) + result, err := Aes128Encrypt([]byte(src), key, iv) + if err != nil { + return "", err + } + return base64.RawStdEncoding.EncodeToString(result), nil +} + +// PswDecrypt 解密 +func PswDecrypt(src string) (string, error) { + key := []byte(sKey) + iv := []byte(ivParameter) + var result []byte + var err error + result, err = base64.StdEncoding.DecodeString(src) + if err != nil { + return "", err + } + origData, err := Aes128Decrypt(result, key, iv) + if err != nil { + return "", err + } + return string(origData), nil +} + +func Aes128Encrypt(origData, key []byte, IV []byte) ([]byte, error) { + if key == nil || len(key) != 16 { + return nil, nil + } + if IV != nil && len(IV) != 16 { + return nil, nil + } + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + blockSize := block.BlockSize() + origData = PKCS5Padding(origData, blockSize) + blockMode := cipher.NewCBCEncrypter(block, IV[:blockSize]) + crypted := make([]byte, len(origData)) + // 根据CryptBlocks方法的说明,如下方式初始化crypted也可以 + blockMode.CryptBlocks(crypted, origData) + return crypted, nil +} +func Aes128Decrypt(crypted, key []byte, IV []byte) ([]byte, error) { + if key == nil || len(key) != 16 { + return nil, nil + } + if IV != nil && len(IV) != 16 { + return nil, nil + } + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + blockSize := block.BlockSize() + blockMode := cipher.NewCBCDecrypter(block, IV[:blockSize]) + origData := make([]byte, len(crypted)) + blockMode.CryptBlocks(origData, crypted) + origData = PKCS5UnPadding(origData) + return origData, nil +} +func PKCS5Padding(ciphertext []byte, blockSize int) []byte { + padding := blockSize - len(ciphertext)%blockSize + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(ciphertext, padtext...) +} +func PKCS5UnPadding(origData []byte) []byte { + length := len(origData) + // 去掉最后一个字节 unpadding 次 + unpadding := int(origData[length-1]) + return origData[:(length - unpadding)] +} + +// 密码加密 +func AesEcbEncrypt(origData string) string { + //加密 + dst, _ := openssl.AesECBEncrypt([]byte(origData), []byte(sKey), openssl.PKCS7_PADDING) + return hex.EncodeToString(dst) +} + +// 密码解密 +func AesEcbDecrypt(origData string) string { + value, _ := hex.DecodeString(origData) + dst, _ := openssl.AesECBDecrypt(value, []byte(sKey), openssl.PKCS7_PADDING) + return string(dst) +} diff --git a/utils/aeshelper/aeshelper_test.go b/utils/aeshelper/aeshelper_test.go new file mode 100644 index 0000000..b918f4a --- /dev/null +++ b/utils/aeshelper/aeshelper_test.go @@ -0,0 +1,65 @@ +package aeshelper + +import ( + "encoding/json" + "fmt" + "testing" + "time" +) + +// 测试加密解密 +func Test_EnDe(t *testing.T) { + a := `asg` + b := Encrypt(a, `code_verify_success`) + c := Decrypt(b, `code_verify_success`) + fmt.Println(`原始为`, a) + fmt.Println(`加密后`, b) + fmt.Println(`解密后`, c) +} +func TestAesEcbEncrypt(t *testing.T) { + //aes := AesEcbEncrypt("123456") + aes := AesEcbEncrypt(fmt.Sprintf("%v_%v", time.Now().Unix(), 1332355333)) + dst := AesEcbDecrypt(aes) + fmt.Println(aes) + fmt.Println(dst) +} + +// TODO:需要加密的接口 +/** +1.合约下单接口 /api/futures/trade/order +2.合约撤单 /api/futures/trade/cancelorder +3.调整保证金 /api/futures/trade/adjustmargin +4.变换逐全仓模式 /api/futures/trade/marginType +5.更改持仓模式(方向) /api/futures/trade/positionSide/dual +6.资产划转 /api/futures/transfer +*/ +func TestAesEcbEncryptOrder(t *testing.T) { + data := addFutOrderReq{ + OrderType: 1, + BuyType: 3, + TriggerDecide: 3, + IsReduce: 2, + Coin: "asdf", + Price: "333.23", + Num: "23.20", + TriggerPrice: "1.023", + PositionSide: "long", + } + b, _ := json.Marshal(data) + aes := AesEcbEncrypt(string(b)) + dst := AesEcbDecrypt(aes) + fmt.Println(aes) + fmt.Println(dst) +} + +type addFutOrderReq struct { + OrderType int `json:"order_type"` // 订单类型:1限价,2限价止盈止损,3市价,4市价止盈止损,5强平委托(就是限价委托) + BuyType int `json:"buy_type"` // 买卖类型:1买,2卖 + TriggerDecide int `json:"trigger_decide"` // 触发条件 1按最新成交价格算,2按标记价格算 + IsReduce int `json:"is_reduce"` // 1是只减仓位(点击仓位列表中的平仓按钮),0正常 + Coin string `json:"coin"` // 交易币 + Price string `json:"price"` // 下单价格(限价+止盈止损时,该字段必填) + Num string `json:"num"` // 下单数量(市价时该字段必填) + TriggerPrice string `json:"trigger_price"` // 触发价格 + PositionSide string `json:"position_side"` // 持仓方向,单向持仓模式下可填both;在双向持仓模式下必填,且仅可选择 long 或 short +} diff --git a/utils/aeshelper/aesoldhelper/aesoldhelper.go b/utils/aeshelper/aesoldhelper/aesoldhelper.go new file mode 100644 index 0000000..c9812da --- /dev/null +++ b/utils/aeshelper/aesoldhelper/aesoldhelper.go @@ -0,0 +1,94 @@ +package aesoldhelper + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "encoding/base64" + "errors" + "io" + "strings" +) + +func addBase64Padding(value string) string { + m := len(value) % 4 + if m != 0 { + value += strings.Repeat("=", 4-m) + } + + return value +} + +func removeBase64Padding(value string) string { + return strings.Replace(value, "=", "", -1) +} + +// Pad Pad +func Pad(src []byte) []byte { + padding := aes.BlockSize - len(src)%aes.BlockSize + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(src, padtext...) +} + +// Unpad Unpad +func Unpad(src []byte) ([]byte, error) { + length := len(src) + unpadding := int(src[length-1]) + + if unpadding > length { + return nil, errors.New("unpad error. This could happen when incorrect encryption key is used") + } + + return src[:(length - unpadding)], nil +} + +// AesEncrypt AesEncrypt +func AesEncrypt(key []byte, text string) (string, error) { + block, err := aes.NewCipher(key) + if err != nil { + return "", err + } + + msg := Pad([]byte(text)) + ciphertext := make([]byte, aes.BlockSize+len(msg)) + iv := ciphertext[:aes.BlockSize] + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + return "", err + } + + cfb := cipher.NewCFBEncrypter(block, iv) + cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(msg)) + finalMsg := removeBase64Padding(base64.URLEncoding.EncodeToString(ciphertext)) + return finalMsg, nil +} + +// AesDecrypt AesDecrypt +func AesDecrypt(key []byte, text string) (string, error) { + block, err := aes.NewCipher(key) + if err != nil { + return "", err + } + + decodedMsg, err := base64.URLEncoding.DecodeString(addBase64Padding(text)) + if err != nil { + return "", err + } + + if (len(decodedMsg) % aes.BlockSize) != 0 { + return "", errors.New("blocksize must be multipe of decoded message length") + } + + iv := decodedMsg[:aes.BlockSize] + msg := decodedMsg[aes.BlockSize:] + + cfb := cipher.NewCFBDecrypter(block, iv) + cfb.XORKeyStream(msg, msg) + + unpadMsg, err := Unpad(msg) + if err != nil { + return "", err + } + + return string(unpadMsg), nil +} diff --git a/utils/aeshelper/apikey.go b/utils/aeshelper/apikey.go new file mode 100644 index 0000000..006c1a5 --- /dev/null +++ b/utils/aeshelper/apikey.go @@ -0,0 +1,19 @@ +package aeshelper + +var ( + apiaeskey = "9jxFTkydwCJsmIA1TUrv" +) + +// EncryptApi 加密apikey +func EncryptApi(apikey, secretkey string) (apikeyen, secrekeyen string) { + apikeyen = Encrypt(apikey, apiaeskey) + secrekeyen = Encrypt(secretkey, apiaeskey) + return apikeyen, secrekeyen +} + +// DecryptApi 解密apikey +func DecryptApi(apikeyen, secrekeyen string) (apikey, secrekey string) { + apikey = Decrypt(apikeyen, apiaeskey) + secrekey = Decrypt(secrekeyen, apiaeskey) + return apikey, secrekey +} diff --git a/utils/ethbalanceofhelper/baalanceof_helper.go b/utils/ethbalanceofhelper/baalanceof_helper.go new file mode 100644 index 0000000..210eee3 --- /dev/null +++ b/utils/ethbalanceofhelper/baalanceof_helper.go @@ -0,0 +1,113 @@ +package ethbalanceofhelper + +import ( + "context" + "errors" + "fmt" + "go-admin/abis" + "math/big" + "net/http" + "net/url" + "strings" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/rpc" + "github.com/shopspring/decimal" +) + +// ERC-20 代币合约 ABI (部分,只包含 balanceOf) +// ERC-20 代币合约 ABI (部分,只包含 balanceOf 和 decimals) +const erc20ABI = `[ + { + "constant": true, + "inputs": [ { "name": "_owner", "type": "address" } ], + "name": "balanceOf", + "outputs": [ { "name": "balance", "type": "uint256" } ], + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ { "name": "", "type": "uint8" } ], + "type": "function" + } +]` + +// GetERC20Balance 查询 ERC-20 代币余额并转换为正常单位 (使用 decimal) +func GetERC20Balance(client *ethclient.Client, tokenAbi abis.TokenABI, accountAddress string) (decimal.Decimal, error) { + // 1. 解析 ABI + contractABI, err := abi.JSON(strings.NewReader(erc20ABI)) + if err != nil { + return decimal.Zero, fmt.Errorf("解析 ABI 失败: %w", err) + } + + // 2. 构造 balanceOf 函数调用数据 + balanceOfCallData, err := contractABI.Pack("balanceOf", common.HexToAddress(accountAddress)) + if err != nil { + return decimal.Zero, fmt.Errorf("构造 balanceOf 调用数据失败: %w", err) + } + + address := common.HexToAddress(tokenAbi.TestAddress) + // 3. 执行 balanceOf 调用 + balanceResult, err := client.CallContract(context.Background(), ethereum.CallMsg{ + To: &address, + Data: balanceOfCallData, + }, nil) + if err != nil { + return decimal.Zero, fmt.Errorf("调用 balanceOf 失败: %w", err) + } + + // 4. 解析 balanceOf 结果 + unpackedBalance, err := contractABI.Unpack("balanceOf", balanceResult) + if err != nil { + return decimal.Zero, fmt.Errorf("解析 balanceOf 结果失败: %w", err) + } + + balance, ok := unpackedBalance[0].(*big.Int) + if !ok { + return decimal.Zero, errors.New("解析 balanceOf 结果为 *big.Int 失败") + } + + // 8. 转换为正常单位 (使用 decimal) + balanceDecimal := decimal.NewFromBigInt(balance, 0) // Create decimal from big.Int + decimalFactor := decimal.NewFromInt(10).Pow(decimal.NewFromInt(int64(tokenAbi.Decimals))) + readableBalance := balanceDecimal.Div(decimalFactor) + + return readableBalance, nil +} + +// 初始化以太坊客户端连接代理 +func EthClientWithProxy(rawURL string, proxyURL string) (*ethclient.Client, error) { + u, err := url.Parse(rawURL) + if err != nil { + return nil, fmt.Errorf("invalid URL: %v", err) + } + + if proxyURL == "" { + return ethclient.Dial(rawURL) + } + + proxy, err := url.Parse(proxyURL) + if err != nil { + return nil, fmt.Errorf("invalid proxy URL: %v", err) + } + + httpClient := &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyURL(proxy), + }, + } + dialOptions := []rpc.ClientOption{ + rpc.WithHTTPClient(httpClient), + } + rpcClient, err := rpc.DialOptions(context.Background(), u.String(), dialOptions...) + if err != nil { + return nil, fmt.Errorf("failed to dial RPC client with options: %v", err) + } + + return ethclient.NewClient(rpcClient), nil +} diff --git a/utils/ethtransferhelper/transfer_helper.go b/utils/ethtransferhelper/transfer_helper.go new file mode 100644 index 0000000..60d2498 --- /dev/null +++ b/utils/ethtransferhelper/transfer_helper.go @@ -0,0 +1,175 @@ +package ethtransferhelper + +import ( + "context" + "crypto/ecdsa" + "errors" + "fmt" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" + "golang.org/x/crypto/sha3" +) + +// transferErc20Token 发送 ERC-20 代币交易。 +// tokenAmount: 要发送的代币数量 (例如,1.0 代表 1 个代币)。 +// fromPrivateKey: 发送者的私钥。 +// tokenAddress: ERC-20 代币的合约地址。 +// toAddress: 接收者的地址。 +// tokenDecimals: 代币的小数位数 (例如,USDC 是 6,很多其他代币是 18)。 +func transferErc20Token( + client *ethclient.Client, + fromPrivateKey string, + tokenAddress string, + toAddress string, + tokenAmount float64, + tokenDecimals uint8, +) (*types.Transaction, error) { + // 1. 解析私钥 + privateKey, fromAddressCommon, err := GetAddressFromPrivateKey(fromPrivateKey) + if err != nil { + return nil, err + } + + // 3. 获取发送者的 nonce(交易序号) + nonce, err := client.PendingNonceAt(context.Background(), fromAddressCommon) + if err != nil { + return nil, fmt.Errorf("获取 nonce 失败: %w", err) + } + + // 4. 设置交易的 value (对于代币转账,value 是 0) + value := big.NewInt(0) // 不发送 ETH + + // 5. 获取 Gas 价格 + gasPrice, err := client.SuggestGasPrice(context.Background()) + if err != nil { + return nil, fmt.Errorf("获取 Gas 价格失败: %w", err) + } + + // 6. 将地址字符串转换为 common.Address 类型 + toAddressCommon := common.HexToAddress(toAddress) + tokenAddressCommon := common.HexToAddress(tokenAddress) + + // 7. 构造 ERC-20 transfer 函数的调用数据 + // 7.1 函数签名:transfer(address,uint256) + transferFnSignature := []byte("transfer(address,uint256)") + hash := sha3.New256() + hash.Write(transferFnSignature) + methodID := hash.Sum(nil)[:4] // 取前 4 个字节作为方法 ID + + // 7.2 填充接收者地址和转账金额 + paddedAddress := common.LeftPadBytes(toAddressCommon.Bytes(), 32) + // 7.3 将代币数量转换为最小单位 + amountBigInt, err := convertTokenAmountToBigInt(tokenAmount, tokenDecimals) + if err != nil { + return nil, fmt.Errorf("转换代币数量失败: %w", err) + } + paddedAmount := common.LeftPadBytes(amountBigInt.Bytes(), 32) + + // 7.4 拼接调用数据 + var data []byte + data = append(data, methodID...) + data = append(data, paddedAddress...) + data = append(data, paddedAmount...) + + // 8. 估算 Gas 消耗 + gasLimit, err := client.EstimateGas(context.Background(), ethereum.CallMsg{ + From: fromAddressCommon, + To: &tokenAddressCommon, + Data: data, + }) + if err != nil { + return nil, fmt.Errorf("估算 Gas 消耗失败: %w", err) + } + + // 9. 增加 Gas 限制的安全边际 + gasLimit = gasLimit * 11 / 10 // 增加 10% + if gasLimit < 23000 { + gasLimit = 23000 // 最小 Gas 限制 + } + + // 10. 创建交易 + tx := types.NewTransaction(nonce, tokenAddressCommon, value, gasLimit, gasPrice, data) + + // 11. 获取链 ID + chainID, err := client.NetworkID(context.Background()) + if err != nil { + return nil, fmt.Errorf("获取链 ID 失败: %w", err) + } + + // 12. 签名交易 + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey) + if err != nil { + return nil, fmt.Errorf("签名交易失败: %w", err) + } + + // 13. 发送交易 + err = client.SendTransaction(context.Background(), signedTx) + if err != nil { + return nil, fmt.Errorf("发送交易失败: %w", err) + } + + return signedTx, nil +} + +// getAddressFromPrivateKey 从私钥获取公钥和地址。 +func GetAddressFromPrivateKey(fromPrivateKey string) (*ecdsa.PrivateKey, common.Address, error) { + // 1. 移除 "0x" 前缀(如果存在) + if strings.HasPrefix(fromPrivateKey, "0x") { + fromPrivateKey = fromPrivateKey[2:] + } + + privateKey, err := crypto.HexToECDSA(fromPrivateKey) + if err != nil { + return nil, common.Address{}, fmt.Errorf("解析私钥失败: %w", err) + } + + // 2. 从私钥获取公钥和发送者地址 + publicKey := privateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return nil, common.Address{}, errors.New("无法将公钥转换为 ECDSA 类型") + } + fromAddressCommon := crypto.PubkeyToAddress(*publicKeyECDSA) + return privateKey, fromAddressCommon, nil +} + +// convertTokenAmountToBigInt 将代币数量 (例如,1.0) 转换为最小单位的整数表示 (例如,USDC 的 1000000)。 +func convertTokenAmountToBigInt(tokenAmount float64, tokenDecimals uint8) (*big.Int, error) { + // 1. 使用最大精度格式化浮点数 + amountStr := fmt.Sprintf("%.18f", tokenAmount) // 使用最大精度 + + // 2. 找到小数点的位置 + decimalPointIndex := strings.Index(amountStr, ".") + + // 3. 如果没有小数点,则添加足够的 0 + if decimalPointIndex == -1 { + amountStr += "." + strings.Repeat("0", int(tokenDecimals)) + } else { + // 4. 计算需要填充的 0 的数量 + paddingNeeded := int(tokenDecimals) - (len(amountStr) - decimalPointIndex - 1) + + // 5. 填充 0 或截断多余的小数位 + if paddingNeeded > 0 { + amountStr += strings.Repeat("0", paddingNeeded) + } else if paddingNeeded < 0 { + amountStr = amountStr[:decimalPointIndex+int(tokenDecimals)+1] + } + // 6. 移除小数点 + amountStr = strings.ReplaceAll(amountStr, ".", "") + } + + // 7. 将字符串转换为 big.Int + amountBigInt := new(big.Int) + amountBigInt, ok := amountBigInt.SetString(amountStr, 10) + if !ok { + return nil, fmt.Errorf("将金额字符串转换为 big.Int 失败: %s", amountStr) + } + + return amountBigInt, nil +} diff --git a/utils/stringhelper/address_helper.go b/utils/stringhelper/address_helper.go new file mode 100644 index 0000000..2de7fe4 --- /dev/null +++ b/utils/stringhelper/address_helper.go @@ -0,0 +1,9 @@ +package stringhelper + +func DesensitizeWalletAddress(address string) string { + if len(address) < 10 { // 更严格的长度检查 + return "0x...invalid" + } + + return address[:6] + "..." + address[len(address)-4:] +}