|
|
|
package services
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"math/big"
|
|
|
|
|
|
|
|
"github.com/coinbase/rosetta-sdk-go/types"
|
|
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
|
|
"github.com/harmony-one/harmony/crypto/bls"
|
|
|
|
common2 "github.com/harmony-one/harmony/internal/common"
|
|
|
|
"github.com/harmony-one/harmony/numeric"
|
|
|
|
types2 "github.com/harmony-one/harmony/staking/types"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
|
|
|
|
hmyTypes "github.com/harmony-one/harmony/core/types"
|
|
|
|
"github.com/harmony-one/harmony/rosetta/common"
|
|
|
|
)
|
|
|
|
|
|
|
|
// TransactionMetadata contains all (optional) information for a transaction.
|
|
|
|
type TransactionMetadata struct {
|
|
|
|
// CrossShardIdentifier is the transaction identifier on the from/source shard
|
|
|
|
CrossShardIdentifier *types.TransactionIdentifier `json:"cross_shard_transaction_identifier,omitempty"`
|
|
|
|
ToShardID *uint32 `json:"to_shard,omitempty"`
|
|
|
|
FromShardID *uint32 `json:"from_shard,omitempty"`
|
|
|
|
// ContractAccountIdentifier is the 'main' contract account ID associated with a transaction
|
|
|
|
ContractAccountIdentifier *types.AccountIdentifier `json:"contract_account_identifier,omitempty"`
|
|
|
|
Data *string `json:"data,omitempty"`
|
|
|
|
Logs []*hmyTypes.Log `json:"logs,omitempty"`
|
|
|
|
// SlotPubKeys SlotPubKeyToAdd SlotPubKeyToRemove are all hex representation of bls public key
|
|
|
|
SlotPubKeys []string `json:"slot_pub_keys,omitempty"`
|
|
|
|
SlotKeySigs []string `json:"slot_key_sigs,omitempty"`
|
|
|
|
SlotKeyToAddSig string `json:"slot_key_to_add_sig,omitempty"`
|
|
|
|
SlotPubKeyToAdd string `json:"slot_pub_key_to_add,omitempty"`
|
|
|
|
SlotPubKeyToRemove string `json:"slot_pub_key_to_remove,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalFromInterface ..
|
|
|
|
func (t *TransactionMetadata) UnmarshalFromInterface(metaData interface{}) error {
|
|
|
|
var args TransactionMetadata
|
|
|
|
dat, err := json.Marshal(metaData)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := json.Unmarshal(dat, &args); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
*t = args
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ConstructTransaction object (unsigned).
|
|
|
|
func ConstructTransaction(
|
|
|
|
components *OperationComponents, metadata *ConstructMetadata, sourceShardID uint32,
|
|
|
|
) (response hmyTypes.PoolTransaction, rosettaError *types.Error) {
|
|
|
|
if components == nil || metadata == nil {
|
|
|
|
return nil, common.NewError(common.CatchAllError, map[string]interface{}{
|
|
|
|
"message": "nil components or metadata",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
if metadata.Transaction.FromShardID != nil && *metadata.Transaction.FromShardID != sourceShardID {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": fmt.Sprintf("intended source shard %v != tx metadata source shard %v",
|
|
|
|
sourceShardID, *metadata.Transaction.FromShardID),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
var tx hmyTypes.PoolTransaction
|
|
|
|
switch components.Type {
|
|
|
|
case common.NativeCrossShardTransferOperation:
|
|
|
|
if tx, rosettaError = constructCrossShardTransaction(components, metadata, sourceShardID); rosettaError != nil {
|
|
|
|
return nil, rosettaError
|
|
|
|
}
|
|
|
|
case common.ContractCreationOperation:
|
|
|
|
if tx, rosettaError = constructContractCreationTransaction(components, metadata, sourceShardID); rosettaError != nil {
|
|
|
|
return nil, rosettaError
|
|
|
|
}
|
|
|
|
case common.NativeTransferOperation:
|
|
|
|
if tx, rosettaError = constructPlainTransaction(components, metadata, sourceShardID); rosettaError != nil {
|
|
|
|
return nil, rosettaError
|
|
|
|
}
|
|
|
|
case common.CreateValidatorOperation:
|
|
|
|
if tx, rosettaError = constructCreateValidatorTransaction(components, metadata); rosettaError != nil {
|
|
|
|
return nil, rosettaError
|
|
|
|
}
|
|
|
|
case common.EditValidatorOperation:
|
|
|
|
if tx, rosettaError = constructEditValidatorTransaction(components, metadata); rosettaError != nil {
|
|
|
|
return nil, rosettaError
|
|
|
|
}
|
|
|
|
case common.DelegateOperation:
|
|
|
|
if tx, rosettaError = constructDelegateTransaction(components, metadata); rosettaError != nil {
|
|
|
|
return nil, rosettaError
|
|
|
|
}
|
|
|
|
case common.UndelegateOperation:
|
|
|
|
if tx, rosettaError = constructUndelegateTransaction(components, metadata); rosettaError != nil {
|
|
|
|
return nil, rosettaError
|
|
|
|
}
|
|
|
|
case common.CollectRewardsOperation:
|
|
|
|
if tx, rosettaError = constructCollectRewardsTransaction(components, metadata); rosettaError != nil {
|
|
|
|
return nil, rosettaError
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": fmt.Sprintf("cannot create transaction with component type %v", components.Type),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return tx, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// constructCrossShardTransaction ..
|
|
|
|
func constructCrossShardTransaction(
|
|
|
|
components *OperationComponents, metadata *ConstructMetadata, sourceShardID uint32,
|
|
|
|
) (hmyTypes.PoolTransaction, *types.Error) {
|
|
|
|
if components.To == nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": "cross shard transfer requires a receiver",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
to, err := getAddress(components.To)
|
|
|
|
if err != nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": errors.WithMessage(err, "invalid sender address").Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
if metadata.Transaction.FromShardID == nil || metadata.Transaction.ToShardID == nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": "cross shard transfer requires to & from shard IDs",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
if *metadata.Transaction.FromShardID != sourceShardID {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": fmt.Sprintf("cannot send transaction for shard %v", *metadata.Transaction.FromShardID),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
if *metadata.Transaction.FromShardID == *metadata.Transaction.ToShardID {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": "cross-shard transfer cannot be within same shard",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
data := hexutil.Bytes{}
|
|
|
|
if metadata.Transaction.Data != nil {
|
|
|
|
if data, err = hexutil.Decode(*metadata.Transaction.Data); err != nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": errors.WithMessage(err, "improper data format for transaction data").Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return hmyTypes.NewCrossShardTransaction(
|
|
|
|
metadata.Nonce, &to, *metadata.Transaction.FromShardID, *metadata.Transaction.ToShardID,
|
|
|
|
components.Amount, metadata.GasLimit, metadata.GasPrice, data,
|
|
|
|
), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// constructContractCreationTransaction ..
|
|
|
|
func constructContractCreationTransaction(
|
|
|
|
components *OperationComponents, metadata *ConstructMetadata, sourceShardID uint32,
|
|
|
|
) (hmyTypes.PoolTransaction, *types.Error) {
|
|
|
|
if metadata.Transaction.Data == nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": "contract creation requires data, but none found in transaction metadata",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
data, err := hexutil.Decode(*metadata.Transaction.Data)
|
|
|
|
if err != nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": errors.WithMessage(err, "improper data format for transaction data").Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return hmyTypes.NewContractCreation(
|
|
|
|
metadata.Nonce, sourceShardID, components.Amount, metadata.GasLimit, metadata.GasPrice, data,
|
|
|
|
), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func constructCreateValidatorTransaction(
|
|
|
|
components *OperationComponents, metadata *ConstructMetadata,
|
|
|
|
) (hmyTypes.PoolTransaction, *types.Error) {
|
|
|
|
createValidatorMsg := components.StakingMessage.(common.CreateValidatorOperationMetadata)
|
|
|
|
validatorAddr, err := common2.Bech32ToAddress(createValidatorMsg.ValidatorAddress)
|
|
|
|
if err != nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": errors.WithMessage(err, "convert validator address error").Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
var slotPubKeys []bls.SerializedPublicKey
|
|
|
|
for _, slotPubKey := range metadata.Transaction.SlotPubKeys {
|
|
|
|
var pubKey bls.SerializedPublicKey
|
|
|
|
key, err := hexutil.Decode(slotPubKey)
|
|
|
|
if err != nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": errors.WithMessage(err, "decode slot public key error").Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
copy(pubKey[:], key)
|
|
|
|
slotPubKeys = append(slotPubKeys, pubKey)
|
|
|
|
}
|
|
|
|
var slotKeySigs []bls.SerializedSignature
|
|
|
|
for _, slotKeySig := range metadata.Transaction.SlotKeySigs {
|
|
|
|
var keySig bls.SerializedSignature
|
|
|
|
sig, err := hexutil.Decode(slotKeySig)
|
|
|
|
if err != nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": errors.WithMessage(err, "decode slot key sig error").Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
copy(keySig[:], sig)
|
|
|
|
slotKeySigs = append(slotKeySigs, keySig)
|
|
|
|
}
|
|
|
|
stakePayloadMaker := func() (types2.Directive, interface{}) {
|
|
|
|
return types2.DirectiveCreateValidator, types2.CreateValidator{
|
|
|
|
Description: types2.Description{
|
|
|
|
Name: createValidatorMsg.Name,
|
|
|
|
Identity: createValidatorMsg.Identity,
|
|
|
|
Website: createValidatorMsg.Website,
|
|
|
|
SecurityContact: createValidatorMsg.SecurityContact,
|
|
|
|
Details: createValidatorMsg.Details,
|
|
|
|
},
|
|
|
|
CommissionRates: types2.CommissionRates{
|
|
|
|
Rate: numeric.Dec{createValidatorMsg.CommissionRate},
|
|
|
|
MaxRate: numeric.Dec{createValidatorMsg.MaxCommissionRate},
|
|
|
|
MaxChangeRate: numeric.Dec{createValidatorMsg.MaxChangeRate},
|
|
|
|
},
|
|
|
|
MinSelfDelegation: new(big.Int).Mul(createValidatorMsg.MinSelfDelegation, big.NewInt(1e18)),
|
|
|
|
MaxTotalDelegation: new(big.Int).Mul(createValidatorMsg.MaxTotalDelegation, big.NewInt(1e18)),
|
|
|
|
ValidatorAddress: validatorAddr,
|
|
|
|
SlotPubKeys: slotPubKeys,
|
|
|
|
SlotKeySigs: slotKeySigs,
|
|
|
|
Amount: new(big.Int).Mul(createValidatorMsg.Amount, big.NewInt(1e18)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stakingTransaction, err := types2.NewStakingTransaction(metadata.Nonce, metadata.GasLimit, metadata.GasPrice, stakePayloadMaker)
|
|
|
|
if err != nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": errors.WithMessage(err, "new staking transaction error").Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return stakingTransaction, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func constructEditValidatorTransaction(
|
|
|
|
components *OperationComponents, metadata *ConstructMetadata,
|
|
|
|
) (hmyTypes.PoolTransaction, *types.Error) {
|
|
|
|
editValidatorMsg := components.StakingMessage.(common.EditValidatorOperationMetadata)
|
|
|
|
validatorAddr, err := common2.Bech32ToAddress(editValidatorMsg.ValidatorAddress)
|
|
|
|
if err != nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": errors.WithMessage(err, "convert validator address error").Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
var slotKeyToAdd bls.SerializedPublicKey
|
|
|
|
slotKeyToAddBytes, err := hexutil.Decode(metadata.Transaction.SlotPubKeyToAdd)
|
|
|
|
copy(slotKeyToAdd[:], slotKeyToAddBytes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": errors.WithMessage(err, "parse slotKeyToAdd error").Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
var slotKeyToRemove bls.SerializedPublicKey
|
|
|
|
slotKeyToRemoveBytes, err := hexutil.Decode(metadata.Transaction.SlotPubKeyToRemove)
|
|
|
|
copy(slotKeyToRemove[:], slotKeyToRemoveBytes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": errors.WithMessage(err, "parse slotKeyToRemove error").Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
var slotKeyToAddSig bls.SerializedSignature
|
|
|
|
SlotKeyToAddSigBytes, err := hexutil.Decode(metadata.Transaction.SlotKeyToAddSig)
|
|
|
|
copy(slotKeyToAddSig[:], SlotKeyToAddSigBytes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": errors.WithMessage(err, "parse slotKeyToAddSig error").Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
stakePayloadMaker := func() (types2.Directive, interface{}) {
|
|
|
|
return types2.DirectiveEditValidator, types2.EditValidator{
|
|
|
|
ValidatorAddress: validatorAddr,
|
|
|
|
Description: types2.Description{
|
|
|
|
Name: editValidatorMsg.Name,
|
|
|
|
Identity: editValidatorMsg.Identity,
|
|
|
|
Website: editValidatorMsg.Website,
|
|
|
|
SecurityContact: editValidatorMsg.SecurityContact,
|
|
|
|
Details: editValidatorMsg.Details,
|
|
|
|
},
|
|
|
|
CommissionRate: &numeric.Dec{editValidatorMsg.CommissionRate},
|
|
|
|
MinSelfDelegation: new(big.Int).Mul(editValidatorMsg.MinSelfDelegation, big.NewInt(1e18)),
|
|
|
|
MaxTotalDelegation: new(big.Int).Mul(editValidatorMsg.MaxTotalDelegation, big.NewInt(1e18)),
|
|
|
|
SlotKeyToAdd: &slotKeyToAdd,
|
|
|
|
SlotKeyToRemove: &slotKeyToRemove,
|
|
|
|
SlotKeyToAddSig: &slotKeyToAddSig,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stakingTransaction, err := types2.NewStakingTransaction(metadata.Nonce, metadata.GasLimit, metadata.GasPrice, stakePayloadMaker)
|
|
|
|
if err != nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": errors.WithMessage(err, "new staking transaction error").Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return stakingTransaction, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func constructDelegateTransaction(
|
|
|
|
components *OperationComponents, metadata *ConstructMetadata,
|
|
|
|
) (hmyTypes.PoolTransaction, *types.Error) {
|
|
|
|
delegaterMsg := components.StakingMessage.(common.DelegateOperationMetadata)
|
|
|
|
delegatorAddr, err := common2.Bech32ToAddress(delegaterMsg.DelegatorAddress)
|
|
|
|
if err != nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": errors.WithMessage(err, "convert delegator address error").Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
validatorAddr, err := common2.Bech32ToAddress(delegaterMsg.ValidatorAddress)
|
|
|
|
if err != nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": errors.WithMessage(err, "convert validator address error").Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
stakePayloadMaker := func() (types2.Directive, interface{}) {
|
|
|
|
return types2.DirectiveDelegate, types2.Delegate{
|
|
|
|
DelegatorAddress: delegatorAddr,
|
|
|
|
ValidatorAddress: validatorAddr,
|
|
|
|
Amount: new(big.Int).Mul(delegaterMsg.Amount, big.NewInt(1e18)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stakingTransaction, err := types2.NewStakingTransaction(metadata.Nonce, metadata.GasLimit, metadata.GasPrice, stakePayloadMaker)
|
|
|
|
if err != nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": errors.WithMessage(err, "new staking transaction error").Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return stakingTransaction, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func constructUndelegateTransaction(
|
|
|
|
components *OperationComponents, metadata *ConstructMetadata,
|
|
|
|
) (hmyTypes.PoolTransaction, *types.Error) {
|
|
|
|
undelegaterMsg := components.StakingMessage.(common.UndelegateOperationMetadata)
|
|
|
|
delegatorAddr, err := common2.Bech32ToAddress(undelegaterMsg.DelegatorAddress)
|
|
|
|
if err != nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": errors.WithMessage(err, "convert delegator address error").Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
validatorAddr, err := common2.Bech32ToAddress(undelegaterMsg.ValidatorAddress)
|
|
|
|
if err != nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": errors.WithMessage(err, "convert validator address error").Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
stakePayloadMaker := func() (types2.Directive, interface{}) {
|
|
|
|
return types2.DirectiveUndelegate, types2.Undelegate{
|
|
|
|
DelegatorAddress: delegatorAddr,
|
|
|
|
ValidatorAddress: validatorAddr,
|
|
|
|
Amount: new(big.Int).Mul(undelegaterMsg.Amount, big.NewInt(1e18)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stakingTransaction, err := types2.NewStakingTransaction(metadata.Nonce, metadata.GasLimit, metadata.GasPrice, stakePayloadMaker)
|
|
|
|
if err != nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": errors.WithMessage(err, "new staking transaction error").Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return stakingTransaction, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func constructCollectRewardsTransaction(
|
|
|
|
components *OperationComponents, metadata *ConstructMetadata,
|
|
|
|
) (hmyTypes.PoolTransaction, *types.Error) {
|
|
|
|
collectRewardsMsg := components.StakingMessage.(common.CollectRewardsMetadata)
|
|
|
|
delegatorAddr, err := common2.Bech32ToAddress(collectRewardsMsg.DelegatorAddress)
|
|
|
|
if err != nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": errors.WithMessage(err, "convert delegator address error").Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
stakePayloadMaker := func() (types2.Directive, interface{}) {
|
|
|
|
return types2.DirectiveCollectRewards, types2.CollectRewards{
|
|
|
|
DelegatorAddress: delegatorAddr,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stakingTransaction, err := types2.NewStakingTransaction(metadata.Nonce, metadata.GasLimit, metadata.GasPrice, stakePayloadMaker)
|
|
|
|
if err != nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": errors.WithMessage(err, "new staking transaction error").Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return stakingTransaction, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// constructPlainTransaction ..
|
|
|
|
func constructPlainTransaction(
|
|
|
|
components *OperationComponents, metadata *ConstructMetadata, sourceShardID uint32,
|
|
|
|
) (hmyTypes.PoolTransaction, *types.Error) {
|
|
|
|
if components.To == nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": "cross shard transfer requires a receiver",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
to, err := getAddress(components.To)
|
|
|
|
if err != nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": errors.WithMessage(err, "invalid sender address").Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
data := hexutil.Bytes{}
|
|
|
|
if metadata.Transaction.Data != nil {
|
|
|
|
if data, err = hexutil.Decode(*metadata.Transaction.Data); err != nil {
|
|
|
|
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
|
|
|
|
"message": errors.WithMessage(err, "improper data format for transaction data").Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return hmyTypes.NewTransaction(
|
|
|
|
metadata.Nonce, to, sourceShardID, components.Amount, metadata.GasLimit, metadata.GasPrice, data,
|
|
|
|
), nil
|
|
|
|
}
|