diff --git a/action/protocol/rewarding/ethabi/availablebalance.go b/action/protocol/rewarding/ethabi/availablebalance.go index f909333c1e..f0f6de8521 100644 --- a/action/protocol/rewarding/ethabi/availablebalance.go +++ b/action/protocol/rewarding/ethabi/availablebalance.go @@ -6,8 +6,9 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/iotexproject/iotex-proto/golang/iotexapi" - "github.com/iotexproject/iotex-proto/golang/protocol" + proto "github.com/iotexproject/iotex-proto/golang/protocol" + "github.com/iotexproject/iotex-core/action/protocol" "github.com/iotexproject/iotex-core/action/protocol/abiutil" ) @@ -35,14 +36,14 @@ func init() { // AvailableBalanceStateContext context for AvailableBalance type AvailableBalanceStateContext struct { - *baseStateContext + *protocol.BaseStateContext } func newAvailableBalanceStateContext() (*AvailableBalanceStateContext, error) { return &AvailableBalanceStateContext{ - &baseStateContext{ - &Parameters{ - MethodName: []byte(protocol.ReadAvailableBalanceMethodName), + &protocol.BaseStateContext{ + Parameter: &protocol.Parameters{ + MethodName: []byte(proto.ReadAvailableBalanceMethodName), Arguments: nil, }, }, diff --git a/action/protocol/rewarding/ethabi/availablebalance_test.go b/action/protocol/rewarding/ethabi/availablebalance_test.go index e683b546b2..cfcaaf83a8 100644 --- a/action/protocol/rewarding/ethabi/availablebalance_test.go +++ b/action/protocol/rewarding/ethabi/availablebalance_test.go @@ -13,7 +13,7 @@ func TestAvailableBalanceEncodeToEth(t *testing.T) { ctx, err := newAvailableBalanceStateContext() r.Nil(err) - r.EqualValues("AvailableBalance", string(ctx.parameters.MethodName)) + r.EqualValues("AvailableBalance", string(ctx.Parameters().MethodName)) amount := big.NewInt(10000) resp := &iotexapi.ReadStateResponse{ diff --git a/action/protocol/rewarding/ethabi/base.go b/action/protocol/rewarding/ethabi/base.go index 35e51771cf..3e20c405cf 100644 --- a/action/protocol/rewarding/ethabi/base.go +++ b/action/protocol/rewarding/ethabi/base.go @@ -4,7 +4,7 @@ import ( "encoding/hex" "errors" - "github.com/iotexproject/iotex-proto/golang/iotexapi" + "github.com/iotexproject/iotex-core/action/protocol" ) var ( @@ -14,30 +14,8 @@ var ( errDecodeFailure = errors.New("decode data error") ) -type ( - // Parameters state request parameters - Parameters struct { - MethodName []byte - Arguments [][]byte - } - - // StateContext context for ReadState - StateContext interface { - Parameters() *Parameters - EncodeToEth(*iotexapi.ReadStateResponse) (string, error) - } - - baseStateContext struct { - parameters *Parameters - } -) - -func (r *baseStateContext) Parameters() *Parameters { - return r.parameters -} - // BuildReadStateRequest decode eth_call data to StateContext -func BuildReadStateRequest(data []byte) (StateContext, error) { +func BuildReadStateRequest(data []byte) (protocol.StateContext, error) { if len(data) < 4 { return nil, errInvalidCallData } diff --git a/action/protocol/rewarding/ethabi/totalbalance.go b/action/protocol/rewarding/ethabi/totalbalance.go index e0696a5445..786bbd1935 100644 --- a/action/protocol/rewarding/ethabi/totalbalance.go +++ b/action/protocol/rewarding/ethabi/totalbalance.go @@ -6,8 +6,9 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/iotexproject/iotex-proto/golang/iotexapi" - "github.com/iotexproject/iotex-proto/golang/protocol" + proto "github.com/iotexproject/iotex-proto/golang/protocol" + "github.com/iotexproject/iotex-core/action/protocol" "github.com/iotexproject/iotex-core/action/protocol/abiutil" ) @@ -35,14 +36,14 @@ func init() { // TotalBalanceStateContext context for TotalBalance type TotalBalanceStateContext struct { - *baseStateContext + *protocol.BaseStateContext } func newTotalBalanceStateContext() (*TotalBalanceStateContext, error) { return &TotalBalanceStateContext{ - &baseStateContext{ - &Parameters{ - MethodName: []byte(protocol.ReadTotalBalanceMethodName), + &protocol.BaseStateContext{ + Parameter: &protocol.Parameters{ + MethodName: []byte(proto.ReadTotalBalanceMethodName), Arguments: nil, }, }, diff --git a/action/protocol/rewarding/ethabi/totalbalance_test.go b/action/protocol/rewarding/ethabi/totalbalance_test.go index 4e8339ce00..31f61a68e9 100644 --- a/action/protocol/rewarding/ethabi/totalbalance_test.go +++ b/action/protocol/rewarding/ethabi/totalbalance_test.go @@ -13,7 +13,7 @@ func TestTotalBalanceEncodeToEth(t *testing.T) { ctx, err := newTotalBalanceStateContext() r.Nil(err) - r.EqualValues("TotalBalance", string(ctx.parameters.MethodName)) + r.EqualValues("TotalBalance", string(ctx.Parameters().MethodName)) amount := big.NewInt(10000) resp := &iotexapi.ReadStateResponse{ diff --git a/action/protocol/rewarding/ethabi/unclaimedbalance.go b/action/protocol/rewarding/ethabi/unclaimedbalance.go index 0b2e608d4a..ef4060cc9f 100644 --- a/action/protocol/rewarding/ethabi/unclaimedbalance.go +++ b/action/protocol/rewarding/ethabi/unclaimedbalance.go @@ -8,8 +8,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/iotexproject/iotex-address/address" "github.com/iotexproject/iotex-proto/golang/iotexapi" - "github.com/iotexproject/iotex-proto/golang/protocol" + proto "github.com/iotexproject/iotex-proto/golang/protocol" + "github.com/iotexproject/iotex-core/action/protocol" "github.com/iotexproject/iotex-core/action/protocol/abiutil" ) @@ -43,7 +44,7 @@ func init() { // UnclaimedBalanceStateContext context for UnclaimedBalance type UnclaimedBalanceStateContext struct { - *baseStateContext + *protocol.BaseStateContext } func newUnclaimedBalanceStateContext(data []byte) (*UnclaimedBalanceStateContext, error) { @@ -62,9 +63,9 @@ func newUnclaimedBalanceStateContext(data []byte) (*UnclaimedBalanceStateContext } return &UnclaimedBalanceStateContext{ - &baseStateContext{ - &Parameters{ - MethodName: []byte(protocol.ReadUnclaimedBalanceMethodName), + &protocol.BaseStateContext{ + Parameter: &protocol.Parameters{ + MethodName: []byte(proto.ReadUnclaimedBalanceMethodName), Arguments: [][]byte{[]byte(accountAddress.String())}, }, }, diff --git a/action/protocol/rewarding/ethabi/unclaimedbalance_test.go b/action/protocol/rewarding/ethabi/unclaimedbalance_test.go index 5edc381747..59d402b6b5 100644 --- a/action/protocol/rewarding/ethabi/unclaimedbalance_test.go +++ b/action/protocol/rewarding/ethabi/unclaimedbalance_test.go @@ -31,6 +31,6 @@ func TestNewUnclaimedBalanceStateContext(t *testing.T) { ctx, err := newUnclaimedBalanceStateContext(data) r.Nil(err) - r.EqualValues("io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqps833xv", string(ctx.parameters.Arguments[0])) - r.EqualValues("UnclaimedBalance", string(ctx.parameters.MethodName)) + r.EqualValues("io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqps833xv", string(ctx.Parameters().Arguments[0])) + r.EqualValues("UnclaimedBalance", string(ctx.Parameters().MethodName)) } diff --git a/action/protocol/staking/ethabi/stake_base.go b/action/protocol/staking/ethabi/stake_base.go new file mode 100644 index 0000000000..d1e94cbd0b --- /dev/null +++ b/action/protocol/staking/ethabi/stake_base.go @@ -0,0 +1,143 @@ +package ethabi + +import ( + "encoding/hex" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/iotexproject/iotex-proto/golang/iotextypes" + + "github.com/iotexproject/iotex-core/action/protocol" + "github.com/iotexproject/iotex-core/pkg/util/addrutil" +) + +var ( + errInvalidCallData = errors.New("invalid call binary data") + errInvalidCallSig = errors.New("invalid call sig") + errConvertBigNumber = errors.New("convert big number error") + errDecodeFailure = errors.New("decode data error") +) + +type ( + // BucketEth struct for eth + BucketEth struct { + Index uint64 + CandidateAddress common.Address + StakedAmount *big.Int + StakedDuration uint32 + CreateTime int64 + StakeStartTime int64 + UnstakeStartTime int64 + AutoStake bool + Owner common.Address + } + + // CandidateEth struct for eth + CandidateEth struct { + OwnerAddress common.Address + OperatorAddress common.Address + RewardAddress common.Address + Name string + TotalWeightedVotes *big.Int + SelfStakeBucketIdx uint64 + SelfStakingTokens *big.Int + } +) + +func encodeVoteBucketListToEth(outputs abi.Arguments, buckets iotextypes.VoteBucketList) (string, error) { + args := make([]BucketEth, len(buckets.Buckets)) + for i, bucket := range buckets.Buckets { + args[i] = BucketEth{} + args[i].Index = bucket.Index + addr, err := addrutil.IoAddrToEvmAddr(bucket.CandidateAddress) + if err != nil { + return "", err + } + args[i].CandidateAddress = addr + if amount, ok := new(big.Int).SetString(bucket.StakedAmount, 10); ok { + args[i].StakedAmount = amount + } else { + return "", errConvertBigNumber + } + args[i].StakedDuration = bucket.StakedDuration + args[i].CreateTime = bucket.CreateTime.Seconds + args[i].StakeStartTime = bucket.StakeStartTime.Seconds + args[i].UnstakeStartTime = bucket.UnstakeStartTime.Seconds + args[i].AutoStake = bucket.AutoStake + addr, err = addrutil.IoAddrToEvmAddr(bucket.Owner) + if err != nil { + return "", err + } + args[i].Owner = addr + } + + data, err := outputs.Pack(args) + if err != nil { + return "", nil + } + return hex.EncodeToString(data), nil +} + +func encodeCandidateToEth(candidate *iotextypes.CandidateV2) (*CandidateEth, error) { + result := &CandidateEth{} + addr, err := addrutil.IoAddrToEvmAddr(candidate.OwnerAddress) + if err != nil { + return nil, err + } + result.OwnerAddress = addr + addr, err = addrutil.IoAddrToEvmAddr(candidate.OperatorAddress) + if err != nil { + return nil, err + } + result.OperatorAddress = addr + addr, err = addrutil.IoAddrToEvmAddr(candidate.RewardAddress) + if err != nil { + return nil, err + } + result.RewardAddress = addr + result.Name = candidate.Name + if amount, ok := new(big.Int).SetString(candidate.TotalWeightedVotes, 10); ok { + result.TotalWeightedVotes = amount + } else { + return nil, errConvertBigNumber + } + result.SelfStakeBucketIdx = candidate.SelfStakeBucketIdx + if amount, ok := new(big.Int).SetString(candidate.SelfStakingTokens, 10); ok { + result.SelfStakingTokens = amount + } else { + return nil, errConvertBigNumber + } + return result, nil +} + +// BuildReadStateRequest decode eth_call data to StateContext +func BuildReadStateRequest(data []byte) (protocol.StateContext, error) { + if len(data) < 4 { + return nil, errInvalidCallData + } + + switch methodSig := hex.EncodeToString(data[:4]); methodSig { + case hex.EncodeToString(_bucketsMethod.ID): + return newBucketsStateContext(data[4:]) + case hex.EncodeToString(_bucketsByCandidateMethod.ID): + return newBucketsByCandidateStateContext(data[4:]) + case hex.EncodeToString(_bucketsByIndexesMethod.ID): + return newBucketsByIndexesStateContext(data[4:]) + case hex.EncodeToString(_bucketsByVoterMethod.ID): + return newBucketsByVoterStateContext(data[4:]) + case hex.EncodeToString(_bucketsCountMethod.ID): + return newBucketsCountStateContext() + case hex.EncodeToString(_candidatesMethod.ID): + return newCandidatesStateContext(data[4:]) + case hex.EncodeToString(_candidateByNameMethod.ID): + return newCandidateByNameStateContext(data[4:]) + case hex.EncodeToString(_candidateByAddressMethod.ID): + return newCandidateByAddressStateContext(data[4:]) + case hex.EncodeToString(_totalStakingAmountMethod.ID): + return newTotalStakingAmountContext() + default: + return nil, errInvalidCallSig + } +} diff --git a/action/protocol/staking/ethabi/stake_buckets.go b/action/protocol/staking/ethabi/stake_buckets.go new file mode 100644 index 0000000000..8fe8715093 --- /dev/null +++ b/action/protocol/staking/ethabi/stake_buckets.go @@ -0,0 +1,151 @@ +package ethabi + +import ( + "github.com/ethereum/go-ethereum/accounts/abi" + "google.golang.org/protobuf/proto" + + "github.com/iotexproject/iotex-core/action/protocol" + "github.com/iotexproject/iotex-core/action/protocol/abiutil" + "github.com/iotexproject/iotex-proto/golang/iotexapi" + "github.com/iotexproject/iotex-proto/golang/iotextypes" +) + +const _bucketsInterfaceABI = `[ + { + "inputs": [ + { + "internalType": "uint32", + "name": "offset", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "limit", + "type": "uint32" + } + ], + "name": "buckets", + "outputs": [ + { + "components": [ + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "address", + "name": "candidateAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "stakedAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "stakedDuration", + "type": "uint32" + }, + { + "internalType": "int64", + "name": "createTime", + "type": "int64" + }, + { + "internalType": "int64", + "name": "stakeStartTime", + "type": "int64" + }, + { + "internalType": "int64", + "name": "unstakeStartTime", + "type": "int64" + }, + { + "internalType": "bool", + "name": "autoStake", + "type": "bool" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "internalType": "struct IStaking.VoteBucket[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + } +]` + +var _bucketsMethod abi.Method + +func init() { + _bucketsMethod = abiutil.MustLoadMethod(_bucketsInterfaceABI, "buckets") +} + +// BucketsStateContext context for Buckets +type BucketsStateContext struct { + *protocol.BaseStateContext +} + +func newBucketsStateContext(data []byte) (*BucketsStateContext, error) { + paramsMap := map[string]interface{}{} + ok := false + if err := _bucketsMethod.Inputs.UnpackIntoMap(paramsMap, data); err != nil { + return nil, err + } + var offset, limit uint32 + if offset, ok = paramsMap["offset"].(uint32); !ok { + return nil, errDecodeFailure + } + if limit, ok = paramsMap["limit"].(uint32); !ok { + return nil, errDecodeFailure + } + + method := &iotexapi.ReadStakingDataMethod{ + Method: iotexapi.ReadStakingDataMethod_BUCKETS, + } + methodBytes, err := proto.Marshal(method) + if err != nil { + return nil, err + } + arguments := &iotexapi.ReadStakingDataRequest{ + Request: &iotexapi.ReadStakingDataRequest_Buckets{ + Buckets: &iotexapi.ReadStakingDataRequest_VoteBuckets{ + Pagination: &iotexapi.PaginationParam{ + Offset: offset, + Limit: limit, + }, + }, + }, + } + argumentsBytes, err := proto.Marshal(arguments) + if err != nil { + return nil, err + } + return &BucketsStateContext{ + &protocol.BaseStateContext{ + Parameter: &protocol.Parameters{ + MethodName: methodBytes, + Arguments: [][]byte{argumentsBytes}, + }, + }, + }, nil +} + +// EncodeToEth encode proto to eth +func (r *BucketsStateContext) EncodeToEth(resp *iotexapi.ReadStateResponse) (string, error) { + var result iotextypes.VoteBucketList + if err := proto.Unmarshal(resp.Data, &result); err != nil { + return "", err + } + + return encodeVoteBucketListToEth(_bucketsMethod.Outputs, result) +} diff --git a/action/protocol/staking/ethabi/stake_bucketsbycandidate.go b/action/protocol/staking/ethabi/stake_bucketsbycandidate.go new file mode 100644 index 0000000000..013ddf4f0b --- /dev/null +++ b/action/protocol/staking/ethabi/stake_bucketsbycandidate.go @@ -0,0 +1,161 @@ +package ethabi + +import ( + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/iotexproject/iotex-proto/golang/iotexapi" + "github.com/iotexproject/iotex-proto/golang/iotextypes" + "google.golang.org/protobuf/proto" + + "github.com/iotexproject/iotex-core/action/protocol" + "github.com/iotexproject/iotex-core/action/protocol/abiutil" +) + +const _bucketsByCandidateInterfaceABI = `[ + { + "inputs": [ + { + "internalType": "string", + "name": "candName", + "type": "string" + }, + { + "internalType": "uint32", + "name": "offset", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "limit", + "type": "uint32" + } + ], + "name": "bucketsByCandidate", + "outputs": [ + { + "components": [ + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "address", + "name": "candidateAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "stakedAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "stakedDuration", + "type": "uint32" + }, + { + "internalType": "int64", + "name": "createTime", + "type": "int64" + }, + { + "internalType": "int64", + "name": "stakeStartTime", + "type": "int64" + }, + { + "internalType": "int64", + "name": "unstakeStartTime", + "type": "int64" + }, + { + "internalType": "bool", + "name": "autoStake", + "type": "bool" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "internalType": "struct IStaking.VoteBucket[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + } +]` + +var _bucketsByCandidateMethod abi.Method + +func init() { + _bucketsByCandidateMethod = abiutil.MustLoadMethod(_bucketsByCandidateInterfaceABI, "bucketsByCandidate") +} + +// BucketsByCandidateStateContext context for BucketsByCandidate +type BucketsByCandidateStateContext struct { + *protocol.BaseStateContext +} + +func newBucketsByCandidateStateContext(data []byte) (*BucketsByCandidateStateContext, error) { + paramsMap := map[string]interface{}{} + ok := false + if err := _bucketsByCandidateMethod.Inputs.UnpackIntoMap(paramsMap, data); err != nil { + return nil, err + } + var candName string + if candName, ok = paramsMap["candName"].(string); !ok { + return nil, errDecodeFailure + } + var offset, limit uint32 + if offset, ok = paramsMap["offset"].(uint32); !ok { + return nil, errDecodeFailure + } + if limit, ok = paramsMap["limit"].(uint32); !ok { + return nil, errDecodeFailure + } + + method := &iotexapi.ReadStakingDataMethod{ + Method: iotexapi.ReadStakingDataMethod_BUCKETS_BY_CANDIDATE, + } + methodBytes, err := proto.Marshal(method) + if err != nil { + return nil, err + } + arguments := &iotexapi.ReadStakingDataRequest{ + Request: &iotexapi.ReadStakingDataRequest_BucketsByCandidate{ + BucketsByCandidate: &iotexapi.ReadStakingDataRequest_VoteBucketsByCandidate{ + CandName: candName, + Pagination: &iotexapi.PaginationParam{ + Offset: offset, + Limit: limit, + }, + }, + }, + } + argumentsBytes, err := proto.Marshal(arguments) + if err != nil { + return nil, err + } + return &BucketsByCandidateStateContext{ + &protocol.BaseStateContext{ + Parameter: &protocol.Parameters{ + MethodName: methodBytes, + Arguments: [][]byte{argumentsBytes}, + }, + }, + }, nil +} + +// EncodeToEth encode proto to eth +func (r *BucketsByCandidateStateContext) EncodeToEth(resp *iotexapi.ReadStateResponse) (string, error) { + var result iotextypes.VoteBucketList + if err := proto.Unmarshal(resp.Data, &result); err != nil { + return "", err + } + + return encodeVoteBucketListToEth(_bucketsByCandidateMethod.Outputs, result) +} diff --git a/action/protocol/staking/ethabi/stake_bucketsbyindexes.go b/action/protocol/staking/ethabi/stake_bucketsbyindexes.go new file mode 100644 index 0000000000..699904582f --- /dev/null +++ b/action/protocol/staking/ethabi/stake_bucketsbyindexes.go @@ -0,0 +1,140 @@ +package ethabi + +import ( + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/iotexproject/iotex-proto/golang/iotexapi" + "github.com/iotexproject/iotex-proto/golang/iotextypes" + "google.golang.org/protobuf/proto" + + "github.com/iotexproject/iotex-core/action/protocol" + "github.com/iotexproject/iotex-core/action/protocol/abiutil" +) + +const _bucketsByIndexesInterfaceABI = `[ + { + "inputs": [ + { + "internalType": "uint64[]", + "name": "indexes", + "type": "uint64[]" + } + ], + "name": "bucketsByIndexes", + "outputs": [ + { + "components": [ + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "address", + "name": "candidateAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "stakedAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "stakedDuration", + "type": "uint32" + }, + { + "internalType": "int64", + "name": "createTime", + "type": "int64" + }, + { + "internalType": "int64", + "name": "stakeStartTime", + "type": "int64" + }, + { + "internalType": "int64", + "name": "unstakeStartTime", + "type": "int64" + }, + { + "internalType": "bool", + "name": "autoStake", + "type": "bool" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "internalType": "struct IStaking.VoteBucket[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + } +]` + +var _bucketsByIndexesMethod abi.Method + +func init() { + _bucketsByIndexesMethod = abiutil.MustLoadMethod(_bucketsByIndexesInterfaceABI, "bucketsByIndexes") +} + +// BucketsByIndexesStateContext context for BucketsByIndexes +type BucketsByIndexesStateContext struct { + *protocol.BaseStateContext +} + +func newBucketsByIndexesStateContext(data []byte) (*BucketsByIndexesStateContext, error) { + paramsMap := map[string]interface{}{} + ok := false + if err := _bucketsByIndexesMethod.Inputs.UnpackIntoMap(paramsMap, data); err != nil { + return nil, err + } + var index []uint64 + if index, ok = paramsMap["indexes"].([]uint64); !ok { + return nil, errDecodeFailure + } + + method := &iotexapi.ReadStakingDataMethod{ + Method: iotexapi.ReadStakingDataMethod_BUCKETS_BY_INDEXES, + } + methodBytes, err := proto.Marshal(method) + if err != nil { + return nil, err + } + arguments := &iotexapi.ReadStakingDataRequest{ + Request: &iotexapi.ReadStakingDataRequest_BucketsByIndexes{ + BucketsByIndexes: &iotexapi.ReadStakingDataRequest_VoteBucketsByIndexes{ + Index: index, + }, + }, + } + argumentsBytes, err := proto.Marshal(arguments) + if err != nil { + return nil, err + } + return &BucketsByIndexesStateContext{ + &protocol.BaseStateContext{ + Parameter: &protocol.Parameters{ + MethodName: methodBytes, + Arguments: [][]byte{argumentsBytes}, + }, + }, + }, nil +} + +// EncodeToEth encode proto to eth +func (r *BucketsByIndexesStateContext) EncodeToEth(resp *iotexapi.ReadStateResponse) (string, error) { + var result iotextypes.VoteBucketList + if err := proto.Unmarshal(resp.Data, &result); err != nil { + return "", err + } + + return encodeVoteBucketListToEth(_bucketsByIndexesMethod.Outputs, result) +} diff --git a/action/protocol/staking/ethabi/stake_bucketsbyvoter.go b/action/protocol/staking/ethabi/stake_bucketsbyvoter.go new file mode 100644 index 0000000000..772090424b --- /dev/null +++ b/action/protocol/staking/ethabi/stake_bucketsbyvoter.go @@ -0,0 +1,167 @@ +package ethabi + +import ( + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/iotexproject/iotex-address/address" + "github.com/iotexproject/iotex-proto/golang/iotexapi" + "github.com/iotexproject/iotex-proto/golang/iotextypes" + "google.golang.org/protobuf/proto" + + "github.com/iotexproject/iotex-core/action/protocol" + "github.com/iotexproject/iotex-core/action/protocol/abiutil" +) + +var _bucketsByVoterInterfaceABI = `[ + { + "inputs": [ + { + "internalType": "address", + "name": "voter", + "type": "address" + }, + { + "internalType": "uint32", + "name": "offset", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "limit", + "type": "uint32" + } + ], + "name": "bucketsByVoter", + "outputs": [ + { + "components": [ + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "address", + "name": "candidateAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "stakedAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "stakedDuration", + "type": "uint32" + }, + { + "internalType": "int64", + "name": "createTime", + "type": "int64" + }, + { + "internalType": "int64", + "name": "stakeStartTime", + "type": "int64" + }, + { + "internalType": "int64", + "name": "unstakeStartTime", + "type": "int64" + }, + { + "internalType": "bool", + "name": "autoStake", + "type": "bool" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "internalType": "struct IStaking.VoteBucket[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + } +]` + +var _bucketsByVoterMethod abi.Method + +func init() { + _bucketsByVoterMethod = abiutil.MustLoadMethod(_bucketsByVoterInterfaceABI, "bucketsByVoter") +} + +// BucketsByVoterStateContext context for BucketsByVoter +type BucketsByVoterStateContext struct { + *protocol.BaseStateContext +} + +func newBucketsByVoterStateContext(data []byte) (*BucketsByVoterStateContext, error) { + paramsMap := map[string]interface{}{} + ok := false + if err := _bucketsByVoterMethod.Inputs.UnpackIntoMap(paramsMap, data); err != nil { + return nil, err + } + var voter common.Address + if voter, ok = paramsMap["voter"].(common.Address); !ok { + return nil, errDecodeFailure + } + voterAddress, err := address.FromBytes(voter[:]) + if err != nil { + return nil, err + } + var offset, limit uint32 + if offset, ok = paramsMap["offset"].(uint32); !ok { + return nil, errDecodeFailure + } + if limit, ok = paramsMap["limit"].(uint32); !ok { + return nil, errDecodeFailure + } + + method := &iotexapi.ReadStakingDataMethod{ + Method: iotexapi.ReadStakingDataMethod_BUCKETS_BY_VOTER, + } + methodBytes, err := proto.Marshal(method) + if err != nil { + return nil, err + } + arguments := &iotexapi.ReadStakingDataRequest{ + Request: &iotexapi.ReadStakingDataRequest_BucketsByVoter{ + BucketsByVoter: &iotexapi.ReadStakingDataRequest_VoteBucketsByVoter{ + VoterAddress: voterAddress.String(), + Pagination: &iotexapi.PaginationParam{ + Offset: offset, + Limit: limit, + }, + }, + }, + } + argumentsBytes, err := proto.Marshal(arguments) + if err != nil { + return nil, err + } + return &BucketsByVoterStateContext{ + &protocol.BaseStateContext{ + Parameter: &protocol.Parameters{ + MethodName: methodBytes, + Arguments: [][]byte{argumentsBytes}, + }, + }, + }, nil +} + +// EncodeToEth encode proto to eth +func (r *BucketsByVoterStateContext) EncodeToEth(resp *iotexapi.ReadStateResponse) (string, error) { + var result iotextypes.VoteBucketList + if err := proto.Unmarshal(resp.Data, &result); err != nil { + return "", err + } + + return encodeVoteBucketListToEth(_bucketsByVoterMethod.Outputs, result) +} diff --git a/action/protocol/staking/ethabi/stake_bucketscount.go b/action/protocol/staking/ethabi/stake_bucketscount.go new file mode 100644 index 0000000000..0c696ef160 --- /dev/null +++ b/action/protocol/staking/ethabi/stake_bucketscount.go @@ -0,0 +1,86 @@ +package ethabi + +import ( + "encoding/hex" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/iotexproject/iotex-proto/golang/iotexapi" + "github.com/iotexproject/iotex-proto/golang/iotextypes" + "google.golang.org/protobuf/proto" + + "github.com/iotexproject/iotex-core/action/protocol" + "github.com/iotexproject/iotex-core/action/protocol/abiutil" +) + +var _bucketsCountInterfaceABI = `[ + { + "inputs": [], + "name": "bucketsCount", + "outputs": [ + { + "internalType": "uint64", + "name": "total", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "active", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + } +]` + +var _bucketsCountMethod abi.Method + +func init() { + _bucketsCountMethod = abiutil.MustLoadMethod(_bucketsCountInterfaceABI, "bucketsCount") +} + +// BucketsCountStateContext context for BucketsCount +type BucketsCountStateContext struct { + *protocol.BaseStateContext +} + +func newBucketsCountStateContext() (*BucketsCountStateContext, error) { + method := &iotexapi.ReadStakingDataMethod{ + Method: iotexapi.ReadStakingDataMethod_BUCKETS_COUNT, + } + methodBytes, err := proto.Marshal(method) + if err != nil { + return nil, err + } + arguments := &iotexapi.ReadStakingDataRequest{ + Request: &iotexapi.ReadStakingDataRequest_BucketsCount_{ + BucketsCount: &iotexapi.ReadStakingDataRequest_BucketsCount{}, + }, + } + argumentsBytes, err := proto.Marshal(arguments) + if err != nil { + return nil, err + } + return &BucketsCountStateContext{ + &protocol.BaseStateContext{ + Parameter: &protocol.Parameters{ + MethodName: methodBytes, + Arguments: [][]byte{argumentsBytes}, + }, + }, + }, nil +} + +// EncodeToEth encode proto to eth +func (r *BucketsCountStateContext) EncodeToEth(resp *iotexapi.ReadStateResponse) (string, error) { + var result iotextypes.BucketsCount + if err := proto.Unmarshal(resp.Data, &result); err != nil { + return "", err + } + + data, err := _bucketsCountMethod.Outputs.Pack(result.Total, result.Active) + if err != nil { + return "", err + } + return hex.EncodeToString(data), nil +} diff --git a/action/protocol/staking/ethabi/stake_candidatebyaddress.go b/action/protocol/staking/ethabi/stake_candidatebyaddress.go new file mode 100644 index 0000000000..37248187bc --- /dev/null +++ b/action/protocol/staking/ethabi/stake_candidatebyaddress.go @@ -0,0 +1,147 @@ +package ethabi + +import ( + "encoding/hex" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/iotexproject/iotex-address/address" + "github.com/iotexproject/iotex-proto/golang/iotexapi" + "github.com/iotexproject/iotex-proto/golang/iotextypes" + "google.golang.org/protobuf/proto" + + "github.com/iotexproject/iotex-core/action/protocol" + "github.com/iotexproject/iotex-core/action/protocol/abiutil" +) + +const _candidateByAddressInterfaceABI = `[ + { + "inputs": [ + { + "internalType": "address", + "name": "ownerAddress", + "type": "address" + } + ], + "name": "candidateByAddress", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "ownerAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "operatorAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "rewardAddress", + "type": "address" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "uint256", + "name": "totalWeightedVotes", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "selfStakeBucketIdx", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "selfStakingTokens", + "type": "uint256" + } + ], + "internalType": "struct IStaking.Candidate", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + } +]` + +var _candidateByAddressMethod abi.Method + +func init() { + _candidateByAddressMethod = abiutil.MustLoadMethod(_candidateByAddressInterfaceABI, "candidateByAddress") +} + +// CandidateByAddressStateContext context for candidateByAddress +type CandidateByAddressStateContext struct { + *protocol.BaseStateContext +} + +func newCandidateByAddressStateContext(data []byte) (*CandidateByAddressStateContext, error) { + paramsMap := map[string]interface{}{} + ok := false + if err := _candidateByAddressMethod.Inputs.UnpackIntoMap(paramsMap, data); err != nil { + return nil, err + } + var ownerAddress common.Address + if ownerAddress, ok = paramsMap["ownerAddress"].(common.Address); !ok { + return nil, errDecodeFailure + } + owner, err := address.FromBytes(ownerAddress[:]) + if err != nil { + return nil, err + } + + method := &iotexapi.ReadStakingDataMethod{ + Method: iotexapi.ReadStakingDataMethod_CANDIDATE_BY_ADDRESS, + } + methodBytes, err := proto.Marshal(method) + if err != nil { + return nil, err + } + arguments := &iotexapi.ReadStakingDataRequest{ + Request: &iotexapi.ReadStakingDataRequest_CandidateByAddress_{ + CandidateByAddress: &iotexapi.ReadStakingDataRequest_CandidateByAddress{ + OwnerAddr: owner.String(), + }, + }, + } + argumentsBytes, err := proto.Marshal(arguments) + if err != nil { + return nil, err + } + return &CandidateByAddressStateContext{ + &protocol.BaseStateContext{ + Parameter: &protocol.Parameters{ + MethodName: methodBytes, + Arguments: [][]byte{argumentsBytes}, + }, + }, + }, nil +} + +// EncodeToEth encode proto to eth +func (r *CandidateByAddressStateContext) EncodeToEth(resp *iotexapi.ReadStateResponse) (string, error) { + var result iotextypes.CandidateV2 + if err := proto.Unmarshal(resp.Data, &result); err != nil { + return "", err + } + + cand, err := encodeCandidateToEth(&result) + if err != nil { + return "", err + } + + data, err := _candidateByAddressMethod.Outputs.Pack(cand) + if err != nil { + return "", nil + } + return hex.EncodeToString(data), nil +} diff --git a/action/protocol/staking/ethabi/stake_candidatebyname.go b/action/protocol/staking/ethabi/stake_candidatebyname.go new file mode 100644 index 0000000000..630230d640 --- /dev/null +++ b/action/protocol/staking/ethabi/stake_candidatebyname.go @@ -0,0 +1,141 @@ +package ethabi + +import ( + "encoding/hex" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/iotexproject/iotex-proto/golang/iotexapi" + "github.com/iotexproject/iotex-proto/golang/iotextypes" + "google.golang.org/protobuf/proto" + + "github.com/iotexproject/iotex-core/action/protocol" + "github.com/iotexproject/iotex-core/action/protocol/abiutil" +) + +const _candidateByNameInterfaceABI = `[ + { + "inputs": [ + { + "internalType": "string", + "name": "candName", + "type": "string" + } + ], + "name": "candidateByName", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "ownerAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "operatorAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "rewardAddress", + "type": "address" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "uint256", + "name": "totalWeightedVotes", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "selfStakeBucketIdx", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "selfStakingTokens", + "type": "uint256" + } + ], + "internalType": "struct IStaking.Candidate", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + } +]` + +var _candidateByNameMethod abi.Method + +func init() { + _candidateByNameMethod = abiutil.MustLoadMethod(_candidateByNameInterfaceABI, "candidateByName") +} + +// CandidateByNameStateContext context for CandidateByName +type CandidateByNameStateContext struct { + *protocol.BaseStateContext +} + +func newCandidateByNameStateContext(data []byte) (*CandidateByNameStateContext, error) { + paramsMap := map[string]interface{}{} + ok := false + if err := _candidateByNameMethod.Inputs.UnpackIntoMap(paramsMap, data); err != nil { + return nil, err + } + var candName string + if candName, ok = paramsMap["candName"].(string); !ok { + return nil, errDecodeFailure + } + + method := &iotexapi.ReadStakingDataMethod{ + Method: iotexapi.ReadStakingDataMethod_CANDIDATE_BY_NAME, + } + methodBytes, err := proto.Marshal(method) + if err != nil { + return nil, err + } + arguments := &iotexapi.ReadStakingDataRequest{ + Request: &iotexapi.ReadStakingDataRequest_CandidateByName_{ + CandidateByName: &iotexapi.ReadStakingDataRequest_CandidateByName{ + CandName: candName, + }, + }, + } + argumentsBytes, err := proto.Marshal(arguments) + if err != nil { + return nil, err + } + return &CandidateByNameStateContext{ + &protocol.BaseStateContext{ + Parameter: &protocol.Parameters{ + MethodName: methodBytes, + Arguments: [][]byte{argumentsBytes}, + }, + }, + }, nil +} + +// EncodeToEth encode proto to eth +func (r *CandidateByNameStateContext) EncodeToEth(resp *iotexapi.ReadStateResponse) (string, error) { + var result iotextypes.CandidateV2 + if err := proto.Unmarshal(resp.Data, &result); err != nil { + return "", err + } + + cand, err := encodeCandidateToEth(&result) + if err != nil { + return "", err + } + + data, err := _candidateByNameMethod.Outputs.Pack(cand) + if err != nil { + return "", nil + } + return hex.EncodeToString(data), nil +} diff --git a/action/protocol/staking/ethabi/stake_candidates.go b/action/protocol/staking/ethabi/stake_candidates.go new file mode 100644 index 0000000000..9512a0dbd9 --- /dev/null +++ b/action/protocol/staking/ethabi/stake_candidates.go @@ -0,0 +1,156 @@ +package ethabi + +import ( + "encoding/hex" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/iotexproject/iotex-proto/golang/iotexapi" + "github.com/iotexproject/iotex-proto/golang/iotextypes" + "google.golang.org/protobuf/proto" + + "github.com/iotexproject/iotex-core/action/protocol" + "github.com/iotexproject/iotex-core/action/protocol/abiutil" +) + +const _candidatesInterfaceABI = `[ + { + "inputs": [ + { + "internalType": "uint32", + "name": "offset", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "limit", + "type": "uint32" + } + ], + "name": "candidates", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "ownerAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "operatorAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "rewardAddress", + "type": "address" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "uint256", + "name": "totalWeightedVotes", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "selfStakeBucketIdx", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "selfStakingTokens", + "type": "uint256" + } + ], + "internalType": "struct IStaking.Candidate[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + } +]` + +var _candidatesMethod abi.Method + +func init() { + _candidatesMethod = abiutil.MustLoadMethod(_candidatesInterfaceABI, "candidates") +} + +// CandidatesStateContext context for Candidates +type CandidatesStateContext struct { + *protocol.BaseStateContext +} + +func newCandidatesStateContext(data []byte) (*CandidatesStateContext, error) { + paramsMap := map[string]interface{}{} + ok := false + if err := _candidatesMethod.Inputs.UnpackIntoMap(paramsMap, data); err != nil { + return nil, err + } + var offset, limit uint32 + if offset, ok = paramsMap["offset"].(uint32); !ok { + return nil, errDecodeFailure + } + if limit, ok = paramsMap["limit"].(uint32); !ok { + return nil, errDecodeFailure + } + + method := &iotexapi.ReadStakingDataMethod{ + Method: iotexapi.ReadStakingDataMethod_CANDIDATES, + } + methodBytes, err := proto.Marshal(method) + if err != nil { + return nil, err + } + arguments := &iotexapi.ReadStakingDataRequest{ + Request: &iotexapi.ReadStakingDataRequest_Candidates_{ + Candidates: &iotexapi.ReadStakingDataRequest_Candidates{ + Pagination: &iotexapi.PaginationParam{ + Offset: offset, + Limit: limit, + }, + }, + }, + } + argumentsBytes, err := proto.Marshal(arguments) + if err != nil { + return nil, err + } + return &CandidatesStateContext{ + &protocol.BaseStateContext{ + Parameter: &protocol.Parameters{ + MethodName: methodBytes, + Arguments: [][]byte{argumentsBytes}, + }, + }, + }, nil +} + +// EncodeToEth encode proto to eth +func (r *CandidatesStateContext) EncodeToEth(resp *iotexapi.ReadStateResponse) (string, error) { + var result iotextypes.CandidateListV2 + if err := proto.Unmarshal(resp.Data, &result); err != nil { + return "", err + } + + args := make([]CandidateEth, len(result.Candidates)) + for i, candidate := range result.Candidates { + cand, err := encodeCandidateToEth(candidate) + if err != nil { + return "", err + } + args[i] = *cand + } + + data, err := _candidatesMethod.Outputs.Pack(args) + if err != nil { + return "", nil + } + return hex.EncodeToString(data), nil +} diff --git a/action/protocol/staking/ethabi/stake_totalstakingamount.go b/action/protocol/staking/ethabi/stake_totalstakingamount.go new file mode 100644 index 0000000000..937b0d100d --- /dev/null +++ b/action/protocol/staking/ethabi/stake_totalstakingamount.go @@ -0,0 +1,87 @@ +package ethabi + +import ( + "encoding/hex" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/iotexproject/iotex-proto/golang/iotexapi" + "github.com/iotexproject/iotex-proto/golang/iotextypes" + "google.golang.org/protobuf/proto" + + "github.com/iotexproject/iotex-core/action/protocol" + "github.com/iotexproject/iotex-core/action/protocol/abiutil" +) + +var _totalStakingAmountInterfaceABI = `[ + { + "inputs": [], + "name": "totalStakingAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +]` + +var _totalStakingAmountMethod abi.Method + +func init() { + _totalStakingAmountMethod = abiutil.MustLoadMethod(_totalStakingAmountInterfaceABI, "totalStakingAmount") +} + +// TotalStakingAmountStateContext context for TotalStakingAmount +type TotalStakingAmountStateContext struct { + *protocol.BaseStateContext +} + +func newTotalStakingAmountContext() (*TotalStakingAmountStateContext, error) { + method := &iotexapi.ReadStakingDataMethod{ + Method: iotexapi.ReadStakingDataMethod_TOTAL_STAKING_AMOUNT, + } + methodBytes, err := proto.Marshal(method) + if err != nil { + return nil, err + } + arguments := &iotexapi.ReadStakingDataRequest{ + Request: &iotexapi.ReadStakingDataRequest_TotalStakingAmount_{ + TotalStakingAmount: &iotexapi.ReadStakingDataRequest_TotalStakingAmount{}, + }, + } + argumentsBytes, err := proto.Marshal(arguments) + if err != nil { + return nil, err + } + return &TotalStakingAmountStateContext{ + &protocol.BaseStateContext{ + Parameter: &protocol.Parameters{ + MethodName: methodBytes, + Arguments: [][]byte{argumentsBytes}, + }, + }, + }, nil +} + +// EncodeToEth encode proto to eth +func (r *TotalStakingAmountStateContext) EncodeToEth(resp *iotexapi.ReadStateResponse) (string, error) { + var meta iotextypes.AccountMeta + if err := proto.Unmarshal(resp.Data, &meta); err != nil { + return "", err + } + + total, ok := new(big.Int).SetString(meta.Balance, 10) + if !ok { + return "", errConvertBigNumber + } + + data, err := _totalStakingAmountMethod.Outputs.Pack(total) + if err != nil { + return "", nil + } + return hex.EncodeToString(data), nil +} diff --git a/action/protocol/staking/ethabi/stakebase_test.go b/action/protocol/staking/ethabi/stakebase_test.go new file mode 100644 index 0000000000..13eda5d758 --- /dev/null +++ b/action/protocol/staking/ethabi/stakebase_test.go @@ -0,0 +1,151 @@ +package ethabi + +import ( + "encoding/hex" + "testing" + + "github.com/iotexproject/iotex-proto/golang/iotextypes" + "github.com/stretchr/testify/require" +) + +var errInvalidMsg = "address length = 40, expecting 41: invalid address" + +func TestBuildReadStateRequestError(t *testing.T) { + r := require.New(t) + + data, _ := hex.DecodeString("8ae8a8a4") + req, err := BuildReadStateRequest(data) + + r.Nil(req) + r.EqualValues("invalid call sig", err.Error()) +} + +func TestBuildReadStateRequestInvalid(t *testing.T) { + r := require.New(t) + + data, _ := hex.DecodeString("8ae8a8") + req, err := BuildReadStateRequest(data) + + r.Nil(req) + r.EqualValues("invalid call binary data", err.Error()) +} + +func TestEncodeCandidateToEth(t *testing.T) { + r := require.New(t) + + candidate := &iotextypes.CandidateV2{ + OwnerAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqps833xv", + OperatorAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqz75y8gn", + RewardAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrzsj4p", + Name: "hello", + TotalWeightedVotes: "10000000000000000000", + SelfStakeBucketIdx: 100, + SelfStakingTokens: "5000000000000000000", + } + + cand, err := encodeCandidateToEth(candidate) + + r.Nil(err) + r.EqualValues("0x0000000000000000000000000000000000000001", cand.OwnerAddress.Hex()) + r.EqualValues("0x0000000000000000000000000000000000000002", cand.OperatorAddress.Hex()) + r.EqualValues("0x0000000000000000000000000000000000000003", cand.RewardAddress.Hex()) + r.EqualValues("hello", cand.Name) + r.EqualValues("10000000000000000000", cand.TotalWeightedVotes.String()) + r.EqualValues(100, cand.SelfStakeBucketIdx) + r.EqualValues("5000000000000000000", cand.SelfStakingTokens.String()) +} + +func TestEncodeCandidateToEthErrorOwnerAddress(t *testing.T) { + r := require.New(t) + + candidate := &iotextypes.CandidateV2{ + OwnerAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqps833x", + OperatorAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqz75y8gn", + RewardAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrzsj4p", + Name: "hello", + TotalWeightedVotes: "10000000000000000000", + SelfStakeBucketIdx: 100, + SelfStakingTokens: "5000000000000000000", + } + + cand, err := encodeCandidateToEth(candidate) + + r.Nil(cand) + r.EqualError(err, errInvalidMsg) +} + +func TestEncodeCandidateToEthErrorOperatorAddress(t *testing.T) { + r := require.New(t) + + candidate := &iotextypes.CandidateV2{ + OwnerAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqps833xv", + OperatorAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqz75y8g", + RewardAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrzsj4p", + Name: "hello", + TotalWeightedVotes: "10000000000000000000", + SelfStakeBucketIdx: 100, + SelfStakingTokens: "5000000000000000000", + } + + cand, err := encodeCandidateToEth(candidate) + + r.Nil(cand) + r.EqualError(err, errInvalidMsg) +} + +func TestEncodeCandidateToEthErrorRewardAddress(t *testing.T) { + r := require.New(t) + + candidate := &iotextypes.CandidateV2{ + OwnerAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqps833xv", + OperatorAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqz75y8gn", + RewardAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrzddd", + Name: "hello", + TotalWeightedVotes: "10000000000000000000", + SelfStakeBucketIdx: 100, + SelfStakingTokens: "5000000000000000000", + } + + cand, err := encodeCandidateToEth(candidate) + + r.Nil(cand) + r.EqualError(err, errInvalidMsg) +} + +func TestEncodeCandidateToEthErrorTotalWeightedVotes(t *testing.T) { + r := require.New(t) + + candidate := &iotextypes.CandidateV2{ + OwnerAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqps833xv", + OperatorAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqz75y8gn", + RewardAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrzsj4p", + Name: "hello", + TotalWeightedVotes: "XXX", + SelfStakeBucketIdx: 100, + SelfStakingTokens: "5000000000000000000", + } + + cand, err := encodeCandidateToEth(candidate) + + r.Nil(cand) + r.EqualValues("convert big number error", err.Error()) +} + +func TestEncodeCandidateToEthErrorSelfStakingTokens(t *testing.T) { + r := require.New(t) + + candidate := &iotextypes.CandidateV2{ + OwnerAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqps833xv", + OperatorAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqz75y8gn", + RewardAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrzsj4p", + Name: "hello", + TotalWeightedVotes: "5000000000000000000", + SelfStakeBucketIdx: 100, + SelfStakingTokens: "XXXX", + } + + cand, err := encodeCandidateToEth(candidate) + + r.Nil(cand) + r.EqualValues("convert big number error", err.Error()) +} diff --git a/action/protocol/staking/ethabi/stakebuckets_test.go b/action/protocol/staking/ethabi/stakebuckets_test.go new file mode 100644 index 0000000000..84fa062d5d --- /dev/null +++ b/action/protocol/staking/ethabi/stakebuckets_test.go @@ -0,0 +1,163 @@ +package ethabi + +import ( + "encoding/hex" + "reflect" + "testing" + + "github.com/iotexproject/iotex-proto/golang/iotexapi" + "github.com/iotexproject/iotex-proto/golang/iotextypes" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" +) + +func TestBuildReadStateRequestBuckets(t *testing.T) { + r := require.New(t) + + data, _ := hex.DecodeString("b1ff5c2400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000005") + req, err := BuildReadStateRequest(data) + + r.Nil(err) + r.EqualValues("*ethabi.BucketsStateContext", reflect.TypeOf(req).String()) + + method := &iotexapi.ReadStakingDataMethod{ + Method: iotexapi.ReadStakingDataMethod_BUCKETS, + } + methodBytes, _ := proto.Marshal(method) + r.EqualValues(methodBytes, req.Parameters().MethodName) + + arguments := &iotexapi.ReadStakingDataRequest{ + Request: &iotexapi.ReadStakingDataRequest_Buckets{ + Buckets: &iotexapi.ReadStakingDataRequest_VoteBuckets{ + Pagination: &iotexapi.PaginationParam{ + Offset: 1, + Limit: 5, + }, + }, + }, + } + argumentsBytes, _ := proto.Marshal(arguments) + r.EqualValues([][]byte{argumentsBytes}, req.Parameters().Arguments) +} + +func TestEncodeVoteBucketListToEth(t *testing.T) { + r := require.New(t) + + buckets := make([]*iotextypes.VoteBucket, 2) + + buckets[0] = &iotextypes.VoteBucket{ + Index: 1, + CandidateAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqryn4k9fw", + StakedAmount: "1000000000000000000", + StakedDuration: 1_000_000, + CreateTime: ×tamppb.Timestamp{Seconds: 1_000_000_000}, + StakeStartTime: ×tamppb.Timestamp{Seconds: 1_000_000_001}, + UnstakeStartTime: ×tamppb.Timestamp{Seconds: 1_000_000_002}, + AutoStake: true, + Owner: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqxgce2xkh", + } + buckets[1] = &iotextypes.VoteBucket{ + Index: 2, + CandidateAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqr9wrzs5u", + StakedAmount: "2000000000000000000", + StakedDuration: 2_000_000, + CreateTime: ×tamppb.Timestamp{Seconds: 1_000_000_100}, + StakeStartTime: ×tamppb.Timestamp{Seconds: 1_000_000_101}, + UnstakeStartTime: ×tamppb.Timestamp{Seconds: 1_000_000_102}, + AutoStake: false, + Owner: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqxf907nt9", + } + + data, err := encodeVoteBucketListToEth(_bucketsMethod.Outputs, iotextypes.VoteBucketList{ + Buckets: buckets, + }) + + r.Nil(err) + r.EqualValues("00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000f4240000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000003b9aca01000000000000000000000000000000000000000000000000000000003b9aca02000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000650000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000001e8480000000000000000000000000000000000000000000000000000000003b9aca64000000000000000000000000000000000000000000000000000000003b9aca65000000000000000000000000000000000000000000000000000000003b9aca66000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c9", data) +} + +func TestEncodeVoteBucketListToEthEmptyBuckets(t *testing.T) { + r := require.New(t) + + buckets := make([]*iotextypes.VoteBucket, 0) + + data, err := encodeVoteBucketListToEth(_bucketsMethod.Outputs, iotextypes.VoteBucketList{ + Buckets: buckets, + }) + + r.Nil(err) + r.EqualValues("00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000", data) +} + +func TestEncodeVoteBucketListToEthErrorCandidateAddress(t *testing.T) { + r := require.New(t) + + buckets := make([]*iotextypes.VoteBucket, 1) + + buckets[0] = &iotextypes.VoteBucket{ + Index: 1, + CandidateAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqryn4k9f", + StakedAmount: "1000000000000000000", + StakedDuration: 1_000_000, + CreateTime: ×tamppb.Timestamp{Seconds: 1_000_000_000}, + StakeStartTime: ×tamppb.Timestamp{Seconds: 1_000_000_001}, + UnstakeStartTime: ×tamppb.Timestamp{Seconds: 1_000_000_002}, + AutoStake: true, + Owner: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqxgce2xkh", + } + + _, err := encodeVoteBucketListToEth(_bucketsMethod.Outputs, iotextypes.VoteBucketList{ + Buckets: buckets, + }) + + r.EqualError(err, errInvalidMsg) +} + +func TestEncodeVoteBucketListToEthErrorStakedAmount(t *testing.T) { + r := require.New(t) + + buckets := make([]*iotextypes.VoteBucket, 1) + + buckets[0] = &iotextypes.VoteBucket{ + Index: 1, + CandidateAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqryn4k9fw", + StakedAmount: "xxx", + StakedDuration: 1_000_000, + CreateTime: ×tamppb.Timestamp{Seconds: 1_000_000_000}, + StakeStartTime: ×tamppb.Timestamp{Seconds: 1_000_000_001}, + UnstakeStartTime: ×tamppb.Timestamp{Seconds: 1_000_000_002}, + AutoStake: true, + Owner: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqxgce2xkh", + } + + _, err := encodeVoteBucketListToEth(_bucketsMethod.Outputs, iotextypes.VoteBucketList{ + Buckets: buckets, + }) + + r.EqualValues("convert big number error", err.Error()) +} + +func TestEncodeVoteBucketListToEthErrorOwner(t *testing.T) { + r := require.New(t) + + buckets := make([]*iotextypes.VoteBucket, 1) + + buckets[0] = &iotextypes.VoteBucket{ + Index: 1, + CandidateAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqryn4k9fw", + StakedAmount: "1000000000000000000", + StakedDuration: 1_000_000, + CreateTime: ×tamppb.Timestamp{Seconds: 1_000_000_000}, + StakeStartTime: ×tamppb.Timestamp{Seconds: 1_000_000_001}, + UnstakeStartTime: ×tamppb.Timestamp{Seconds: 1_000_000_002}, + AutoStake: true, + Owner: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqxgce2xk", + } + + _, err := encodeVoteBucketListToEth(_bucketsMethod.Outputs, iotextypes.VoteBucketList{ + Buckets: buckets, + }) + + r.EqualError(err, errInvalidMsg) +} diff --git a/action/protocol/staking/ethabi/stakebucketsbycandidate_test.go b/action/protocol/staking/ethabi/stakebucketsbycandidate_test.go new file mode 100644 index 0000000000..8fafd32ede --- /dev/null +++ b/action/protocol/staking/ethabi/stakebucketsbycandidate_test.go @@ -0,0 +1,41 @@ +package ethabi + +import ( + "encoding/hex" + "reflect" + "testing" + + "github.com/iotexproject/iotex-proto/golang/iotexapi" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" +) + +func TestBuildReadStateRequestBucketsByCandidate(t *testing.T) { + r := require.New(t) + + data, _ := hex.DecodeString("387c001b000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000568656c6c6f000000000000000000000000000000000000000000000000000000") + req, err := BuildReadStateRequest(data) + + r.Nil(err) + r.EqualValues("*ethabi.BucketsByCandidateStateContext", reflect.TypeOf(req).String()) + + method := &iotexapi.ReadStakingDataMethod{ + Method: iotexapi.ReadStakingDataMethod_BUCKETS_BY_CANDIDATE, + } + methodBytes, _ := proto.Marshal(method) + r.EqualValues(methodBytes, req.Parameters().MethodName) + + arguments := &iotexapi.ReadStakingDataRequest{ + Request: &iotexapi.ReadStakingDataRequest_BucketsByCandidate{ + BucketsByCandidate: &iotexapi.ReadStakingDataRequest_VoteBucketsByCandidate{ + CandName: "hello", + Pagination: &iotexapi.PaginationParam{ + Offset: 0, + Limit: 1, + }, + }, + }, + } + argumentsBytes, _ := proto.Marshal(arguments) + r.EqualValues([][]byte{argumentsBytes}, req.Parameters().Arguments) +} diff --git a/action/protocol/staking/ethabi/stakebucketsbyindexes_test.go b/action/protocol/staking/ethabi/stakebucketsbyindexes_test.go new file mode 100644 index 0000000000..6f35722a6a --- /dev/null +++ b/action/protocol/staking/ethabi/stakebucketsbyindexes_test.go @@ -0,0 +1,37 @@ +package ethabi + +import ( + "encoding/hex" + "reflect" + "testing" + + "github.com/iotexproject/iotex-proto/golang/iotexapi" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" +) + +func TestBuildReadStateRequestBucketsByIndexes(t *testing.T) { + r := require.New(t) + + data, _ := hex.DecodeString("7d141b790000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002") + req, err := BuildReadStateRequest(data) + + r.Nil(err) + r.EqualValues("*ethabi.BucketsByIndexesStateContext", reflect.TypeOf(req).String()) + + method := &iotexapi.ReadStakingDataMethod{ + Method: iotexapi.ReadStakingDataMethod_BUCKETS_BY_INDEXES, + } + methodBytes, _ := proto.Marshal(method) + r.EqualValues(methodBytes, req.Parameters().MethodName) + + arguments := &iotexapi.ReadStakingDataRequest{ + Request: &iotexapi.ReadStakingDataRequest_BucketsByIndexes{ + BucketsByIndexes: &iotexapi.ReadStakingDataRequest_VoteBucketsByIndexes{ + Index: []uint64{1, 2}, + }, + }, + } + argumentsBytes, _ := proto.Marshal(arguments) + r.EqualValues([][]byte{argumentsBytes}, req.Parameters().Arguments) +} diff --git a/action/protocol/staking/ethabi/stakebucketsbyvoter_test.go b/action/protocol/staking/ethabi/stakebucketsbyvoter_test.go new file mode 100644 index 0000000000..760343c14c --- /dev/null +++ b/action/protocol/staking/ethabi/stakebucketsbyvoter_test.go @@ -0,0 +1,41 @@ +package ethabi + +import ( + "encoding/hex" + "reflect" + "testing" + + "github.com/iotexproject/iotex-proto/golang/iotexapi" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" +) + +func TestBuildReadStateRequestBucketsByVoter(t *testing.T) { + r := require.New(t) + + data, _ := hex.DecodeString("4a0c59f9000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002") + req, err := BuildReadStateRequest(data) + + r.Nil(err) + r.EqualValues("*ethabi.BucketsByVoterStateContext", reflect.TypeOf(req).String()) + + method := &iotexapi.ReadStakingDataMethod{ + Method: iotexapi.ReadStakingDataMethod_BUCKETS_BY_VOTER, + } + methodBytes, _ := proto.Marshal(method) + r.EqualValues(methodBytes, req.Parameters().MethodName) + + arguments := &iotexapi.ReadStakingDataRequest{ + Request: &iotexapi.ReadStakingDataRequest_BucketsByVoter{ + BucketsByVoter: &iotexapi.ReadStakingDataRequest_VoteBucketsByVoter{ + VoterAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqryn4k9fw", + Pagination: &iotexapi.PaginationParam{ + Offset: 1, + Limit: 2, + }, + }, + }, + } + argumentsBytes, _ := proto.Marshal(arguments) + r.EqualValues([][]byte{argumentsBytes}, req.Parameters().Arguments) +} diff --git a/action/protocol/staking/ethabi/stakebucketscount_test.go b/action/protocol/staking/ethabi/stakebucketscount_test.go new file mode 100644 index 0000000000..a591ffde18 --- /dev/null +++ b/action/protocol/staking/ethabi/stakebucketscount_test.go @@ -0,0 +1,54 @@ +package ethabi + +import ( + "encoding/hex" + "reflect" + "testing" + + "github.com/iotexproject/iotex-proto/golang/iotexapi" + "github.com/iotexproject/iotex-proto/golang/iotextypes" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" +) + +func TestBuildReadStateRequestBucketsCount(t *testing.T) { + r := require.New(t) + + data, _ := hex.DecodeString("fa87b185") + req, err := BuildReadStateRequest(data) + + r.Nil(err) + r.EqualValues("*ethabi.BucketsCountStateContext", reflect.TypeOf(req).String()) + + method := &iotexapi.ReadStakingDataMethod{ + Method: iotexapi.ReadStakingDataMethod_BUCKETS_COUNT, + } + methodBytes, _ := proto.Marshal(method) + r.EqualValues(methodBytes, req.Parameters().MethodName) + + arguments := &iotexapi.ReadStakingDataRequest{ + Request: &iotexapi.ReadStakingDataRequest_BucketsCount_{ + BucketsCount: &iotexapi.ReadStakingDataRequest_BucketsCount{}, + }, + } + argumentsBytes, _ := proto.Marshal(arguments) + r.EqualValues([][]byte{argumentsBytes}, req.Parameters().Arguments) +} + +func TestEncodeBucketsCountToEth(t *testing.T) { + r := require.New(t) + + count := &iotextypes.BucketsCount{ + Total: 5, + Active: 2, + } + countBytes, _ := proto.Marshal(count) + resp := &iotexapi.ReadStateResponse{ + Data: countBytes, + } + + ctx := &BucketsCountStateContext{} + data, err := ctx.EncodeToEth(resp) + r.Nil(err) + r.EqualValues("00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000002", data) +} diff --git a/action/protocol/staking/ethabi/stakecandidatebyaddress_test.go b/action/protocol/staking/ethabi/stakecandidatebyaddress_test.go new file mode 100644 index 0000000000..5062cc0e24 --- /dev/null +++ b/action/protocol/staking/ethabi/stakecandidatebyaddress_test.go @@ -0,0 +1,62 @@ +package ethabi + +import ( + "encoding/hex" + "reflect" + "testing" + + "github.com/iotexproject/iotex-proto/golang/iotexapi" + "github.com/iotexproject/iotex-proto/golang/iotextypes" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" +) + +func TestBuildReadStateRequestCandidateByAddress(t *testing.T) { + r := require.New(t) + + data, _ := hex.DecodeString("43f75ae40000000000000000000000000000000000000000000000000000000000000001") + req, err := BuildReadStateRequest(data) + + r.Nil(err) + r.EqualValues("*ethabi.CandidateByAddressStateContext", reflect.TypeOf(req).String()) + + method := &iotexapi.ReadStakingDataMethod{ + Method: iotexapi.ReadStakingDataMethod_CANDIDATE_BY_ADDRESS, + } + methodBytes, _ := proto.Marshal(method) + r.EqualValues(methodBytes, req.Parameters().MethodName) + + arguments := &iotexapi.ReadStakingDataRequest{ + Request: &iotexapi.ReadStakingDataRequest_CandidateByAddress_{ + CandidateByAddress: &iotexapi.ReadStakingDataRequest_CandidateByAddress{ + OwnerAddr: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqps833xv", + }, + }, + } + argumentsBytes, _ := proto.Marshal(arguments) + r.EqualValues([][]byte{argumentsBytes}, req.Parameters().Arguments) +} + +func TestCandidateByAddressToEth(t *testing.T) { + r := require.New(t) + + candidate := &iotextypes.CandidateV2{ + OwnerAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqps833xv", + OperatorAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqz75y8gn", + RewardAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrzsj4p", + Name: "hello", + TotalWeightedVotes: "10000000000000000000", + SelfStakeBucketIdx: 100, + SelfStakingTokens: "5000000000000000000", + } + + candidateBytes, _ := proto.Marshal(candidate) + resp := &iotexapi.ReadStateResponse{ + Data: candidateBytes, + } + + ctx := &CandidateByAddressStateContext{} + data, err := ctx.EncodeToEth(resp) + r.Nil(err) + r.EqualValues("000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000008ac7230489e8000000000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000004563918244f40000000000000000000000000000000000000000000000000000000000000000000568656c6c6f000000000000000000000000000000000000000000000000000000", data) +} diff --git a/action/protocol/staking/ethabi/stakecandidatebyname_test.go b/action/protocol/staking/ethabi/stakecandidatebyname_test.go new file mode 100644 index 0000000000..496de86ac2 --- /dev/null +++ b/action/protocol/staking/ethabi/stakecandidatebyname_test.go @@ -0,0 +1,62 @@ +package ethabi + +import ( + "encoding/hex" + "reflect" + "testing" + + "github.com/iotexproject/iotex-proto/golang/iotexapi" + "github.com/iotexproject/iotex-proto/golang/iotextypes" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" +) + +func TestBuildReadStateRequestCandidateByName(t *testing.T) { + r := require.New(t) + + data, _ := hex.DecodeString("6da1bb770000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000568656c6c6f000000000000000000000000000000000000000000000000000000") + req, err := BuildReadStateRequest(data) + + r.Nil(err) + r.EqualValues("*ethabi.CandidateByNameStateContext", reflect.TypeOf(req).String()) + + method := &iotexapi.ReadStakingDataMethod{ + Method: iotexapi.ReadStakingDataMethod_CANDIDATE_BY_NAME, + } + methodBytes, _ := proto.Marshal(method) + r.EqualValues(methodBytes, req.Parameters().MethodName) + + arguments := &iotexapi.ReadStakingDataRequest{ + Request: &iotexapi.ReadStakingDataRequest_CandidateByName_{ + CandidateByName: &iotexapi.ReadStakingDataRequest_CandidateByName{ + CandName: "hello", + }, + }, + } + argumentsBytes, _ := proto.Marshal(arguments) + r.EqualValues([][]byte{argumentsBytes}, req.Parameters().Arguments) +} + +func TestCandidateByNameToEth(t *testing.T) { + r := require.New(t) + + candidate := &iotextypes.CandidateV2{ + OwnerAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqps833xv", + OperatorAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqz75y8gn", + RewardAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrzsj4p", + Name: "hello", + TotalWeightedVotes: "10000000000000000000", + SelfStakeBucketIdx: 100, + SelfStakingTokens: "5000000000000000000", + } + + candidateBytes, _ := proto.Marshal(candidate) + resp := &iotexapi.ReadStateResponse{ + Data: candidateBytes, + } + + ctx := &CandidateByNameStateContext{} + data, err := ctx.EncodeToEth(resp) + r.Nil(err) + r.EqualValues("000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000008ac7230489e8000000000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000004563918244f40000000000000000000000000000000000000000000000000000000000000000000568656c6c6f000000000000000000000000000000000000000000000000000000", data) +} diff --git a/action/protocol/staking/ethabi/stakecandidates_test.go b/action/protocol/staking/ethabi/stakecandidates_test.go new file mode 100644 index 0000000000..0647d5ad64 --- /dev/null +++ b/action/protocol/staking/ethabi/stakecandidates_test.go @@ -0,0 +1,93 @@ +package ethabi + +import ( + "encoding/hex" + "reflect" + "testing" + + "github.com/iotexproject/iotex-proto/golang/iotexapi" + "github.com/iotexproject/iotex-proto/golang/iotextypes" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" +) + +func TestBuildReadStateRequestCandidates(t *testing.T) { + r := require.New(t) + + data, _ := hex.DecodeString("c473090600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002") + req, err := BuildReadStateRequest(data) + + r.Nil(err) + r.EqualValues("*ethabi.CandidatesStateContext", reflect.TypeOf(req).String()) + + method := &iotexapi.ReadStakingDataMethod{ + Method: iotexapi.ReadStakingDataMethod_CANDIDATES, + } + methodBytes, _ := proto.Marshal(method) + r.EqualValues(methodBytes, req.Parameters().MethodName) + + arguments := &iotexapi.ReadStakingDataRequest{ + Request: &iotexapi.ReadStakingDataRequest_Candidates_{ + Candidates: &iotexapi.ReadStakingDataRequest_Candidates{ + Pagination: &iotexapi.PaginationParam{ + Offset: 1, + Limit: 2, + }, + }, + }, + } + argumentsBytes, _ := proto.Marshal(arguments) + r.EqualValues([][]byte{argumentsBytes}, req.Parameters().Arguments) +} + +func TestCandidatesToEth(t *testing.T) { + r := require.New(t) + + candidates := &iotextypes.CandidateListV2{ + Candidates: []*iotextypes.CandidateV2{ + { + OwnerAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqps833xv", + OperatorAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqz75y8gn", + RewardAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrzsj4p", + Name: "hello", + TotalWeightedVotes: "10000000000000000000", + SelfStakeBucketIdx: 100, + SelfStakingTokens: "5000000000000000000", + }, { + OwnerAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqyzm8z5y", + OperatorAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq9ldnhfk", + RewardAddress: "io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqx37xp8f", + Name: "world", + TotalWeightedVotes: "11000000000000000000", + SelfStakeBucketIdx: 101, + SelfStakingTokens: "6000000000000000000", + }, + }, + } + candidatesBytes, _ := proto.Marshal(candidates) + resp := &iotexapi.ReadStateResponse{ + Data: candidatesBytes, + } + + ctx := &CandidatesStateContext{} + data, err := ctx.EncodeToEth(resp) + r.Nil(err) + r.EqualValues("000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000008ac7230489e8000000000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000004563918244f40000000000000000000000000000000000000000000000000000000000000000000568656c6c6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000098a7d9b8314c0000000000000000000000000000000000000000000000000000000000000000006500000000000000000000000000000000000000000000000053444835ec5800000000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000", data) +} + +func TestCandidatesToEthEmptyCandidates(t *testing.T) { + r := require.New(t) + + candidates := &iotextypes.CandidateListV2{ + Candidates: []*iotextypes.CandidateV2{}, + } + candidatesBytes, _ := proto.Marshal(candidates) + resp := &iotexapi.ReadStateResponse{ + Data: candidatesBytes, + } + + ctx := &CandidatesStateContext{} + data, err := ctx.EncodeToEth(resp) + r.Nil(err) + r.EqualValues("00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000", data) +} diff --git a/action/protocol/staking/ethabi/staketotalstakingamount_test.go b/action/protocol/staking/ethabi/staketotalstakingamount_test.go new file mode 100644 index 0000000000..89e2dc697c --- /dev/null +++ b/action/protocol/staking/ethabi/staketotalstakingamount_test.go @@ -0,0 +1,54 @@ +package ethabi + +import ( + "encoding/hex" + "reflect" + "testing" + + "github.com/iotexproject/iotex-proto/golang/iotexapi" + "github.com/iotexproject/iotex-proto/golang/iotextypes" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" +) + +func TestBuildReadStateRequestTotalStakingAmount(t *testing.T) { + r := require.New(t) + + data, _ := hex.DecodeString("d201114a") + req, err := BuildReadStateRequest(data) + + r.Nil(err) + r.EqualValues("*ethabi.TotalStakingAmountStateContext", reflect.TypeOf(req).String()) + + method := &iotexapi.ReadStakingDataMethod{ + Method: iotexapi.ReadStakingDataMethod_TOTAL_STAKING_AMOUNT, + } + methodBytes, _ := proto.Marshal(method) + r.EqualValues(methodBytes, req.Parameters().MethodName) + + arguments := &iotexapi.ReadStakingDataRequest{ + Request: &iotexapi.ReadStakingDataRequest_TotalStakingAmount_{ + TotalStakingAmount: &iotexapi.ReadStakingDataRequest_TotalStakingAmount{}, + }, + } + argumentsBytes, _ := proto.Marshal(arguments) + r.EqualValues([][]byte{argumentsBytes}, req.Parameters().Arguments) +} + +func TestEncodeTotalStakingAmountToEth(t *testing.T) { + r := require.New(t) + + meta := &iotextypes.AccountMeta{ + Address: "io000000000000000000000000stakingprotocol", + Balance: "100000000000000000000", + } + metaBytes, _ := proto.Marshal(meta) + resp := &iotexapi.ReadStateResponse{ + Data: metaBytes, + } + + ctx := &TotalStakingAmountStateContext{} + data, err := ctx.EncodeToEth(resp) + r.Nil(err) + r.EqualValues("0000000000000000000000000000000000000000000000056bc75e2d63100000", data) +} diff --git a/action/protocol/types.go b/action/protocol/types.go new file mode 100644 index 0000000000..dfa5e40610 --- /dev/null +++ b/action/protocol/types.go @@ -0,0 +1,27 @@ +package protocol + +import "github.com/iotexproject/iotex-proto/golang/iotexapi" + +type ( + // Parameters state request parameters + Parameters struct { + MethodName []byte + Arguments [][]byte + } + + // StateContext context for ReadState + StateContext interface { + Parameters() *Parameters + EncodeToEth(*iotexapi.ReadStateResponse) (string, error) + } + + // BaseStateContext base state context + BaseStateContext struct { + Parameter *Parameters + } +) + +// Parameters base state parameters +func (r *BaseStateContext) Parameters() *Parameters { + return r.Parameter +} diff --git a/api/web3server.go b/api/web3server.go index a9525f38cd..54de6dfc13 100644 --- a/api/web3server.go +++ b/api/web3server.go @@ -22,6 +22,7 @@ import ( "go.uber.org/zap" "github.com/iotexproject/iotex-core/action" + stakingabi "github.com/iotexproject/iotex-core/action/protocol/staking/ethabi" rewardingabi "github.com/iotexproject/iotex-core/action/protocol/rewarding/ethabi" apitypes "github.com/iotexproject/iotex-core/api/types" "github.com/iotexproject/iotex-core/pkg/log" @@ -330,6 +331,21 @@ func (svr *web3Handler) call(in *gjson.Result) (interface{}, error) { if to == _metamaskBalanceContractAddr { return nil, nil } + if to == address.StakingProtocolAddr { + sctx, err := stakingabi.BuildReadStateRequest(data) + if err != nil { + return nil, err + } + states, err := svr.coreService.ReadState("staking", "", sctx.Parameters().MethodName, sctx.Parameters().Arguments) + if err != nil { + return nil, err + } + ret, err := sctx.EncodeToEth(states) + if err != nil { + return nil, err + } + return "0x" + ret, nil + } if to == address.RewardingProtocol { sctx, err := rewardingabi.BuildReadStateRequest(data) if err != nil {