XNHNS is an easy way for any Handshake TLD owner to use their domain trustlessly on other blockchains. XNHNS works on any EVM compatible blockchain.
We utilize HIP5 namespace delegation records to point to your blockchain of choice and TXT record with xnhns={your address}
to claim your NFTLD to an address on that network. The ChainLink decentralized oracle network securely verifies your DNS records on the Handshake chain to verify your TLD is valid on that chain (by checking NS record) and tells the XNHNS registry who to give ownership of NFTLD to.
Built With:
- Handshake
- Solidity smart contracts
- Ethereum Name Service
- ChainLink oracles + custom external adapters
- The Graph
Adapters Adapter contracts are only used for token controller logic, accounting, etc. They do not directly hold deposited assets, those are held in the respective registrar that registered the TLD. This allows us to use assets in other adapters (e.g DeFi integrations) said once deposited by DepositAdapter.
Migrating
Once you've verified you TLD on a chain, it does not need to be verified again, you can register to any XNHNS registrar on that chain to gain it's benefits like timelocks or DeFi integrations to yield farm deposits.
All you need to do to migrate registrars is call unregister()
on your current registrar and then register()
on your new registrar. Since each registrar has different requirements for TLD deposits this isn't an automated process yet.
Refferals Referrer is set in register() instead of verify() because any person or register can call verify(). Only the owner of NFTLD can call register() meaning that they have given consent to referrer by signing tx with their address.
Clone the repo and install dependencies:
git clone https://github.com/hnsfund/xnhns.git
cd xnhns
yarn install
Start the frontend dev server with:
yarn start
In a second terminal window, start a local hardhat network (this will deploy all contracts on a fresh chain):
cd packages/hardhat
yarn chain --network hardhat
In a third terminal window, run this to compile and publish to frontend (required after every contract modification):
cd packages/hardhat
yarn deploy:test
dapp hot reloads as you build your smart contracts and frontend together
- Go to Migrate page /migrate
- Type in whatever TLD you want to migrate
- Click "Migrate" button
- Then Click "Confirm TLD" (this stubs call the oracle call to confirm you own TLD)
- Go over to Manage page /manage (working on modal to help this UX)
- Your TLD should show up there and have a "Mint NFTLD" button next to it. Click it
- If that tx goes through, congrats you have an NFT on Ethereum representing ownership of you HNS TLD
Lets say you are migrating the TLD hnsregistry/
- You submit your HIP5 NS records and TXT record with your address on the chain you are migrating hnsregistry/ to
- Wait for those transactions are mined on Handshake
- Submit a transaction on your host chain to
HNSRegistrar.verify(hnsregistry)
with a value of 0.1 ETH for your deposit (read more about deposits) - ChainLink oracles will verify you have appropriate records. They will update
XNHNSOracle
contract with owner of TLD to the address you set in your TXT record - You can now call
HNSRegistrar.register( namehash(hnsregistry) )
to mint your NFTLD and list your TLD on theENSRegistry
- Go wild, you are free. You have the power of NFTs and ENS at your disposal. (TODO write article of cool shit to do)
- When you are done using your NFTLD on this chain, call
HNSRegistrar.unregister( namehash(hnsregistry) )
to receive your deposit back.
Deposits are simple on XNHNS. When you want to migrate a domain to another chain, you provide a deposit to "anchor" your TLD to that chain. The deposit amount is entirely up to you, although each type of registrar has a different minimum deposit. When you are done using your TLD on that chain you simply call HNSRegistrar.unregister(hnsregistry)
and you will get your full deposit back.
Anchoring your TLD with a deposit increases the utility of your domain on it's host chain. This shows you have skin in the game and are committing to using your TLD on that chain. This can be used by DeFi apps before you take a loan against your NFTLD to make sure you aren't borrowing more than is deposited in your TLD to prevent fraud and liquidate your NFTLD if you fail to pay your loan back on time. Identity systems can increase your reputation points based on your deposit value because you are committed to participating in that chains community.
Your deposit also prevents double spending your TLD across two different chains at once. This happens when you change your NS record to point at a chain other than the one with your deposit. When this happens, anyone can call HNSRegistrar.snitchOn(hnsregistry)
. This will trigger the same ChainLink oracle job as when you call verify(), if the oracles return a null (address(0) in solidity) then the snitch was successful and can claim half your deposit (the other half gets donated to the HNS Fund)
XNHNS registrars are responsible for issuing NFTLDs to domain owners after verifying the domain is pointing at XNHNS properly for its chain. It's the main contract that XNHNS users interact with to manage their NFTLDs.
Registrars may or may not be run by XNHNS team directly. Once we are out of alpha/beta phase we will be looking to partner with existing registrars.
You can switch registrars at any time by calling unregister() on your current registrar, then inscreaseDeposit() and register() on your new registrar. You do not need to verify on the new registrar since your ownership will still be stored in the oracle (assuming no one snitches on you but there is no reward for snitches once you unregister so you should be fine).
Simple registrar. Deposit 0.1ETH to migrate your domain, get 0.1ETH back when you unregister your TLD on that chain.
(WIP) For owners of premium domains that want to maximize NFTLD utilization on your host chain. Minimum deposit of 20ETH to migrate your domain. Deposit is immediately invested in DeFi protocols (TBD) so you earn yield and increase capital efficiency of your deposit. When you unregister you get back more ETH than you deposited.
TODO. A registrar that timelocks a TLD owners deposit for set amount of time. The TLD owner will not be able to call unregister() until timelock has passed at which point they can renew the lock
Always open to developing new cool ideas. Open an issue if you have an idea for a different type of TLD registrar.
We have built external adapters so any ChainLink oracle can join the XNHNS oracle network and help verify DNS records. ChainLink is inherently crosschain protocol just like XNHNS so it is a perfect fit since we can utilize the same smart contracts and service providers on every chain we use.
Any smart contract can read TLD owners registered by the XNHNSOracle
contract. There is a whitelist of external contracts that are allowed to initiate requests to verify domains to prevent spam (deposit on verify() reduces spam). This allows multiple registrars to operate at once, and reducing .
NOTICE: We are currently using a non-Chainlink, trusted oracle while we are in alpha to improve product iterations.
Use the graph for data querying on the frontend. Pulled from ENS subgraph with some minor additions for XNHNS requirements like oracle events. Subgraphs names follow the format xnhns-{networkName}
where networkName is the short code used in the HIP5 NS record e.g. 'eth' for Ethereum mainnet.
The frontend has three different providers that provide different levels of access to different chains:
mainnetProvider
: (read only) Infura connection to main Ethereum network (and contracts already deployed like DAI or Uniswap).
localProvider
: local HardHat accounts, used to read from your contracts (.env
file points you at testnet or mainnet)
injectedProvider
: your personal MetaMask, WalletConnect via Argent, or other injected wallet (generates burner-provider on page load)
π Ant.design is the UI library with components like the grids, menus, dates, times, buttons, etc.
Transactor
: The transactor returns a tx()
function to make running and tracking transactions as simple and standardized as possible. We will bring in BlockNative's Notify library to track our testnet and mainnet transactions.
const tx = Transactor(props.injectedProvider, props.gasPrice)
Then you can use the tx()
function to send funds and write to your smart contracts:
tx({
to: readContracts[contractName].address,
value: parseEther('0.001'),
})
tx(writeContracts['SmartContractWallet'].updateOwner(newOwner))
β’οΈ Warning: You will need to update the configuration for
react-app/src/helpers/Transactor.js
to use your BlockNative dappId
Commonly used Ethereum hooks located in packages/react-app/src/
:
usePoller(fn, delay)
: runs a function on app load and then on a custom interval
usePoller(() => {
//do something cool at start and then every three seconds
}, 3000)
useBalance(address, provider, [pollTime])
: poll for the balance of an address from a provider
const localBalance = useBalance(address, localProvider)
useBlockNumber(provider,[pollTime])
: get current block number from a provider
const blockNumber = useBlockNumber(props.provider)
useGasPrice([speed])
: gets current "fast" price from ethgasstation
const gasPrice = useGasPrice()
useExchangePrice(mainnetProvider, [pollTime])
: gets current price of Ethereum on the Uniswap exchange
const price = useExchangePrice(mainnetProvider)
useContractLoader(provider)
: loads your smart contract interface
const readContracts = useContractLoader(localProvider)
const writeContracts = useContractLoader(injectedProvider)
useContractReader(contracts, contractName, variableName, [pollTime])
: reads a variable from your contract and keeps it in the state
const title = useContractReader(props.readContracts, contractName, 'title')
const owner = useContractReader(props.readContracts, contractName, 'owner')
useEventListener(contracts, contractName, eventName, [provider], [startBlock])
: listens for events from a smart contract and keeps them in the state
const ownerUpdates = useEventListener(
readContracts,
contractName,
'UpdateOwner',
props.localProvider,
1
)
Your commonly used React Ethereum components located in packages/react-app/src/
:
π¬ <Address />
: A simple display for an Ethereum address that uses a Blockie, lets you copy, and links to Etherescan.
<Address value={address} />
<Address value={address} size="short" />
<Address value={address} size="long" blockexplorer="https://blockscout.com/poa/xdai/address/"/>
<Address value={address} ensProvider={mainnetProvider}/>
π <AddressInput />
: An input box you control with useState for an Ethereum address that uses a Blockie and ENS lookup/display.
const [ address, setAddress ] = useState("")
<AddressInput
value={address}
ensProvider={props.ensProvider}
onChange={(address)=>{
setAddress(address)
}}
/>
TODO GIF
π΅ <Balance />
: Displays the balance of an address in either dollars or decimal.
<Balance
address={address}
provider={injectedProvider}
dollarMultiplier={price}
/>
π€ <Account />
: Allows your users to start with an Ethereum address on page load but upgrade to a more secure, injected provider, using Web3Modal. It will track your address
and localProvider
in your app's state:
const [address, setAddress] = useState()
const [injectedProvider, setInjectedProvider] = useState()
const price = useExchangePrice(mainnetProvider)
<Account
address={address}
setAddress={setAddress}
localProvider={localProvider}
injectedProvider={injectedProvider}
setInjectedProvider={setInjectedProvider}
dollarMultiplier={price}
/>
π‘ Notice: the
<Account />
component will callsetAddress
andsetInjectedProvider
for you.
β’οΈ Warning: You will need to update the configuration for
Web3Modal
to use your Infura Id
π‘ <Provider />
: You can choose to display the provider connection status to your users with:
<Provider name={"mainnet"} provider={mainnetProvider} />
<Provider name={"local"} provider={localProvider} />
<Provider name={"injected"} provider={injectedProvider} />
π‘ Notice: you will need to check the network id of your
injectedProvider
compared to yourlocalProvider
ormainnetProvider
and alert your users if they are on the wrong network!
You can deploy your static site and your dapp can go live:
yarn run build
# ship it!
yarn run surge
OR
yarn run s3
OR
yarn run ipfs