github.com/iotexproject/iotex-core@v1.14.1-rc1/action/stake_transferownership.go (about)

     1  // Copyright (c) 2020 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  package action
     7  
     8  import (
     9  	"bytes"
    10  	"math/big"
    11  	"strings"
    12  
    13  	"github.com/ethereum/go-ethereum/accounts/abi"
    14  	"github.com/ethereum/go-ethereum/common"
    15  	"github.com/ethereum/go-ethereum/core/types"
    16  	"github.com/pkg/errors"
    17  	"google.golang.org/protobuf/proto"
    18  
    19  	"github.com/iotexproject/iotex-address/address"
    20  	"github.com/iotexproject/iotex-core/pkg/util/byteutil"
    21  	"github.com/iotexproject/iotex-proto/golang/iotextypes"
    22  
    23  	"github.com/iotexproject/iotex-core/pkg/version"
    24  )
    25  
    26  const (
    27  	_transferStakeInterfaceABI = `[
    28  		{
    29  			"inputs": [
    30  				{
    31  					"internalType": "address",
    32  					"name": "voterAddress",
    33  					"type": "address"
    34  				},
    35  				{
    36  					"internalType": "uint64",
    37  					"name": "bucketIndex",
    38  					"type": "uint64"
    39  				},
    40  				{
    41  					"internalType": "uint8[]",
    42  					"name": "data",
    43  					"type": "uint8[]"
    44  				}
    45  			],
    46  			"name": "transferStake",
    47  			"outputs": [],
    48  			"stateMutability": "nonpayable",
    49  			"type": "function"
    50  		}
    51  	]`
    52  )
    53  
    54  var (
    55  	// _transferStakeMethod is the interface of the abi encoding of stake action
    56  	_transferStakeMethod abi.Method
    57  	_                    EthCompatibleAction = (*TransferStake)(nil)
    58  )
    59  
    60  // TransferStake defines the action of transfering stake ownership ts the other
    61  type TransferStake struct {
    62  	AbstractAction
    63  
    64  	voterAddress address.Address
    65  	bucketIndex  uint64
    66  	payload      []byte
    67  }
    68  
    69  func init() {
    70  	transferStakeInterface, err := abi.JSON(strings.NewReader(_transferStakeInterfaceABI))
    71  	if err != nil {
    72  		panic(err)
    73  	}
    74  	var ok bool
    75  	_transferStakeMethod, ok = transferStakeInterface.Methods["transferStake"]
    76  	if !ok {
    77  		panic("fail to load the method")
    78  	}
    79  }
    80  
    81  // NewTransferStake returns a TransferStake instance
    82  func NewTransferStake(
    83  	nonce uint64,
    84  	voterAddress string,
    85  	bucketIndex uint64,
    86  	payload []byte,
    87  	gasLimit uint64,
    88  	gasPrice *big.Int,
    89  ) (*TransferStake, error) {
    90  	voterAddr, err := address.FromString(voterAddress)
    91  	if err != nil {
    92  		return nil, errors.Wrap(err, "failed to load address from string")
    93  	}
    94  	return &TransferStake{
    95  		AbstractAction: AbstractAction{
    96  			version:  version.ProtocolVersion,
    97  			nonce:    nonce,
    98  			gasLimit: gasLimit,
    99  			gasPrice: gasPrice,
   100  		},
   101  		voterAddress: voterAddr,
   102  		bucketIndex:  bucketIndex,
   103  		payload:      payload,
   104  	}, nil
   105  }
   106  
   107  // VoterAddress returns the address of recipient
   108  func (ts *TransferStake) VoterAddress() address.Address { return ts.voterAddress }
   109  
   110  // BucketIndex returns bucket index
   111  func (ts *TransferStake) BucketIndex() uint64 { return ts.bucketIndex }
   112  
   113  // Payload returns the payload bytes
   114  func (ts *TransferStake) Payload() []byte { return ts.payload }
   115  
   116  // Serialize returns a raw byte stream of the transfer stake action struct
   117  func (ts *TransferStake) Serialize() []byte {
   118  	return byteutil.Must(proto.Marshal(ts.Proto()))
   119  }
   120  
   121  // Proto converts transfer stake to protobuf
   122  func (ts *TransferStake) Proto() *iotextypes.StakeTransferOwnership {
   123  	act := &iotextypes.StakeTransferOwnership{
   124  		VoterAddress: ts.voterAddress.String(),
   125  		BucketIndex:  ts.bucketIndex,
   126  		Payload:      ts.payload,
   127  	}
   128  
   129  	return act
   130  }
   131  
   132  // LoadProto loads transfer stake protobuf
   133  func (ts *TransferStake) LoadProto(pbAct *iotextypes.StakeTransferOwnership) error {
   134  	if pbAct == nil {
   135  		return ErrNilProto
   136  	}
   137  	voterAddress, err := address.FromString(pbAct.GetVoterAddress())
   138  	if err != nil {
   139  		return errors.Wrap(err, "failed to load address from string")
   140  	}
   141  	ts.voterAddress = voterAddress
   142  	ts.bucketIndex = pbAct.GetBucketIndex()
   143  	ts.payload = pbAct.GetPayload()
   144  	return nil
   145  }
   146  
   147  // IntrinsicGas returns the intrinsic gas of a TransferStake
   148  func (ts *TransferStake) IntrinsicGas() (uint64, error) {
   149  	payloadSize := uint64(len(ts.Payload()))
   150  	return CalculateIntrinsicGas(MoveStakeBaseIntrinsicGas, MoveStakePayloadGas, payloadSize)
   151  }
   152  
   153  // Cost returns the tstal cost of a TransferStake
   154  func (ts *TransferStake) Cost() (*big.Int, error) {
   155  	intrinsicGas, err := ts.IntrinsicGas()
   156  	if err != nil {
   157  		return nil, errors.Wrap(err, "failed ts get intrinsic gas for the TransferStake")
   158  	}
   159  	transferStakeFee := big.NewInt(0).Mul(ts.GasPrice(), big.NewInt(0).SetUint64(intrinsicGas))
   160  	return transferStakeFee, nil
   161  }
   162  
   163  // EncodeABIBinary encodes data in abi encoding
   164  func (ts *TransferStake) EncodeABIBinary() ([]byte, error) {
   165  	return ts.encodeABIBinary()
   166  }
   167  
   168  func (ts *TransferStake) encodeABIBinary() ([]byte, error) {
   169  	voterEthAddr := common.BytesToAddress(ts.voterAddress.Bytes())
   170  	data, err := _transferStakeMethod.Inputs.Pack(voterEthAddr, ts.bucketIndex, ts.payload)
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  	return append(_transferStakeMethod.ID, data...), nil
   175  }
   176  
   177  // NewTransferStakeFromABIBinary decodes data into TransferStake action
   178  func NewTransferStakeFromABIBinary(data []byte) (*TransferStake, error) {
   179  	var (
   180  		paramsMap = map[string]interface{}{}
   181  		ok        bool
   182  		err       error
   183  		ts        TransferStake
   184  	)
   185  	// sanity check
   186  	if len(data) <= 4 || !bytes.Equal(_transferStakeMethod.ID, data[:4]) {
   187  		return nil, errDecodeFailure
   188  	}
   189  	if err := _transferStakeMethod.Inputs.UnpackIntoMap(paramsMap, data[4:]); err != nil {
   190  		return nil, err
   191  	}
   192  	if ts.voterAddress, err = ethAddrToNativeAddr(paramsMap["voterAddress"]); err != nil {
   193  		return nil, err
   194  	}
   195  	if ts.bucketIndex, ok = paramsMap["bucketIndex"].(uint64); !ok {
   196  		return nil, errDecodeFailure
   197  	}
   198  	if ts.payload, ok = paramsMap["data"].([]byte); !ok {
   199  		return nil, errDecodeFailure
   200  	}
   201  	return &ts, nil
   202  }
   203  
   204  // ToEthTx converts action to eth-compatible tx
   205  func (ts *TransferStake) ToEthTx(_ uint32) (*types.Transaction, error) {
   206  	data, err := ts.encodeABIBinary()
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  	return types.NewTx(&types.LegacyTx{
   211  		Nonce:    ts.Nonce(),
   212  		GasPrice: ts.GasPrice(),
   213  		Gas:      ts.GasLimit(),
   214  		To:       &_stakingProtocolEthAddr,
   215  		Value:    big.NewInt(0),
   216  		Data:     data,
   217  	}), nil
   218  }