github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/feesplit/handler.go (about)

     1  package feesplit
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/ethereum/go-ethereum/common"
     7  	"github.com/ethereum/go-ethereum/crypto"
     8  
     9  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    10  	sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors"
    11  	tmtypes "github.com/fibonacci-chain/fbc/libs/tendermint/types"
    12  	"github.com/fibonacci-chain/fbc/x/feesplit/keeper"
    13  	"github.com/fibonacci-chain/fbc/x/feesplit/types"
    14  )
    15  
    16  // NewHandler defines the fees module handler instance
    17  func NewHandler(k keeper.Keeper) sdk.Handler {
    18  	return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
    19  		ctx.SetEventManager(sdk.NewEventManager())
    20  
    21  		if !tmtypes.HigherThanVenus3(ctx.BlockHeight()) {
    22  			errMsg := fmt.Sprintf("feesplt module not support at height %d", ctx.BlockHeight())
    23  			return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, errMsg)
    24  		}
    25  
    26  		params := k.GetParams(ctx)
    27  		if !params.EnableFeeSplit {
    28  			return nil, types.ErrFeeSplitDisabled
    29  		}
    30  
    31  		switch msg := msg.(type) {
    32  		case types.MsgRegisterFeeSplit:
    33  			return handleMsgRegisterFeeSplit(ctx, msg, k, params)
    34  		case types.MsgUpdateFeeSplit:
    35  			return handleMsgUpdateFeeSplit(ctx, msg, k)
    36  		case types.MsgCancelFeeSplit:
    37  			return handleMsgCancelFeeSplit(ctx, msg, k)
    38  		default:
    39  			return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg)
    40  		}
    41  	}
    42  }
    43  
    44  // handleMsgRegisterFeeSplit registers a contract to receive transaction fees
    45  func handleMsgRegisterFeeSplit(
    46  	ctx sdk.Context,
    47  	msg types.MsgRegisterFeeSplit,
    48  	k keeper.Keeper,
    49  	params types.Params,
    50  ) (*sdk.Result, error) {
    51  	contract := common.HexToAddress(msg.ContractAddress)
    52  	if k.IsFeeSplitRegistered(ctx, contract) {
    53  		return nil, sdkerrors.Wrapf(
    54  			types.ErrFeeSplitAlreadyRegistered,
    55  			"contract is already registered %s", contract,
    56  		)
    57  	}
    58  
    59  	deployer := sdk.MustAccAddressFromBech32(msg.DeployerAddress)
    60  	deployerAccount, isExist := k.GetEthAccount(ctx, common.BytesToAddress(deployer))
    61  	if !isExist {
    62  		return nil, sdkerrors.Wrapf(
    63  			types.ErrFeeAccountNotFound,
    64  			"deployer account not found %s", msg.DeployerAddress,
    65  		)
    66  	}
    67  
    68  	if deployerAccount != nil && deployerAccount.IsContract() {
    69  		return nil, sdkerrors.Wrapf(
    70  			types.ErrFeeSplitDeployerIsNotEOA,
    71  			"deployer cannot be a contract %s", msg.DeployerAddress,
    72  		)
    73  	}
    74  
    75  	// contract must already be deployed, to avoid spam registrations
    76  	contractAccount, _ := k.GetEthAccount(ctx, contract)
    77  	if contractAccount == nil || !contractAccount.IsContract() {
    78  		return nil, sdkerrors.Wrapf(
    79  			types.ErrFeeSplitNoContractDeployed,
    80  			"no contract code found at address %s", msg.ContractAddress,
    81  		)
    82  	}
    83  
    84  	if msg.WithdrawerAddress == "" {
    85  		msg.WithdrawerAddress = msg.DeployerAddress
    86  	}
    87  	withdrawer := sdk.MustAccAddressFromBech32(msg.WithdrawerAddress)
    88  
    89  	derivedContract := common.BytesToAddress(deployer)
    90  
    91  	// the contract can be directly deployed by an EOA or created through one
    92  	// or more factory contracts. If it was deployed by an EOA account, then
    93  	// msg.Nonces contains the EOA nonce for the deployment transaction.
    94  	// If it was deployed by one or more factories, msg.Nonces contains the EOA
    95  	// nonce for the origin factory contract, then the nonce of the factory
    96  	// for the creation of the next factory/contract.
    97  	for _, nonce := range msg.Nonces {
    98  		ctx.GasMeter().ConsumeGas(
    99  			params.AddrDerivationCostCreate,
   100  			"fee split registration: address derivation CREATE opcode",
   101  		)
   102  
   103  		derivedContract = crypto.CreateAddress(derivedContract, nonce)
   104  	}
   105  
   106  	if contract != derivedContract {
   107  		return nil, sdkerrors.Wrapf(
   108  			types.ErrDerivedNotMatched,
   109  			"not contract deployer or wrong nonce: expected %s instead of %s",
   110  			derivedContract, msg.ContractAddress,
   111  		)
   112  	}
   113  
   114  	// prevent storing the same address for deployer and withdrawer
   115  	feeSplit := types.NewFeeSplit(contract, deployer, withdrawer)
   116  	k.SetFeeSplit(ctx, feeSplit)
   117  	k.SetDeployerMap(ctx, deployer, contract)
   118  	k.SetWithdrawerMap(ctx, withdrawer, contract)
   119  
   120  	k.Logger(ctx).Debug(
   121  		"registering contract for transaction fees",
   122  		"contract", msg.ContractAddress, "deployer", msg.DeployerAddress,
   123  		"withdraw", msg.WithdrawerAddress,
   124  	)
   125  
   126  	ctx.EventManager().EmitEvents(
   127  		sdk.Events{
   128  			sdk.NewEvent(
   129  				types.EventTypeRegisterFeeSplit,
   130  				sdk.NewAttribute(sdk.AttributeKeySender, msg.DeployerAddress),
   131  				sdk.NewAttribute(types.AttributeKeyContract, msg.ContractAddress),
   132  				sdk.NewAttribute(types.AttributeKeyWithdrawerAddress, msg.WithdrawerAddress),
   133  			),
   134  		},
   135  	)
   136  
   137  	return &sdk.Result{Events: ctx.EventManager().Events()}, nil
   138  }
   139  
   140  // handleMsgUpdateFeeSplit updates the withdraw address of a given FeeSplit. If the given
   141  // withdraw address is empty or the same as the deployer address, the withdraw
   142  // address is removed.
   143  func handleMsgUpdateFeeSplit(
   144  	ctx sdk.Context,
   145  	msg types.MsgUpdateFeeSplit,
   146  	k keeper.Keeper,
   147  ) (*sdk.Result, error) {
   148  	contract := common.HexToAddress(msg.ContractAddress)
   149  	feeSplit, found := k.GetFeeSplit(ctx, contract)
   150  	if !found {
   151  		return nil, sdkerrors.Wrapf(
   152  			types.ErrFeeSplitContractNotRegistered,
   153  			"contract %s is not registered", msg.ContractAddress,
   154  		)
   155  	}
   156  
   157  	// error if the msg deployer address is not the same as the fee's deployer
   158  	if !sdk.MustAccAddressFromBech32(msg.DeployerAddress).Equals(feeSplit.DeployerAddress) {
   159  		return nil, sdkerrors.Wrapf(
   160  			sdkerrors.ErrUnauthorized,
   161  			"%s is not the contract deployer", msg.DeployerAddress,
   162  		)
   163  	}
   164  
   165  	var withdrawer sdk.AccAddress
   166  	withdrawer = sdk.MustAccAddressFromBech32(msg.WithdrawerAddress)
   167  
   168  	// fee split with the given withdraw address is already registered
   169  	if withdrawer.Equals(feeSplit.WithdrawerAddress) {
   170  		return nil, sdkerrors.Wrapf(
   171  			types.ErrFeeSplitAlreadyRegistered,
   172  			"fee split with withdraw address %s", msg.WithdrawerAddress,
   173  		)
   174  	}
   175  
   176  	k.DeleteWithdrawerMap(ctx, feeSplit.WithdrawerAddress, contract)
   177  	k.SetWithdrawerMap(ctx, withdrawer, contract)
   178  	// update fee split
   179  	feeSplit.WithdrawerAddress = withdrawer
   180  	k.SetFeeSplit(ctx, feeSplit)
   181  
   182  	ctx.EventManager().EmitEvents(
   183  		sdk.Events{
   184  			sdk.NewEvent(
   185  				types.EventTypeUpdateFeeSplit,
   186  				sdk.NewAttribute(types.AttributeKeyContract, msg.ContractAddress),
   187  				sdk.NewAttribute(sdk.AttributeKeySender, msg.DeployerAddress),
   188  				sdk.NewAttribute(types.AttributeKeyWithdrawerAddress, msg.WithdrawerAddress),
   189  			),
   190  		},
   191  	)
   192  
   193  	return &sdk.Result{Events: ctx.EventManager().Events()}, nil
   194  }
   195  
   196  // handleMsgCancelFeeSplit deletes the FeeSplit for a given contract
   197  func handleMsgCancelFeeSplit(
   198  	ctx sdk.Context,
   199  	msg types.MsgCancelFeeSplit,
   200  	k keeper.Keeper,
   201  ) (*sdk.Result, error) {
   202  	contract := common.HexToAddress(msg.ContractAddress)
   203  	fee, found := k.GetFeeSplit(ctx, contract)
   204  	if !found {
   205  		return nil, sdkerrors.Wrapf(
   206  			types.ErrFeeSplitContractNotRegistered,
   207  			"contract %s is not registered", msg.ContractAddress,
   208  		)
   209  	}
   210  
   211  	if !sdk.MustAccAddressFromBech32(msg.DeployerAddress).Equals(fee.DeployerAddress) {
   212  		return nil, sdkerrors.Wrapf(
   213  			sdkerrors.ErrUnauthorized,
   214  			"%s is not the contract deployer", msg.DeployerAddress,
   215  		)
   216  	}
   217  
   218  	k.DeleteFeeSplit(ctx, fee)
   219  	k.DeleteDeployerMap(ctx, fee.DeployerAddress, contract)
   220  	k.DeleteWithdrawerMap(ctx, fee.WithdrawerAddress, contract)
   221  
   222  	ctx.EventManager().EmitEvents(
   223  		sdk.Events{
   224  			sdk.NewEvent(
   225  				types.EventTypeCancelFeeSplit,
   226  				sdk.NewAttribute(sdk.AttributeKeySender, msg.DeployerAddress),
   227  				sdk.NewAttribute(types.AttributeKeyContract, msg.ContractAddress),
   228  			),
   229  		},
   230  	)
   231  
   232  	return &sdk.Result{Events: ctx.EventManager().Events()}, nil
   233  }