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 }