diff --git a/cmd/server/main.go b/cmd/server/main.go index 97475641..8118cf09 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -44,10 +44,6 @@ var Engine *gin.Engine // @contact.name AAStar Support // @contact.url https://aastar.xyz -// @securityDefinitions.apikey JWT -// @in header -// @name Authorization -// @description Type 'Bearer \' to correctly set the AccessToken // @BasePath /api func main() { secretPath := os.Getenv("secret_config_path") @@ -61,7 +57,6 @@ func main() { } func initEngine(strategyPath string, basicConfigPath string, secretPath string) { - logrus.Infof("secretPath: %s", secretPath) config.InitConfig(strategyPath, basicConfigPath, secretPath) if envirment.Environment.IsDevelopment() { diff --git a/common/model/secret_config.go b/common/model/secret_config.go index c105af47..f00221c3 100644 --- a/common/model/secret_config.go +++ b/common/model/secret_config.go @@ -15,11 +15,21 @@ type SecretConfig struct { NetWorkSecretConfigMap map[string]NetWorkSecretConfig `json:"network_secret_configs"` - ConfigDBConfig DBConfig `json:"config_db_config"` - RelayDBConfig DBConfig `json:"relay_db_config"` - ApiKeyTableName string `json:"api_key_table_name"` - StrategyConfigTableName string `json:"strategy_config_table_name"` - FreeSponsorWhitelist []string `json:"free_sponsor_whitelist"` + ConfigDBConfig DBConfig `json:"config_db_config"` + RelayDBConfig DBConfig `json:"relay_db_config"` + ApiKeyTableName string `json:"api_key_table_name"` + StrategyConfigTableName string `json:"strategy_config_table_name"` + FreeSponsorWhitelist []string `json:"free_sponsor_whitelist"` + SponsorConfig SponsorConfig `json:"sponsor_config"` +} +type SponsorConfig struct { + SponsorDepositAddress string `json:"sponsor_deposit_address"` + SponsorDepositPrivateKey string `json:"sponsor_deposit_private_key"` + DashBoardSignerAddress string `json:"dashboard_signer_address"` + DepositTestNetUrl string `json:"deposit_test_net_url"` + DepositMainNetUrl string `json:"deposit_main_net_url"` + SponsorTestClientUrl string `json:"sponsor_client_rpc_test_net"` + SponsorMainClientUrl string `json:"sponsor_client_rpc_main_net"` } type NetWorkSecretConfig struct { diff --git a/common/model/sponsor.go b/common/model/sponsor.go index d93700e4..7e4145a0 100644 --- a/common/model/sponsor.go +++ b/common/model/sponsor.go @@ -1,28 +1,19 @@ package model -import "math/big" - type DepositSponsorRequest struct { - Source string `json:"source"` - Amount *big.Float `json:"amount"` - TxHash string `json:"tx_hash"` - - TxInfo map[string]string `json:"tx_info"` - PayUserId string `json:"pay_user_id"` - IsTestNet bool `json:"is_test_net"` + TimeStamp int64 `json:"time_stamp"` + DepositAddress string `json:"deposit_address"` + TxHash string `json:"tx_hash"` + IsTestNet bool `json:"is_test_net"` + PayUserId string `json:"pay_user_id"` + DepositSource string `json:"deposit_source"` } type WithdrawSponsorRequest struct { - Amount *big.Float - - PayUserId string - IsTestNet bool - TxInfo map[string]string - TxHash string -} -type GetSponsorTransactionsRequest struct { -} -type GetSponsorMetaDataRequest struct { -} - -type Transaction struct { + Amount float64 `json:"amount"` + TimeStamp int64 `json:"time_stamp"` + PayUserId string `json:"pay_user_id"` + IsTestNet bool `json:"is_test_net"` + WithdrawSource string `json:"withdraw_source"` + RefundAddress string `json:"refund_address"` + DepositSource string `json:"deposit_source"` } diff --git a/common/utils/util.go b/common/utils/util.go index 3333ccbd..1a202652 100644 --- a/common/utils/util.go +++ b/common/utils/util.go @@ -3,6 +3,7 @@ package utils import ( "AAStarCommunity/EthPaymaster_BackService/common/global_const" "bytes" + "context" "crypto/ecdsa" "encoding/hex" "fmt" @@ -10,11 +11,14 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/rpc" "github.com/sirupsen/logrus" "golang.org/x/xerrors" "gorm.io/gorm" + "log" "math/big" "regexp" "runtime" @@ -24,7 +28,7 @@ import ( var HexPattern = regexp.MustCompile(`^0x[a-fA-F\d]*$`) -const defaultStackSize = 4096 +const defaultStackSize = 10000 type EthCallReq struct { From common.Address `json:"from"` @@ -164,6 +168,11 @@ func ConvertBalanceToEther(balance *big.Int) *big.Float { balanceFloat = new(big.Float).Quo(balanceFloat, global_const.EthWeiFactor) return balanceFloat } +func CounverEtherToWei(ether *big.Float) *big.Int { + afterEther := ether.Mul(ether, global_const.EthWeiFactor) + afterEtherInt, _ := afterEther.Int(nil) + return afterEtherInt +} func ConvertStringToSet(input string, split string) mapset.Set[string] { set := mapset.NewSet[string]() arr := strings.Split(input, split) @@ -202,18 +211,63 @@ func GetCurrentGoroutineStack() string { n := runtime.Stack(buf[:], false) return string(buf[:n]) } -func DBTransactional(db *gorm.DB, handle func() error) (err error) { +func DBTransactional(db *gorm.DB, handle func(*gorm.DB) error) (err error) { tx := db.Begin() defer func() { if p := recover(); p != nil { tx.Rollback() - panic(p) + logrus.Errorf("TX ERROR [%s] ", GetCurrentGoroutineStack()) + err = xerrors.Errorf("TX ERROR [%v]", p) + //panic(p) } else if err != nil { tx.Rollback() } else { err = tx.Commit().Error } }() - err = handle() - return + err = handle(tx) + return err +} + +func TransEth(from *ecdsa.PrivateKey, toAddress *common.Address, client *ethclient.Client, amount *big.Int, chainID *big.Int) (*types.Transaction, error) { + fromPrivateKey := from.Public() + fromPublicKeyECDSA, ok := fromPrivateKey.(*ecdsa.PublicKey) + if !ok { + logrus.Error("error casting public key to ECDSA") + return nil, xerrors.Errorf("error casting public key to ECDSA") + } + fromAddress := crypto.PubkeyToAddress(*fromPublicKeyECDSA) + nonce, err := client.PendingNonceAt(context.Background(), fromAddress) + if err != nil { + return nil, err + + } + gasLimit := uint64(21000) // in units + gasPrice, err := client.SuggestGasPrice(context.Background()) + if err != nil { + return nil, err + } + + var data []byte + tx := types.NewTx(&types.DynamicFeeTx{ + + Nonce: nonce, + Data: data, + Gas: gasLimit, + GasFeeCap: gasPrice, + GasTipCap: gasPrice, + Value: amount, + To: toAddress, + }) + + signTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), from) + + if err != nil { + log.Fatal(err) + } + err = client.SendTransaction(context.Background(), signTx) + if err != nil { + log.Fatal(err) + } + return signTx, nil } diff --git a/config/appsettings.yaml b/config/appsettings.yaml deleted file mode 100644 index e86f54a2..00000000 --- a/config/appsettings.yaml +++ /dev/null @@ -1,4 +0,0 @@ -jwt: - security: hello-ethereum-paymaster - realm: aastar - idkey: id diff --git a/config/secret_config.go b/config/secret_config.go index d66b7879..89982f21 100644 --- a/config/secret_config.go +++ b/config/secret_config.go @@ -3,18 +3,46 @@ package config import ( "AAStarCommunity/EthPaymaster_BackService/common/global_const" "AAStarCommunity/EthPaymaster_BackService/common/model" + "context" "encoding/json" "fmt" + "github.com/ethereum/go-ethereum/ethclient" + "math/big" "os" + "sync" ) var dsnTemplate = "host=%s port=%v user=%s password=%s dbname=%s TimeZone=%s sslmode=%s" var secretConfig *model.SecretConfig var signerConfig = make(SignerConfigMap) +var depositer *global_const.EOA + +var sponsorTestNetClient *ethclient.Client +var sponsorTestNetClientChainId *big.Int +var sponsorMainNetClient *ethclient.Client +var sponsorMainNetClientChainId *big.Int +var onlyOnce = sync.Once{} + +func GetPaymasterSponsorClient(isTestNet bool) *ethclient.Client { + if isTestNet { + return sponsorTestNetClient + + } + return sponsorMainNetClient +} +func GetPaymasterSponsorChainId(isTestNet bool) *big.Int { + if isTestNet { + return sponsorTestNetClientChainId + } + return sponsorMainNetClientChainId +} type SignerConfigMap map[global_const.Network]*global_const.EOA +func GetDepositer() *global_const.EOA { + return depositer +} func secretConfigInit(secretConfigPath string) { if secretConfigPath == "" { panic("secretConfigPath is empty") @@ -39,7 +67,35 @@ func secretConfigInit(secretConfigPath string) { signerConfig[global_const.Network(network)] = eoa } + depositer, err = global_const.NewEoa(secretConfig.SponsorConfig.SponsorDepositPrivateKey) + if err != nil { + panic(fmt.Sprintf("signer key error: %s", err)) + } + onlyOnce.Do(func() { + paymasterSponsorMainNetClient, err := ethclient.Dial(secretConfig.SponsorConfig.SponsorMainClientUrl) + if err != nil { + panic(fmt.Sprintf("paymaster inner client error: %s", err)) + } + paymasterInnerClientChainId, err := paymasterSponsorMainNetClient.ChainID(context.Background()) + if err != nil { + panic(fmt.Sprintf("paymaster inner client chain id error: %s", err)) + } + sponsorMainNetClient = paymasterSponsorMainNetClient + sponsorMainNetClientChainId = paymasterInnerClientChainId + + paymasterSponsorTestNetClient, err := ethclient.Dial(secretConfig.SponsorConfig.SponsorTestClientUrl) + if err != nil { + panic(fmt.Sprintf("paymaster inner client error: %s", err)) + } + paymasterInnerClientChainId, err = paymasterSponsorTestNetClient.ChainID(context.Background()) + if err != nil { + panic(fmt.Sprintf("paymaster inner client chain id error: %s", err)) + } + sponsorTestNetClient = paymasterSponsorTestNetClient + sponsorTestNetClientChainId = paymasterInnerClientChainId + }) } + func IsSponsorWhitelist(address string) bool { //TODO @@ -68,6 +124,10 @@ func GetSigner(network global_const.Network) *global_const.EOA { func GetAPIKeyTableName() string { return secretConfig.ApiKeyTableName } +func GetSponsorConfig() *model.SponsorConfig { + //TODO + return &secretConfig.SponsorConfig +} func GetStrategyConfigTableName() string { return secretConfig.StrategyConfigTableName } diff --git a/docs/docs.go b/docs/docs.go index c6ae8568..7773b095 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -18,24 +18,54 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { - "/api/auth": { + "/api/healthz": { + "get": { + "description": "Get Healthz", + "consumes": [ + "application/json" + ], + "tags": [ + "Healthz" + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/api/v1/paymaster/{network}": { "post": { - "description": "Get AccessToken By ApiKey", + "description": "Paymaster JSON-RPC API", "consumes": [ "application/json" ], "tags": [ - "Auth" + "Paymaster" ], "parameters": [ { - "description": "AccessToken Model", - "name": "credential", + "type": "string", + "description": "Network", + "name": "network", + "in": "path", + "required": true + }, + { + "description": "JsonRpcRequest Model", + "name": "rpcRequest", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.ClientCredential" + "$ref": "#/definitions/model.JsonRpcRequest" } + }, + { + "type": "string", + "description": "apiKey", + "name": "apiKey", + "in": "query", + "required": true } ], "responses": { @@ -45,14 +75,37 @@ const docTemplate = `{ } } }, - "/api/healthz": { - "get": { - "description": "Get Healthz", + "/api/v1/paymaster_sponsor/deposit": { + "post": { + "description": "Deposit Sponsor", "consumes": [ "application/json" ], "tags": [ - "Healthz" + "DepositSponsor" + ], + "parameters": [ + { + "description": "DepositSponsorRequest Model", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.DepositSponsorRequest" + } + }, + { + "type": "string", + "description": "relay Request Body Hash", + "name": "relay_hash", + "in": "header" + }, + { + "type": "string", + "description": "relay Request Body Hash", + "name": "relay_signature", + "in": "header" + } ], "responses": { "200": { @@ -61,36 +114,31 @@ const docTemplate = `{ } } }, - "/api/v1/paymaster/{network}": { + "/api/v1/paymaster_sponsor/withdraw": { "post": { - "security": [ - { - "JWT": [] - } - ], - "description": "Paymaster JSON-RPC API", + "description": "Withdraw Sponsor", "consumes": [ "application/json" ], "tags": [ - "Paymaster" + "Sponsor" ], "parameters": [ { - "type": "string", - "description": "Network", - "name": "network", - "in": "path", - "required": true - }, - { - "description": "JsonRpcRequest Model", - "name": "rpcRequest", + "description": "WithdrawSponsorRequest Model", + "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.JsonRpcRequest" + "$ref": "#/definitions/model.WithdrawSponsorRequest" } + }, + { + "type": "boolean", + "description": "Is Test Net", + "name": "is_test_net", + "in": "path", + "required": true } ], "responses": { @@ -102,10 +150,25 @@ const docTemplate = `{ } }, "definitions": { - "model.ClientCredential": { + "model.DepositSponsorRequest": { "type": "object", "properties": { - "apiKey": { + "deposit_address": { + "type": "string" + }, + "deposit_source": { + "type": "string" + }, + "is_test_net": { + "type": "boolean" + }, + "pay_user_id": { + "type": "string" + }, + "time_stamp": { + "type": "integer" + }, + "tx_hash": { "type": "string" } } @@ -127,14 +190,32 @@ const docTemplate = `{ "items": {} } } - } - }, - "securityDefinitions": { - "JWT": { - "description": "Type 'Bearer \\\u003cTOKEN\\\u003e' to correctly set the AccessToken", - "type": "apiKey", - "name": "Authorization", - "in": "header" + }, + "model.WithdrawSponsorRequest": { + "type": "object", + "properties": { + "amount": { + "type": "number" + }, + "deposit_source": { + "type": "string" + }, + "isTestNet": { + "type": "boolean" + }, + "payUserId": { + "type": "string" + }, + "refund_address": { + "type": "string" + }, + "time_stamp": { + "type": "integer" + }, + "withdraw_source": { + "type": "string" + } + } } } }` @@ -143,7 +224,7 @@ const docTemplate = `{ var SwaggerInfo = &swag.Spec{ Version: "", Host: "", - BasePath: "", + BasePath: "/api", Schemes: []string{}, Title: "", Description: "", diff --git a/docs/swagger.json b/docs/swagger.json index 9eede293..f5b6663d 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -6,25 +6,56 @@ "url": "https://aastar.xyz" } }, + "basePath": "/api", "paths": { - "/api/auth": { + "/api/healthz": { + "get": { + "description": "Get Healthz", + "consumes": [ + "application/json" + ], + "tags": [ + "Healthz" + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/api/v1/paymaster/{network}": { "post": { - "description": "Get AccessToken By ApiKey", + "description": "Paymaster JSON-RPC API", "consumes": [ "application/json" ], "tags": [ - "Auth" + "Paymaster" ], "parameters": [ { - "description": "AccessToken Model", - "name": "credential", + "type": "string", + "description": "Network", + "name": "network", + "in": "path", + "required": true + }, + { + "description": "JsonRpcRequest Model", + "name": "rpcRequest", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.ClientCredential" + "$ref": "#/definitions/model.JsonRpcRequest" } + }, + { + "type": "string", + "description": "apiKey", + "name": "apiKey", + "in": "query", + "required": true } ], "responses": { @@ -34,14 +65,37 @@ } } }, - "/api/healthz": { - "get": { - "description": "Get Healthz", + "/api/v1/paymaster_sponsor/deposit": { + "post": { + "description": "Deposit Sponsor", "consumes": [ "application/json" ], "tags": [ - "Healthz" + "DepositSponsor" + ], + "parameters": [ + { + "description": "DepositSponsorRequest Model", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.DepositSponsorRequest" + } + }, + { + "type": "string", + "description": "relay Request Body Hash", + "name": "relay_hash", + "in": "header" + }, + { + "type": "string", + "description": "relay Request Body Hash", + "name": "relay_signature", + "in": "header" + } ], "responses": { "200": { @@ -50,36 +104,31 @@ } } }, - "/api/v1/paymaster/{network}": { + "/api/v1/paymaster_sponsor/withdraw": { "post": { - "security": [ - { - "JWT": [] - } - ], - "description": "Paymaster JSON-RPC API", + "description": "Withdraw Sponsor", "consumes": [ "application/json" ], "tags": [ - "Paymaster" + "Sponsor" ], "parameters": [ { - "type": "string", - "description": "Network", - "name": "network", - "in": "path", - "required": true - }, - { - "description": "JsonRpcRequest Model", - "name": "rpcRequest", + "description": "WithdrawSponsorRequest Model", + "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.JsonRpcRequest" + "$ref": "#/definitions/model.WithdrawSponsorRequest" } + }, + { + "type": "boolean", + "description": "Is Test Net", + "name": "is_test_net", + "in": "path", + "required": true } ], "responses": { @@ -91,10 +140,25 @@ } }, "definitions": { - "model.ClientCredential": { + "model.DepositSponsorRequest": { "type": "object", "properties": { - "apiKey": { + "deposit_address": { + "type": "string" + }, + "deposit_source": { + "type": "string" + }, + "is_test_net": { + "type": "boolean" + }, + "pay_user_id": { + "type": "string" + }, + "time_stamp": { + "type": "integer" + }, + "tx_hash": { "type": "string" } } @@ -116,14 +180,32 @@ "items": {} } } - } - }, - "securityDefinitions": { - "JWT": { - "description": "Type 'Bearer \\\u003cTOKEN\\\u003e' to correctly set the AccessToken", - "type": "apiKey", - "name": "Authorization", - "in": "header" + }, + "model.WithdrawSponsorRequest": { + "type": "object", + "properties": { + "amount": { + "type": "number" + }, + "deposit_source": { + "type": "string" + }, + "isTestNet": { + "type": "boolean" + }, + "payUserId": { + "type": "string" + }, + "refund_address": { + "type": "string" + }, + "time_stamp": { + "type": "integer" + }, + "withdraw_source": { + "type": "string" + } + } } } } \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 052a2180..c16f4f60 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,7 +1,18 @@ +basePath: /api definitions: - model.ClientCredential: + model.DepositSponsorRequest: properties: - apiKey: + deposit_address: + type: string + deposit_source: + type: string + is_test_net: + type: boolean + pay_user_id: + type: string + time_stamp: + type: integer + tx_hash: type: string type: object model.JsonRpcRequest: @@ -16,28 +27,28 @@ definitions: items: {} type: array type: object + model.WithdrawSponsorRequest: + properties: + amount: + type: number + deposit_source: + type: string + isTestNet: + type: boolean + payUserId: + type: string + refund_address: + type: string + time_stamp: + type: integer + withdraw_source: + type: string + type: object info: contact: name: AAStar Support url: https://aastar.xyz paths: - /api/auth: - post: - consumes: - - application/json - description: Get AccessToken By ApiKey - parameters: - - description: AccessToken Model - in: body - name: credential - required: true - schema: - $ref: '#/definitions/model.ClientCredential' - responses: - "200": - description: OK - tags: - - Auth /api/healthz: get: consumes: @@ -65,17 +76,61 @@ paths: required: true schema: $ref: '#/definitions/model.JsonRpcRequest' + - description: apiKey + in: query + name: apiKey + required: true + type: string responses: "200": description: OK - security: - - JWT: [] tags: - Paymaster -securityDefinitions: - JWT: - description: Type 'Bearer \' to correctly set the AccessToken - in: header - name: Authorization - type: apiKey + /api/v1/paymaster_sponsor/deposit: + post: + consumes: + - application/json + description: Deposit Sponsor + parameters: + - description: DepositSponsorRequest Model + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.DepositSponsorRequest' + - description: relay Request Body Hash + in: header + name: relay_hash + type: string + - description: relay Request Body Hash + in: header + name: relay_signature + type: string + responses: + "200": + description: OK + tags: + - DepositSponsor + /api/v1/paymaster_sponsor/withdraw: + post: + consumes: + - application/json + description: Withdraw Sponsor + parameters: + - description: WithdrawSponsorRequest Model + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.WithdrawSponsorRequest' + - description: Is Test Net + in: path + name: is_test_net + required: true + type: boolean + responses: + "200": + description: OK + tags: + - Sponsor swagger: "2.0" diff --git a/envirment/app_config.go b/envirment/app_config.go index bbc91bd5..df93d99b 100644 --- a/envirment/app_config.go +++ b/envirment/app_config.go @@ -2,20 +2,13 @@ package envirment import ( "AAStarCommunity/EthPaymaster_BackService/common/model" - "fmt" - "k8s.io/apimachinery/pkg/util/yaml" "os" - "strings" "sync" ) var once sync.Once var Environment *model.Env -type Conf struct { - Jwt JWT -} - func init() { envName := model.DevEnv if len(os.Getenv(model.EnvKey)) > 0 { @@ -28,59 +21,3 @@ func init() { }(), } } - -var conf *Conf - -// GetAppConf read conf from file -func GetAppConf() *Conf { - once.Do(func() { - if conf == nil { - filePath := getConfFilePath() - conf = getConfiguration(filePath) - } - }) - return conf -} -func getConfFilePath() *string { - path := fmt.Sprintf("conf/appsettings.%s.yaml", strings.ToLower(Environment.Name)) - if _, err := os.Stat(path); err != nil && os.IsNotExist(err) { - path = fmt.Sprintf("config/appsettings.yaml") - } - return &path -} - -// getConfiguration -func getConfiguration(filePath *string) *Conf { - if file, err := os.ReadFile(*filePath); err != nil { - return mappingEnvToConf( - &Conf{ - Jwt: JWT{}, - }, - ) - } else { - c := Conf{} - err := yaml.Unmarshal(file, &c) - if err != nil { - return mappingEnvToConf(&c) - } - - return &c - } -} - -func mappingEnvToConf(conf *Conf) *Conf { - - // TODO: read from env - // e.g. if dummy := os.Getenv("dummy"); len(dummy) > 0 {conf.Dummy = dummy} - if jwtSecurity := os.Getenv("jwt__security"); len(jwtSecurity) > 0 { - conf.Jwt.Security = jwtSecurity - } - if jwtRealm := os.Getenv("jwt__realm"); len(jwtRealm) > 0 { - conf.Jwt.Security = jwtRealm - } - if jetIkey := os.Getenv("jwt__idkey"); len(jetIkey) > 0 { - conf.Jwt.Security = jetIkey - } - - return conf -} diff --git a/envirment/jwt.go b/envirment/jwt.go deleted file mode 100644 index 867c3460..00000000 --- a/envirment/jwt.go +++ /dev/null @@ -1,28 +0,0 @@ -package envirment - -import "sync" - -type JWT struct { - Security string - Realm string - IdKey string -} - -var jwt *JWT - -var onceJwt sync.Once - -// GetJwtKey represents jwt object -func GetJwtKey() *JWT { - onceJwt.Do(func() { - if jwt == nil { - j := GetAppConf().Jwt - jwt = &JWT{ - Security: j.Security, - Realm: j.Realm, - } - } - }) - - return jwt -} diff --git a/go.mod b/go.mod index 5d7e7e81..e0df251a 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.22.4 require ( github.com/NethermindEth/starknet.go v0.7.0 - github.com/appleboy/gin-jwt/v2 v2.9.2 github.com/deckarep/golang-set/v2 v2.6.0 github.com/ethereum/go-ethereum v1.14.3 github.com/gin-contrib/cors v1.7.1 @@ -21,7 +20,6 @@ require ( gorm.io/datatypes v1.2.0 gorm.io/driver/postgres v1.5.7 gorm.io/gorm v1.25.10 - k8s.io/apimachinery v0.29.3 ) require ( @@ -32,6 +30,7 @@ require ( github.com/fxamacker/cbor/v2 v2.4.0 // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/holiman/uint256 v1.2.4 // indirect github.com/huin/goupnp v1.3.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect @@ -43,6 +42,7 @@ require ( github.com/jinzhu/now v1.1.5 // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/klauspost/compress v1.17.0 // indirect + github.com/onsi/gomega v1.29.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/test-go/testify v1.1.4 // indirect github.com/x448/float16 v0.8.4 // indirect @@ -72,7 +72,6 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/goccy/go-json v0.10.2 // indirect - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -105,6 +104,4 @@ require ( google.golang.org/protobuf v1.34.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index bf71db54..84fba8f2 100644 --- a/go.sum +++ b/go.sum @@ -10,10 +10,6 @@ github.com/NethermindEth/starknet.go v0.7.0 h1:OmKkPs7EvS8uUkoxh+y/4xeluFx4fepZo github.com/NethermindEth/starknet.go v0.7.0/go.mod h1:k6qFeYocOAeY7sdF7oAaabvjXvmcVtBbLn7YE2azVyQ= github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= -github.com/appleboy/gin-jwt/v2 v2.9.2 h1:GeS3lm9mb9HMmj7+GNjYUtpp3V1DAQ1TkUFa5poiZ7Y= -github.com/appleboy/gin-jwt/v2 v2.9.2/go.mod h1:mxGjKt9Lrx9Xusy1SrnmsCJMZG6UJwmdHN9bN27/QDw= -github.com/appleboy/gofight/v2 v2.1.2 h1:VOy3jow4vIK8BRQJoC/I9muxyYlJ2yb9ht2hZoS3rf4= -github.com/appleboy/gofight/v2 v2.1.2/go.mod h1:frW+U1QZEdDgixycTj4CygQ48yLTUhplt43+Wczp3rw= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= @@ -140,7 +136,6 @@ github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -292,12 +287,6 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= -github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= -github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= -github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= -github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= @@ -424,13 +413,7 @@ gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHD gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= -k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU= -k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/rpc_server/api/auth.go b/rpc_server/api/auth.go deleted file mode 100644 index cad8302f..00000000 --- a/rpc_server/api/auth.go +++ /dev/null @@ -1,18 +0,0 @@ -package api - -import ( - "AAStarCommunity/EthPaymaster_BackService/rpc_server/middlewares" - "github.com/gin-gonic/gin" -) - -// Auth -// @Tags Auth -// @Description Get AccessToken By ApiKey -// @Accept json -// @Product json -// @Param credential body model.ClientCredential true "AccessToken Model" -// @Router /api/auth [post] -// @Success 200 -func Auth(ctx *gin.Context) { - middlewares.GinJwtMiddleware().LoginHandler(ctx) -} diff --git a/rpc_server/api/utils/current_user.go b/rpc_server/api/utils/current_user.go deleted file mode 100644 index 52dc8eb6..00000000 --- a/rpc_server/api/utils/current_user.go +++ /dev/null @@ -1,24 +0,0 @@ -package utils - -import ( - jwt "github.com/appleboy/gin-jwt/v2" - "github.com/gin-gonic/gin" -) - -// CurrentUser is a util tool for getting current user(ApiKey) from each rpc request -func CurrentUser(ctx *gin.Context) (exists bool, user string) { - - defer func() { - if r := recover(); r != nil { - exists = false - } - }() - - mapping := ctx.MustGet("JWT_PAYLOAD").(jwt.MapClaims) - - user = mapping["jti"].(string) - - exists = true - - return -} diff --git a/rpc_server/api/v1/paymaster.go b/rpc_server/api/v1/paymaster.go index af329106..3a46e24f 100644 --- a/rpc_server/api/v1/paymaster.go +++ b/rpc_server/api/v1/paymaster.go @@ -30,9 +30,9 @@ func init() { // @Product json // @param network path string true "Network" // @Param rpcRequest body model.JsonRpcRequest true "JsonRpcRequest Model" +// @Param apiKey query string true "apiKey" // @Router /api/v1/paymaster/{network} [post] // @Success 200 -// @Security JWT func Paymaster(ctx *gin.Context) { jsonRpcRequest := model.JsonRpcRequest{} diff --git a/rpc_server/api/v1/sponsor.go b/rpc_server/api/v1/sponsor.go index e64071e9..01857af9 100644 --- a/rpc_server/api/v1/sponsor.go +++ b/rpc_server/api/v1/sponsor.go @@ -3,27 +3,39 @@ package v1 import ( "AAStarCommunity/EthPaymaster_BackService/common/global_const" "AAStarCommunity/EthPaymaster_BackService/common/model" + "AAStarCommunity/EthPaymaster_BackService/common/price_compoent" + "AAStarCommunity/EthPaymaster_BackService/common/utils" + "AAStarCommunity/EthPaymaster_BackService/config" "AAStarCommunity/EthPaymaster_BackService/sponsor_manager" + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" "errors" "fmt" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts" + "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" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "golang.org/x/xerrors" "gorm.io/gorm" + "log" + "math/big" "net/http" - "strconv" ) -var sourcePublicKeyMap = map[string]string{ - "Dashboard": "0x17EE97b5F4Ab8a4b2CfEcfb42b66718F15557687", -} - // DepositSponsor -// @Tags Sponsor +// @Tags DepositSponsor // @Description Deposit Sponsor // @Accept json // @Product json -// @Param request body DepositSponsorRequest true "DepositSponsorRequest Model -// @Param is_test_net path boolean true "Is Test Net" +// @Param request body model.DepositSponsorRequest true "DepositSponsorRequest Model" +// @Param relay_hash header string false "relay Request Body Hash" +// @Param relay_signature header string false "relay Request Body Hash" // @Router /api/v1/paymaster_sponsor/deposit [post] // @Success 200 func DepositSponsor(ctx *gin.Context) { @@ -34,33 +46,155 @@ func DepositSponsor(ctx *gin.Context) { response.SetHttpCode(http.StatusBadRequest).FailCode(ctx, http.StatusBadRequest, errStr) return } - signerAddress, ok := sourcePublicKeyMap["Dashboard"] - if !ok { - response.SetHttpCode(http.StatusInternalServerError).FailCode(ctx, http.StatusInternalServerError, "Invalid Source") + if request.DepositSource != "dashboard" { + errStr := fmt.Sprintf("not Support Source") + response.SetHttpCode(http.StatusBadRequest).FailCode(ctx, http.StatusBadRequest, errStr) + return + } + //validate Signature + inputJson, err := json.Marshal(request) + if err != nil { + response.SetHttpCode(http.StatusInternalServerError).FailCode(ctx, http.StatusInternalServerError, err.Error()) + return + } + var signerAddress string + //TODO change to Enum + if request.DepositSource == "dashboard" { + signerAddress = config.GetSponsorConfig().DashBoardSignerAddress + } else { + response.SetHttpCode(http.StatusBadRequest).FailCode(ctx, http.StatusBadRequest, "Deposit Source Error :Not Support Source") + return + } + err = ValidateSignature(ctx.GetHeader("relay_hash"), ctx.GetHeader("relay_signature"), inputJson, signerAddress) + if err != nil { + response.SetHttpCode(http.StatusBadRequest).FailCode(ctx, http.StatusBadRequest, err.Error()) + return + } + //validate Deposit + sender, amount, err := validateDeposit(&request) + if err != nil { + response.SetHttpCode(http.StatusBadRequest).FailCode(ctx, http.StatusBadRequest, err.Error()) return } - logrus.Debugf("Signer Address [%v]", signerAddress) - //TODO Add Signature Verification - result, err := sponsor_manager.DepositSponsor(&request) + depositInput := sponsor_manager.DepositSponsorInput{ + From: sender.Hex(), + Amount: amount, + TxHash: request.TxHash, + PayUserId: request.PayUserId, + Source: request.DepositSource, + IsTestNet: request.IsTestNet, + } + result, err := sponsor_manager.DepositSponsor(&depositInput) if err != nil { response.SetHttpCode(http.StatusInternalServerError).FailCode(ctx, http.StatusInternalServerError, err.Error()) return } + response.WithDataSuccess(ctx, result) return } +func ValidateSignature(originHash string, signatureHex string, inputJson []byte, signerAddress string) error { + hash := sha256.New() + hash.Write(inputJson) + hashBytes := hash.Sum(nil) + hashHex := hex.EncodeToString(hashBytes) + if hashHex != originHash { + return xerrors.Errorf("Hash Not Match") + } + signerAddressHex := common.HexToAddress(signerAddress) + + hashByte, _ := utils.DecodeStringWithPrefix(originHash) + signatureByte, _ := utils.DecodeStringWithPrefix(signatureHex) + pubKey, err := crypto.SigToPub(accounts.TextHash(hashByte), signatureByte) + if err != nil { + log.Fatalf("Failed to recover public key: %v", err) + } + recoveredAddr := crypto.PubkeyToAddress(*pubKey) + if signerAddressHex != recoveredAddr { + return xerrors.Errorf("Signer Address Not Match") + } + return nil +} +func validateDeposit(request *model.DepositSponsorRequest) (sender *common.Address, amount *big.Float, err error) { + txHash := request.TxHash + client := config.GetPaymasterSponsorClient(request.IsTestNet) + + // check tx + _, err = sponsor_manager.GetLogByTxHash(txHash, request.IsTestNet) + if err == nil { + return nil, nil, xerrors.Errorf("Transaction [%s] already exist", txHash) + } + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil, err + } + } + tx, err := GetInfoByHash(txHash, client) + if err != nil { + return nil, nil, err + } + if tx.Type() != types.DynamicFeeTxType { + return nil, nil, xerrors.Errorf("Tx Type is not DynamicFeeTxType") + } + txSender, err := types.Sender(types.NewLondonSigner(tx.ChainId()), tx) + if err != nil { + logrus.Errorf("Get Sender Error [%v]", err) + return nil, nil, err + } + sender = &txSender + if request.IsTestNet { + //Only ETH + if tx.Value().Uint64() == 0 { + return nil, nil, xerrors.Errorf("Tx Value is 0") + } + if tx.To() == nil { + return nil, nil, xerrors.Errorf("Tx To Address is nil") + } + if tx.To().Hex() != config.GetDepositer().Address.String() { + return nil, nil, xerrors.Errorf("Tx To Address is not Sponsor Address") + } + value := tx.Value() + valueEth := utils.ConvertBalanceToEther(value) + logrus.Infof("ETH amount : %s", valueEth) + + amount, err = price_compoent.GetTokenCostInUsd(global_const.TokenTypeETH, valueEth) + if err != nil { + return nil, nil, err + } + } else { + return nil, nil, xerrors.Errorf("not Support MainNet Right Now") + //contractAddress := tx.To() + //chain_service.CheckContractAddressAccess(contractAddress,"") + //Only Usdt + + } + return sender, amount, nil +} + +func GetInfoByHash(txHash string, client *ethclient.Client) (*types.Transaction, error) { + txHashHex := common.HexToHash(txHash) + //TODO consider about pending + tx, _, err := client.TransactionByHash(context.Background(), txHashHex) + if err != nil { + if errors.Is(err, ethereum.NotFound) { + return nil, xerrors.Errorf("Transaction [%s] not found", txHash) + } + return nil, err + } + return tx, nil +} // WithdrawSponsor // @Tags Sponsor // @Description Withdraw Sponsor // @Accept json // @Product json -// @Param request body WithdrawSponsorRequest true "WithdrawSponsorRequest Model" -// @Param is_test_net path boolean true "Is Test Net" +// @Param request body model.WithdrawSponsorRequest true "WithdrawSponsorRequest Model" // @Router /api/v1/paymaster_sponsor/withdraw [post] // @Success 200 func WithdrawSponsor(ctx *gin.Context) { + request := model.WithdrawSponsorRequest{} response := model.GetResponse() if err := ctx.ShouldBindJSON(&request); err != nil { @@ -68,80 +202,63 @@ func WithdrawSponsor(ctx *gin.Context) { response.SetHttpCode(http.StatusBadRequest).FailCode(ctx, http.StatusBadRequest, errStr) return } - //TODO Add Signature Verification - result, err := sponsor_manager.WithDrawSponsor(&request) + client := config.GetPaymasterSponsorClient(request.IsTestNet) + //validate Sign + //validate Signature + inputJson, err := json.Marshal(request) if err != nil { response.SetHttpCode(http.StatusInternalServerError).FailCode(ctx, http.StatusInternalServerError, err.Error()) return } - response.WithDataSuccess(ctx, result) - return -} - -type sponsorDepositTransaction struct { - TxHash string `json:"tx_hash"` - Amount string `json:"amount"` - UpdateType global_const.UpdateType `json:"update_type"` -} - -// GetSponsorDepositAndWithdrawTransactions -// @Tags Sponsor -// @Description Get Sponsor Deposit And Withdraw Transactions -// @Accept json -// @Product json -// @Param userId path string true "User Id" -// @Param is_test_net path boolean true "Is Test Net" -// @Router /api/v1/paymaster_sponsor/deposit_log -// @Success 200 -func GetSponsorDepositAndWithdrawTransactions(ctx *gin.Context) { - userId := ctx.Param("user_id") - textNet := ctx.Param("is_test_net") - // convertTOBool - isTestNet, _ := strconv.ParseBool(textNet) - response := model.GetResponse() - models, err := sponsor_manager.GetDepositAndWithDrawLog(userId, isTestNet) + var signerAddress string + if request.DepositSource == "dashboard" { + signerAddress = config.GetSponsorConfig().DashBoardSignerAddress + } else { + response.SetHttpCode(http.StatusBadRequest).FailCode(ctx, http.StatusBadRequest, "Deposit Source Error :Not Support Source") + return + } + err = ValidateSignature(ctx.GetHeader("relay_hash"), ctx.GetHeader("relay_signature"), inputJson, signerAddress) if err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - response.FailCode(ctx, 400, "No Deposit Transactions") - } + response.SetHttpCode(http.StatusBadRequest).FailCode(ctx, http.StatusBadRequest, err.Error()) + return } - trans := make([]sponsorDepositTransaction, 0) - for _, depositModel := range models { - tran := sponsorDepositTransaction{ - TxHash: depositModel.TxHash, - Amount: depositModel.Amount.String(), + //checkBalance + balanceModel, err := sponsor_manager.FindUserSponsorBalance(request.PayUserId, request.IsTestNet) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + response.SetHttpCode(http.StatusBadRequest).FailCode(ctx, http.StatusBadRequest, "Balance Not Found") + return } - trans = append(trans, tran) + response.SetHttpCode(http.StatusInternalServerError).FailCode(ctx, http.StatusInternalServerError, err.Error()) + return } - response.WithDataSuccess(ctx, trans) - return -} + amountFloat := big.NewFloat(request.Amount) -// GetSponsorMetaData -// @Tags Sponsor -// @Description Get Sponsor Balance -// @Accept json -// @Product json -// @Param userId path string true "User Id" -// @Router /api/v1/paymaster_sponsor/balance/{userId} -// @Success 200 -func GetSponsorMetaData(ctx *gin.Context) { - userId := ctx.Param("userId") - textNet := ctx.Param("is_test_net") - isTestNet, _ := strconv.ParseBool(textNet) - response := model.GetResponse() - balance, err := sponsor_manager.FindUserSponsorBalance(userId, isTestNet) + if balanceModel.AvailableBalance.Cmp(amountFloat) < 0 { + response.SetHttpCode(http.StatusBadRequest).FailCode(ctx, http.StatusBadRequest, "Insufficient Balance") + return + } + // U -> ETH + ethPrice, err := price_compoent.GetTokenCostInUsd(global_const.TokenTypeETH, amountFloat) if err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - response.FailCode(ctx, 400, "No Balance") - } + response.SetHttpCode(http.StatusInternalServerError).FailCode(ctx, http.StatusInternalServerError, err.Error()) + return } - result := struct { - AvailableBalance string `json:"available_balance"` - SponsorAddress string `json:"sponsor_address"` - }{ - AvailableBalance: balance.AvailableBalance.String(), - SponsorAddress: balance.SponsorAddress, + // U -> ETH -> Wei + ethCount := new(big.Float) + ethCount.Quo(ethPrice, amountFloat) + + ethWeiValue := utils.CounverEtherToWei(ethCount) + + toAddress := common.HexToAddress(request.RefundAddress) + chainId := config.GetPaymasterSponsorChainId(request.IsTestNet) + // Execute transfer + tx, err := utils.TransEth(config.GetDepositer().PrivateKey, &toAddress, client, ethWeiValue, chainId) + // WithDrawSponsor + result, err := sponsor_manager.WithDrawSponsor(&request, tx.Hash().Hex()) + if err != nil { + response.SetHttpCode(http.StatusInternalServerError).FailCode(ctx, http.StatusInternalServerError, err.Error()) + return } response.WithDataSuccess(ctx, result) return diff --git a/rpc_server/api/v1/sponsor_test.go b/rpc_server/api/v1/sponsor_test.go new file mode 100644 index 00000000..272bbc73 --- /dev/null +++ b/rpc_server/api/v1/sponsor_test.go @@ -0,0 +1,80 @@ +package v1 + +import ( + "AAStarCommunity/EthPaymaster_BackService/common/global_const" + "AAStarCommunity/EthPaymaster_BackService/common/model" + "AAStarCommunity/EthPaymaster_BackService/config" + "AAStarCommunity/EthPaymaster_BackService/sponsor_manager" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/crypto" + "testing" +) + +func TestValidateDeposit(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + config.InitConfig("../../../config/basic_strategy_config.json", "../../../config/basic_config.json", "../../../config/secret_config.json") + sponsor_manager.Init() + request := &model.DepositSponsorRequest{ + DepositAddress: "0xFD44DF0Fe211d5EFDBe1423483Fcb3FDeF84540f", + TxHash: "0x367428ad744c2fd80054283f7143b934d630433f9c40a411d91e65893dbabdf1", + PayUserId: "5", + DepositSource: "dashboard", + IsTestNet: true, + } + sender, amount, err := validateDeposit(request) + if err != nil { + t.Error(err) + return + } + t.Logf("sender: %v, amount: %v", sender.Hex(), amount.String()) + +} +func TestValidateSignature(t *testing.T) { + //Only For Test + //privateKeyHex: 95ecbd0ae2055889d6c40ed042ef846d091313541482e61129f48867cd15fc4a + //publicKey: 0401e57b7947d19b224a98700dea8bcff3dd556980823aca6182e12eebe64e42ecee690d4c2d6eee6bbf9f82423e38314beec2be0a6833741a917a8abaebf66a76 + //address: 0x3aCF4b1F443a088186Cbd66c5F81479C6e968eCA + request := &model.DepositSponsorRequest{ + DepositAddress: "0xFD44DF0Fe211d5EFDBe1423483Fcb3FDeF84540f", + TxHash: "0x367428ad744c2fd80054283f7143b934d630433f9c40a411d91e65893dbabdf1", + PayUserId: "5", + DepositSource: "dashboard", + IsTestNet: true, + } + + jsonData, err := json.Marshal(request) + if err != nil { + fmt.Println("Error marshalling JSON:", err) + return + } + hash := sha256.New() + hash.Write(jsonData) + hashByte := hash.Sum(nil) + hashHex := hex.EncodeToString(hashByte) + t.Logf("hash: %v", hashHex) + signerEoa, err := global_const.NewEoa("95ecbd0ae2055889d6c40ed042ef846d091313541482e61129f48867cd15fc4a") + if err != nil { + t.Error(err) + return + } + signatureByte, err := crypto.Sign(accounts.TextHash(hashByte), signerEoa.PrivateKey) + if err != nil { + t.Error(err) + return + } + signatureByteHex := hex.EncodeToString(signatureByte) + t.Logf("signatureByteHex: %v", signatureByteHex) + + err = ValidateSignature(hashHex, signatureByteHex, jsonData, "0x3aCF4b1F443a088186Cbd66c5F81479C6e968eCA") + if err != nil { + t.Error(err) + return + } + t.Logf("ValidateSignature success") +} diff --git a/rpc_server/middlewares/auth.go b/rpc_server/middlewares/auth.go index e5570230..d8eabd66 100644 --- a/rpc_server/middlewares/auth.go +++ b/rpc_server/middlewares/auth.go @@ -2,108 +2,37 @@ package middlewares import ( "AAStarCommunity/EthPaymaster_BackService/common/global_const" - "AAStarCommunity/EthPaymaster_BackService/common/model" - "AAStarCommunity/EthPaymaster_BackService/envirment" "AAStarCommunity/EthPaymaster_BackService/service/dashboard_service" - jwt "github.com/appleboy/gin-jwt/v2" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" - "golang.org/x/xerrors" - "time" + "net/http" ) type ApiKey struct { Key string `form:"apiKey" json:"apiKey" binding:"required"` } -var jwtMiddleware *jwt.GinJWTMiddleware - -func GinJwtMiddleware() *jwt.GinJWTMiddleware { - return jwtMiddleware -} - func AuthHandler() gin.HandlerFunc { - m, _ := jwt.New(&jwt.GinJWTMiddleware{ - Realm: envirment.GetJwtKey().Realm, - Key: []byte(envirment.GetJwtKey().Security), - Timeout: time.Hour * 24, - MaxRefresh: time.Hour / 2, - IdentityKey: "jti", - PayloadFunc: func(data interface{}) jwt.MapClaims { - if v, ok := data.(string); ok { - return jwt.MapClaims{ - "jti": v, - } - } - return jwt.MapClaims{} - }, - Authenticator: func(c *gin.Context) (interface{}, error) { - var apiKey ApiKey - if err := c.ShouldBind(&apiKey); err != nil { - return "", jwt.ErrMissingLoginValues - } - apiModel, err := dashboard_service.GetAPiInfoByApiKey(apiKey.Key) - if err != nil { - return "", err - } - err = checkAPIKeyAvailable(apiModel) - if err != nil { - return "", err - } - return apiKey.Key, nil - }, - //every Request will be checked - Authorizator: func(data interface{}, c *gin.Context) bool { - // always return true unless the permission feature started - if data == nil { - logrus.Errorf("Authorizator id is nil") - return false - } - apiKey := data.(string) - if apiKey == "" { - logrus.Errorf("Authorizator id is nil") - return false - } + return func(c *gin.Context) { + apiKey := c.Query("apiKey") + if apiKey == "" { + c.JSON(http.StatusForbidden, gin.H{"error": "ApiKey is mandatory, visit to https://dashboard.aastar.io for more detail."}) + c.Abort() + return + } + apiModel, err := dashboard_service.GetAPiInfoByApiKey(apiKey) + if err != nil { + logrus.Errorf("GetAPiInfoByApiKey err: %v", err) + c.JSON(http.StatusBadRequest, gin.H{"error": "Can Not Find Your Api Key"}) + c.Abort() + return + } + if apiModel.Disable { + c.JSON(http.StatusForbidden, gin.H{"error": "Api Key Is Disabled"}) + c.Abort() + return + } - apiModel, err := dashboard_service.GetAPiInfoByApiKey(apiKey) - if err != nil { - c.Set("ERROR_REASON", err.Error()) - return false - } - if apiModel == nil { - c.Set("ERROR_REASON", "API Key is not found") - return false - } - err = checkAPIKeyAvailable(apiModel) - if err != nil { - c.Set("ERROR_REASON", err.Error()) - return false - } - c.Set(global_const.ContextKeyApiMoDel, apiModel) - return true - }, - Unauthorized: func(c *gin.Context, code int, message string) { - if c.GetString("ERROR_REASON") != "" { - message = c.GetString("ERROR_REASON") - } - c.JSON(code, gin.H{ - "code": code, - "message": message, - }) - }, - TokenLookup: "header: Authorization", - TokenHeadName: "Bearer", - TimeFunc: time.Now, - HTTPStatusMessageFunc: func(e error, c *gin.Context) string { - return "401 Unauthorized" - }, - }) - jwtMiddleware = m - return m.MiddlewareFunc() -} -func checkAPIKeyAvailable(apiModel *model.ApiKeyModel) error { - if apiModel.Disable { - return xerrors.Errorf("API Key is disabled") + c.Set(global_const.ContextKeyApiMoDel, apiModel) } - return nil } diff --git a/rpc_server/middlewares/rate_limit.go b/rpc_server/middlewares/rate_limit.go index f3cfdd57..fc1b6079 100644 --- a/rpc_server/middlewares/rate_limit.go +++ b/rpc_server/middlewares/rate_limit.go @@ -3,7 +3,6 @@ package middlewares import ( "AAStarCommunity/EthPaymaster_BackService/common/global_const" "AAStarCommunity/EthPaymaster_BackService/common/model" - "AAStarCommunity/EthPaymaster_BackService/rpc_server/api/utils" "errors" "github.com/gin-gonic/gin" "golang.org/x/time/rate" @@ -20,17 +19,15 @@ var limiter map[string]*rate.Limiter // RateLimiterByApiKeyHandler represents the rate limit by each ApiKey for each api calling func RateLimiterByApiKeyHandler() gin.HandlerFunc { return func(ctx *gin.Context) { - if exists, current := utils.CurrentUser(ctx); exists { - apiKeyModel := ctx.MustGet(global_const.ContextKeyApiMoDel) - defaultLimit := DefaultLimit - defaultLimit = apiKeyModel.(*model.ApiKeyModel).RateLimit - if limiting(¤t, defaultLimit) { - ctx.Next() - } else { - _ = ctx.AbortWithError(http.StatusTooManyRequests, errors.New("too many requests")) - } + apiKeyModelInterface := ctx.MustGet(global_const.ContextKeyApiMoDel) + defaultLimit := DefaultLimit + apiKeyModel := apiKeyModelInterface.(*model.ApiKeyModel) + defaultLimit = apiKeyModel.RateLimit + + if limiting(&apiKeyModel.ApiKey, defaultLimit) { + ctx.Next() } else { - _ = ctx.AbortWithError(http.StatusUnauthorized, errors.New("401 Unauthorized")) + _ = ctx.AbortWithError(http.StatusTooManyRequests, errors.New("too many requests")) } } } diff --git a/rpc_server/routers/boot.go b/rpc_server/routers/boot.go index aa33bd97..a08235e8 100644 --- a/rpc_server/routers/boot.go +++ b/rpc_server/routers/boot.go @@ -11,7 +11,6 @@ import ( "github.com/go-playground/validator/v10" swaggerFiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" - "io" "net/http" ) @@ -60,18 +59,17 @@ func buildRoute(routers *gin.Engine) { // buildMod set Mode by envirment func buildMod(routers *gin.Engine) { - + buildSwagger(routers) // prod mode if envirment.Environment.IsProduction() { gin.SetMode(gin.ReleaseMode) - gin.DefaultWriter = io.Discard // disable gin log + //gin.DefaultWriter = io.Discard // disable gin log return } // dev mod if envirment.Environment.IsDevelopment() { gin.SetMode(gin.DebugMode) - buildSwagger(routers) return } } diff --git a/rpc_server/routers/routers_map.go b/rpc_server/routers/routers_map.go index 5e4f7c56..92d397b5 100644 --- a/rpc_server/routers/routers_map.go +++ b/rpc_server/routers/routers_map.go @@ -11,18 +11,14 @@ var PublicRouterMaps []RouterMap func init() { PrivateRouterMaps = make([]RouterMap, 0) PrivateRouterMaps = append(PrivateRouterMaps, RouterMap{string(Paymaster), []RestfulMethod{POST}, v1.Paymaster}) - PublicRouterMaps = append(PublicRouterMaps, RouterMap{string(Auth), []RestfulMethod{POST}, api.Auth}) PublicRouterMaps = append(PublicRouterMaps, RouterMap{string(Healthz), []RestfulMethod{GET, HEAD, OPTIONS}, api.Healthz}) - PublicRouterMaps = append(PublicRouterMaps, RouterMap{string(GetSponsorLog), []RestfulMethod{GET}, v1.GetSponsorDepositAndWithdrawTransactions}) PublicRouterMaps = append(PublicRouterMaps, RouterMap{string(DepositSponsor), []RestfulMethod{POST}, v1.DepositSponsor}) PublicRouterMaps = append(PublicRouterMaps, RouterMap{string(WithdrawSponsor), []RestfulMethod{POST}, v1.WithdrawSponsor}) - PublicRouterMaps = append(PublicRouterMaps, RouterMap{string(GetSponsorData), []RestfulMethod{GET}, v1.GetSponsorMetaData}) } type Path string const ( - Auth Path = "api/auth" Healthz Path = "api/healthz" Paymaster Path = "api/v1/paymaster/:network" GetSponsorLog Path = "api/v1/paymaster_sponsor/deposit_log" diff --git a/sponsor_manager/sponsor_balance_repository.go b/sponsor_manager/sponsor_balance_repository.go index dd2adfee..15c8416a 100644 --- a/sponsor_manager/sponsor_balance_repository.go +++ b/sponsor_manager/sponsor_balance_repository.go @@ -11,6 +11,7 @@ type UserSponsorBalanceDBModel struct { model.BaseData PayUserId string `gorm:"type:varchar(255);index" json:"pay_user_id"` AvailableBalance BigFloat `gorm:"type:numeric(30,18)" json:"available_balance"` + SponsoredBalance BigFloat `gorm:"type:numeric(30,18)" json:"sponsored_balance"` LockBalance BigFloat `gorm:"type:numeric(30,18)" json:"lock_balance"` Source string `gorm:"type:varchar(255)" json:"source"` SponsorAddress string `gorm:"type:varchar(255)" json:"sponsor_address"` diff --git a/sponsor_manager/sponsor_changelog_repository.go b/sponsor_manager/sponsor_changelog_repository.go index 768ed50d..882b6116 100644 --- a/sponsor_manager/sponsor_changelog_repository.go +++ b/sponsor_manager/sponsor_changelog_repository.go @@ -26,11 +26,7 @@ func (UserSponsorBalanceUpdateLogDBModel) TableName() string { func AddBalanceChangeLog(changeDbModel *UserSponsorBalanceUpdateLogDBModel) error { return relayDB.Create(changeDbModel).Error } -func LogBalanceChange(updateType global_const.UpdateType, balanceType global_const.BalanceType, data interface{}, amount *big.Float) { - //TODO - return -} func GetDepositAndWithDrawLog(userId string, IsTestNet bool) (models []*UserSponsorBalanceUpdateLogDBModel, err error) { tx := relayDB.Model(&UserSponsorBalanceUpdateLogDBModel{}).Where("pay_user_id = ?", userId).Where("is_test_net = ?", IsTestNet).Where("update_type in (?)", []global_const.UpdateType{global_const.UpdateTypeDeposit, global_const.UpdateTypeWithdraw}).Find(&models) if tx.Error != nil { diff --git a/sponsor_manager/sponsor_service.go b/sponsor_manager/sponsor_service.go index 009d001c..59f46a01 100644 --- a/sponsor_manager/sponsor_service.go +++ b/sponsor_manager/sponsor_service.go @@ -7,6 +7,7 @@ import ( "AAStarCommunity/EthPaymaster_BackService/config" "encoding/json" "errors" + "github.com/sirupsen/logrus" "golang.org/x/xerrors" "gorm.io/driver/postgres" "gorm.io/gorm" @@ -28,6 +29,7 @@ var ( func Init() { onlyOnce.Do(func() { + logrus.Info("Init Sponsor Manager") relayDBDsn := config.GetRelayDBDSN() relayDBVar, err := gorm.Open(postgres.Open(relayDBDsn), &gorm.Config{}) @@ -77,7 +79,7 @@ func LockUserBalance(userId string, userOpHash []byte, isTestNet bool, availableBalance := new(big.Float).Sub(balanceModel.AvailableBalance.Float, lockAmount) balanceModel.LockBalance = BigFloat{lockBalance} balanceModel.AvailableBalance = BigFloat{availableBalance} - err = utils.DBTransactional(relayDB, func() error { + err = utils.DBTransactional(relayDB, func(tx *gorm.DB) error { if updateErr := relayDB.Model(&UserSponsorBalanceDBModel{}). Where("pay_user_id = ?", balanceModel.PayUserId). Where("is_test_net = ?", isTestNet).Updates(balanceModel).Error; updateErr != nil { @@ -91,7 +93,7 @@ func LockUserBalance(userId string, userOpHash []byte, isTestNet bool, IsTestNet: isTestNet, UpdateType: global_const.UpdateTypeLock, } - if createErr := relayDB.Create(changeModel).Error; createErr != nil { + if createErr := tx.Create(changeModel).Error; createErr != nil { return err } return nil @@ -109,13 +111,14 @@ func ReleaseBalanceWithActualCost(userId string, userOpHash []byte, return nil, err } balanceModel, err := findUserSponsor(changeModel.PayUserId, changeModel.IsTestNet) - + //TODO 10% Fee lockBalance := changeModel.Amount balanceModel.LockBalance = BigFloat{new(big.Float).Sub(balanceModel.LockBalance.Float, lockBalance.Float)} refundBalance := new(big.Float).Sub(lockBalance.Float, actualGasCost) balanceModel.AvailableBalance = BigFloat{new(big.Float).Add(balanceModel.AvailableBalance.Float, refundBalance)} - err = utils.DBTransactional(relayDB, func() error { + balanceModel.SponsoredBalance = BigFloat{new(big.Float).Add(balanceModel.SponsoredBalance.Float, actualGasCost)} + err = utils.DBTransactional(relayDB, func(tx *gorm.DB) error { if updateErr := relayDB.Model(&UserSponsorBalanceDBModel{}). Model(&UserSponsorBalanceDBModel{}). Where("pay_user_id = ?", balanceModel.PayUserId). @@ -159,7 +162,7 @@ func ReleaseUserOpHashLockWhenFail(userOpHash []byte, isTestNet bool) (*UserSpon lockBalance := changeModel.Amount balanceModel.LockBalance = BigFloat{new(big.Float).Sub(balanceModel.LockBalance.Float, lockBalance.Float)} balanceModel.AvailableBalance = BigFloat{new(big.Float).Add(balanceModel.AvailableBalance.Float, lockBalance.Float)} - err = utils.DBTransactional(relayDB, func() error { + err = utils.DBTransactional(relayDB, func(tx *gorm.DB) error { if updateErr := relayDB.Model(&UserSponsorBalanceDBModel{}). Where("pay_user_id = ?", balanceModel.PayUserId). Where("is_test_net = ?", isTestNet).Updates(balanceModel).Error; updateErr != nil { @@ -184,76 +187,87 @@ func ReleaseUserOpHashLockWhenFail(userOpHash []byte, isTestNet bool) (*UserSpon return balanceModel, nil } -//----------Functions---------- - -func DepositSponsor(input *model.DepositSponsorRequest) (*UserSponsorBalanceDBModel, error) { - - balanceModel, err := FindUserSponsorBalance(input.PayUserId, input.IsTestNet) +func GetLogByTxHash(txHash string, isTestNet bool) (*UserSponsorBalanceUpdateLogDBModel, error) { + changeModel := &UserSponsorBalanceUpdateLogDBModel{} + err := relayDB.Where("tx_hash = ?", txHash).Where("is_test_net = ?", isTestNet).First(changeModel).Error if err != nil { - return nil, err + return changeModel, err } + return changeModel, nil +} - err = utils.DBTransactional(relayDB, func() error { - if errors.Is(err, gorm.ErrRecordNotFound) { +// ----------Functions---------- +type DepositSponsorInput struct { + TxHash string `json:"tx_hash"` + From string `json:"from"` + To string `json:"to"` + Amount *big.Float + IsTestNet bool `json:"is_test_net"` + PayUserId string `json:"pay_user_id"` + TxInfo map[string]string + Source string +} + +func DepositSponsor(input *DepositSponsorInput) (*UserSponsorBalanceDBModel, error) { + balanceModel, findBalanceError := FindUserSponsorBalance(input.PayUserId, input.IsTestNet) + txErr := utils.DBTransactional(relayDB, func(tx *gorm.DB) error { + if errors.Is(findBalanceError, gorm.ErrRecordNotFound) { //init Data balanceModel = &UserSponsorBalanceDBModel{} balanceModel.AvailableBalance = BigFloat{big.NewFloat(0)} balanceModel.PayUserId = input.PayUserId balanceModel.LockBalance = BigFloat{big.NewFloat(0)} + balanceModel.SponsoredBalance = BigFloat{big.NewFloat(0)} balanceModel.IsTestNet = input.IsTestNet - err = relayDB.Create(balanceModel).Error + balanceModel.Source = input.Source + balanceModel.SponsorAddress = input.From + err := tx.Create(balanceModel).Error if err != nil { - + logrus.Info("Create Balance ERROR ") return err } } - if err != nil { - - return err - } newAvailableBalance := BigFloat{new(big.Float).Add(balanceModel.AvailableBalance.Float, input.Amount)} balanceModel.AvailableBalance = newAvailableBalance - if updateErr := relayDB.Model(balanceModel). + if updateErr := tx.Model(balanceModel). Where("pay_user_id = ?", balanceModel.PayUserId). Where("is_test_net = ?", input.IsTestNet).Updates(balanceModel).Error; updateErr != nil { - return updateErr } - - txInfoJSon, _ := json.Marshal(input.TxInfo) changeModel := &UserSponsorBalanceUpdateLogDBModel{ PayUserId: input.PayUserId, Amount: BigFloat{input.Amount}, - Source: "Deposit", + Source: input.Source, IsTestNet: input.IsTestNet, UpdateType: global_const.UpdateTypeDeposit, TxHash: input.TxHash, - TxInfo: txInfoJSon, } - if createErr := relayDB.Create(changeModel).Error; createErr != nil { + if input.TxInfo != nil { + txInfo, _ := json.Marshal(input.TxInfo) + changeModel.TxInfo = txInfo + } + if createErr := tx.Create(changeModel).Error; createErr != nil { return createErr } return nil }) - if err != nil { - return nil, err - } - return balanceModel, nil + return balanceModel, txErr } -func WithDrawSponsor(input *model.WithdrawSponsorRequest) (*UserSponsorBalanceDBModel, error) { +func WithDrawSponsor(input *model.WithdrawSponsorRequest, txHash string) (*UserSponsorBalanceDBModel, error) { + amount := big.NewFloat(input.Amount) balanceModel, err := FindUserSponsorBalance(input.PayUserId, input.IsTestNet) if err != nil { return nil, err } - if balanceModel.AvailableBalance.Cmp(input.Amount) < 0 { - return nil, xerrors.Errorf("Insufficient balance [%s] not Enough to Withdraw [%s]", balanceModel.AvailableBalance.String(), input.Amount.String()) + if balanceModel.AvailableBalance.Cmp(amount) < 0 { + return nil, xerrors.Errorf("Insufficient balance [%s] not Enough to Withdraw [%s]", balanceModel.AvailableBalance.String(), amount.String()) } - newAvailableBalance := new(big.Float).Sub(balanceModel.AvailableBalance.Float, input.Amount) + newAvailableBalance := new(big.Float).Sub(balanceModel.AvailableBalance.Float, amount) balanceModel.AvailableBalance = BigFloat{newAvailableBalance} - err = utils.DBTransactional(relayDB, func() error { + err = utils.DBTransactional(relayDB, func(tx *gorm.DB) error { if updateErr := relayDB.Model(&UserSponsorBalanceDBModel{}). Where("pay_user_id = ?", balanceModel.PayUserId). Where("is_test_net = ?", input.IsTestNet).Updates(balanceModel).Error; updateErr != nil { @@ -261,11 +275,11 @@ func WithDrawSponsor(input *model.WithdrawSponsorRequest) (*UserSponsorBalanceDB } changeModel := &UserSponsorBalanceUpdateLogDBModel{ PayUserId: input.PayUserId, - Amount: BigFloat{input.Amount}, - Source: "Withdraw", + Amount: BigFloat{amount}, + Source: input.WithdrawSource, IsTestNet: input.IsTestNet, UpdateType: global_const.UpdateTypeWithdraw, - TxHash: input.TxHash, + TxHash: txHash, } if createErr := relayDB.Create(changeModel).Error; createErr != nil { return createErr diff --git a/sponsor_manager/sponsor_test.go b/sponsor_manager/sponsor_test.go index 9169f8f4..05c96aa7 100644 --- a/sponsor_manager/sponsor_test.go +++ b/sponsor_manager/sponsor_test.go @@ -28,11 +28,9 @@ func TestSponsor(t *testing.T) { { "TestDepositSponsor", func(t *testing.T) { - request := model.DepositSponsorRequest{ + request := DepositSponsorInput{ PayUserId: "test", - Amount: big.NewFloat(3), IsTestNet: true, - Source: string(SourceDashboard), } result, err := DepositSponsor(&request) if err != nil { @@ -107,11 +105,10 @@ func testWithDrawSponsor(t *testing.T, userId string, isTestNet bool, amount *bi request := model.WithdrawSponsorRequest{ PayUserId: userId, - Amount: amount, IsTestNet: isTestNet, - TxInfo: map[string]string{}, } - result, err := WithDrawSponsor(&request) + txHash := "0x123456" + result, err := WithDrawSponsor(&request, txHash) if err != nil { t.Error(err) }