Skip to content

Commit

Permalink
Merge pull request #429 from ahsan-javaiid/add-rootstock-name-service
Browse files Browse the repository at this point in the history
Add `RNS` Rootstock name service
  • Loading branch information
serg-plusplus authored Jul 3, 2024
2 parents 5bd9acd + 815cb18 commit f2bf935
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 5 deletions.
6 changes: 4 additions & 2 deletions src/app/components/elements/ContactAutocomplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
LOAD_MORE_ON_CONTACTS_DROPDOWN_FROM_END,
} from "app/defaults";
import { useContacts } from "app/hooks/contacts";
import { useAccounts, useEns } from "app/hooks";
import { useAccounts, useEns, useRns } from "app/hooks";
import ScrollAreaContainer from "./ScrollAreaContainer";
import AddressField, { AddressFieldProps } from "./AddressField";
import WalletName from "./WalletName";
Expand Down Expand Up @@ -160,10 +160,12 @@ const ContactAutocomplete = forwardRef<
const { paste } = usePasteFromClipboard(setValue);

const { getAddressByEns, watchEns } = useEns();
const { getAddressByRns, watchRns } = useRns();

useEffect(() => {
watchEns(value, setValue);
}, [value, setValue, getAddressByEns, watchEns]);
watchRns(value, setValue);
}, [value, setValue, getAddressByEns, watchEns, getAddressByRns, watchRns]);

const pasteButton = useMemo(() => {
return (
Expand Down
9 changes: 6 additions & 3 deletions src/app/components/elements/WalletName.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import classNames from "clsx";
import { TReplace } from "lib/ext/i18n/react";

import { Account, AccountSource } from "core/types";
import { useEns } from "app/hooks";
import { useEns, useRns } from "app/hooks";

import { ReactComponent as GoogleIcon } from "app/icons/google.svg";
import { ReactComponent as FacebookIcon } from "app/icons/facebook.svg";
Expand All @@ -27,21 +27,24 @@ const WalletName: FC<WalletNameProps> = ({
iconClassName,
}) => {
const { getEnsName } = useEns();
const { getRnsName } = useRns();

const [ensName, setEnsName] = useState<string | null>(null);

useEffect(() => {
const fetchEnsName = async () => {
try {
const name = await getEnsName(wallet.address);
const name =
(await getEnsName(wallet.address)) ||
(await getRnsName(wallet.address));
setEnsName(name);
} catch (error) {
console.error(error);
}
};

fetchEnsName();
}, [getEnsName, wallet.address]);
}, [getEnsName, getRnsName, wallet.address]);

return (
<span
Expand Down
1 change: 1 addition & 0 deletions src/app/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export * from "./tokens";
export * from "./nftMetadata";
export * from "./ramp";
export * from "./ens";
export * from "./rns";
export * from "./hideToken";
141 changes: 141 additions & 0 deletions src/app/hooks/rns.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { useMemo, useCallback } from "react";
import { JsonRpcProvider } from "@ethersproject/providers";
import { Contract } from "@ethersproject/contracts";
import { AddressZero } from "@ethersproject/constants";
import { namehash } from "@ethersproject/hash";

const ONE_DAY = 24 * 60 * 60 * 1000;

const ROOTSTOCK_RPC_NODE = "https://public-node.rsk.co";

// REF: https://developers.rsk.co/rif/rns/architecture/registry/
const RNS_REGISTRY_ADDRESS = "0xcb868aeabd31e2b66f74e9a55cf064abb31a4ad5";

const stripHexPrefix = (hex: string): string => hex.slice(2);

const RNS_REGISTRY_ABI = [
"function resolver(bytes32 node) public view returns (address)",
];

const RNS_ADDR_RESOLVER_ABI = [
"function addr(bytes32 node) public view returns (address)",
];

const RNS_NAME_RESOLVER_ABI = [
"function name(bytes32 node) external view returns (string)",
];

const RNSProvider = new JsonRpcProvider(ROOTSTOCK_RPC_NODE);
const rnsRegistryContract = new Contract(
RNS_REGISTRY_ADDRESS,
RNS_REGISTRY_ABI,
RNSProvider,
);

const resolveRnsName = async (name: string): Promise<string | null> => {
const nameHash = namehash(name);
const resolverAddress = await rnsRegistryContract.resolver(nameHash);

if (resolverAddress === AddressZero) {
return null;
}

const addrResolverContract = new Contract(
resolverAddress,
RNS_ADDR_RESOLVER_ABI,
RNSProvider,
);

const address = await addrResolverContract.addr(nameHash);

if (address === undefined || address === null) {
return null;
}

return address.toLowerCase();
};

const lookupAddress = async (address: string): Promise<string | null> => {
const reverseRecordHash = namehash(`${stripHexPrefix(address)}.addr.reverse`);

const resolverAddress = await rnsRegistryContract.resolver(reverseRecordHash);

if (resolverAddress === AddressZero) {
return null;
}

const nameResolverContract = new Contract(
resolverAddress,
RNS_NAME_RESOLVER_ABI,
RNSProvider,
);

const name = await nameResolverContract.name(reverseRecordHash);

if (name === undefined) {
return null;
}

return name;
};

const useRns = () => {
const getRnsName = useCallback(async (address: string) => {
const rnsNameLS = localStorage.getItem(`RNS_${address}`);
const parsedData = rnsNameLS ? JSON.parse(rnsNameLS) : null;
if (parsedData && parsedData.expirationTimestamp > Date.now()) {
return parsedData.ensName;
} else {
const rnsName = await lookupAddress(address);

if (rnsName) {
const data = {
rnsName,
expirationTimestamp: Date.now() + ONE_DAY,
};
localStorage.setItem(`RNS_${address}`, JSON.stringify(data));
return rnsName;
} else {
return null;
}
}
}, []);

const getAddressByRns = useCallback(async (rnsName: string) => {
const address = await resolveRnsName(rnsName);

if (address) {
return address;
} else {
return null;
}
}, []);

const watchRns = useCallback(
async (value: any, cb: (address: string) => void) => {
const ethereumAddressOrRNSRegex =
/^(0x[a-fA-F0-9]{40})|([a-zA-Z0-9-]+\.rsk)$/;
if (value && typeof value == "string") {
const isValid = ethereumAddressOrRNSRegex.test(value);
if (isValid && value.includes(".rsk")) {
const response = await getAddressByRns(value);
if (response) {
cb(response);
}
}
}
},
[getAddressByRns],
);

return useMemo(
() => ({
getRnsName,
getAddressByRns,
watchRns,
}),
[getRnsName, getAddressByRns, watchRns],
);
};

export { useRns };

0 comments on commit f2bf935

Please sign in to comment.