diff --git a/services/wallet/bridge/bridge.go b/services/wallet/bridge/bridge.go index 5f680a121..5b8c5a5f7 100644 --- a/services/wallet/bridge/bridge.go +++ b/services/wallet/bridge/bridge.go @@ -35,6 +35,7 @@ const ( ERC721TransferName = "ERC721Transfer" ERC1155TransferName = "ERC1155Transfer" ENSRegisterName = "ENSRegister" + ENSReleaseName = "ENSRelease" ) func getSigner(chainID uint64, from types.Address, verifiedAccount *account.SelectedExtKey) bind.SignerFn { diff --git a/services/wallet/bridge/ens_release.go b/services/wallet/bridge/ens_release.go new file mode 100644 index 000000000..1859f2b23 --- /dev/null +++ b/services/wallet/bridge/ens_release.go @@ -0,0 +1,127 @@ +package bridge + +import ( + "context" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + ethTypes "github.com/ethereum/go-ethereum/core/types" + "github.com/status-im/status-go/account" + "github.com/status-im/status-go/contracts" + "github.com/status-im/status-go/contracts/registrar" + "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/rpc" + "github.com/status-im/status-go/services/ens" + walletCommon "github.com/status-im/status-go/services/wallet/common" + "github.com/status-im/status-go/transactions" +) + +type ENSReleaseBridge struct { + contractMaker *contracts.ContractMaker + transactor transactions.TransactorIface + ensService *ens.Service +} + +func NewENSReleaseBridge(rpcClient *rpc.Client, transactor transactions.TransactorIface, ensService *ens.Service) *ENSReleaseBridge { + return &ENSReleaseBridge{ + contractMaker: &contracts.ContractMaker{ + RPCClient: rpcClient, + }, + transactor: transactor, + ensService: ensService, + } +} + +func (s *ENSReleaseBridge) Name() string { + return ENSReleaseName +} + +func (s *ENSReleaseBridge) AvailableFor(params BridgeParams) (bool, error) { + return params.FromChain.ChainID == walletCommon.EthereumMainnet || params.FromChain.ChainID == walletCommon.EthereumSepolia, nil +} + +func (s *ENSReleaseBridge) CalculateFees(params BridgeParams) (*big.Int, *big.Int, error) { + return ZeroBigIntValue, ZeroBigIntValue, nil +} + +func (s *ENSReleaseBridge) PackTxInputData(params BridgeParams, contractType string) ([]byte, error) { + registrarABI, err := abi.JSON(strings.NewReader(registrar.UsernameRegistrarABI)) + if err != nil { + return []byte{}, err + } + + return registrarABI.Pack("release", usernameToLabel(params.Username)) +} + +func (s *ENSReleaseBridge) EstimateGas(params BridgeParams) (uint64, error) { + contractAddress, err := s.GetContractAddress(params) + if err != nil { + return 0, err + } + + input, err := s.PackTxInputData(params, "") + if err != nil { + return 0, err + } + + ethClient, err := s.contractMaker.RPCClient.EthClient(params.FromChain.ChainID) + if err != nil { + return 0, err + } + + msg := ethereum.CallMsg{ + From: params.FromAddr, + To: &contractAddress, + Value: ZeroBigIntValue, + Data: input, + } + + estimation, err := ethClient.EstimateGas(context.Background(), msg) + if err != nil { + return 0, err + } + + increasedEstimation := float64(estimation) * IncreaseEstimatedGasFactor + + return uint64(increasedEstimation), nil +} + +func (s *ENSReleaseBridge) BuildTx(params BridgeParams) (*ethTypes.Transaction, error) { + toAddr := types.Address(params.ToAddr) + inputData, err := s.PackTxInputData(params, "") + if err != nil { + return nil, err + } + + sendArgs := &TransactionBridge{ + TransferTx: &transactions.SendTxArgs{ + From: types.Address(params.FromAddr), + To: &toAddr, + Value: (*hexutil.Big)(ZeroBigIntValue), + Data: inputData, + }, + ChainID: params.FromChain.ChainID, + } + + return s.BuildTransaction(sendArgs) +} + +func (s *ENSReleaseBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) { + return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, verifiedAccount) +} + +func (s *ENSReleaseBridge) BuildTransaction(sendArgs *TransactionBridge) (*ethTypes.Transaction, error) { + return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, *sendArgs.TransferTx) +} + +func (s *ENSReleaseBridge) CalculateAmountOut(params BridgeParams) (*big.Int, error) { + return params.AmountIn, nil +} + +func (s *ENSReleaseBridge) GetContractAddress(params BridgeParams) (common.Address, error) { + return s.ensService.API().GetRegistrarAddress(context.Background(), params.FromChain.ChainID) +} diff --git a/services/wallet/router/router.go b/services/wallet/router/router.go index 485bd7caa..06535055a 100644 --- a/services/wallet/router/router.go +++ b/services/wallet/router/router.go @@ -284,6 +284,7 @@ func NewRouter(rpcClient *rpc.Client, transactor *transactions.Transactor, token hop := bridge.NewHopBridge(rpcClient, transactor, tokenManager) paraswap := bridge.NewSwapParaswap(rpcClient, transactor, tokenManager) ensRegister := bridge.NewENSRegisterBridge(rpcClient, transactor, ensService) + ensRelease := bridge.NewENSReleaseBridge(rpcClient, transactor, ensService) bridges[transfer.Name()] = transfer bridges[erc721Transfer.Name()] = erc721Transfer @@ -292,6 +293,7 @@ func NewRouter(rpcClient *rpc.Client, transactor *transactions.Transactor, token bridges[erc1155Transfer.Name()] = erc1155Transfer bridges[paraswap.Name()] = paraswap bridges[ensRegister.Name()] = ensRegister + bridges[ensRelease.Name()] = ensRelease return &Router{ rpcClient: rpcClient, @@ -544,7 +546,7 @@ func (r *Router) SuggestedRoutes( estimatedTime := r.feesManager.TransactionEstimatedTime(ctx, network.ChainID, maxFees) for _, brdg := range r.bridges { // Skip bridges that are added because of the Router V2, to not break the current functionality - if brdg.Name() == bridge.ENSRegisterName { + if brdg.Name() == bridge.ENSRegisterName || brdg.Name() == bridge.ENSReleaseName { continue } diff --git a/services/wallet/router/router_send_type.go b/services/wallet/router/router_send_type.go index fb84f5d78..e941e350a 100644 --- a/services/wallet/router/router_send_type.go +++ b/services/wallet/router/router_send_type.go @@ -108,6 +108,8 @@ func (s SendType) canUseBridge(b bridge.Bridge) bool { return bridgeName == bridge.ERC1155TransferName case ENSRegister: return bridgeName == bridge.ENSRegisterName + case ENSRelease: + return bridgeName == bridge.ENSReleaseName default: return true } diff --git a/services/wallet/router/router_v2.go b/services/wallet/router/router_v2.go index fed918fe9..0c5d2af2c 100644 --- a/services/wallet/router/router_v2.go +++ b/services/wallet/router/router_v2.go @@ -305,6 +305,12 @@ func validateInputData(input *RouteInputParams) error { return nil } + if input.SendType == ENSRelease { + if input.Username == "" { + return errors.New("username is required for ENSRelease") + } + } + if input.FromLockedAmount != nil && len(input.FromLockedAmount) > 0 { for chainID, amount := range input.FromLockedAmount { if input.TestnetMode {