diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ac14f931d..73c1a29c28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,13 +7,15 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ### Bug Fixes -* +* fixed several code issues found by sonar [#2113](https://github.com/hyperledger/web3j/pull/2113) +* update GitHub actions versions [#2114](https://github.com/hyperledger/web3j/pull/2114) ### Features * bump snapshot version to 4.12.3 [#2101](https://github.com/hyperledger/web3j/pull/2101) * Add HSM kms implementation [#2105](https://github.com/hyperledger/web3j/pull/2105) * Added support for Holesky [#2111](https://github.com/hyperledger/web3j/pull/2111) +* Advance ENS features and metadata retrieval [#2116](https://github.com/hyperledger/web3j/pull/2116) ### BREAKING CHANGES diff --git a/core/src/main/java/org/web3j/ens/EnsMetadataResponse.java b/core/src/main/java/org/web3j/ens/EnsMetadataResponse.java new file mode 100644 index 0000000000..0c91390ccf --- /dev/null +++ b/core/src/main/java/org/web3j/ens/EnsMetadataResponse.java @@ -0,0 +1,124 @@ +/* + * Copyright 2024 Web3 Labs Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.web3j.ens; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class EnsMetadataResponse { + public boolean is_normalized; + public String name; + public String description; + public Attribute[] attributes; + public String url; + public long last_request_date; + public int version; + public String background_image; + public String image; + public String image_url; + + public boolean isIs_normalized() { + return is_normalized; + } + + public void setIs_normalized(boolean is_normalized) { + this.is_normalized = is_normalized; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Attribute[] getAttributes() { + return attributes; + } + + public void setAttributes(Attribute[] attributes) { + this.attributes = attributes; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public long getLast_request_date() { + return last_request_date; + } + + public void setLast_request_date(long last_request_date) { + this.last_request_date = last_request_date; + } + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + + public String getBackground_image() { + return background_image; + } + + public void setBackground_image(String background_image) { + this.background_image = background_image; + } + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + + public String getImage_url() { + return image_url; + } + + public void setImage_url(String image_url) { + this.image_url = image_url; + } + + public static class Attribute { + public String trait_type; + public String display_type; + public Object value; + } + + @Override + public String toString() { + try { + return new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this); + } catch (JsonProcessingException e) { + return "Error serializing EnsMetadataResponse: " + e.getMessage(); + } + } +} diff --git a/core/src/main/java/org/web3j/ens/EnsResolver.java b/core/src/main/java/org/web3j/ens/EnsResolver.java index 73018379b2..15cac0dad6 100644 --- a/core/src/main/java/org/web3j/ens/EnsResolver.java +++ b/core/src/main/java/org/web3j/ens/EnsResolver.java @@ -25,12 +25,14 @@ import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; +import okhttp3.Response; import okhttp3.ResponseBody; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.web3j.abi.DefaultFunctionReturnDecoder; import org.web3j.abi.datatypes.ens.OffchainLookup; +import org.web3j.crypto.Credentials; import org.web3j.crypto.Keys; import org.web3j.crypto.WalletUtils; import org.web3j.dto.EnsGatewayRequestDTO; @@ -38,12 +40,14 @@ import org.web3j.ens.contracts.generated.ENS; import org.web3j.ens.contracts.generated.OffchainResolverContract; import org.web3j.ens.contracts.generated.PublicResolver; +import org.web3j.ens.contracts.generated.ReverseRegistrar; import org.web3j.protocol.ObjectMapperFactory; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.DefaultBlockParameterName; import org.web3j.protocol.core.methods.response.EthBlock; import org.web3j.protocol.core.methods.response.EthSyncing; import org.web3j.protocol.core.methods.response.NetVersion; +import org.web3j.protocol.core.methods.response.TransactionReceipt; import org.web3j.tx.ClientTransactionManager; import org.web3j.tx.TransactionManager; import org.web3j.tx.gas.DefaultGasProvider; @@ -150,6 +154,31 @@ protected OffchainResolverContract obtainOffchainResolver(String ensName) { } } + protected OffchainResolverContract obtainOffchainResolver( + String ensName, Credentials credentials) { + if (isValidEnsName(ensName, addressLength)) { + boolean isSynced; + + try { + isSynced = isSynced(); + } catch (Exception e) { + throw new EnsResolutionException("Unable to determine sync status of node", e); + } + + if (!isSynced) { + throw new EnsResolutionException("Node is not currently synced"); + } + + try { + return lookupOffchainResolver(ensName, credentials); + } catch (Exception e) { + throw new EnsResolutionException("Unable to get resolver", e); + } + } else { + throw new EnsResolutionException("EnsName is invalid: " + ensName); + } + } + /** * Returns the address of the resolver for the specified node. * @@ -336,6 +365,19 @@ protected Request buildRequest(String url, String sender, String data) } } + public TransactionReceipt setReverseName(String name, Credentials credentials) + throws Exception { + ReverseRegistrar reverseRegistrar = getReverseRegistrarContract(credentials); + return reverseRegistrar.setName(name).send(); + } + + public TransactionReceipt setReverseName( + String addr, String owner, String resolver, String name, Credentials credentials) + throws Exception { + ReverseRegistrar reverseRegistrar = getReverseRegistrarContract(credentials); + return reverseRegistrar.setNameForAddr(addr, owner, resolver, name).send(); + } + /** * Reverse name resolution as documented in the specification. @@ -376,13 +418,14 @@ private OffchainResolverContract lookupOffchainResolver(String ensName) throws E getResolverAddress(ensName), web3j, transactionManager, new DefaultGasProvider()); } - private String getResolverAddress(String ensName) throws Exception { - NetVersion netVersion = web3j.netVersion().send(); - String registryContract = Contracts.resolveRegistryContract(netVersion.getNetVersion()); - - ENS ensRegistry = - ENS.load(registryContract, web3j, transactionManager, new DefaultGasProvider()); + private OffchainResolverContract lookupOffchainResolver(String ensName, Credentials credentials) + throws Exception { + return OffchainResolverContract.load( + getResolverAddress(ensName), web3j, credentials, new DefaultGasProvider()); + } + public String getResolverAddress(String ensName) throws Exception { + ENS ensRegistry = getRegistryContract(); byte[] nameHash = NameHash.nameHashAsBytes(ensName); String address = ensRegistry.resolver(nameHash).send(); @@ -393,6 +436,66 @@ private String getResolverAddress(String ensName) throws Exception { return address; } + public String getOwnerAddress(String ensName) throws Exception { + ENS ensRegistry = getRegistryContract(); + byte[] nameHash = NameHash.nameHashAsBytes(ensName); + return ensRegistry.owner(nameHash).send(); + } + + private ENS getRegistryContract() throws IOException { + NetVersion netVersion = web3j.netVersion().send(); + String registryContract = Contracts.resolveRegistryContract(netVersion.getNetVersion()); + + return ENS.load(registryContract, web3j, transactionManager, new DefaultGasProvider()); + } + + protected ReverseRegistrar getReverseRegistrarContract(Credentials credentials) + throws IOException { + NetVersion netVersion = web3j.netVersion().send(); + String reverseRegistrarContract = + ReverseRegistrarContracts.resolveReverseRegistrarContract( + netVersion.getNetVersion()); + + return ReverseRegistrar.load( + reverseRegistrarContract, web3j, credentials, new DefaultGasProvider()); + } + + public EnsMetadataResponse getEnsMetadata(String name) throws IOException { + NetVersion netVersion = web3j.netVersion().send(); + byte[] nameHash = NameHash.nameHashAsBytes(name); + String apiUrl = + NameWrapperUrl.getEnsMetadataApi(netVersion.getNetVersion()) + + Numeric.toHexString(nameHash); + + Request request = new Request.Builder().url(apiUrl).get().build(); + + try (Response response = client.newCall(request).execute()) { + if (!response.isSuccessful()) { + throw new IOException( + "Failed to fetch ENS metadata. HTTP error code: " + response.code()); + } + + // Parse the JSON response + assert response.body() != null; + String responseBody = response.body().string(); + return new ObjectMapper().readValue(responseBody, EnsMetadataResponse.class); + } + } + + public String getEnsText(String name, String key) throws Exception { + OffchainResolverContract offchainResolverContract = obtainOffchainResolver(name); + byte[] nameHash = NameHash.nameHashAsBytes(name); + return offchainResolverContract.text(nameHash, key).send(); + } + + public TransactionReceipt setEnsText( + String name, String key, String value, Credentials credentials) throws Exception { + OffchainResolverContract offchainResolverContract = + obtainOffchainResolver(name, credentials); + byte[] nameHash = NameHash.nameHashAsBytes(name); + return offchainResolverContract.setText(nameHash, key, value).send(); + } + boolean isSynced() throws Exception { EthSyncing ethSyncing = web3j.ethSyncing().send(); if (ethSyncing.isSyncing()) { diff --git a/core/src/main/java/org/web3j/ens/NameWrapperUrl.java b/core/src/main/java/org/web3j/ens/NameWrapperUrl.java new file mode 100644 index 0000000000..6d41000f75 --- /dev/null +++ b/core/src/main/java/org/web3j/ens/NameWrapperUrl.java @@ -0,0 +1,42 @@ +/* + * Copyright 2024 Web3 Labs Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.web3j.ens; + +import org.web3j.tx.ChainIdLong; + +public class NameWrapperUrl { + + public static final String BASE_URL = "https://ens-metadata-service.appspot.com/"; + public static final String MAINNET_URL = + BASE_URL + "mainnet/" + "0xD4416b13d2b3a9aBae7AcD5D6C2BbDBE25686401/"; + public static final String SEPOLIA_URL = + BASE_URL + "sepolia/" + "0x0635513f179D50A207757E05759CbD106d7dFcE8/"; + public static final String HOLESKY_URL = + BASE_URL + "holesky/" + "0xab50971078225D365994dc1Edcb9b7FD72Bb4862/"; + + private NameWrapperUrl() {} + + public static String getEnsMetadataApi(String chainId) { + final Long chainIdLong = Long.parseLong(chainId); + if (chainIdLong.equals(ChainIdLong.MAINNET)) { + return MAINNET_URL; + } else if (chainIdLong.equals(ChainIdLong.SEPOLIA)) { + return SEPOLIA_URL; + } else if (chainIdLong.equals(ChainIdLong.HOLESKY)) { + return HOLESKY_URL; + } else { + throw new EnsResolutionException( + "Unable to get ENS metadata API for network id: " + chainId); + } + } +} diff --git a/core/src/main/java/org/web3j/ens/ReverseRegistrarContracts.java b/core/src/main/java/org/web3j/ens/ReverseRegistrarContracts.java new file mode 100644 index 0000000000..9d02946a2a --- /dev/null +++ b/core/src/main/java/org/web3j/ens/ReverseRegistrarContracts.java @@ -0,0 +1,44 @@ +/* + * Copyright 2024 Web3 Labs Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.web3j.ens; + +import org.web3j.tx.ChainIdLong; + +public class ReverseRegistrarContracts { + + public static final String MAINNET = "0xa58E81fe9b61B5c3fE2AFD33CF304c454AbFc7Cb"; + public static final String SEPOLIA = "0xA0a1AbcDAe1a2a4A2EF8e9113Ff0e02DD81DC0C6"; + public static final String HOLESKY = "0x132AC0B116a73add4225029D1951A9A707Ef673f"; + public static final String LINEA = "0x08D3fF6E65f680844fd2465393ff6f0d742b67D5"; + public static final String LINEA_SEPOLIA = "0x4aAA964D8EB65508ca3DA3b0A3C060c16059E613"; + + private ReverseRegistrarContracts() {} + + public static String resolveReverseRegistrarContract(String chainId) { + final Long chainIdLong = Long.parseLong(chainId); + if (chainIdLong.equals(ChainIdLong.MAINNET)) { + return MAINNET; + } else if (chainIdLong.equals(ChainIdLong.SEPOLIA)) { + return SEPOLIA; + } else if (chainIdLong.equals(ChainIdLong.HOLESKY)) { + return HOLESKY; + } else if (chainIdLong.equals(ChainIdLong.LINEA)) { + return LINEA; + } else if (chainIdLong.equals(ChainIdLong.LINEA_SEPOLIA)) { + return LINEA_SEPOLIA; + } else { + throw new EnsResolutionException( + "Unable to resolve ENS reverse registrar contract for network id: " + chainId); + } + } +} diff --git a/core/src/main/java/org/web3j/ens/contracts/generated/ReverseRegistrar.java b/core/src/main/java/org/web3j/ens/contracts/generated/ReverseRegistrar.java new file mode 100644 index 0000000000..a882ebf876 --- /dev/null +++ b/core/src/main/java/org/web3j/ens/contracts/generated/ReverseRegistrar.java @@ -0,0 +1,450 @@ +package org.web3j.ens.contracts.generated; + +import io.reactivex.Flowable; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.web3j.abi.EventEncoder; +import org.web3j.abi.FunctionEncoder; +import org.web3j.abi.TypeReference; +import org.web3j.abi.datatypes.Address; +import org.web3j.abi.datatypes.Bool; +import org.web3j.abi.datatypes.Event; +import org.web3j.abi.datatypes.Function; +import org.web3j.abi.datatypes.Type; +import org.web3j.abi.datatypes.generated.Bytes32; +import org.web3j.crypto.Credentials; +import org.web3j.protocol.Web3j; +import org.web3j.protocol.core.DefaultBlockParameter; +import org.web3j.protocol.core.RemoteCall; +import org.web3j.protocol.core.RemoteFunctionCall; +import org.web3j.protocol.core.methods.request.EthFilter; +import org.web3j.protocol.core.methods.response.BaseEventResponse; +import org.web3j.protocol.core.methods.response.Log; +import org.web3j.protocol.core.methods.response.TransactionReceipt; +import org.web3j.tx.Contract; +import org.web3j.tx.TransactionManager; +import org.web3j.tx.gas.ContractGasProvider; + +/** + *

