From 3e5c9f7862911da53f0314cca606697bc8c0468f Mon Sep 17 00:00:00 2001 From: dylanyang Date: Mon, 11 Mar 2024 22:44:25 +0800 Subject: [PATCH] add Support gas --- common/model/api_request.go | 2 +- common/model/api_response.go | 5 +- common/model/strategy.go | 3 +- common/model/user_operation.go | 6 +- common/types/chain.go | 14 +++-- common/types/error_prefix.go | 9 +++ common/types/pay_type.go | 8 +++ common/utils/util.go | 18 ++++++ common/utils/util_test.go | 5 ++ .../erc20_paymaster_generator.go | 11 ++++ .../paymaster_generator.go | 7 +++ .../vertifying_paymaster_generator.go | 12 ++++ rpc_server/routers/boot.go | 18 +++++- service/chain_service/chain_config.go | 8 +-- service/chain_service/chain_service.go | 17 +++++- .../dashboard_service/dashboard_service.go | 29 +++++++++- service/gas_service/gas_computor.go | 26 +++++++++ service/operator/try_pay_user_op_execute.go | 23 ++++++++ service/pay_service/pay_service.go | 1 + service/validator_service/basic_validator.go | 56 +++++++++++++++++++ 20 files changed, 259 insertions(+), 19 deletions(-) create mode 100644 common/types/error_prefix.go create mode 100644 common/types/pay_type.go create mode 100644 paymaster_data_generator/erc20_paymaster_generator.go create mode 100644 paymaster_data_generator/paymaster_generator.go create mode 100644 paymaster_data_generator/vertifying_paymaster_generator.go diff --git a/common/model/api_request.go b/common/model/api_request.go index 5069dcc7..6a52f9ce 100644 --- a/common/model/api_request.go +++ b/common/model/api_request.go @@ -7,7 +7,7 @@ import ( type TryPayUserOpRequest struct { ForceStrategyId string `json:"force_strategy_id"` - ForceNetwork types.NetWork `json:"force_network"` + ForceNetwork types.Network `json:"force_network"` ForceToken string `json:"force_token"` ForceEntryPointAddress string `json:"force_entrypoint_address"` UserOperation UserOperationItem `json:"user_operation"` diff --git a/common/model/api_response.go b/common/model/api_response.go index e0175ee2..dcb0d4b4 100644 --- a/common/model/api_response.go +++ b/common/model/api_response.go @@ -16,11 +16,12 @@ type TryPayUserOpResponse struct { } type ComputeGasResponse struct { + CallGasLimit uint64 `json:"call_gas_limit"` GasPriceInWei uint64 `json:"gas_price_wei"` // wei GasPriceInGwei *big.Float `json:"gas_price_gwei"` GasPriceInEther string `json:"gas_price_ether"` TokenCost string `json:"token_cost"` - Network types.NetWork `json:"network"` + Network types.Network `json:"network"` Token types.TokenType `json:"token"` UsdCost string `json:"usd_cost"` BlobEnable bool `json:"blob_enable"` @@ -36,7 +37,7 @@ type GetSupportEntryPointResponse struct { type EntrypointDomain struct { Address string `json:"address"` Desc string `json:"desc"` - NetWork types.NetWork `json:"network"` + NetWork types.Network `json:"network"` StrategyId string `json:"strategy_id"` } diff --git a/common/model/strategy.go b/common/model/strategy.go index a86516c0..8d02f8cf 100644 --- a/common/model/strategy.go +++ b/common/model/strategy.go @@ -6,7 +6,8 @@ type Strategy struct { Id string `json:"id"` EntryPointAddress string `json:"entrypoint_address"` PayMasterAddress string `json:"paymaster_address"` - NetWork types.NetWork `json:"network"` + PayType types.PayType `json:"pay_type"` + NetWork types.Network `json:"network"` Token types.TokenType `json:"token"` Description string `json:"description"` ExecuteRestriction StrategyExecuteRestriction `json:"execute_restriction"` diff --git a/common/model/user_operation.go b/common/model/user_operation.go index c1fb22b5..1bfd2361 100644 --- a/common/model/user_operation.go +++ b/common/model/user_operation.go @@ -1,7 +1,7 @@ package model type UserOperationItem struct { - Sender string `json:"sender" binding:"required"` + Sender string `json:"sender" binding:"required,hexParam"` Nonce string `json:"nonce" binding:"required"` InitCode string `json:"init_code"` CallData string `json:"call_data" binding:"required"` @@ -10,6 +10,6 @@ type UserOperationItem struct { PreVerificationGas string `json:"per_verification_gas" binding:"required"` MaxFeePerGas string `json:"max_fee_per_gas" binding:"required"` MaxPriorityFeePerGas string `json:"max_priority_fee_per_gas" binding:"required"` - Signature string `json:"signature"` - //paymasterAndData string `json:"paymaster_and_data"` + Signature string `json:"signature" binding:"required"` + PaymasterAndData string `json:"paymaster_and_data"` } diff --git a/common/types/chain.go b/common/types/chain.go index fa0e68d1..b4f0c507 100644 --- a/common/types/chain.go +++ b/common/types/chain.go @@ -13,10 +13,16 @@ type NetworkInfo struct { // Optimism Chain = "Optimism" //) -type NetWork string +type Network string const ( - Ethereum NetWork = "ethereum" - Sepolia NetWork = "sepolia" - Arbitrum NetWork = "arbitrum" + Ethereum Network = "ethereum" + Sepolia Network = "sepolia" + Arbitrum Network = "arbitrum" ) + +var TestNetWork = map[Network]bool{} + +func init() { + TestNetWork[Sepolia] = true +} diff --git a/common/types/error_prefix.go b/common/types/error_prefix.go new file mode 100644 index 00000000..8cb95325 --- /dev/null +++ b/common/types/error_prefix.go @@ -0,0 +1,9 @@ +package types + +type ErrorPrefix string + +const ( + ValidateParamError ErrorPrefix = "AA2" + ValidateUserOpError ErrorPrefix = "AA3" + ValidateGasError ErrorPrefix = "AA4" +) diff --git a/common/types/pay_type.go b/common/types/pay_type.go new file mode 100644 index 00000000..258a39d9 --- /dev/null +++ b/common/types/pay_type.go @@ -0,0 +1,8 @@ +package types + +type PayType string + +const ( + PayTypeVerifying PayType = "0" + PayTypeERC20 PayType = "1" +) diff --git a/common/utils/util.go b/common/utils/util.go index faef363c..621b6ddd 100644 --- a/common/utils/util.go +++ b/common/utils/util.go @@ -4,8 +4,12 @@ import ( "AAStarCommunity/EthPaymaster_BackService/common/model" "encoding/json" "github.com/ethereum/go-ethereum/crypto" + "regexp" + "strconv" ) +var HexPattern = regexp.MustCompile(`^0x[a-fA-F\d]*$`) + func GenerateMockUserOperation() *model.UserOperationItem { //TODO use config return &model.UserOperationItem{ @@ -21,6 +25,20 @@ func GenerateMockUserOperation() *model.UserOperationItem { Signature: "0x760868cd7d9539c6e31c2169c4cab6817beb8247516a90e4301e929011451658623455035b83d38e987ef2e57558695040a25219c39eaa0e31a0ead16a5c925c1c", } } +func ValidateHex(value string) bool { + if HexPattern.MatchString(value) { + return true + } + return false +} +func IsStringInUint64Range(s string) bool { + num, err := strconv.ParseUint(s, 10, 64) + if err != nil { + return false + } + // 0 <= num <= MaxUint64 + return num <= ^uint64(0) +} func GenerateUserOperation() *model.UserOperationItem { return &model.UserOperationItem{} } diff --git a/common/utils/util_test.go b/common/utils/util_test.go index f9574b11..e6d69edb 100644 --- a/common/utils/util_test.go +++ b/common/utils/util_test.go @@ -31,3 +31,8 @@ func TestSignUserOp(t *testing.T) { fmt.Printf("singature: %s\n", singature) } + +func TestValidate(t *testing.T) { + userOp := GenerateMockUserOperation() + assert.True(t, ValidateHex(userOp.Sender)) +} diff --git a/paymaster_data_generator/erc20_paymaster_generator.go b/paymaster_data_generator/erc20_paymaster_generator.go new file mode 100644 index 00000000..735af168 --- /dev/null +++ b/paymaster_data_generator/erc20_paymaster_generator.go @@ -0,0 +1,11 @@ +package paymaster_data_generator + +import "AAStarCommunity/EthPaymaster_BackService/common/model" + +type Erc20PaymasterGenerator struct { +} + +func (e Erc20PaymasterGenerator) GeneratePayMaster(strategy *model.Strategy, userOp *model.UserOperationItem) (string, error) { + //ERC20:[0-1]pay type,[1-21]paymaster address,[21-53]token Amount + return "0x", nil +} diff --git a/paymaster_data_generator/paymaster_generator.go b/paymaster_data_generator/paymaster_generator.go new file mode 100644 index 00000000..f7f935b3 --- /dev/null +++ b/paymaster_data_generator/paymaster_generator.go @@ -0,0 +1,7 @@ +package paymaster_data_generator + +import "AAStarCommunity/EthPaymaster_BackService/common/model" + +type PaymasterGenerator interface { + GeneratePayMaster(strategy *model.Strategy, userOp *model.UserOperationItem) (string, error) +} diff --git a/paymaster_data_generator/vertifying_paymaster_generator.go b/paymaster_data_generator/vertifying_paymaster_generator.go new file mode 100644 index 00000000..05ec5d6f --- /dev/null +++ b/paymaster_data_generator/vertifying_paymaster_generator.go @@ -0,0 +1,12 @@ +package paymaster_data_generator + +import "AAStarCommunity/EthPaymaster_BackService/common/model" + +type VerifyingPaymasterGenerator struct { +} + +func (v VerifyingPaymasterGenerator) GeneratePayMaster(strategy *model.Strategy, userOp *model.UserOperationItem) (string, error) { + //verifying:[0-1]pay type,[1-21]paymaster address,[21-85]valid timestamp,[85-] signature + return "0x", nil + +} diff --git a/rpc_server/routers/boot.go b/rpc_server/routers/boot.go index 70263d43..a8ab5d12 100644 --- a/rpc_server/routers/boot.go +++ b/rpc_server/routers/boot.go @@ -2,20 +2,36 @@ package routers import ( "AAStarCommunity/EthPaymaster_BackService/common/model" + "AAStarCommunity/EthPaymaster_BackService/common/utils" "AAStarCommunity/EthPaymaster_BackService/conf" "AAStarCommunity/EthPaymaster_BackService/docs" "AAStarCommunity/EthPaymaster_BackService/rpc_server/middlewares" "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" + "github.com/go-playground/validator/v10" swaggerFiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" "io" "net/http" ) +var hexParam validator.Func = func(fl validator.FieldLevel) bool { + param, ok := fl.Field().Interface().(string) + if ok { + return utils.ValidateHex(param) + } + return true +} + // SetRouters set routers func SetRouters() (routers *gin.Engine) { routers = gin.New() - + if v, ok := binding.Validator.Engine().(*validator.Validate); ok { + err := v.RegisterValidation("hexParam", hexParam) + if err != nil { + return nil + } + } buildMod(routers) buildRoute(routers) routers.NoRoute(func(ctx *gin.Context) { diff --git a/service/chain_service/chain_config.go b/service/chain_service/chain_config.go index 3ce2b73d..d4240b59 100644 --- a/service/chain_service/chain_config.go +++ b/service/chain_service/chain_config.go @@ -5,8 +5,8 @@ import ( "github.com/ethereum/go-ethereum/ethclient" ) -var NetworkInfoMap map[types.NetWork]*types.NetworkInfo -var NetWorkClientMap map[types.NetWork]*ethclient.Client +var NetworkInfoMap map[types.Network]*types.NetworkInfo +var NetWorkClientMap map[types.Network]*ethclient.Client func init() { ConfigInit() @@ -14,7 +14,7 @@ func init() { } func ConfigInit() { //TODO api key secret store - NetworkInfoMap = map[types.NetWork]*types.NetworkInfo{ + NetworkInfoMap = map[types.Network]*types.NetworkInfo{ types.Ethereum: { Name: "ethereum", RpcUrl: "https://eth-mainnet.g.alchemy.com/v2/bIZQS43-rJMgv2_SiHqfVvXa-Z1UGoGt", @@ -27,7 +27,7 @@ func ConfigInit() { } func ClientInit() { - NetWorkClientMap = make(map[types.NetWork]*ethclient.Client) + NetWorkClientMap = make(map[types.Network]*ethclient.Client) for chain, networkInfo := range NetworkInfoMap { client, err := ethclient.Dial(networkInfo.RpcUrl) if err != nil { diff --git a/service/chain_service/chain_service.go b/service/chain_service/chain_service.go index 0a43ca0e..fbb86e59 100644 --- a/service/chain_service/chain_service.go +++ b/service/chain_service/chain_service.go @@ -3,7 +3,9 @@ package chain_service import ( "AAStarCommunity/EthPaymaster_BackService/common/types" "context" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" "golang.org/x/xerrors" "math/big" ) @@ -11,7 +13,7 @@ import ( var GweiFactor = new(big.Float).SetInt(big.NewInt(1e9)) var EthWeiFactor = new(big.Float).SetInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) -func CheckContractAddressAccess(contract string, chain types.NetWork) (bool, error) { +func CheckContractAddressAccess(contract string, chain types.Network) (bool, error) { if chain == "" { return false, xerrors.Errorf("chain can not be empty") } @@ -32,7 +34,7 @@ func CheckContractAddressAccess(contract string, chain types.NetWork) (bool, err } // GetGasPrice return gas price in wei, gwei, ether -func GetGasPrice(chain types.NetWork) (*big.Int, *big.Float, *string, error) { +func GetGasPrice(chain types.Network) (*big.Int, *big.Float, *string, error) { client, exist := NetWorkClientMap[chain] if !exist { return nil, nil, nil, xerrors.Errorf("chain Client [%s] not exist", chain) @@ -50,3 +52,14 @@ func GetGasPrice(chain types.NetWork) (*big.Int, *big.Float, *string, error) { gasPriceInEtherStr := gasPriceInEther.Text('f', 18) return priceWei, gasPriceInGwei, &gasPriceInEtherStr, nil } + +func GetEntryPointDeposit(entrypoint string, depositAddress string) uint256.Int { + return uint256.Int{1} +} +func EstimateGasLimitAndCost(chain types.Network, msg ethereum.CallMsg) (uint64, error) { + client, exist := NetWorkClientMap[chain] + if !exist { + return 0, xerrors.Errorf("chain Client [%s] not exist", chain) + } + return client.EstimateGas(context.Background(), msg) +} diff --git a/service/dashboard_service/dashboard_service.go b/service/dashboard_service/dashboard_service.go index 0aa0b4c0..ba5feec6 100644 --- a/service/dashboard_service/dashboard_service.go +++ b/service/dashboard_service/dashboard_service.go @@ -6,7 +6,10 @@ import ( "errors" ) +// TODO just Temp Mock var mockStrategyMap = map[string]*model.Strategy{} +var payMasterSupport = map[string]bool{} +var entryPointSupport = map[string]bool{} func init() { mockStrategyMap["1"] = &model.Strategy{ @@ -16,11 +19,35 @@ func init() { NetWork: types.Sepolia, Token: types.USDT, } + mockStrategyMap["2"] = &model.Strategy{ + Id: "2", + EntryPointAddress: "0x0576a174D229E3cFA37253523E645A78A0C91B57", + PayMasterAddress: "0x0000000000325602a77416A16136FDafd04b299f", + NetWork: types.Sepolia, + Token: types.ETH, + } + + entryPointSupport["0x0576a174D229E3cFA37253523E645A78A0C91B57"] = true + payMasterSupport["0x0000000000325602a77416A16136FDafd04b299f"] = true } func GetStrategyById(strategyId string) *model.Strategy { return mockStrategyMap[strategyId] } +func GetSupportEntryPoint() { -func GetSuitableStrategy(entrypoint string, chain types.NetWork, token string) (*model.Strategy, error) { +} +func GetSuitableStrategy(entrypoint string, chain types.Network, token string) (*model.Strategy, error) { return nil, errors.New("not implemented") } +func IsEntryPointsSupport(address string) bool { + if entryPointSupport[address] { + return true + } + return false +} +func IsPayMasterSupport(address string) bool { + if payMasterSupport[address] { + return true + } + return false +} diff --git a/service/gas_service/gas_computor.go b/service/gas_service/gas_computor.go index 37c0b65c..aab3a666 100644 --- a/service/gas_service/gas_computor.go +++ b/service/gas_service/gas_computor.go @@ -4,17 +4,39 @@ import ( "AAStarCommunity/EthPaymaster_BackService/common/model" "AAStarCommunity/EthPaymaster_BackService/common/types" "AAStarCommunity/EthPaymaster_BackService/service/chain_service" + "encoding/hex" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "golang.org/x/xerrors" + "strconv" ) func ComputeGas(userOp *model.UserOperationItem, strategy *model.Strategy) (*model.ComputeGasResponse, error) { priceInWei, gasPriceInGwei, gasPriceInEtherStr, getGasErr := chain_service.GetGasPrice(types.Sepolia) + //TODO calculate the maximum possible fee the account needs to pay (based on validation and call gas limits, and current gas values) if getGasErr != nil { return nil, getGasErr } + sender := common.HexToAddress(userOp.Sender) + callData, _ := hex.DecodeString(userOp.CallData) + // + estimateCallGasLimit, _ := chain_service.EstimateGasLimitAndCost(strategy.NetWork, ethereum.CallMsg{ + From: common.HexToAddress(strategy.EntryPointAddress), + To: &sender, + Data: callData, + }) + userOpCallGasLimit, _ := strconv.ParseUint(userOp.CallGasLimit, 10, 64) + if estimateCallGasLimit > userOpCallGasLimit { + return nil, xerrors.Errorf("estimateCallGasLimit %d > userOpCallGasLimit %d", estimateCallGasLimit, userOpCallGasLimit) + } + + // TODO get PaymasterCallGasLimit + return &model.ComputeGasResponse{ GasPriceInWei: priceInWei.Uint64(), GasPriceInGwei: gasPriceInGwei, GasPriceInEther: *gasPriceInEtherStr, + CallGasLimit: estimateCallGasLimit, TokenCost: "0.0001", Network: strategy.NetWork, Token: strategy.Token, @@ -24,5 +46,9 @@ func ComputeGas(userOp *model.UserOperationItem, strategy *model.Strategy) (*mod } func ValidateGas(userOp *model.UserOperationItem, gasComputeResponse *model.ComputeGasResponse) error { + //1.if ERC20 check address balacnce + //Validate the account’s deposit in the entryPoint is high enough to cover the max possible cost (cover the already-done verification and max execution gas) + //2 if Paymaster check paymaster balance + //The maxFeePerGas and maxPriorityFeePerGas are above a configurable minimum value that the client is willing to accept. At the minimum, they are sufficiently high to be included with the current block.basefee. return nil } diff --git a/service/operator/try_pay_user_op_execute.go b/service/operator/try_pay_user_op_execute.go index 6144587b..1787d110 100644 --- a/service/operator/try_pay_user_op_execute.go +++ b/service/operator/try_pay_user_op_execute.go @@ -2,7 +2,9 @@ package operator import ( "AAStarCommunity/EthPaymaster_BackService/common/model" + "AAStarCommunity/EthPaymaster_BackService/common/types" "AAStarCommunity/EthPaymaster_BackService/common/utils" + "AAStarCommunity/EthPaymaster_BackService/conf" "AAStarCommunity/EthPaymaster_BackService/service/chain_service" "AAStarCommunity/EthPaymaster_BackService/service/dashboard_service" "AAStarCommunity/EthPaymaster_BackService/service/gas_service" @@ -45,6 +47,9 @@ func TryPayUserOpExecute(request *model.TryPayUserOpRequest) (*model.TryPayUserO if payError != nil { return nil, payError } + paymasterAndData := getPayMasterAndData(strategy, &userOp) + userOp.PaymasterAndData = paymasterAndData + //validatePaymasterUserOp paymasterSignature := getPayMasterSignature(strategy, &userOp) var result = &model.TryPayUserOpResponse{ StrategyId: strategy.Id, @@ -52,6 +57,7 @@ func TryPayUserOpExecute(request *model.TryPayUserOpRequest) (*model.TryPayUserO PayMasterAddress: strategy.PayMasterAddress, PayReceipt: payReceipt, PayMasterSignature: paymasterSignature, + PayMasterAndData: paymasterAndData, GasInfo: gasResponse, } @@ -62,7 +68,14 @@ func businessParamValidate(request *model.TryPayUserOpRequest) error { if request.ForceStrategyId == "" && (request.ForceToken == "" || request.ForceNetwork == "") { return xerrors.Errorf("Token And Network Must Set When ForceStrategyId Is Empty") } + if conf.Environment.IsDevelopment() && request.ForceNetwork != "" { + if types.TestNetWork[request.ForceNetwork] { + return xerrors.Errorf("Test Network Not Support") + } + } + //recall simulate? //UserOp Validate + //check nonce if err := validator_service.ValidateUserOp(&request.UserOperation); err != nil { return err } @@ -96,6 +109,16 @@ func getPayMasterSignature(strategy *model.Strategy, userOp *model.UserOperation signatureBytes, _ := utils.SignUserOp("1d8a58126e87e53edc7b24d58d1328230641de8c4242c135492bf5560e0ff421", userOp) return hex.EncodeToString(signatureBytes) } +func getPayMasterAndData(strategy *model.Strategy, userOp *model.UserOperationItem) string { + //TODO + if strategy.PayType == types.PayTypeERC20 { + return "" + } + if strategy.PayType == types.PayTypeVerifying { + return "" + } + return "" +} func strategyGenerate(request *model.TryPayUserOpRequest) (*model.Strategy, error) { if forceStrategyId := request.ForceStrategyId; forceStrategyId != "" { diff --git a/service/pay_service/pay_service.go b/service/pay_service/pay_service.go index 86ae6fc0..45f11298 100644 --- a/service/pay_service/pay_service.go +++ b/service/pay_service/pay_service.go @@ -20,6 +20,7 @@ func (e *EthereumPayService) GetReceipt() { } func (e *EthereumPayService) Pay() error { + //1.if validate Paymaster //TODO implement me return nil } diff --git a/service/validator_service/basic_validator.go b/service/validator_service/basic_validator.go index d52effca..b7dfe4de 100644 --- a/service/validator_service/basic_validator.go +++ b/service/validator_service/basic_validator.go @@ -2,7 +2,11 @@ package validator_service import ( "AAStarCommunity/EthPaymaster_BackService/common/model" + "AAStarCommunity/EthPaymaster_BackService/common/types" + "AAStarCommunity/EthPaymaster_BackService/common/utils" "AAStarCommunity/EthPaymaster_BackService/service/chain_service" + "encoding/hex" + "github.com/ethereum/go-ethereum/common" "golang.org/x/xerrors" ) @@ -23,7 +27,59 @@ func ValidateStrategy(strategy *model.Strategy, userOp *model.UserOperationItem) } func ValidateUserOp(userOp *model.UserOperationItem) error { + + if err := checkSender(userOp, types.Sepolia); err != nil { + return err + } + if !utils.IsStringInUint64Range(userOp.Nonce) { + return xerrors.Errorf("nonce is not in uint64 range") + } + + //If initCode is not empty, parse its first 20 bytes as a factory address. Record whether the factory is staked, in case the later simulation indicates that it needs to be. If the factory accesses global state, it must be staked - see reputation, throttling and banning section for details. + //The verificationGasLimit is sufficiently low (<= MAX_VERIFICATION_GAS) and the preVerificationGas is sufficiently high (enough to pay for the calldata gas cost of serializing the UserOperation plus PRE_VERIFICATION_OVERHEAD_GAS) // check Sender is valid ,if sender is invalid And InitCode empty, return error + // // nonce is valid + //validate trusted entrypoint + return nil +} +func checkSender(userOp *model.UserOperationItem, netWork types.Network) error { + //check sender + if userOp.Sender != "" { + if ok, err := chain_service.CheckContractAddressAccess(userOp.Sender, netWork); err != nil { + return err + } else if !ok { + return xerrors.Errorf("sender address not exist in [%s] network", netWork) + } + //check balance + } + if userOp.InitCode == "" { + return xerrors.Errorf("initCode can not be empty if sender is empty") + } + if err := checkInitCode(userOp.InitCode, netWork); err != nil { + + } + return nil +} +func checkInitCode(initCode string, network types.Network) error { + initCodeByte, err := hex.DecodeString(initCode) + if err != nil { + return xerrors.Errorf("initCode is not hex string %s", initCode) + } + if len(initCodeByte) < 20 { + return xerrors.Errorf("initCode is not valid %s", initCode) + } + factoryAddress := common.BytesToAddress(initCodeByte[:20]) + if ok, err := chain_service.CheckContractAddressAccess(factoryAddress.String(), network); err != nil { + return err + } else if !ok { + return xerrors.Errorf("sender address not exist in [%s] network", network) + } + // TODO checkFactoryAddress stack + //parse its first 20 bytes as a factory address. Record whether the factory is staked, + //factory and factoryData - either both exist, or none + + //parse its first 20 bytes as a factory address. Record whether the factory is staked, + //factory and factoryData - either both exist, or none return nil }