Auto generated code. + *

Do not modify! + *

Please use the web3j command line tools, + * or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the + * codegen module to update. + * + *

Generated with web3j version 4.12.2. + */ +@SuppressWarnings("rawtypes") +public class ReverseRegistrar extends Contract { + public static final String BINARY = "60a060405234801561000f575f80fd5b50604051610e27380380610e2783398101604081905261002e916101ab565b61003733610145565b6001600160a01b03811660808190526040516302571be360e01b81527f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e260048201525f91906302571be390602401602060405180830381865afa1580156100a0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100c491906101ab565b90506001600160a01b0381161561013e57604051630f41a04d60e11b81523360048201526001600160a01b03821690631e83409a906024016020604051808303815f875af1158015610118573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061013c91906101cd565b505b50506101e4565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b03811681146101a8575f80fd5b50565b5f602082840312156101bb575f80fd5b81516101c681610194565b9392505050565b5f602082840312156101dd575f80fd5b5051919050565b608051610c1d61020a5f395f8181610127015281816102cb01526104c40152610c1d5ff3fe608060405234801561000f575f80fd5b50600436106100e5575f3560e01c80638da5cb5b11610088578063c66485b211610063578063c66485b2146101da578063da8c229e146101ed578063e0dba60f1461021f578063f2fde38b14610232575f80fd5b80638da5cb5b146101a4578063bffbe61c146101b4578063c47f0027146101c7575f80fd5b806365669631116100c35780636566963114610161578063715018a6146101745780637a806d6b1461017e578063828eab0e14610191575f80fd5b80630f5a5466146100e95780631e83409a1461010f5780633f15457f14610122575b5f80fd5b6100fc6100f7366004610958565b610245565b6040519081526020015b60405180910390f35b6100fc61011d36600461098f565b610258565b6101497f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610106565b6100fc61016f3660046109aa565b610279565b61017c610526565b005b6100fc61018c366004610a91565b610539565b600254610149906001600160a01b031681565b5f546001600160a01b0316610149565b6100fc6101c236600461098f565b6105ae565b6100fc6101d5366004610b02565b610608565b61017c6101e836600461098f565b610624565b61020f6101fb36600461098f565b60016020525f908152604090205460ff1681565b6040519015158152602001610106565b61017c61022d366004610b49565b6106e4565b61017c61024036600461098f565b61074a565b5f610251338484610279565b9392505050565b6002545f9061027390339084906001600160a01b0316610279565b92915050565b5f836001600160a01b0381163314806102a05750335f9081526001602052604090205460ff165b80610334575060405163e985e9c560e01b81526001600160a01b0382811660048301523360248301527f0000000000000000000000000000000000000000000000000000000000000000169063e985e9c590604401602060405180830381865afa158015610310573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103349190610b75565b806103435750610343816107c3565b6103e05760405162461bcd60e51b815260206004820152605b60248201527f526576657273655265676973747261723a2043616c6c6572206973206e6f742060448201527f6120636f6e74726f6c6c6572206f7220617574686f726973656420627920616460648201527f6472657373206f7220746865206164647265737320697473656c660000000000608482015260a4015b60405180910390fd5b5f6103ea8661083a565b604080517f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2602080830191909152818301849052825180830384018152606090920192839052815191012091925081906001600160a01b038916907f6ada868dd3058cf77a48a74489fd7963688e5464b2b0fa957ace976243270e92905f90a36040516305ef2c7f60e41b81527f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e26004820152602481018390526001600160a01b03878116604483015286811660648301525f60848301527f00000000000000000000000000000000000000000000000000000000000000001690635ef2c7f09060a4015f604051808303815f87803b158015610505575f80fd5b505af1158015610517573d5f803e3d5ffd5b50929998505050505050505050565b61052e61089c565b6105375f6108f5565b565b5f80610546868686610279565b604051637737221360e01b81529091506001600160a01b038516906377372213906105779084908790600401610b90565b5f604051808303815f87803b15801561058e575f80fd5b505af11580156105a0573d5f803e3d5ffd5b509298975050505050505050565b5f7f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e26105d98361083a565b604080516020810193909352820152606001604051602081830303815290604052805190602001209050919050565b6002545f9061027390339081906001600160a01b031685610539565b61062c61089c565b6001600160a01b03811661069b5760405162461bcd60e51b815260206004820152603060248201527f526576657273655265676973747261723a205265736f6c76657220616464726560448201526f07373206d757374206e6f7420626520360841b60648201526084016103d7565b600280546001600160a01b0319166001600160a01b0383169081179091556040517feae17a84d9eb83d8c8eb317f9e7d64857bc363fa51674d996c023f4340c577cf905f90a250565b6106ec61089c565b6001600160a01b0382165f81815260016020908152604091829020805460ff191685151590811790915591519182527f4c97694570a07277810af7e5669ffd5f6a2d6b74b6e9a274b8b870fd5114cf87910160405180910390a25050565b61075261089c565b6001600160a01b0381166107b75760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016103d7565b6107c0816108f5565b50565b5f816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa92505050801561081e575060408051601f3d908101601f1916820190925261081b91810190610bcc565b60015b61082957505f919050565b6001600160a01b0316331492915050565b5f60285b8015610891575f19016f181899199a1a9b1b9c1cb0b131b232b360811b600f84161a81536010909204915f19016f181899199a1a9b1b9c1cb0b131b232b360811b600f84161a815360108304925061083e565b505060285f20919050565b5f546001600160a01b031633146105375760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016103d7565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b03811681146107c0575f80fd5b5f8060408385031215610969575f80fd5b823561097481610944565b9150602083013561098481610944565b809150509250929050565b5f6020828403121561099f575f80fd5b813561025181610944565b5f805f606084860312156109bc575f80fd5b83356109c781610944565b925060208401356109d781610944565b915060408401356109e781610944565b809150509250925092565b634e487b7160e01b5f52604160045260245ffd5b5f82601f830112610a15575f80fd5b813567ffffffffffffffff811115610a2f57610a2f6109f2565b604051601f8201601f19908116603f0116810167ffffffffffffffff81118282101715610a5e57610a5e6109f2565b604052818152838201602001851015610a75575f80fd5b816020850160208301375f918101602001919091529392505050565b5f805f8060808587031215610aa4575f80fd5b8435610aaf81610944565b93506020850135610abf81610944565b92506040850135610acf81610944565b9150606085013567ffffffffffffffff811115610aea575f80fd5b610af687828801610a06565b91505092959194509250565b5f60208284031215610b12575f80fd5b813567ffffffffffffffff811115610b28575f80fd5b610b3484828501610a06565b949350505050565b80151581146107c0575f80fd5b5f8060408385031215610b5a575f80fd5b8235610b6581610944565b9150602083013561098481610b3c565b5f60208284031215610b85575f80fd5b815161025181610b3c565b828152604060208201525f82518060408401528060208501606085015e5f606082850101526060601f19601f8301168401019150509392505050565b5f60208284031215610bdc575f80fd5b81516102518161094456fea2646970667358221220c370a6c9398b82bdee5c1487b08a61e11feefc977b7701fd97121b9b92b9c81164736f6c634300081a0033"; + + private static String librariesLinkedBinary; + + public static final String FUNC_CLAIM = "claim"; + + public static final String FUNC_CLAIMFORADDR = "claimForAddr"; + + public static final String FUNC_CLAIMWITHRESOLVER = "claimWithResolver"; + + public static final String FUNC_CONTROLLERS = "controllers"; + + public static final String FUNC_DEFAULTRESOLVER = "defaultResolver"; + + public static final String FUNC_ENS = "ens"; + + public static final String FUNC_NODE = "node"; + + public static final String FUNC_OWNER = "owner"; + + public static final String FUNC_RENOUNCEOWNERSHIP = "renounceOwnership"; + + public static final String FUNC_SETCONTROLLER = "setController"; + + public static final String FUNC_SETDEFAULTRESOLVER = "setDefaultResolver"; + + public static final String FUNC_SETNAME = "setName"; + + public static final String FUNC_SETNAMEFORADDR = "setNameForAddr"; + + public static final String FUNC_TRANSFEROWNERSHIP = "transferOwnership"; + + public static final Event CONTROLLERCHANGED_EVENT = new Event("ControllerChanged", + Arrays.>asList(new TypeReference

(true) {}, new TypeReference() {})); + ; + + public static final Event DEFAULTRESOLVERCHANGED_EVENT = new Event("DefaultResolverChanged", + Arrays.>asList(new TypeReference
(true) {})); + ; + + public static final Event OWNERSHIPTRANSFERRED_EVENT = new Event("OwnershipTransferred", + Arrays.>asList(new TypeReference
(true) {}, new TypeReference
(true) {})); + ; + + public static final Event REVERSECLAIMED_EVENT = new Event("ReverseClaimed", + Arrays.>asList(new TypeReference
(true) {}, new TypeReference(true) {})); + ; + + @Deprecated + protected ReverseRegistrar(String contractAddress, Web3j web3j, Credentials credentials, + BigInteger gasPrice, BigInteger gasLimit) { + super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit); + } + + protected ReverseRegistrar(String contractAddress, Web3j web3j, Credentials credentials, + ContractGasProvider contractGasProvider) { + super(BINARY, contractAddress, web3j, credentials, contractGasProvider); + } + + @Deprecated + protected ReverseRegistrar(String contractAddress, Web3j web3j, + TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { + super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit); + } + + protected ReverseRegistrar(String contractAddress, Web3j web3j, + TransactionManager transactionManager, ContractGasProvider contractGasProvider) { + super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider); + } + + public static List getControllerChangedEvents( + TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(CONTROLLERCHANGED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (Contract.EventValuesWithLog eventValues : valueList) { + ControllerChangedEventResponse typedResponse = new ControllerChangedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.controller = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.enabled = (Boolean) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static ControllerChangedEventResponse getControllerChangedEventFromLog(Log log) { + Contract.EventValuesWithLog eventValues = staticExtractEventParametersWithLog(CONTROLLERCHANGED_EVENT, log); + ControllerChangedEventResponse typedResponse = new ControllerChangedEventResponse(); + typedResponse.log = log; + typedResponse.controller = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.enabled = (Boolean) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + public Flowable controllerChangedEventFlowable( + EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getControllerChangedEventFromLog(log)); + } + + public Flowable controllerChangedEventFlowable( + DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(CONTROLLERCHANGED_EVENT)); + return controllerChangedEventFlowable(filter); + } + + public static List getDefaultResolverChangedEvents( + TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(DEFAULTRESOLVERCHANGED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (Contract.EventValuesWithLog eventValues : valueList) { + DefaultResolverChangedEventResponse typedResponse = new DefaultResolverChangedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.resolver = (String) eventValues.getIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static DefaultResolverChangedEventResponse getDefaultResolverChangedEventFromLog( + Log log) { + Contract.EventValuesWithLog eventValues = staticExtractEventParametersWithLog(DEFAULTRESOLVERCHANGED_EVENT, log); + DefaultResolverChangedEventResponse typedResponse = new DefaultResolverChangedEventResponse(); + typedResponse.log = log; + typedResponse.resolver = (String) eventValues.getIndexedValues().get(0).getValue(); + return typedResponse; + } + + public Flowable defaultResolverChangedEventFlowable( + EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getDefaultResolverChangedEventFromLog(log)); + } + + public Flowable defaultResolverChangedEventFlowable( + DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(DEFAULTRESOLVERCHANGED_EVENT)); + return defaultResolverChangedEventFlowable(filter); + } + + public static List getOwnershipTransferredEvents( + TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(OWNERSHIPTRANSFERRED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (Contract.EventValuesWithLog eventValues : valueList) { + OwnershipTransferredEventResponse typedResponse = new OwnershipTransferredEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.previousOwner = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.newOwner = (String) eventValues.getIndexedValues().get(1).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static OwnershipTransferredEventResponse getOwnershipTransferredEventFromLog(Log log) { + Contract.EventValuesWithLog eventValues = staticExtractEventParametersWithLog(OWNERSHIPTRANSFERRED_EVENT, log); + OwnershipTransferredEventResponse typedResponse = new OwnershipTransferredEventResponse(); + typedResponse.log = log; + typedResponse.previousOwner = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.newOwner = (String) eventValues.getIndexedValues().get(1).getValue(); + return typedResponse; + } + + public Flowable ownershipTransferredEventFlowable( + EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getOwnershipTransferredEventFromLog(log)); + } + + public Flowable ownershipTransferredEventFlowable( + DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(OWNERSHIPTRANSFERRED_EVENT)); + return ownershipTransferredEventFlowable(filter); + } + + public static List getReverseClaimedEvents( + TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(REVERSECLAIMED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (Contract.EventValuesWithLog eventValues : valueList) { + ReverseClaimedEventResponse typedResponse = new ReverseClaimedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.addr = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.node = (byte[]) eventValues.getIndexedValues().get(1).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static ReverseClaimedEventResponse getReverseClaimedEventFromLog(Log log) { + Contract.EventValuesWithLog eventValues = staticExtractEventParametersWithLog(REVERSECLAIMED_EVENT, log); + ReverseClaimedEventResponse typedResponse = new ReverseClaimedEventResponse(); + typedResponse.log = log; + typedResponse.addr = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.node = (byte[]) eventValues.getIndexedValues().get(1).getValue(); + return typedResponse; + } + + public Flowable reverseClaimedEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getReverseClaimedEventFromLog(log)); + } + + public Flowable reverseClaimedEventFlowable( + DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(REVERSECLAIMED_EVENT)); + return reverseClaimedEventFlowable(filter); + } + + public RemoteFunctionCall claim(String owner) { + final Function function = new Function( + FUNC_CLAIM, + Arrays.asList(new org.web3j.abi.datatypes.Address(160, owner)), + Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall claimForAddr(String addr, String owner, + String resolver) { + final Function function = new Function( + FUNC_CLAIMFORADDR, + Arrays.asList(new org.web3j.abi.datatypes.Address(160, addr), + new org.web3j.abi.datatypes.Address(160, owner), + new org.web3j.abi.datatypes.Address(160, resolver)), + Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall claimWithResolver(String owner, String resolver) { + final Function function = new Function( + FUNC_CLAIMWITHRESOLVER, + Arrays.asList(new org.web3j.abi.datatypes.Address(160, owner), + new org.web3j.abi.datatypes.Address(160, resolver)), + Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall controllers(String param0) { + final Function function = new Function(FUNC_CONTROLLERS, + Arrays.asList(new org.web3j.abi.datatypes.Address(160, param0)), + Arrays.>asList(new TypeReference() {})); + return executeRemoteCallSingleValueReturn(function, Boolean.class); + } + + public RemoteFunctionCall defaultResolver() { + final Function function = new Function(FUNC_DEFAULTRESOLVER, + Arrays.asList(), + Arrays.>asList(new TypeReference
() {})); + return executeRemoteCallSingleValueReturn(function, String.class); + } + + public RemoteFunctionCall ens() { + final Function function = new Function(FUNC_ENS, + Arrays.asList(), + Arrays.>asList(new TypeReference
() {})); + return executeRemoteCallSingleValueReturn(function, String.class); + } + + public RemoteFunctionCall node(String addr) { + final Function function = new Function(FUNC_NODE, + Arrays.asList(new org.web3j.abi.datatypes.Address(160, addr)), + Arrays.>asList(new TypeReference() {})); + return executeRemoteCallSingleValueReturn(function, byte[].class); + } + + public RemoteFunctionCall owner() { + final Function function = new Function(FUNC_OWNER, + Arrays.asList(), + Arrays.>asList(new TypeReference
() {})); + return executeRemoteCallSingleValueReturn(function, String.class); + } + + public RemoteFunctionCall renounceOwnership() { + final Function function = new Function( + FUNC_RENOUNCEOWNERSHIP, + Arrays.asList(), + Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall setController(String controller, + Boolean enabled) { + final Function function = new Function( + FUNC_SETCONTROLLER, + Arrays.asList(new org.web3j.abi.datatypes.Address(160, controller), + new org.web3j.abi.datatypes.Bool(enabled)), + Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall setDefaultResolver(String resolver) { + final Function function = new Function( + FUNC_SETDEFAULTRESOLVER, + Arrays.asList(new org.web3j.abi.datatypes.Address(160, resolver)), + Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall setName(String name) { + final Function function = new Function( + FUNC_SETNAME, + Arrays.asList(new org.web3j.abi.datatypes.Utf8String(name)), + Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall setNameForAddr(String addr, String owner, + String resolver, String name) { + final Function function = new Function( + FUNC_SETNAMEFORADDR, + Arrays.asList(new org.web3j.abi.datatypes.Address(160, addr), + new org.web3j.abi.datatypes.Address(160, owner), + new org.web3j.abi.datatypes.Address(160, resolver), + new org.web3j.abi.datatypes.Utf8String(name)), + Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall transferOwnership(String newOwner) { + final Function function = new Function( + FUNC_TRANSFEROWNERSHIP, + Arrays.asList(new org.web3j.abi.datatypes.Address(160, newOwner)), + Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + @Deprecated + public static ReverseRegistrar load(String contractAddress, Web3j web3j, + Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { + return new ReverseRegistrar(contractAddress, web3j, credentials, gasPrice, gasLimit); + } + + @Deprecated + public static ReverseRegistrar load(String contractAddress, Web3j web3j, + TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { + return new ReverseRegistrar(contractAddress, web3j, transactionManager, gasPrice, gasLimit); + } + + public static ReverseRegistrar load(String contractAddress, Web3j web3j, + Credentials credentials, ContractGasProvider contractGasProvider) { + return new ReverseRegistrar(contractAddress, web3j, credentials, contractGasProvider); + } + + public static ReverseRegistrar load(String contractAddress, Web3j web3j, + TransactionManager transactionManager, ContractGasProvider contractGasProvider) { + return new ReverseRegistrar(contractAddress, web3j, transactionManager, contractGasProvider); + } + + public static RemoteCall deploy(Web3j web3j, Credentials credentials, + ContractGasProvider contractGasProvider, String ensAddr) { + String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.asList(new org.web3j.abi.datatypes.Address(160, ensAddr))); + return deployRemoteCall(ReverseRegistrar.class, web3j, credentials, contractGasProvider, getDeploymentBinary(), encodedConstructor); + } + + public static RemoteCall deploy(Web3j web3j, + TransactionManager transactionManager, ContractGasProvider contractGasProvider, + String ensAddr) { + String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.asList(new org.web3j.abi.datatypes.Address(160, ensAddr))); + return deployRemoteCall(ReverseRegistrar.class, web3j, transactionManager, contractGasProvider, getDeploymentBinary(), encodedConstructor); + } + + @Deprecated + public static RemoteCall deploy(Web3j web3j, Credentials credentials, + BigInteger gasPrice, BigInteger gasLimit, String ensAddr) { + String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.asList(new org.web3j.abi.datatypes.Address(160, ensAddr))); + return deployRemoteCall(ReverseRegistrar.class, web3j, credentials, gasPrice, gasLimit, getDeploymentBinary(), encodedConstructor); + } + + @Deprecated + public static RemoteCall deploy(Web3j web3j, + TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit, + String ensAddr) { + String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.asList(new org.web3j.abi.datatypes.Address(160, ensAddr))); + return deployRemoteCall(ReverseRegistrar.class, web3j, transactionManager, gasPrice, gasLimit, getDeploymentBinary(), encodedConstructor); + } + + public static void linkLibraries(List references) { + librariesLinkedBinary = linkBinaryWithReferences(BINARY, references); + } + + private static String getDeploymentBinary() { + if (librariesLinkedBinary != null) { + return librariesLinkedBinary; + } else { + return BINARY; + } + } + + public static class ControllerChangedEventResponse extends BaseEventResponse { + public String controller; + + public Boolean enabled; + } + + public static class DefaultResolverChangedEventResponse extends BaseEventResponse { + public String resolver; + } + + public static class OwnershipTransferredEventResponse extends BaseEventResponse { + public String previousOwner; + + public String newOwner; + } + + public static class ReverseClaimedEventResponse extends BaseEventResponse { + public String addr; + + public byte[] node; + } +} diff --git a/core/src/main/resources/solidity/ens/abi/reverseRegistrar/ReverseRegistrar.abi b/core/src/main/resources/solidity/ens/abi/reverseRegistrar/ReverseRegistrar.abi new file mode 100644 index 0000000000..1e0c33c3f1 --- /dev/null +++ b/core/src/main/resources/solidity/ens/abi/reverseRegistrar/ReverseRegistrar.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"contract ENS","name":"ensAddr","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"controller","type":"address"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"ControllerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract NameResolver","name":"resolver","type":"address"}],"name":"DefaultResolverChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"addr","type":"address"},{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"ReverseClaimed","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"claim","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"resolver","type":"address"}],"name":"claimForAddr","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"resolver","type":"address"}],"name":"claimWithResolver","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"controllers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultResolver","outputs":[{"internalType":"contract NameResolver","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ens","outputs":[{"internalType":"contract ENS","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"node","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"controller","type":"address"},{"internalType":"bool","name":"enabled","type":"bool"}],"name":"setController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"resolver","type":"address"}],"name":"setDefaultResolver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"setName","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"resolver","type":"address"},{"internalType":"string","name":"name","type":"string"}],"name":"setNameForAddr","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/core/src/test/java/org/web3j/ens/ContractsTest.java b/core/src/test/java/org/web3j/ens/ContractsTest.java index ef1c40cae6..265ab8c66e 100644 --- a/core/src/test/java/org/web3j/ens/ContractsTest.java +++ b/core/src/test/java/org/web3j/ens/ContractsTest.java @@ -26,6 +26,8 @@ import static org.web3j.ens.Contracts.ROPSTEN; import static org.web3j.ens.Contracts.SEPOLIA; import static org.web3j.ens.Contracts.resolveRegistryContract; +import static org.web3j.ens.NameWrapperUrl.getEnsMetadataApi; +import static org.web3j.ens.ReverseRegistrarContracts.resolveReverseRegistrarContract; @SuppressWarnings("deprecation") class ContractsTest { @@ -41,9 +43,42 @@ void testResolveRegistryContract() { assertEquals(resolveRegistryContract(ChainIdLong.LINEA_SEPOLIA + ""), (LINEA_SEPOLIA)); } + @Test + void testReverseRegistrarContract() { + assertEquals( + resolveReverseRegistrarContract(ChainIdLong.MAINNET + ""), + (ReverseRegistrarContracts.MAINNET)); + assertEquals( + resolveReverseRegistrarContract(ChainIdLong.SEPOLIA + ""), + (ReverseRegistrarContracts.SEPOLIA)); + assertEquals( + resolveReverseRegistrarContract(ChainIdLong.HOLESKY + ""), + (ReverseRegistrarContracts.HOLESKY)); + assertEquals( + resolveReverseRegistrarContract(ChainIdLong.LINEA + ""), + (ReverseRegistrarContracts.LINEA)); + assertEquals( + resolveReverseRegistrarContract(ChainIdLong.LINEA_SEPOLIA + ""), + (ReverseRegistrarContracts.LINEA_SEPOLIA)); + } + + @Test + void testNameWrapperApiLinks() { + assertEquals(getEnsMetadataApi(ChainIdLong.MAINNET + ""), (NameWrapperUrl.MAINNET_URL)); + assertEquals(getEnsMetadataApi(ChainIdLong.SEPOLIA + ""), (NameWrapperUrl.SEPOLIA_URL)); + assertEquals(getEnsMetadataApi(ChainIdLong.HOLESKY + ""), (NameWrapperUrl.HOLESKY_URL)); + } + @Test void testResolveRegistryContractInvalid() { assertThrows( EnsResolutionException.class, () -> resolveRegistryContract(ChainIdLong.NONE + "")); } + + @Test + void testReverseRegistrarContractInvalid() { + assertThrows( + EnsResolutionException.class, + () -> resolveReverseRegistrarContract(ChainIdLong.NONE + "")); + } } diff --git a/core/src/test/java/org/web3j/ens/EnsResolverTest.java b/core/src/test/java/org/web3j/ens/EnsResolverTest.java index 8fbe4aed13..92a5214351 100644 --- a/core/src/test/java/org/web3j/ens/EnsResolverTest.java +++ b/core/src/test/java/org/web3j/ens/EnsResolverTest.java @@ -20,8 +20,10 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import okhttp3.Call; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Protocol; +import okhttp3.Response; import okhttp3.ResponseBody; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -29,8 +31,10 @@ import org.web3j.abi.TypeEncoder; import org.web3j.abi.datatypes.Utf8String; +import org.web3j.crypto.Credentials; import org.web3j.dto.EnsGatewayResponseDTO; import org.web3j.ens.contracts.generated.OffchainResolverContract; +import org.web3j.ens.contracts.generated.ReverseRegistrar; import org.web3j.protocol.ObjectMapperFactory; import org.web3j.protocol.Web3j; import org.web3j.protocol.Web3jService; @@ -40,6 +44,7 @@ import org.web3j.protocol.core.methods.response.EthCall; import org.web3j.protocol.core.methods.response.EthSyncing; import org.web3j.protocol.core.methods.response.NetVersion; +import org.web3j.protocol.core.methods.response.TransactionReceipt; import org.web3j.tx.ChainIdLong; import org.web3j.utils.EnsUtils; import org.web3j.utils.Numeric; @@ -456,6 +461,8 @@ void resolveOffchainWhenLookUpCallsOutOfLimit() throws Exception { class EnsResolverForTest extends EnsResolver { private OffchainResolverContract resolverMock; + private ReverseRegistrar reverseRegistrarMock; + public EnsResolverForTest(Web3j web3j) { super(web3j); } @@ -465,6 +472,17 @@ protected OffchainResolverContract obtainOffchainResolver(String ensName) { return resolverMock; } + @Override + protected OffchainResolverContract obtainOffchainResolver( + String ensName, Credentials credentials) { + return resolverMock; + } + + @Override + protected ReverseRegistrar getReverseRegistrarContract(Credentials credentials) { + return reverseRegistrarMock; + } + public OffchainResolverContract getResolverMock() { return resolverMock; } @@ -472,6 +490,10 @@ public OffchainResolverContract getResolverMock() { public void setResolverMock(OffchainResolverContract resolverMock) { this.resolverMock = resolverMock; } + + public void setReverseRegistrarMock(ReverseRegistrar reverseRegistrarMock) { + this.reverseRegistrarMock = reverseRegistrarMock; + } } @Test @@ -525,6 +547,134 @@ public void testResolveWildCardWhenResolvedAddressNotValid() throws Exception { () -> ensResolverForTest.resolve("1.offchainexample.eth")); } + @Test + void testGetEnsTextSuccess() throws Exception { + String expectedText = "value"; + String name = "example.eth"; + String key = "key"; + + OffchainResolverContract resolverMock = mock(OffchainResolverContract.class); + + RemoteFunctionCall remoteFunctionCallMock = mock(RemoteFunctionCall.class); + when(resolverMock.text(any(), eq(key))).thenReturn(remoteFunctionCallMock); + when(remoteFunctionCallMock.send()).thenReturn(expectedText); + + EnsResolverForTest ensResolverForTest = new EnsResolverForTest(web3j); + ensResolverForTest.setResolverMock(resolverMock); + + String result = ensResolverForTest.getEnsText(name, key); + + assertNotNull(result); + assertEquals(expectedText, result); + } + + @Test + void testSetEnsTextSuccess() throws Exception { + String name = "example.eth"; + String key = "key"; + String value = "value"; + + TransactionReceipt receipt = new TransactionReceipt(); + receipt.setTransactionHash("0x123"); + + Credentials credentials = mock(Credentials.class); + + OffchainResolverContract resolverMock = mock(OffchainResolverContract.class); + RemoteFunctionCall remoteFunctionCallMock = + mock(RemoteFunctionCall.class); + + when(resolverMock.setText(any(), eq(key), eq(value))).thenReturn(remoteFunctionCallMock); + when(remoteFunctionCallMock.send()).thenReturn(receipt); + + EnsResolverForTest ensResolverForTest = new EnsResolverForTest(web3j); + ensResolverForTest.setResolverMock(resolverMock); + + TransactionReceipt receiptResult = + ensResolverForTest.setEnsText(name, key, value, credentials); + + assertNotNull(receiptResult); + assertEquals(receipt.getTransactionHash(), receiptResult.getTransactionHash()); + } + + @Test + void testSetReverseName() throws Exception { + String name = "example.eth"; + Credentials credentials = mock(Credentials.class); + + TransactionReceipt receipt = new TransactionReceipt(); + receipt.setTransactionHash("0x123"); + ReverseRegistrar reverseRegistrarMock = mock(ReverseRegistrar.class); + RemoteFunctionCall remoteFunctionCallMock = + mock(RemoteFunctionCall.class); + + when(remoteFunctionCallMock.send()).thenReturn(receipt); + when(reverseRegistrarMock.setName(name)).thenReturn(remoteFunctionCallMock); + + EnsResolverForTest resolver = new EnsResolverForTest(web3j); + resolver.setReverseRegistrarMock(reverseRegistrarMock); + + TransactionReceipt receiptResult = resolver.setReverseName(name, credentials); + + assertNotNull(receiptResult); + assertEquals(receipt.getTransactionHash(), receiptResult.getTransactionHash()); + } + + @Test + void testSetNameForAddr() throws Exception { + String addr = "0x41563129cdbbd0c5d3e1c86cf9563926b243834d"; + String owner = "0x226159d592E2b063810a10Ebf6dcbADA94Ed68b8"; + String resolver = "0x226159d592E2b063810a10Ebf6dcbADA94Ed68b8"; + String name = "example.eth"; + Credentials credentials = mock(Credentials.class); + + TransactionReceipt receipt = new TransactionReceipt(); + receipt.setTransactionHash("0xabc123"); + + ReverseRegistrar reverseRegistrarMock = mock(ReverseRegistrar.class); + RemoteFunctionCall remoteFunctionCallMock = + mock(RemoteFunctionCall.class); + + when(remoteFunctionCallMock.send()).thenReturn(receipt); + when(reverseRegistrarMock.setNameForAddr(addr, owner, resolver, name)) + .thenReturn(remoteFunctionCallMock); + + EnsResolverForTest resolverObj = new EnsResolverForTest(web3j); + resolverObj.setReverseRegistrarMock(reverseRegistrarMock); + + TransactionReceipt receiptResult = + resolverObj.setReverseName(addr, owner, resolver, name, credentials); + + assertNotNull(receiptResult); + assertEquals(receipt.getTransactionHash(), receiptResult.getTransactionHash()); + } + + @Test + void testGetEnsMetadataSuccess() throws Exception { + OkHttpClient httpClientMock = mock(OkHttpClient.class); + String name = "example.eth"; + String apiUrl = "https://ens-api.example.com/"; + + NetVersion netVersion = new NetVersion(); + netVersion.setResult(Long.toString(ChainIdLong.MAINNET)); + when(web3jService.send(any(Request.class), eq(NetVersion.class))).thenReturn(netVersion); + + EnsMetadataResponse expectedResponse = new EnsMetadataResponse(); + expectedResponse.setName("example.eth"); + expectedResponse.setDescription("example.eth, an ENS name."); + String jsonResponse = new ObjectMapper().writeValueAsString(expectedResponse); + + okhttp3.Response mockResponse = buildResponse(200, apiUrl, jsonResponse); + Call mockCall = mock(Call.class); + when(mockCall.execute()).thenReturn(mockResponse); + when(httpClientMock.newCall(any())).thenReturn(mockCall); + + EnsMetadataResponse actualResponse = ensResolver.getEnsMetadata(name); + + assertNotNull(actualResponse); + assertEquals(expectedResponse.getName(), actualResponse.getName()); + assertEquals(expectedResponse.getDescription(), actualResponse.getDescription()); + } + private okhttp3.Response buildResponse(int code, String url, String sender, String data) throws JsonProcessingException { EnsGatewayResponseDTO responseDTO = new EnsGatewayResponseDTO(data); @@ -537,4 +687,16 @@ private okhttp3.Response buildResponse(int code, String url, String sender, Stri .message("Some error message Code: " + code) .build(); } + + private okhttp3.Response buildResponse(int code, String url, String jsonBody) { + okhttp3.Request request = new okhttp3.Request.Builder().url(url).build(); + + return new Response.Builder() + .request(request) + .protocol(Protocol.HTTP_2) + .code(code) + .message("Response Message") + .body(ResponseBody.create(jsonBody, MediaType.parse("application/json"))) + .build(); + } }