Removes multiple domain functionaility from project (#20)
* refactor ENSSubdomainRegistry * Rename contract * remove obsolete file * typo * Add legacy contract upgradability adaptor * refactor Dapp to use UsernameRegistrar contract * use reservedNames.js in usernameregistrar.spec.js * change array of merkle roots to single merkle root * load reserved words to deploy contract * reorganized contract methods * add withdraw methods for controller move excess funds & wrong ens nodes * add deployed MerkleProof addresses * include contract terms in the @notice of register * approve and call fix & solidity update * add approveAndCall support * clear subnode owner and resolver
This commit is contained in:
parent
ad432139d0
commit
cbfdd9cc72
|
@ -19,4 +19,4 @@ Usage:
|
||||||
| token/ERC20Token | No | Yes | Yes |
|
| token/ERC20Token | No | Yes | Yes |
|
||||||
| ens/ENSRegistry | Yes | Yes | No |
|
| ens/ENSRegistry | Yes | Yes | No |
|
||||||
| ens/PublicRegistry | Yes | Yes | No |
|
| ens/PublicRegistry | Yes | Yes | No |
|
||||||
| registry/ENSSubdomainRegistry | Yes | Yes | No |
|
| registry/UsernameRegistrar | Yes | Yes | Yes |
|
|
@ -1,5 +1,5 @@
|
||||||
import ERC20Token from 'Embark/contracts/ERC20Token'
|
import ERC20Token from 'Embark/contracts/ERC20Token'
|
||||||
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry'
|
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar'
|
||||||
import TestToken from 'Embark/contracts/TestToken'
|
import TestToken from 'Embark/contracts/TestToken'
|
||||||
|
|
||||||
import { getDefaultAccount } from '../utils/web3Helpers'
|
import { getDefaultAccount } from '../utils/web3Helpers'
|
||||||
|
@ -42,7 +42,7 @@ export const checkAndDispatchStatusContactCode = dispatch => {
|
||||||
export const fetchAndDispatchSNTAllowance = dispatch => {
|
export const fetchAndDispatchSNTAllowance = dispatch => {
|
||||||
const { methods: { allowance } } = TestToken;
|
const { methods: { allowance } } = TestToken;
|
||||||
const { receiveSntAllowance } = accountActions;
|
const { receiveSntAllowance } = accountActions;
|
||||||
const spender = ENSSubdomainRegistry._address;
|
const spender = UsernameRegistrar._address;
|
||||||
allowance(getDefaultAccount(), spender)
|
allowance(getDefaultAccount(), spender)
|
||||||
.call()
|
.call()
|
||||||
.then(allowance => {
|
.then(allowance => {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry';
|
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
|
||||||
import web3 from 'web3';
|
import web3 from 'web3';
|
||||||
import ENSRegistry from 'Embark/contracts/ENSRegistry';
|
import ENSRegistry from 'Embark/contracts/ENSRegistry';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
@ -11,16 +11,16 @@ import { debounce } from 'lodash/fp';
|
||||||
const { methods: { owner } } = ENSRegistry;
|
const { methods: { owner } } = ENSRegistry;
|
||||||
|
|
||||||
const delay = debounce(500);
|
const delay = debounce(500);
|
||||||
const getDomain = (hashedDomain, domains) => domains(hashedDomain).call();
|
const getRegistry = (hashedRegistry, registrys) => registrys(hashedRegistry).call();
|
||||||
const registryIsOwner = address => address == ENSSubdomainRegistry._address;
|
const registryIsOwner = address => address == UsernameRegistrar._address;
|
||||||
const fetchOwner = domainName => owner(hash(domainName)).call();
|
const fetchOwner = registryName => owner(hash(registryName)).call();
|
||||||
const debounceFetchOwner = delay(fetchOwner);
|
const debounceFetchOwner = delay(fetchOwner);
|
||||||
const getAndIsOwner = async domainName => {
|
const getAndIsOwner = async registryName => {
|
||||||
const address = await debounceFetchOwner(domainName);
|
const address = await debounceFetchOwner(registryName);
|
||||||
return registryIsOwner(address);
|
return registryIsOwner(address);
|
||||||
}
|
}
|
||||||
const fetchDomain = delay(getDomain);
|
const fetchRegistry = delay(getRegistry);
|
||||||
const setPrice = (domainFn, hashedDomain, price) => domainFn(hashedDomain, price || 0).send();
|
const setPrice = (registryFn, price) => registryFn(price || 0).send();
|
||||||
|
|
||||||
const InnerForm = ({
|
const InnerForm = ({
|
||||||
values,
|
values,
|
||||||
|
@ -33,53 +33,50 @@ const InnerForm = ({
|
||||||
}) => (
|
}) => (
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<FieldGroup
|
<FieldGroup
|
||||||
id="domainName"
|
id="registryName"
|
||||||
name="domainName"
|
name="registryName"
|
||||||
type="text"
|
type="text"
|
||||||
label="Domain Name"
|
label="Registry Name"
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
value={values.domainName}
|
value={values.registryName}
|
||||||
error={errors.domainName}
|
error={errors.registryName}
|
||||||
/>
|
/>
|
||||||
<FieldGroup
|
<FieldGroup
|
||||||
id="domainPrice"
|
id="registryPrice"
|
||||||
name="domainPrice"
|
name="registryPrice"
|
||||||
type="number"
|
type="number"
|
||||||
label="Domain Price"
|
label="Registry Price"
|
||||||
placeholder="(Optional) Domain will be free if left blank"
|
placeholder="(Optional) Registry will be free if left blank"
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
value={values.domainPrice}
|
value={values.registryPrice}
|
||||||
/>
|
/>
|
||||||
<Button bsStyle="primary" type="submit" disabled={isSubmitting || !!Object.keys(errors).length}>{!isSubmitting ? 'Submit' : 'Submitting to the Blockchain - (this may take awhile)'}</Button>
|
<Button bsStyle="primary" type="submit" disabled={isSubmitting || !!Object.keys(errors).length}>{!isSubmitting ? 'Submit' : 'Submitting to the Blockchain - (this may take awhile)'}</Button>
|
||||||
</form>
|
</form>
|
||||||
)
|
)
|
||||||
|
|
||||||
const AddDomain = withFormik({
|
const AddRegistry = withFormik({
|
||||||
mapPropsToValues: props => ({ domainName: '', domainPrice: '' }),
|
mapPropsToValues: props => ({ registryName: '', registryPrice: '' }),
|
||||||
async validate(values) {
|
async validate(values) {
|
||||||
const { domainName } = values;
|
const { registryName } = values;
|
||||||
const errors = {};
|
const errors = {};
|
||||||
if (!domainName) errors.domainName = 'Required';
|
if (!registryName) errors.registryName = 'Required';
|
||||||
if (domainName && !await getAndIsOwner(domainName)) errors.domainName = 'This domain is not owned by registry';
|
if (registryName && !await getAndIsOwner(registryName)) errors.registryName = 'This registry is not owned by registry';
|
||||||
if (Object.keys(errors).length) throw errors;
|
if (Object.keys(errors).length) throw errors;
|
||||||
},
|
},
|
||||||
async handleSubmit(values, { setSubmitting }) {
|
async handleSubmit(values, { setSubmitting }) {
|
||||||
const { domainName, domainPrice } = values;
|
const { registryName, registryPrice } = values;
|
||||||
const { methods: { domains, setDomainPrice, updateDomainPrice } } = ENSSubdomainRegistry;
|
const { methods: { state, activate, updateRegistryPrice } } = UsernameRegistrar;
|
||||||
const hashedDomain = hash(domainName);
|
const { registryState } = await state();
|
||||||
const { state } = await getDomain(hashedDomain, domains);
|
|
||||||
console.log(
|
console.log(
|
||||||
'Inputs for setPrice',
|
'Inputs for setPrice',
|
||||||
Number(state) ? 'updateDomainPrice' : 'setDomainPrice',
|
Number(registryState) ? 'updateRegistryPrice' : 'activate',
|
||||||
hashedDomain,
|
web3.utils.toWei(registryPrice.toString(), 'ether'),
|
||||||
web3.utils.toWei(domainPrice.toString(), 'ether'),
|
|
||||||
);
|
);
|
||||||
setPrice(
|
setPrice(
|
||||||
Number(state) ? updateDomainPrice : setDomainPrice,
|
Number(registryState) ? updateRegistryPrice : activate,
|
||||||
hashedDomain,
|
web3.utils.toWei(registryPrice.toString(), 'ether'),
|
||||||
web3.utils.toWei(domainPrice.toString(), 'ether'),
|
|
||||||
)
|
)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
@ -92,4 +89,4 @@ const AddDomain = withFormik({
|
||||||
}
|
}
|
||||||
})(InnerForm);
|
})(InnerForm);
|
||||||
|
|
||||||
export default AddDomain;
|
export default AddRegistry;
|
||||||
|
|
|
@ -2,7 +2,7 @@ export default {
|
||||||
release: {
|
release: {
|
||||||
title: {
|
title: {
|
||||||
sub: 'Done!',
|
sub: 'Done!',
|
||||||
body: 'The released domain will be available to other users'
|
body: 'The released username will be available to other users'
|
||||||
},
|
},
|
||||||
subheading: 'Follow the progress in the Transaction History section of your wallet.'
|
subheading: 'Follow the progress in the Transaction History section of your wallet.'
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry';
|
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
|
||||||
import web3 from 'web3';
|
import web3 from 'web3';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { hash } from 'eth-ens-namehash';
|
|
||||||
import { Button } from 'react-bootstrap';
|
import { Button } from 'react-bootstrap';
|
||||||
import FieldGroup from '../standard/FieldGroup';
|
import FieldGroup from '../standard/FieldGroup';
|
||||||
import { withFormik } from 'formik';
|
import { withFormik } from 'formik';
|
||||||
|
@ -25,16 +24,6 @@ const InnerForm = ({
|
||||||
value={values.newAddress}
|
value={values.newAddress}
|
||||||
error={errors.newAddress}
|
error={errors.newAddress}
|
||||||
/>
|
/>
|
||||||
<FieldGroup
|
|
||||||
id="domainName"
|
|
||||||
name="domainName"
|
|
||||||
type="text"
|
|
||||||
label="Domain Name"
|
|
||||||
onChange={handleChange}
|
|
||||||
onBlur={handleBlur}
|
|
||||||
value={values.domainName}
|
|
||||||
error={errors.domainName}
|
|
||||||
/>
|
|
||||||
<Button bsStyle="primary" type="submit" disabled={isSubmitting || !!Object.keys(errors).length}>{!isSubmitting ? 'Submit' : 'Submitting to the Blockchain - (this may take awhile)'}</Button>
|
<Button bsStyle="primary" type="submit" disabled={isSubmitting || !!Object.keys(errors).length}>{!isSubmitting ? 'Submit' : 'Submitting to the Blockchain - (this may take awhile)'}</Button>
|
||||||
</form>
|
</form>
|
||||||
)
|
)
|
||||||
|
@ -49,16 +38,14 @@ const MoveDomain = withFormik({
|
||||||
if (Object.keys(errors).length) throw errors;
|
if (Object.keys(errors).length) throw errors;
|
||||||
},
|
},
|
||||||
async handleSubmit(values, { setSubmitting }) {
|
async handleSubmit(values, { setSubmitting }) {
|
||||||
const { newAddress, domainName } = values;
|
const { newAddress } = values;
|
||||||
const { methods: { moveDomain } } = ENSSubdomainRegistry;
|
const { methods: { moveDomain } } = UsernameRegistrar;
|
||||||
const hashedDomain = hash(domainName);
|
|
||||||
console.log(
|
console.log(
|
||||||
`inputs for moveDomain of domain name: ${domainName}`,
|
`inputs for moveDomain:}`,
|
||||||
newAddress,
|
newAddress
|
||||||
hashedDomain,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
moveDomain(newAddress, hashedDomain)
|
moveDomain(newAddress)
|
||||||
.send()
|
.send()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { hash } from 'eth-ens-namehash';
|
||||||
import { isNil } from 'lodash';
|
import { isNil } from 'lodash';
|
||||||
import Hidden from '@material-ui/core/Hidden';
|
import Hidden from '@material-ui/core/Hidden';
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from '@material-ui/core/Typography';
|
||||||
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry';
|
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
|
||||||
import ENSRegistry from 'Embark/contracts/ENSRegistry';
|
import ENSRegistry from 'Embark/contracts/ENSRegistry';
|
||||||
import { Button, Field, TextInput, MobileSearch, MobileButton, Card, Info, Text } from '../../ui/components'
|
import { Button, Field, TextInput, MobileSearch, MobileButton, Card, Info, Text } from '../../ui/components'
|
||||||
import { IconCheck } from '../../ui/icons'
|
import { IconCheck } from '../../ui/icons'
|
||||||
|
@ -23,7 +23,7 @@ import StatusLogo from '../../ui/icons/components/StatusLogo'
|
||||||
import EnsLogo from '../../ui/icons/logos/ens.png';
|
import EnsLogo from '../../ui/icons/logos/ens.png';
|
||||||
import { formatPrice } from '../ens/utils';
|
import { formatPrice } from '../ens/utils';
|
||||||
import CheckCircle from '../../ui/icons/components/baseline_check_circle_outline.png';
|
import CheckCircle from '../../ui/icons/components/baseline_check_circle_outline.png';
|
||||||
const { getPrice, getExpirationTime, release } = ENSSubdomainRegistry.methods;
|
const { getPrice, getExpirationTime, release } = UsernameRegistrar.methods;
|
||||||
import NotInterested from '@material-ui/icons/NotInterested';
|
import NotInterested from '@material-ui/icons/NotInterested';
|
||||||
import Face from '@material-ui/icons/Face';
|
import Face from '@material-ui/icons/Face';
|
||||||
import Copy from './copy';
|
import Copy from './copy';
|
||||||
|
@ -37,7 +37,7 @@ const validStatusAddress = address => !address.includes(invalidSuffix);
|
||||||
const formatName = domainName => domainName.includes('.') ? normalizer.normalize(domainName) : normalizer.normalize(`${domainName}.stateofus.eth`);
|
const formatName = domainName => domainName.includes('.') ? normalizer.normalize(domainName) : normalizer.normalize(`${domainName}.stateofus.eth`);
|
||||||
const getDomain = fullDomain => formatName(fullDomain).split('.').slice(1).join('.');
|
const getDomain = fullDomain => formatName(fullDomain).split('.').slice(1).join('.');
|
||||||
const hashedDomain = domainName => hash(getDomain(domainName));
|
const hashedDomain = domainName => hash(getDomain(domainName));
|
||||||
const registryIsOwner = address => address == ENSSubdomainRegistry._address;
|
const registryIsOwner = address => address == UsernameRegistrar._address;
|
||||||
const { soliditySha3, fromWei } = web3.utils;
|
const { soliditySha3, fromWei } = web3.utils;
|
||||||
|
|
||||||
|
|
||||||
|
@ -123,8 +123,7 @@ class RenderAddresses extends PureComponent {
|
||||||
if (!isNil(value)) {
|
if (!isNil(value)) {
|
||||||
this.setState({ submitted: true })
|
this.setState({ submitted: true })
|
||||||
release(
|
release(
|
||||||
soliditySha3(domainName),
|
soliditySha3(domainName)
|
||||||
hash('stateofus.eth'),
|
|
||||||
)
|
)
|
||||||
.send()
|
.send()
|
||||||
} else {
|
} else {
|
||||||
|
@ -205,7 +204,7 @@ class Register extends PureComponent {
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { domainName } = this.props;
|
const { domainName } = this.props;
|
||||||
getPrice(hashedDomain(domainName))
|
getPrice()
|
||||||
.call()
|
.call()
|
||||||
.then((res) => { this.setState({ domainPrice: res })});
|
.then((res) => { this.setState({ domainPrice: res })});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import web3 from "Embark/web3"
|
import web3 from "Embark/web3"
|
||||||
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry';
|
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
|
||||||
|
import PublicResolver from 'Embark/contracts/PublicResolver';
|
||||||
import TestToken from 'Embark/contracts/TestToken';
|
import TestToken from 'Embark/contracts/TestToken';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
@ -60,7 +61,7 @@ const InnerForm = ({
|
||||||
mode="strong"
|
mode="strong"
|
||||||
style={{ marginTop: '5px' }}
|
style={{ marginTop: '5px' }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
ENSSubdomainRegistry.methods.getPrice(hash(values.domainName))
|
UsernameRegistrar.methods.getPrice()
|
||||||
.call()
|
.call()
|
||||||
.then((res) => { setFieldValue('price', fromWei(res)); });
|
.then((res) => { setFieldValue('price', fromWei(res)); });
|
||||||
}}
|
}}
|
||||||
|
@ -126,7 +127,7 @@ const InnerForm = ({
|
||||||
<div style={{ position: 'relative', left: 0, right: 0, bottom: 0 }}>
|
<div style={{ position: 'relative', left: 0, right: 0, bottom: 0 }}>
|
||||||
{!Number(SNTAllowance) || (Number(domainPrice) && !Number(SNTBalance)) ? <TokenPermissions
|
{!Number(SNTAllowance) || (Number(domainPrice) && !Number(SNTBalance)) ? <TokenPermissions
|
||||||
symbol="SNT"
|
symbol="SNT"
|
||||||
spender={ENSSubdomainRegistry.address}
|
spender={UsernameRegistrar.address}
|
||||||
methods={TestToken.methods}
|
methods={TestToken.methods}
|
||||||
mobile
|
mobile
|
||||||
/>
|
/>
|
||||||
|
@ -151,7 +152,8 @@ const RegisterSubDomain = withFormik({
|
||||||
const { editAccount, preRegisteredCallback } = props;
|
const { editAccount, preRegisteredCallback } = props;
|
||||||
const { address, statusAddress } = values;
|
const { address, statusAddress } = values;
|
||||||
const { subDomain, domainName, registeredCallbackFn } = props || values;
|
const { subDomain, domainName, registeredCallbackFn } = props || values;
|
||||||
const { methods: { register } } = ENSSubdomainRegistry;
|
const { methods: { register } } = UsernameRegistrar;
|
||||||
|
const { methods: { setAddr, setPubkey } } = PublicResolver;
|
||||||
const subdomainHash = soliditySha3(subDomain);
|
const subdomainHash = soliditySha3(subDomain);
|
||||||
const domainNameHash = hash(domainName);
|
const domainNameHash = hash(domainName);
|
||||||
const resolveToAddr = address || zeroAddress;
|
const resolveToAddr = address || zeroAddress;
|
||||||
|
@ -162,7 +164,6 @@ const RegisterSubDomain = withFormik({
|
||||||
const funcsToSend = [];
|
const funcsToSend = [];
|
||||||
const args = [
|
const args = [
|
||||||
subdomainHash,
|
subdomainHash,
|
||||||
domainNameHash,
|
|
||||||
resolveToAddr,
|
resolveToAddr,
|
||||||
points ? points.x : zeroBytes32,
|
points ? points.x : zeroBytes32,
|
||||||
points ? points.y : zeroBytes32,
|
points ? points.y : zeroBytes32,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import { Form, FormGroup, FormControl, HelpBlock, Button, ControlLabel } from 'react-bootstrap';
|
import { Form, FormGroup, FormControl, HelpBlock, Button, ControlLabel } from 'react-bootstrap';
|
||||||
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry';
|
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
|
||||||
import web3Utils from 'web3-utils'
|
import web3Utils from 'web3-utils'
|
||||||
import { hash } from 'eth-ens-namehash'
|
import { hash } from 'eth-ens-namehash'
|
||||||
|
|
||||||
|
@ -12,10 +12,10 @@ const dispatchSetup = (ENSRegistry) => {
|
||||||
setSubnodeOwner(zeroBytes32, sha3('eth'), getUserAddress(ENSRegistry))
|
setSubnodeOwner(zeroBytes32, sha3('eth'), getUserAddress(ENSRegistry))
|
||||||
.send()
|
.send()
|
||||||
.then(res => { console.log(res) })
|
.then(res => { console.log(res) })
|
||||||
setSubnodeOwner(hash('eth'), sha3('stateofus'), ENSSubdomainRegistry._address)
|
setSubnodeOwner(hash('eth'), sha3('stateofus'), UsernameRegistrar._address)
|
||||||
.send()
|
.send()
|
||||||
.then(res => { console.log(res) })
|
.then(res => { console.log(res) })
|
||||||
setSubnodeOwner(hash('eth'), sha3('stateofus'), ENSSubdomainRegistry._address)
|
setSubnodeOwner(hash('eth'), sha3('stateofus'), UsernameRegistrar._address)
|
||||||
.send()
|
.send()
|
||||||
.then(res => { console.log(res) })
|
.then(res => { console.log(res) })
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry';
|
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
|
||||||
import web3 from 'web3';
|
import web3 from 'web3';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Button } from 'react-bootstrap';
|
import { Button } from 'react-bootstrap';
|
||||||
|
@ -39,7 +39,7 @@ const UpdateController = withFormik({
|
||||||
},
|
},
|
||||||
async handleSubmit(values, { setSubmitting }) {
|
async handleSubmit(values, { setSubmitting }) {
|
||||||
const { newAddress } = values;
|
const { newAddress } = values;
|
||||||
const { methods: { changeController } } = ENSSubdomainRegistry;
|
const { methods: { changeController } } = UsernameRegistrar;
|
||||||
changeController(newAddress)
|
changeController(newAddress)
|
||||||
.send()
|
.send()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import EmbarkJS from 'Embark/EmbarkJS';
|
import EmbarkJS from 'Embark/EmbarkJS';
|
||||||
import ENSRegistry from 'Embark/contracts/ENSRegistry';
|
import ENSRegistry from 'Embark/contracts/ENSRegistry';
|
||||||
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry';
|
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
|
||||||
import TestToken from 'Embark/contracts/TestToken';
|
import TestToken from 'Embark/contracts/TestToken';
|
||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import { Form, FormGroup, FormControl, HelpBlock, Button, ControlLabel } from 'react-bootstrap';
|
import { Form, FormGroup, FormControl, HelpBlock, Button, ControlLabel } from 'react-bootstrap';
|
||||||
|
@ -24,7 +24,7 @@ const ENSSubManagement = props => (
|
||||||
<h2 style={{ textAlign: 'center' }}>Subdomain Management</h2>
|
<h2 style={{ textAlign: 'center' }}>Subdomain Management</h2>
|
||||||
<h3>Change Registry Controller</h3>
|
<h3>Change Registry Controller</h3>
|
||||||
<UpdateController />
|
<UpdateController />
|
||||||
<h3>Add/Update Domain Price</h3>
|
<h3>Activate Registry/Update Registry Price</h3>
|
||||||
<AddDomain />
|
<AddDomain />
|
||||||
<h3>Move Domain To Another Registry</h3>
|
<h3>Move Domain To Another Registry</h3>
|
||||||
<MoveDomain />
|
<MoveDomain />
|
||||||
|
@ -34,7 +34,7 @@ const ENSSubManagement = props => (
|
||||||
<hr/>
|
<hr/>
|
||||||
<TokenPermissions
|
<TokenPermissions
|
||||||
symbol='SNT'
|
symbol='SNT'
|
||||||
spender={ENSSubdomainRegistry._address}
|
spender={UsernameRegistrar._address}
|
||||||
methods={TestToken.methods} />
|
methods={TestToken.methods} />
|
||||||
<hr/>
|
<hr/>
|
||||||
<SetupENS ENSRegistry={ENSRegistry} />
|
<SetupENS ENSRegistry={ENSRegistry} />
|
||||||
|
|
|
@ -1,127 +0,0 @@
|
||||||
import EmbarkJS from 'Embark/EmbarkJS';
|
|
||||||
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry';
|
|
||||||
import React from 'react';
|
|
||||||
import { Form, FormGroup, FormControl, HelpBlock, Button } from 'react-bootstrap';
|
|
||||||
|
|
||||||
class ENSSubdomainRegistryUI extends React.Component {
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
username : "",
|
|
||||||
address: "",
|
|
||||||
pubkey: "",
|
|
||||||
logs: []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
update_username(e){
|
|
||||||
this.setState({username: e.target.value});
|
|
||||||
//lookup ens
|
|
||||||
//- if not found enable register and lookup price
|
|
||||||
// ENSSubdomainRegistry.methods.getPrice(domains.paid.namehash).call()
|
|
||||||
// and enable register button
|
|
||||||
//
|
|
||||||
//- if found, try to resolve address and pubkey
|
|
||||||
//
|
|
||||||
//- lookup subdomain owner and if subdomain owner is defaultAccount
|
|
||||||
// enable edit of address/pubkey and other buttons (except release domain and update funds owner)
|
|
||||||
//
|
|
||||||
//- if ENSSubdomainRegistry.methods.getCreationDate(subdomainNameHash) +
|
|
||||||
// ENSSubdomainRegistry.methods.releaseDelay().call();
|
|
||||||
// is less then current timestamp enable "release domain" button
|
|
||||||
//
|
|
||||||
//- if ens.owner(subdomain) is not equal to ENSSubdomainRegistry.getFundsOwner(subdomain)
|
|
||||||
// and default account is ens.owner(subdomain), enable update funds owner
|
|
||||||
//
|
|
||||||
//- if ens.owner(domain) is not equal ENSubdomainRegistry.address
|
|
||||||
// and default account is ens.owner(subdomain), enable move subdomain
|
|
||||||
}
|
|
||||||
|
|
||||||
update_address(e){
|
|
||||||
this.setState({address: e.target.value});
|
|
||||||
}
|
|
||||||
|
|
||||||
update_pubkey(e){
|
|
||||||
this.setState({pubkey: e.target.value});
|
|
||||||
}
|
|
||||||
|
|
||||||
register() {
|
|
||||||
/*let result = await ENSSubdomainRegistry.methods.register(
|
|
||||||
labelHash,
|
|
||||||
domains.paid.namehash,
|
|
||||||
utils.zeroAddress,
|
|
||||||
utils.zeroBytes32,
|
|
||||||
utils.zeroBytes32
|
|
||||||
).send({from: registrant});
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
updateResolver() {
|
|
||||||
//should call PublicResolver.methods.setAddr(address)
|
|
||||||
// + PublicResolver.methods.setPubKey(bytes32,bytes32)
|
|
||||||
}
|
|
||||||
|
|
||||||
updateFundsOwner() {
|
|
||||||
/*ENSSubdomainRegistry.methods.updateFundsOwner(
|
|
||||||
labelHash,
|
|
||||||
domains.paid.namehash
|
|
||||||
).send({from: newOwner});*/
|
|
||||||
}
|
|
||||||
|
|
||||||
moveSubdomain () {
|
|
||||||
//ENSSubdomainRegistry.methods.moveAccount(labelHash, domainHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
releaseSubdomain() {
|
|
||||||
/**
|
|
||||||
* ENSSubdomainRegistry.methods.release(
|
|
||||||
web3Utils.sha3(subdomain),
|
|
||||||
domains.free.namehash
|
|
||||||
).send({from: registrant});
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
_addToLog(txt){
|
|
||||||
this.state.logs.push(txt);
|
|
||||||
this.setState({logs: this.state.logs});
|
|
||||||
}
|
|
||||||
|
|
||||||
render(){
|
|
||||||
return (<React.Fragment>
|
|
||||||
|
|
||||||
<h2> Subdomain management</h2>
|
|
||||||
<Form inline>
|
|
||||||
<FormGroup>
|
|
||||||
<FormControl
|
|
||||||
type="text"
|
|
||||||
onChange={(e) => this.update_username(e)} />
|
|
||||||
<FormControl
|
|
||||||
type="text"
|
|
||||||
onChange={(e) => this.update_address(e)} />
|
|
||||||
<FormControl
|
|
||||||
type="text"
|
|
||||||
onChange={(e) => this.update_pubkey(e) } />
|
|
||||||
|
|
||||||
<Button bsStyle="primary" onClick={(e) => this.register(e)}>Register Subdomain</Button>
|
|
||||||
<Button bsStyle="primary" onClick={(e) => this.updateResolver(e)}>Update Resolver</Button>
|
|
||||||
<Button bsStyle="primary" onClick={(e) => this.updateFundsOwner(e)}>Update Funds Owner</Button>
|
|
||||||
<Button bsStyle="primary" onClick={(e) => this.moveSubdomain(e)}>Update Account</Button>
|
|
||||||
<Button bsStyle="primary" onClick={(e) => this.releaseSubdomain(e)}>Release Subdomain</Button>
|
|
||||||
</FormGroup>
|
|
||||||
</Form>
|
|
||||||
|
|
||||||
|
|
||||||
<h3> Contract Calls </h3>
|
|
||||||
<p>Javascript calls being made: </p>
|
|
||||||
<div className="logs">
|
|
||||||
{
|
|
||||||
this.state.logs.map((item, i) => <p key={i}>{item}</p>)
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ENSSubdomainRegistryUI;
|
|
|
@ -10,7 +10,7 @@ import TestTokenUI from './components/testtoken';
|
||||||
import ERC20TokenUI from './components/erc20token';
|
import ERC20TokenUI from './components/erc20token';
|
||||||
import TestToken from 'Embark/contracts/TestToken';
|
import TestToken from 'Embark/contracts/TestToken';
|
||||||
import ENSSubManagement from './components/ensSubManagement';
|
import ENSSubManagement from './components/ensSubManagement';
|
||||||
import ENSSubdomainRegistry from 'Embark/contracts/ENSSubdomainRegistry';
|
import UsernameRegistrar from 'Embark/contracts/UsernameRegistrar';
|
||||||
import NameLookup from './components/ens/nameLookup';
|
import NameLookup from './components/ens/nameLookup';
|
||||||
import AdminMode from './components/AdminMode';
|
import AdminMode from './components/AdminMode';
|
||||||
import TokenPermissions from './components/standard/TokenPermissionConnect';
|
import TokenPermissions from './components/standard/TokenPermissionConnect';
|
||||||
|
@ -66,14 +66,14 @@ class App extends React.Component {
|
||||||
</div>
|
</div>
|
||||||
</Fade>}
|
</Fade>}
|
||||||
{searching && <Fade in={searching}>
|
{searching && <Fade in={searching}>
|
||||||
<Web3Render ready={isRopsten} network="ropsten">
|
<Web3Render ready={network == 'private'} network={network}>
|
||||||
<div>
|
<div>
|
||||||
<NameLookup />
|
<NameLookup />
|
||||||
<Hidden mdDown>
|
<Hidden mdDown>
|
||||||
<div style={{ textAlign: 'center', margin: '0px 40px' }}>
|
<div style={{ textAlign: 'center', margin: '0px 40px' }}>
|
||||||
<TokenPermissions
|
<TokenPermissions
|
||||||
symbol={symbols[network] || 'SNT'}
|
symbol={symbols[network] || 'SNT'}
|
||||||
spender={ENSSubdomainRegistry._address}
|
spender={UsernameRegistrar._address}
|
||||||
methods={TestToken.methods} />
|
methods={TestToken.methods} />
|
||||||
<hr/>
|
<hr/>
|
||||||
<Toggle onChange={() => { this.setState({ admin: !admin })}} />
|
<Toggle onChange={() => { this.setState({ admin: !admin })}} />
|
||||||
|
|
|
@ -27,13 +27,15 @@ module.exports = {
|
||||||
"args": ["$ENSRegistry"],
|
"args": ["$ENSRegistry"],
|
||||||
"deploy": true
|
"deploy": true
|
||||||
},
|
},
|
||||||
"ENSSubdomainRegistry": {
|
|
||||||
|
"UsernameRegistrar": {
|
||||||
"args": [
|
"args": [
|
||||||
"$TestToken",
|
"$TestToken",
|
||||||
"$ENSRegistry",
|
"$ENSRegistry",
|
||||||
"$PublicResolver",
|
"$PublicResolver",
|
||||||
"3",
|
"0x5f7791d31ca0493e9ca7c9ca16695ecd9d5044768674d14d31ab5d8277518fff",
|
||||||
[merkleRoot],
|
3,
|
||||||
|
merkleTree.getHexRoot(),
|
||||||
"0x9e183BC54Bb4f3cCa1A478CA6f2c3EdC37B60478"
|
"0x9e183BC54Bb4f3cCa1A478CA6f2c3EdC37B60478"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -56,18 +58,18 @@ module.exports = {
|
||||||
"$ENSRegistry"
|
"$ENSRegistry"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"ENSSubdomainRegistry": {
|
"UsernameRegistrar": {
|
||||||
"args": [
|
"args": [
|
||||||
"$TestToken",
|
"$TestToken",
|
||||||
"$ENSRegistry",
|
"$ENSRegistry",
|
||||||
"$PublicResolver",
|
"$PublicResolver",
|
||||||
"3",
|
"0x5f7791d31ca0493e9ca7c9ca16695ecd9d5044768674d14d31ab5d8277518fff",
|
||||||
[merkleRoot],
|
3,
|
||||||
|
merkleTree.getHexRoot(),
|
||||||
"0x0"
|
"0x0"
|
||||||
],
|
],
|
||||||
"onDeploy": [
|
"onDeploy": [
|
||||||
"ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '0xbd99f8d5e7f81d2d7c1da34b67a2bb3a94dd8c9b0ab40ddc077621b98405983b', ENSSubdomainRegistry.address).send()",
|
"ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '0xbd99f8d5e7f81d2d7c1da34b67a2bb3a94dd8c9b0ab40ddc077621b98405983b', UsernameRegistrar.address).send()"
|
||||||
"ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '0x7b4768a525e733422bf968587a91b4036e5176d36f44a9fb5b29d0bca03ab3a3', ENSSubdomainRegistry.address).send()"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,7 +85,7 @@ module.exports = {
|
||||||
"TestToken": {
|
"TestToken": {
|
||||||
"address": "0x744d70fdbe2ba4cf95131626614a1763df805b9e"
|
"address": "0x744d70fdbe2ba4cf95131626614a1763df805b9e"
|
||||||
},
|
},
|
||||||
"ENSSubdomainRegistry": {
|
"UsernameRegistrar": {
|
||||||
"address": "0xDBf9038cf5Aaa030890790dB87E746E00Fc352b3"
|
"address": "0xDBf9038cf5Aaa030890790dB87E746E00Fc352b3"
|
||||||
},
|
},
|
||||||
"ERC20Receiver": { "deploy": false },
|
"ERC20Receiver": { "deploy": false },
|
||||||
|
@ -93,6 +95,7 @@ module.exports = {
|
||||||
"MerkleProofWrapper": {
|
"MerkleProofWrapper": {
|
||||||
"address": "0x76E55E13C5891a90f7fCA2e1238a6B3463F564e2"
|
"address": "0x76E55E13C5891a90f7fCA2e1238a6B3463F564e2"
|
||||||
},
|
},
|
||||||
|
"ERC20Receiver": { "deploy": false },
|
||||||
"SafeMath": {
|
"SafeMath": {
|
||||||
"address": "0xA115a57952D3337e2a1aB3Cb82bA376EEcDDc469"
|
"address": "0xA115a57952D3337e2a1aB3Cb82bA376EEcDDc469"
|
||||||
}
|
}
|
||||||
|
@ -118,7 +121,7 @@ module.exports = {
|
||||||
"MerkleProofWrapper": {
|
"MerkleProofWrapper": {
|
||||||
"address": "0x58E01078d14142E0370526dFdAE44E4f508c844B"
|
"address": "0x58E01078d14142E0370526dFdAE44E4f508c844B"
|
||||||
},
|
},
|
||||||
"ENSSubdomainRegistry": {
|
"UsernameRegistrar": {
|
||||||
"address": "0x028F3Df706c5295Ba283c326F4692c375D14cb68"
|
"address": "0x028F3Df706c5295Ba283c326F4692c375D14cb68"
|
||||||
},
|
},
|
||||||
"ERC20Receiver": { "deploy": false }
|
"ERC20Receiver": { "deploy": false }
|
||||||
|
|
|
@ -1,122 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
"default": {
|
|
||||||
"deployment": {
|
|
||||||
"host": "localhost",
|
|
||||||
"port": 8545,
|
|
||||||
"type": "rpc"
|
|
||||||
},
|
|
||||||
"dappConnection": [
|
|
||||||
"$WEB3",
|
|
||||||
"http://localhost:8545"
|
|
||||||
],
|
|
||||||
"gas": "auto",
|
|
||||||
"contracts": {
|
|
||||||
"TestToken": {
|
|
||||||
"args": [ ]
|
|
||||||
},
|
|
||||||
"ENSRegistry": {
|
|
||||||
"deploy": true
|
|
||||||
},
|
|
||||||
"PublicResolver": {
|
|
||||||
"args": ["$ENSRegistry"],
|
|
||||||
"deploy": true
|
|
||||||
},
|
|
||||||
"ENSSubdomainRegistry": {
|
|
||||||
"args": [
|
|
||||||
"$TestToken",
|
|
||||||
"$ENSRegistry",
|
|
||||||
"$PublicResolver",
|
|
||||||
"3",
|
|
||||||
[merkleRoot],
|
|
||||||
"0x0"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"development": {
|
|
||||||
"contracts": {
|
|
||||||
"TestToken": {
|
|
||||||
"deploy": true
|
|
||||||
},
|
|
||||||
"ENSRegistry": {
|
|
||||||
"deploy": true,
|
|
||||||
"onDeploy": [
|
|
||||||
"ENSRegistry.methods.setSubnodeOwner('0x0000000000000000000000000000000000000000000000000000000000000000', '0x4f5b812789fc606be1b3b16908db13fc7a9adf7ca72641f84d75b47069d3d7f0', web3.eth.defaultAccount).send()"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"PublicResolver": {
|
|
||||||
"deploy": true,
|
|
||||||
"args": [
|
|
||||||
"$ENSRegistry"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ENSSubdomainRegistry": {
|
|
||||||
"args": [
|
|
||||||
"$TestToken",
|
|
||||||
"$ENSRegistry",
|
|
||||||
"$PublicResolver",
|
|
||||||
"3",
|
|
||||||
[merkleRoot],
|
|
||||||
"0x0"
|
|
||||||
],
|
|
||||||
"onDeploy": [
|
|
||||||
"ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '0xbd99f8d5e7f81d2d7c1da34b67a2bb3a94dd8c9b0ab40ddc077621b98405983b', ENSSubdomainRegistry.address).send()",
|
|
||||||
"ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '0x7b4768a525e733422bf968587a91b4036e5176d36f44a9fb5b29d0bca03ab3a3', ENSSubdomainRegistry.address).send()"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"livenet":{
|
|
||||||
"contracts": {
|
|
||||||
"ENSRegistry": {
|
|
||||||
"address": "0x314159265dd8dbb310642f98f50c066173c1259b"
|
|
||||||
},
|
|
||||||
"PublicResolver": {
|
|
||||||
"address": "0x5FfC014343cd971B7eb70732021E26C35B744cc4"
|
|
||||||
},
|
|
||||||
"TestToken": {
|
|
||||||
"address": "0x744d70fdbe2ba4cf95131626614a1763df805b9e"
|
|
||||||
},
|
|
||||||
"ENSSubdomainRegistry": {
|
|
||||||
"address": "0xDBf9038cf5Aaa030890790dB87E746E00Fc352b3"
|
|
||||||
},
|
|
||||||
"ERC20Receiver": { "deploy": false },
|
|
||||||
"SafeMath": {
|
|
||||||
"address": "0xA115a57952D3337e2a1aB3Cb82bA376EEcDDc469"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"testnet":{
|
|
||||||
"contracts": {
|
|
||||||
"ENSRegistry": {
|
|
||||||
"address": "0x112234455c3a32fd11230c42e7bccd4a84e02010"
|
|
||||||
},
|
|
||||||
"PublicResolver": {
|
|
||||||
"address": "0x29754bADB2640b98F6deF0f52D41418b0d2e0C51"
|
|
||||||
},
|
|
||||||
"TestToken": {
|
|
||||||
"address": "0xc55cF4B03948D7EBc8b9E8BAD92643703811d162"
|
|
||||||
},
|
|
||||||
"SafeMath": {
|
|
||||||
"address": "0x0F9992f7737f9ba3aceD170D4D1259cb2CEcc050"
|
|
||||||
},
|
|
||||||
"MerkleProof": {
|
|
||||||
"address": "0x5df00E70AD165D50228DB6d8285fB6EAAc630FD7"
|
|
||||||
},
|
|
||||||
"MerkleProofWrapper": {
|
|
||||||
"address": "0x58E01078d14142E0370526dFdAE44E4f508c844B"
|
|
||||||
},
|
|
||||||
"ERC20Receiver": { "deploy": false }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rinkeby":{
|
|
||||||
"contracts": {
|
|
||||||
"ENSRegistry": {
|
|
||||||
"address": "0xe7410170f87102DF0055eB195163A03B7F2Bff4A"
|
|
||||||
},
|
|
||||||
"PublicResolver": {
|
|
||||||
"address": "0x5d20cf83cb385e06d2f2a892f9322cd4933eacdc"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,4 @@
|
||||||
exports.ReservedUsernames = [
|
exports.reservedNames = [
|
||||||
|
|
||||||
|
|
||||||
//Ethereum reserved words
|
//Ethereum reserved words
|
||||||
'eth',
|
'eth',
|
||||||
'ether',
|
'ether',
|
||||||
|
|
|
@ -1,464 +0,0 @@
|
||||||
pragma solidity ^0.4.23;
|
|
||||||
|
|
||||||
import "../common/MerkleProof.sol";
|
|
||||||
import "../common/Controlled.sol";
|
|
||||||
import "../token/ERC20Token.sol";
|
|
||||||
import "../ens/ENS.sol";
|
|
||||||
import "../ens/PublicResolver.sol";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
|
|
||||||
* @notice Sell ENS subdomains of owned domains.
|
|
||||||
*/
|
|
||||||
contract ENSSubdomainRegistry is Controlled {
|
|
||||||
|
|
||||||
ERC20Token public token;
|
|
||||||
ENS public ens;
|
|
||||||
PublicResolver public resolver;
|
|
||||||
address public parentRegistry;
|
|
||||||
|
|
||||||
uint256 public releaseDelay = 365 days;
|
|
||||||
mapping (bytes32 => Domain) public domains;
|
|
||||||
mapping (bytes32 => Account) public accounts;
|
|
||||||
|
|
||||||
//slashing conditions
|
|
||||||
uint256 public subdomainMinLenght;
|
|
||||||
bytes32[] public reservedSubdomainsMerkleRoots;
|
|
||||||
|
|
||||||
event DomainPrice(bytes32 indexed namehash, uint256 price);
|
|
||||||
event DomainMoved(bytes32 indexed namehash, address newRegistry);
|
|
||||||
event SubdomainOwner(bytes32 subdomainHash, address accountOwner);
|
|
||||||
|
|
||||||
enum NodeState { Free, Owned, Moved }
|
|
||||||
struct Domain {
|
|
||||||
NodeState state;
|
|
||||||
uint256 price;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Account {
|
|
||||||
uint256 tokenBalance;
|
|
||||||
uint256 creationTime;
|
|
||||||
address accountOwner;
|
|
||||||
}
|
|
||||||
|
|
||||||
modifier onlyParentRegistry {
|
|
||||||
require(msg.sender == parentRegistry, "Migration only.");
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @notice Initializes a UserRegistry contract
|
|
||||||
* @param _token fee token base
|
|
||||||
* @param _ens Ethereum Name Service root address
|
|
||||||
* @param _resolver Default resolver to use in initial settings
|
|
||||||
* @param _subdomainMinLenght Minimum length of usernames
|
|
||||||
* @param _reservedSubdomainsMerkleRoots Merkle Roots of reserved subdomains
|
|
||||||
* @param _parentRegistry Address of old registry (if any) for account migration.
|
|
||||||
*/
|
|
||||||
constructor(
|
|
||||||
ERC20Token _token,
|
|
||||||
ENS _ens,
|
|
||||||
PublicResolver _resolver,
|
|
||||||
uint256 _subdomainMinLenght,
|
|
||||||
bytes32[] _reservedSubdomainsMerkleRoots,
|
|
||||||
address _parentRegistry
|
|
||||||
)
|
|
||||||
public
|
|
||||||
{
|
|
||||||
token = _token;
|
|
||||||
ens = _ens;
|
|
||||||
resolver = _resolver;
|
|
||||||
subdomainMinLenght = _subdomainMinLenght;
|
|
||||||
reservedSubdomainsMerkleRoots = _reservedSubdomainsMerkleRoots;
|
|
||||||
parentRegistry = _parentRegistry;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @notice Registers `_userHash` subdomain to `_domainHash` setting msg.sender as owner.
|
|
||||||
* @param _userHash choosen unowned subdomain hash
|
|
||||||
* @param _domainHash choosen contract owned domain hash
|
|
||||||
* @param _account optional address to set at public resolver
|
|
||||||
* @param _pubkeyA optional pubkey part A to set at public resolver
|
|
||||||
* @param _pubkeyB optional pubkey part B to set at public resolver
|
|
||||||
*/
|
|
||||||
function register(
|
|
||||||
bytes32 _userHash,
|
|
||||||
bytes32 _domainHash,
|
|
||||||
address _account,
|
|
||||||
bytes32 _pubkeyA,
|
|
||||||
bytes32 _pubkeyB
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns(bytes32 subdomainHash)
|
|
||||||
{
|
|
||||||
Domain memory domain = domains[_domainHash];
|
|
||||||
require(domain.state == NodeState.Owned, "Domain unavailable.");
|
|
||||||
subdomainHash = keccak256(abi.encodePacked(_domainHash, _userHash));
|
|
||||||
require(ens.owner(subdomainHash) == address(0), "ENS node already owned.");
|
|
||||||
require(accounts[subdomainHash].creationTime == 0, "Username already registered.");
|
|
||||||
accounts[subdomainHash] = Account(domain.price, block.timestamp, msg.sender);
|
|
||||||
if(domain.price > 0) {
|
|
||||||
require(token.allowance(msg.sender, address(this)) >= domain.price, "Unallowed to spend.");
|
|
||||||
require(
|
|
||||||
token.transferFrom(
|
|
||||||
address(msg.sender),
|
|
||||||
address(this),
|
|
||||||
domain.price
|
|
||||||
),
|
|
||||||
"Transfer failed"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool resolvePubkey = _pubkeyA != 0 || _pubkeyB != 0;
|
|
||||||
bool resolveAccount = _account != address(0);
|
|
||||||
if (resolvePubkey || resolveAccount) {
|
|
||||||
//set to self the ownship to setup initial resolver
|
|
||||||
ens.setSubnodeOwner(_domainHash, _userHash, address(this));
|
|
||||||
ens.setResolver(subdomainHash, resolver); //default resolver
|
|
||||||
if (resolveAccount) {
|
|
||||||
resolver.setAddr(subdomainHash, _account);
|
|
||||||
}
|
|
||||||
if (resolvePubkey) {
|
|
||||||
resolver.setPubkey(subdomainHash, _pubkeyA, _pubkeyB);
|
|
||||||
}
|
|
||||||
ens.setOwner(subdomainHash, msg.sender);
|
|
||||||
}else {
|
|
||||||
//transfer ownship of subdone directly to registrant
|
|
||||||
ens.setSubnodeOwner(_domainHash, _userHash, msg.sender);
|
|
||||||
}
|
|
||||||
emit SubdomainOwner(subdomainHash, msg.sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @notice release subdomain and retrieve locked fee, needs to be called after `releasePeriod` from creation time.
|
|
||||||
* @param _userHash `msg.sender` owned subdomain hash
|
|
||||||
* @param _domainHash choosen contract owned domain hash
|
|
||||||
*/
|
|
||||||
function release(
|
|
||||||
bytes32 _userHash,
|
|
||||||
bytes32 _domainHash
|
|
||||||
)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
bool isDomainController = ens.owner(_domainHash) == address(this);
|
|
||||||
bytes32 subdomainHash = keccak256(abi.encodePacked(_domainHash, _userHash));
|
|
||||||
Account memory account = accounts[subdomainHash];
|
|
||||||
require(account.creationTime > 0, "Username not registered.");
|
|
||||||
if (isDomainController) {
|
|
||||||
require(msg.sender == ens.owner(subdomainHash), "Not owner of ENS node.");
|
|
||||||
require(block.timestamp > account.creationTime + releaseDelay, "Release period not reached.");
|
|
||||||
ens.setSubnodeOwner(_domainHash, _userHash, address(this));
|
|
||||||
ens.setResolver(subdomainHash, address(0));
|
|
||||||
ens.setOwner(subdomainHash, address(0));
|
|
||||||
} else {
|
|
||||||
require(msg.sender == account.accountOwner, "Not the former account owner.");
|
|
||||||
}
|
|
||||||
delete accounts[subdomainHash];
|
|
||||||
if (account.tokenBalance > 0) {
|
|
||||||
require(token.transfer(msg.sender, account.tokenBalance), "Transfer failed");
|
|
||||||
}
|
|
||||||
emit SubdomainOwner(subdomainHash, address(0));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @notice updates funds owner, useful to move subdomain account to new registry.
|
|
||||||
* @param _userHash `msg.sender` owned subdomain hash
|
|
||||||
* @param _domainHash choosen contract owned domain hash
|
|
||||||
**/
|
|
||||||
function updateAccountOwner(
|
|
||||||
bytes32 _userHash,
|
|
||||||
bytes32 _domainHash
|
|
||||||
)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
bytes32 subdomainHash = keccak256(abi.encodePacked(_domainHash, _userHash));
|
|
||||||
require(accounts[subdomainHash].creationTime > 0, "Username not registered.");
|
|
||||||
require(msg.sender == ens.owner(subdomainHash), "Caller not owner of ENS node.");
|
|
||||||
require(ens.owner(_domainHash) == address(this), "Registry not owner of domain.");
|
|
||||||
accounts[subdomainHash].accountOwner = msg.sender;
|
|
||||||
emit SubdomainOwner(subdomainHash, msg.sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @notice slash account due too length restriction
|
|
||||||
* @param _subdomain raw value of offending subdomain
|
|
||||||
* @param _domainHash domain hash
|
|
||||||
*/
|
|
||||||
function slashSmallSubdomain(
|
|
||||||
bytes _subdomain,
|
|
||||||
bytes32 _domainHash
|
|
||||||
)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
require(_subdomain.length < subdomainMinLenght, "Not a small subdomain.");
|
|
||||||
slashSubdomain(_subdomain, _domainHash);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @notice slash account due look like an address
|
|
||||||
* @param _subdomain raw value of offending subdomain
|
|
||||||
* @param _domainHash domain hash
|
|
||||||
*/
|
|
||||||
function slashAddressLikeSubdomain(
|
|
||||||
string _subdomain,
|
|
||||||
bytes32 _domainHash
|
|
||||||
)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
bytes memory subdomain = bytes(_subdomain);
|
|
||||||
require(subdomain.length > 12, "Too small to look like an address.");
|
|
||||||
require(subdomain[0] == byte("0"), "First character need to be 0");
|
|
||||||
require(subdomain[1] == byte("x"), "Second character need to be x");
|
|
||||||
slashSubdomain(subdomain, _domainHash);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @notice slash account due reserved name
|
|
||||||
* @param _subdomain raw value of offending subdomain
|
|
||||||
* @param _domainHash domain hash
|
|
||||||
*/
|
|
||||||
function slashReservedSubdomain(
|
|
||||||
bytes _subdomain,
|
|
||||||
bytes32 _domainHash,
|
|
||||||
uint256 _rootPos,
|
|
||||||
bytes32[] _proof
|
|
||||||
)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
require(reservedSubdomainsMerkleRoots.length > _rootPos, "Invalid Merkle Root");
|
|
||||||
require(
|
|
||||||
MerkleProof.verifyProof(
|
|
||||||
_proof,
|
|
||||||
reservedSubdomainsMerkleRoots[_rootPos],
|
|
||||||
keccak256(_subdomain)
|
|
||||||
),
|
|
||||||
"Invalid Proof."
|
|
||||||
);
|
|
||||||
slashSubdomain(_subdomain, _domainHash);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @notice slash account of invalid subdomain
|
|
||||||
* @param _subdomain raw value of offending subdomain
|
|
||||||
* @param _domainHash domain hash
|
|
||||||
* @param _offendingPos position of invalid character
|
|
||||||
*/
|
|
||||||
function slashInvalidSubdomain(
|
|
||||||
bytes _subdomain,
|
|
||||||
bytes32 _domainHash,
|
|
||||||
uint256 _offendingPos
|
|
||||||
)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
require(_subdomain.length > _offendingPos, "Invalid position.");
|
|
||||||
byte b = _subdomain[_offendingPos];
|
|
||||||
|
|
||||||
require(!((b >= 48 && b <= 57) || (b >= 97 && b <= 122)), "Not invalid character.");
|
|
||||||
|
|
||||||
slashSubdomain(_subdomain, _domainHash);
|
|
||||||
}
|
|
||||||
|
|
||||||
function slashSubdomain(bytes _subdomain, bytes32 _domainHash) internal {
|
|
||||||
bytes32 userHash = keccak256(_subdomain);
|
|
||||||
bytes32 subdomainHash = keccak256(abi.encodePacked(_domainHash, userHash));
|
|
||||||
require(accounts[subdomainHash].creationTime > 0, "Username not registered.");
|
|
||||||
|
|
||||||
ens.setSubnodeOwner(_domainHash, userHash, address(this));
|
|
||||||
ens.setResolver(subdomainHash, address(0));
|
|
||||||
ens.setOwner(subdomainHash, address(0));
|
|
||||||
|
|
||||||
uint256 amountToTransfer = accounts[subdomainHash].tokenBalance;
|
|
||||||
delete accounts[subdomainHash];
|
|
||||||
if(amountToTransfer > 0){
|
|
||||||
require(token.transfer(msg.sender, amountToTransfer), "Error in transfer.");
|
|
||||||
}
|
|
||||||
emit SubdomainOwner(subdomainHash, address(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @notice Migrate account to new registry
|
|
||||||
* @param _userHash `msg.sender` owned subdomain hash
|
|
||||||
* @param _domainHash choosen contract owned domain hash
|
|
||||||
**/
|
|
||||||
function moveAccount(
|
|
||||||
bytes32 _userHash,
|
|
||||||
bytes32 _domainHash
|
|
||||||
)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
bytes32 subdomainHash = keccak256(abi.encodePacked(_domainHash, _userHash));
|
|
||||||
require(msg.sender == accounts[subdomainHash].accountOwner, "Callable only by account owner.");
|
|
||||||
ENSSubdomainRegistry _newRegistry = ENSSubdomainRegistry(ens.owner(_domainHash));
|
|
||||||
Account memory account = accounts[subdomainHash];
|
|
||||||
delete accounts[subdomainHash];
|
|
||||||
//require(address(this) == _newRegistry.parentRegistry(), "Wrong update.");
|
|
||||||
token.approve(_newRegistry, account.tokenBalance);
|
|
||||||
_newRegistry.migrateAccount(
|
|
||||||
_userHash,
|
|
||||||
_domainHash,
|
|
||||||
account.tokenBalance,
|
|
||||||
account.creationTime,
|
|
||||||
account.accountOwner
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev callabe only by parent registry to continue migration of domain
|
|
||||||
**/
|
|
||||||
function migrateDomain(
|
|
||||||
bytes32 _domain,
|
|
||||||
uint256 _price
|
|
||||||
)
|
|
||||||
external
|
|
||||||
onlyParentRegistry
|
|
||||||
{
|
|
||||||
require(ens.owner(_domain) == address(this), "ENS domain owner not transfered.");
|
|
||||||
assert(domains[_domain].state == NodeState.Free);
|
|
||||||
domains[_domain] = Domain(NodeState.Owned, _price);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev callable only by parent registry for continue user opt-in migration
|
|
||||||
* @param _userHash any subdomain hash coming from parent
|
|
||||||
* @param _domainHash choosen contract owned domain hash
|
|
||||||
* @param _tokenBalance amount being transferred
|
|
||||||
* @param _creationTime any value coming from parent
|
|
||||||
* @param _accountOwner accountOwner for opt-out/release at domain move
|
|
||||||
**/
|
|
||||||
function migrateAccount(
|
|
||||||
bytes32 _userHash,
|
|
||||||
bytes32 _domainHash,
|
|
||||||
uint256 _tokenBalance,
|
|
||||||
uint256 _creationTime,
|
|
||||||
address _accountOwner
|
|
||||||
)
|
|
||||||
external
|
|
||||||
onlyParentRegistry
|
|
||||||
{
|
|
||||||
bytes32 subdomainHash = keccak256(abi.encodePacked(_domainHash, _userHash));
|
|
||||||
accounts[subdomainHash] = Account(_tokenBalance, _creationTime, _accountOwner);
|
|
||||||
if (_tokenBalance > 0) {
|
|
||||||
require(
|
|
||||||
token.transferFrom(
|
|
||||||
parentRegistry,
|
|
||||||
address(this),
|
|
||||||
_tokenBalance
|
|
||||||
),
|
|
||||||
"Error moving funds from old registar."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @notice moves a domain to other Registry (will not move subdomains accounts)
|
|
||||||
* @param _newRegistry new registry hodling this domain
|
|
||||||
* @param _domain domain being moved
|
|
||||||
*/
|
|
||||||
function moveDomain(
|
|
||||||
ENSSubdomainRegistry _newRegistry,
|
|
||||||
bytes32 _domain
|
|
||||||
)
|
|
||||||
external
|
|
||||||
onlyController
|
|
||||||
{
|
|
||||||
require(domains[_domain].state == NodeState.Owned, "Wrong domain");
|
|
||||||
require(ens.owner(_domain) == address(this), "Domain not owned anymore.");
|
|
||||||
uint256 price = domains[_domain].price;
|
|
||||||
domains[_domain].state = NodeState.Moved;
|
|
||||||
ens.setOwner(_domain, _newRegistry);
|
|
||||||
_newRegistry.migrateDomain(_domain, price);
|
|
||||||
emit DomainMoved(_domain, _newRegistry);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @notice Controller include new domain available to register
|
|
||||||
* @param _domain domain owned by user registry being activated
|
|
||||||
* @param _price cost to register subnode from this node
|
|
||||||
*/
|
|
||||||
function setDomainPrice(
|
|
||||||
bytes32 _domain,
|
|
||||||
uint256 _price
|
|
||||||
)
|
|
||||||
external
|
|
||||||
onlyController
|
|
||||||
{
|
|
||||||
require(domains[_domain].state == NodeState.Free, "Domain state is not free");
|
|
||||||
require(ens.owner(_domain) == address(this), "Registry does not own domain");
|
|
||||||
domains[_domain] = Domain(NodeState.Owned, _price);
|
|
||||||
emit DomainPrice(_domain, _price);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @notice updates domain price
|
|
||||||
* @param _domain active domain being defined price
|
|
||||||
* @param _price new price
|
|
||||||
*/
|
|
||||||
function updateDomainPrice(
|
|
||||||
bytes32 _domain,
|
|
||||||
uint256 _price
|
|
||||||
)
|
|
||||||
external
|
|
||||||
onlyController
|
|
||||||
{
|
|
||||||
Domain storage domain = domains[_domain];
|
|
||||||
require(domain.state == NodeState.Owned, "Domain not owned");
|
|
||||||
domain.price = _price;
|
|
||||||
emit DomainPrice(_domain, _price);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @notice updates default public resolver for newly registred subdomains
|
|
||||||
* @param _resolver new default resolver
|
|
||||||
*/
|
|
||||||
function setResolver(
|
|
||||||
address _resolver
|
|
||||||
)
|
|
||||||
external
|
|
||||||
onlyController
|
|
||||||
{
|
|
||||||
resolver = PublicResolver(_resolver);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPrice(bytes32 _domainHash)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns(uint256 subdomainPrice)
|
|
||||||
{
|
|
||||||
subdomainPrice = domains[_domainHash].price;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAccountBalance(bytes32 _subdomainHash)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns(uint256 accountBalance)
|
|
||||||
{
|
|
||||||
accountBalance = accounts[_subdomainHash].tokenBalance;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAccountOwner(bytes32 _subdomainHash)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns(address accountOwner)
|
|
||||||
{
|
|
||||||
accountOwner = accounts[_subdomainHash].accountOwner;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCreationTime(bytes32 _subdomainHash)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns(uint256 creationTime)
|
|
||||||
{
|
|
||||||
creationTime = accounts[_subdomainHash].creationTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getExpirationTime(bytes32 _subdomainHash)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns(uint256 expirationTime)
|
|
||||||
{
|
|
||||||
expirationTime = accounts[_subdomainHash].creationTime + releaseDelay;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,661 @@
|
||||||
|
pragma solidity ^0.4.24;
|
||||||
|
|
||||||
|
import "../common/MerkleProof.sol";
|
||||||
|
import "../common/Controlled.sol";
|
||||||
|
import "../token/ERC20Token.sol";
|
||||||
|
import "../token/ApproveAndCallFallBack.sol";
|
||||||
|
import "../ens/ENS.sol";
|
||||||
|
import "../ens/PublicResolver.sol";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
|
||||||
|
* @notice Registers usernames as ENS subnodes of the domain `ensNode`
|
||||||
|
*/
|
||||||
|
contract UsernameRegistrar is Controlled, ApproveAndCallFallBack {
|
||||||
|
|
||||||
|
ERC20Token public token;
|
||||||
|
ENS public ensRegistry;
|
||||||
|
PublicResolver public resolver;
|
||||||
|
address public parentRegistry;
|
||||||
|
|
||||||
|
uint256 public releaseDelay = 365 days;
|
||||||
|
mapping (bytes32 => Account) public accounts;
|
||||||
|
|
||||||
|
//slashing conditions
|
||||||
|
uint256 public usernameMinLenght;
|
||||||
|
bytes32 public reservedUsernamesMerkleRoot;
|
||||||
|
|
||||||
|
event RegistryPrice(uint256 price);
|
||||||
|
event RegistryMoved(address newRegistry);
|
||||||
|
event UsernameOwner(bytes32 indexed nameHash, address owner);
|
||||||
|
|
||||||
|
enum RegistrarState { Unactive, Active, Moved }
|
||||||
|
bytes32 public ensNode;
|
||||||
|
uint256 public price;
|
||||||
|
RegistrarState public state;
|
||||||
|
uint256 private reserveAmount;
|
||||||
|
|
||||||
|
struct Account {
|
||||||
|
uint256 balance;
|
||||||
|
uint256 creationTime;
|
||||||
|
address owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
modifier onlyParentRegistry {
|
||||||
|
require(msg.sender == parentRegistry, "Migration only.");
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Initializes a UserRegistry contract
|
||||||
|
* @param _token fee token base
|
||||||
|
* @param _ensRegistry Ethereum Name Service root address
|
||||||
|
* @param _resolver Default resolver to use in initial settings
|
||||||
|
* @param _ensNode ENS node (registry) being used for usernames subnodes (subregistry)
|
||||||
|
* @param _usernameMinLenght Minimum length of usernames
|
||||||
|
* @param _reservedUsernamesMerkleRoot Merkle Roots of reserved usernames
|
||||||
|
* @param _parentRegistry Address of old registry (if any) for account migration.
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
ERC20Token _token,
|
||||||
|
ENS _ensRegistry,
|
||||||
|
PublicResolver _resolver,
|
||||||
|
bytes32 _ensNode,
|
||||||
|
uint256 _usernameMinLenght,
|
||||||
|
bytes32 _reservedUsernamesMerkleRoot,
|
||||||
|
address _parentRegistry
|
||||||
|
)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
token = _token;
|
||||||
|
ensRegistry = _ensRegistry;
|
||||||
|
resolver = _resolver;
|
||||||
|
ensNode = _ensNode;
|
||||||
|
usernameMinLenght = _usernameMinLenght;
|
||||||
|
reservedUsernamesMerkleRoot = _reservedUsernamesMerkleRoot;
|
||||||
|
parentRegistry = _parentRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Registers `_label` username to `ensNode` setting msg.sender as owner.
|
||||||
|
* Terms of name registration:
|
||||||
|
* - SNT is deposited, not spent; the amount is locked up for 1 year.
|
||||||
|
* - After 1 year, the user can release the name and receive their deposit back (at any time).
|
||||||
|
* - User deposits are completely protected. The contract controller cannot access them.
|
||||||
|
* - User's address(es) will be publicly associated with the ENS name.
|
||||||
|
* - User must authorise the contract to transfer `price` `token.name()` on their behalf.
|
||||||
|
* - Usernames registered with less then `usernameMinLenght` characters can be slashed.
|
||||||
|
* - Usernames contained in the merkle tree of root `reservedUsernamesMerkleRoot` can be slashed.
|
||||||
|
* - Usernames starting with `0x` and bigger then 12 characters can be slashed.
|
||||||
|
* - If terms of the contract change—e.g. Status makes contract upgrades—the user has the right to release the username and get their deposit back.
|
||||||
|
* @param _label choosen unowned username hash
|
||||||
|
* @param _account optional address to set at public resolver
|
||||||
|
* @param _pubkeyA optional pubkey part A to set at public resolver
|
||||||
|
* @param _pubkeyB optional pubkey part B to set at public resolver
|
||||||
|
*/
|
||||||
|
function register(
|
||||||
|
bytes32 _label,
|
||||||
|
address _account,
|
||||||
|
bytes32 _pubkeyA,
|
||||||
|
bytes32 _pubkeyB
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns(bytes32 namehash)
|
||||||
|
{
|
||||||
|
return registerUser(msg.sender, _label, _account, _pubkeyA, _pubkeyB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice release username and retrieve locked fee, needs to be called after `releasePeriod` from creation time.
|
||||||
|
* @param _label `msg.sender` owned username hash
|
||||||
|
*/
|
||||||
|
function release(
|
||||||
|
bytes32 _label
|
||||||
|
)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
bytes32 namehash = keccak256(abi.encodePacked(ensNode, _label));
|
||||||
|
Account memory account = accounts[_label];
|
||||||
|
require(account.creationTime > 0, "Username not registered.");
|
||||||
|
if (state == RegistrarState.Active) {
|
||||||
|
require(msg.sender == ensRegistry.owner(namehash), "Not owner of ENS node.");
|
||||||
|
require(block.timestamp > account.creationTime + releaseDelay, "Release period not reached.");
|
||||||
|
ensRegistry.setSubnodeOwner(ensNode, _label, address(this));
|
||||||
|
ensRegistry.setResolver(namehash, address(0));
|
||||||
|
ensRegistry.setOwner(namehash, address(0));
|
||||||
|
} else {
|
||||||
|
require(msg.sender == account.owner, "Not the former account owner.");
|
||||||
|
address newOwner = ensRegistry.owner(ensNode);
|
||||||
|
//low level call, case dropUsername not implemented or failing, proceed release.
|
||||||
|
newOwner.call(
|
||||||
|
abi.encodeWithSignature(
|
||||||
|
"dropUsername(bytes32)",
|
||||||
|
_label
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
delete accounts[_label];
|
||||||
|
if (account.balance > 0) {
|
||||||
|
reserveAmount -= account.balance;
|
||||||
|
require(token.transfer(msg.sender, account.balance), "Transfer failed");
|
||||||
|
}
|
||||||
|
emit UsernameOwner(_label, address(0));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice updates funds owner, useful to move username account to new registry.
|
||||||
|
* @param _label `msg.sender` owned username hash
|
||||||
|
**/
|
||||||
|
function updateAccountOwner(
|
||||||
|
bytes32 _label
|
||||||
|
)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
bytes32 namehash = keccak256(abi.encodePacked(ensNode, _label));
|
||||||
|
require(msg.sender == ensRegistry.owner(namehash), "Caller not owner of ENS node.");
|
||||||
|
require(accounts[_label].creationTime > 0, "Username not registered.");
|
||||||
|
require(ensRegistry.owner(ensNode) == address(this), "Registry not owner of registry.");
|
||||||
|
accounts[_label].owner = msg.sender;
|
||||||
|
emit UsernameOwner(namehash, msg.sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice slash account due too length restriction
|
||||||
|
* @param _username raw value of offending username
|
||||||
|
*/
|
||||||
|
function slashSmallUsername(
|
||||||
|
bytes _username
|
||||||
|
)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
require(_username.length < usernameMinLenght, "Not a small username.");
|
||||||
|
slashUsername(_username);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice slash account due look like an address
|
||||||
|
* @param _username raw value of offending username
|
||||||
|
*/
|
||||||
|
function slashAddressLikeUsername(
|
||||||
|
string _username
|
||||||
|
)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
bytes memory username = bytes(_username);
|
||||||
|
require(username.length > 12, "Too small to look like an address.");
|
||||||
|
require(username[0] == byte("0"), "First character need to be 0");
|
||||||
|
require(username[1] == byte("x"), "Second character need to be x");
|
||||||
|
slashUsername(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice slash account due reserved name
|
||||||
|
* @param _username raw value of offending username
|
||||||
|
*/
|
||||||
|
function slashReservedUsername(
|
||||||
|
bytes _username,
|
||||||
|
bytes32[] _proof
|
||||||
|
)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
require(
|
||||||
|
MerkleProof.verifyProof(
|
||||||
|
_proof,
|
||||||
|
reservedUsernamesMerkleRoot,
|
||||||
|
keccak256(_username)
|
||||||
|
),
|
||||||
|
"Invalid Proof."
|
||||||
|
);
|
||||||
|
slashUsername(_username);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice slash account of invalid username
|
||||||
|
* @param _username raw value of offending username
|
||||||
|
* @param _offendingPos position of invalid character
|
||||||
|
*/
|
||||||
|
function slashInvalidUsername(
|
||||||
|
bytes _username,
|
||||||
|
uint256 _offendingPos
|
||||||
|
)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
require(_username.length > _offendingPos, "Invalid position.");
|
||||||
|
byte b = _username[_offendingPos];
|
||||||
|
|
||||||
|
require(!((b >= 48 && b <= 57) || (b >= 97 && b <= 122)), "Not invalid character.");
|
||||||
|
|
||||||
|
slashUsername(_username);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Migrate account to new registry
|
||||||
|
* @param _label `msg.sender` owned username hash
|
||||||
|
**/
|
||||||
|
function moveAccount(
|
||||||
|
bytes32 _label
|
||||||
|
)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
require(msg.sender == accounts[_label].owner, "Callable only by account owner.");
|
||||||
|
UsernameRegistrar _newRegistry = UsernameRegistrar(ensRegistry.owner(ensNode));
|
||||||
|
Account memory account = accounts[_label];
|
||||||
|
delete accounts[_label];
|
||||||
|
|
||||||
|
token.approve(_newRegistry, account.balance);
|
||||||
|
_newRegistry.migrateUsername(
|
||||||
|
_label,
|
||||||
|
account.balance,
|
||||||
|
account.creationTime,
|
||||||
|
account.owner
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev callabe only by parent registry to continue migration of ENSSubdomainRegistry
|
||||||
|
* @param _domainHash needs to be this contract ensNode
|
||||||
|
**/
|
||||||
|
function migrateDomain(
|
||||||
|
uint256 _price,
|
||||||
|
bytes32 _domainHash
|
||||||
|
)
|
||||||
|
external
|
||||||
|
onlyParentRegistry
|
||||||
|
{
|
||||||
|
require(_domainHash == ensNode, "Wrong Registry");
|
||||||
|
migrateRegistry(_price);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Activate registry
|
||||||
|
* @param _price The price of username registry
|
||||||
|
*/
|
||||||
|
function activate(
|
||||||
|
uint256 _price
|
||||||
|
)
|
||||||
|
external
|
||||||
|
onlyController
|
||||||
|
{
|
||||||
|
require(state == RegistrarState.Unactive, "Registry state is not unactive");
|
||||||
|
require(ensRegistry.owner(ensNode) == address(this), "Registry does not own registry");
|
||||||
|
price = _price;
|
||||||
|
state = RegistrarState.Active;
|
||||||
|
emit RegistryPrice(_price);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice updates default public resolver for newly registred usernames
|
||||||
|
* @param _resolver new default resolver
|
||||||
|
*/
|
||||||
|
function setResolver(
|
||||||
|
address _resolver
|
||||||
|
)
|
||||||
|
external
|
||||||
|
onlyController
|
||||||
|
{
|
||||||
|
resolver = PublicResolver(_resolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice updates registry price
|
||||||
|
* @param _price new price
|
||||||
|
*/
|
||||||
|
function updateRegistryPrice(
|
||||||
|
uint256 _price
|
||||||
|
)
|
||||||
|
external
|
||||||
|
onlyController
|
||||||
|
{
|
||||||
|
require(state == RegistrarState.Active, "Registry not owned");
|
||||||
|
price = _price;
|
||||||
|
emit RegistryPrice(_price);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice moves a registry to other Registry (will not move usernames accounts)
|
||||||
|
* @param _newRegistry new registry hodling this registry
|
||||||
|
*/
|
||||||
|
function moveRegistry(
|
||||||
|
UsernameRegistrar _newRegistry
|
||||||
|
)
|
||||||
|
external
|
||||||
|
onlyController
|
||||||
|
{
|
||||||
|
require(state == RegistrarState.Active, "Wrong registry");
|
||||||
|
require(ensRegistry.owner(ensNode) == address(this), "Registry not owned anymore.");
|
||||||
|
state = RegistrarState.Moved;
|
||||||
|
ensRegistry.setOwner(ensNode, _newRegistry);
|
||||||
|
_newRegistry.migrateRegistry(price);
|
||||||
|
emit RegistryMoved(_newRegistry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev callable only by parent registry for continue user opt-in migration
|
||||||
|
* @param _domainHash needs to be this contract ensNode
|
||||||
|
**/
|
||||||
|
function migrateAccount(
|
||||||
|
bytes32 _userHash,
|
||||||
|
bytes32 _domainHash,
|
||||||
|
uint256 _tokenBalance,
|
||||||
|
uint256 _creationTime,
|
||||||
|
address _accountOwner
|
||||||
|
)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
require(_domainHash == ensNode, "Wrong Registry");
|
||||||
|
migrateUsername(_userHash, _tokenBalance, _creationTime, _accountOwner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev clears ens registry. Callable only by parant registry for continue user opt-out migration
|
||||||
|
* @param _label any username hash coming from parent
|
||||||
|
*/
|
||||||
|
function dropUsername(
|
||||||
|
bytes32 _label
|
||||||
|
)
|
||||||
|
external
|
||||||
|
onlyParentRegistry
|
||||||
|
{
|
||||||
|
require(accounts[_label].creationTime == 0, "Already migrated");
|
||||||
|
bytes32 namehash = keccak256(abi.encodePacked(ensNode, _label));
|
||||||
|
ensRegistry.setSubnodeOwner(ensNode, _label, address(this));
|
||||||
|
ensRegistry.setResolver(namehash, address(0));
|
||||||
|
ensRegistry.setOwner(namehash, address(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice withdraw tokens forced into the contract
|
||||||
|
* @param _token address of ERC20 withdrawing excess, or address(0) if want ETH/
|
||||||
|
* @param _beneficiary who gets the funds
|
||||||
|
**/
|
||||||
|
function withdrawExcessBalance(
|
||||||
|
address _token,
|
||||||
|
address _beneficiary
|
||||||
|
)
|
||||||
|
external
|
||||||
|
onlyController
|
||||||
|
{
|
||||||
|
require(_beneficiary != address(0), "Cannot burn token");
|
||||||
|
if (_token == address(0)) {
|
||||||
|
_beneficiary.transfer(address(this).balance);
|
||||||
|
} else {
|
||||||
|
ERC20Token excessToken = ERC20Token(_token);
|
||||||
|
uint256 amount = excessToken.balanceOf(address(this));
|
||||||
|
if(_token == address(token)){
|
||||||
|
require(amount > reserveAmount, "Is not excess");
|
||||||
|
amount -= reserveAmount;
|
||||||
|
} else {
|
||||||
|
require(amount > 0, "Is not excess");
|
||||||
|
}
|
||||||
|
excessToken.transfer(_beneficiary, amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice withdraw ens nodes not belonging to this contract
|
||||||
|
* @param _domainHash ens node namehash
|
||||||
|
* @param _beneficiary new owner of ens node
|
||||||
|
**/
|
||||||
|
function withdrawWrongNode(
|
||||||
|
bytes32 _domainHash,
|
||||||
|
address _beneficiary
|
||||||
|
)
|
||||||
|
external
|
||||||
|
onlyController
|
||||||
|
{
|
||||||
|
require(_beneficiary != address(0), "Cannot burn node");
|
||||||
|
require(_domainHash != ensNode, "Cannot withdraw main node");
|
||||||
|
require(ensRegistry.owner(_domainHash) == address(this), "Not owner of this node");
|
||||||
|
ensRegistry.setOwner(_domainHash, _beneficiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice gets registrar price
|
||||||
|
* @return registry price
|
||||||
|
**/
|
||||||
|
function getPrice()
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns(uint256 registryPrice)
|
||||||
|
{
|
||||||
|
return price;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice reads amount tokens locked in username
|
||||||
|
* @param _label hash of username
|
||||||
|
* @return locked username balance
|
||||||
|
**/
|
||||||
|
function getAccountBalance(bytes32 _label)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns(uint256 accountBalance)
|
||||||
|
{
|
||||||
|
accountBalance = accounts[_label].balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice reads username owner at this contract,
|
||||||
|
* which can release or migrate in case of upgrade
|
||||||
|
* @param _label hash of username
|
||||||
|
* @return username owner
|
||||||
|
**/
|
||||||
|
function getAccountOwner(bytes32 _label)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns(address owner)
|
||||||
|
{
|
||||||
|
owner = accounts[_label].owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice reads when the account was registered
|
||||||
|
* @param _label hash of username
|
||||||
|
* @return registration time
|
||||||
|
**/
|
||||||
|
function getCreationTime(bytes32 _label)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns(uint256 creationTime)
|
||||||
|
{
|
||||||
|
creationTime = accounts[_label].creationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice calculate time where username can be released
|
||||||
|
* @param _label hash of username
|
||||||
|
* @return exact time when username can be released
|
||||||
|
**/
|
||||||
|
function getExpirationTime(bytes32 _label)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns(uint256 expirationTime)
|
||||||
|
{
|
||||||
|
expirationTime = accounts[_label].creationTime + releaseDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Receive approval, callable only by `token()`.
|
||||||
|
* @param _from who is approving
|
||||||
|
* @param _amount amount being approved, need to be equal `getPrice()`
|
||||||
|
* @param _token token being approved, need to be equal `token()`
|
||||||
|
* @param _data abi encoded data with selector of `register(bytes32,address,bytes32,bytes32)`
|
||||||
|
*/
|
||||||
|
function receiveApproval(
|
||||||
|
address _from,
|
||||||
|
uint256 _amount,
|
||||||
|
address _token,
|
||||||
|
bytes _data
|
||||||
|
)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
require(_amount == price, "Wrong value");
|
||||||
|
require(_token == address(token), "Wrong token");
|
||||||
|
require(_token == address(msg.sender), "Wrong call");
|
||||||
|
require(_data.length <= 132, "Wrong data length");
|
||||||
|
bytes4 sig;
|
||||||
|
bytes32 label;
|
||||||
|
address account;
|
||||||
|
bytes32 pubkeyA;
|
||||||
|
bytes32 pubkeyB;
|
||||||
|
(sig, label, account, pubkeyA, pubkeyB) = abiDecodeRegister(_data);
|
||||||
|
require(
|
||||||
|
sig == bytes4(0xb82fedbb), //bytes4(keccak256("register(bytes32,address,bytes32,bytes32)"))
|
||||||
|
"Wrong method selector"
|
||||||
|
);
|
||||||
|
registerUser(_from, label, account, pubkeyA, pubkeyB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev decodes abi encoded data with selector for "register(bytes32,address,bytes32,bytes32)"
|
||||||
|
* @param _data abi encoded data
|
||||||
|
*/
|
||||||
|
function abiDecodeRegister(
|
||||||
|
bytes _data
|
||||||
|
)
|
||||||
|
private
|
||||||
|
pure
|
||||||
|
returns(
|
||||||
|
bytes4 sig,
|
||||||
|
bytes32 label,
|
||||||
|
address account,
|
||||||
|
bytes32 pubkeyA,
|
||||||
|
bytes32 pubkeyB
|
||||||
|
)
|
||||||
|
{
|
||||||
|
assembly {
|
||||||
|
sig := mload(add(_data, add(0x20, 0)))
|
||||||
|
label := mload(add(_data, 36))
|
||||||
|
account := mload(add(_data, 68))
|
||||||
|
pubkeyA := mload(add(_data, 100))
|
||||||
|
pubkeyB := mload(add(_data, 132))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @dev callable only by parent registry for continue user opt-in migration
|
||||||
|
* @param _label any username hash coming from parent
|
||||||
|
* @param _tokenBalance amount being transferred
|
||||||
|
* @param _creationTime any value coming from parent
|
||||||
|
* @param _accountOwner owner for opt-out/release at registry move
|
||||||
|
**/
|
||||||
|
function migrateUsername(
|
||||||
|
bytes32 _label,
|
||||||
|
uint256 _tokenBalance,
|
||||||
|
uint256 _creationTime,
|
||||||
|
address _accountOwner
|
||||||
|
)
|
||||||
|
public
|
||||||
|
onlyParentRegistry
|
||||||
|
{
|
||||||
|
if (_tokenBalance > 0) {
|
||||||
|
require(
|
||||||
|
token.transferFrom(
|
||||||
|
parentRegistry,
|
||||||
|
address(this),
|
||||||
|
_tokenBalance
|
||||||
|
),
|
||||||
|
"Error moving funds from old registar."
|
||||||
|
);
|
||||||
|
reserveAmount += _tokenBalance;
|
||||||
|
}
|
||||||
|
accounts[_label] = Account(_tokenBalance, _creationTime, _accountOwner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev callabe only by parent registry to continue migration
|
||||||
|
* of registry and activate registrar
|
||||||
|
* @param _price the price to setup and activate domain
|
||||||
|
**/
|
||||||
|
function migrateRegistry(
|
||||||
|
uint256 _price
|
||||||
|
)
|
||||||
|
public
|
||||||
|
onlyParentRegistry
|
||||||
|
{
|
||||||
|
require(state == RegistrarState.Unactive, "Not unactive");
|
||||||
|
require(ensRegistry.owner(ensNode) == address(this), "ENS registry owner not transfered.");
|
||||||
|
price = _price;
|
||||||
|
state = RegistrarState.Active;
|
||||||
|
emit RegistryPrice(_price);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Registers `_label` username to `ensNode` setting msg.sender as owner.
|
||||||
|
* @param _owner address registering the user and paying registry price.
|
||||||
|
* @param _label choosen unowned username hash
|
||||||
|
* @param _account optional address to set at public resolver
|
||||||
|
* @param _pubkeyA optional pubkey part A to set at public resolver
|
||||||
|
* @param _pubkeyB optional pubkey part B to set at public resolver
|
||||||
|
*/
|
||||||
|
function registerUser(
|
||||||
|
address _owner,
|
||||||
|
bytes32 _label,
|
||||||
|
address _account,
|
||||||
|
bytes32 _pubkeyA,
|
||||||
|
bytes32 _pubkeyB
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
returns(bytes32 namehash)
|
||||||
|
{
|
||||||
|
require(state == RegistrarState.Active, "Registry unavailable.");
|
||||||
|
namehash = keccak256(abi.encodePacked(ensNode, _label));
|
||||||
|
require(ensRegistry.owner(namehash) == address(0), "ENS node already owned.");
|
||||||
|
require(accounts[_label].creationTime == 0, "Username already registered.");
|
||||||
|
accounts[_label] = Account(price, block.timestamp, _owner);
|
||||||
|
if(price > 0) {
|
||||||
|
require(token.allowance(_owner, address(this)) >= price, "Unallowed to spend.");
|
||||||
|
require(
|
||||||
|
token.transferFrom(
|
||||||
|
_owner,
|
||||||
|
address(this),
|
||||||
|
price
|
||||||
|
),
|
||||||
|
"Transfer failed"
|
||||||
|
);
|
||||||
|
reserveAmount += price;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool resolvePubkey = _pubkeyA != 0 || _pubkeyB != 0;
|
||||||
|
bool resolveAccount = _account != address(0);
|
||||||
|
if (resolvePubkey || resolveAccount) {
|
||||||
|
//set to self the ownship to setup initial resolver
|
||||||
|
ensRegistry.setSubnodeOwner(ensNode, _label, address(this));
|
||||||
|
ensRegistry.setResolver(namehash, resolver); //default resolver
|
||||||
|
if (resolveAccount) {
|
||||||
|
resolver.setAddr(namehash, _account);
|
||||||
|
}
|
||||||
|
if (resolvePubkey) {
|
||||||
|
resolver.setPubkey(namehash, _pubkeyA, _pubkeyB);
|
||||||
|
}
|
||||||
|
ensRegistry.setOwner(namehash, _owner);
|
||||||
|
} else {
|
||||||
|
//transfer ownship of subdone directly to registrant
|
||||||
|
ensRegistry.setSubnodeOwner(ensNode, _label, _owner);
|
||||||
|
}
|
||||||
|
emit UsernameOwner(namehash, _owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev removes account hash of `_username` and send account.balance to msg.sender
|
||||||
|
* @param _username username being slashed
|
||||||
|
*/
|
||||||
|
function slashUsername(bytes _username) internal {
|
||||||
|
bytes32 label = keccak256(_username);
|
||||||
|
bytes32 namehash = keccak256(abi.encodePacked(ensNode, label));
|
||||||
|
require(accounts[label].creationTime > 0, "Username not registered.");
|
||||||
|
|
||||||
|
ensRegistry.setSubnodeOwner(ensNode, label, address(this));
|
||||||
|
ensRegistry.setResolver(namehash, address(0));
|
||||||
|
ensRegistry.setOwner(namehash, address(0));
|
||||||
|
|
||||||
|
uint256 amountToTransfer = accounts[label].balance;
|
||||||
|
delete accounts[label];
|
||||||
|
if (amountToTransfer > 0) {
|
||||||
|
reserveAmount -= amountToTransfer;
|
||||||
|
require(token.transfer(msg.sender, amountToTransfer), "Error in transfer.");
|
||||||
|
}
|
||||||
|
emit UsernameOwner(namehash, address(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +0,0 @@
|
||||||
pragma solidity ^0.4.11;
|
|
||||||
|
|
||||||
contract ApprovalReceiver {
|
|
||||||
function receiveApproval(address from, uint value, address tokenContract, bytes extraData) returns (bool);
|
|
||||||
}
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
pragma solidity ^0.4.11;
|
||||||
|
|
||||||
|
contract ApproveAndCallFallBack {
|
||||||
|
function receiveApproval(address from, uint256 _amount, address _token, bytes _data) public;
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
pragma solidity ^0.4.23;
|
pragma solidity ^0.4.24;
|
||||||
|
|
||||||
import "./ERC20Token.sol";
|
import "./ERC20Token.sol";
|
||||||
|
|
||||||
|
@ -20,22 +20,14 @@ contract StandardToken is ERC20Token {
|
||||||
return transfer(msg.sender, _to, _value);
|
return transfer(msg.sender, _to, _value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function approve(
|
||||||
* @dev Aprove the passed address to spend the specified amount of tokens on behalf of msg.sender.
|
address _to,
|
||||||
* @param _spender The address which will spend the funds.
|
uint256 _value
|
||||||
* @param _value The amount of tokens to be spent.
|
)
|
||||||
*/
|
external
|
||||||
function approve(address _spender, uint256 _value) returns (bool) {
|
returns (bool success)
|
||||||
|
{
|
||||||
// To change the approve amount you first have to reduce the addresses`
|
return approve(msg.sender, _to, _value);
|
||||||
// allowance to zero by calling `approve(_spender, 0)` if it is not
|
|
||||||
// already 0 to mitigate the race condition described here:
|
|
||||||
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
|
|
||||||
require((_value == 0) || (allowed[msg.sender][_spender] == 0));
|
|
||||||
|
|
||||||
allowed[msg.sender][_spender] = _value;
|
|
||||||
Approval(msg.sender, _spender, _value);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function transferFrom(
|
function transferFrom(
|
||||||
|
@ -80,6 +72,25 @@ contract StandardToken is ERC20Token {
|
||||||
return supply;
|
return supply;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Aprove the passed address to spend the specified amount of tokens on behalf of msg.sender.
|
||||||
|
* @param _from The address that is approving the spend
|
||||||
|
* @param _spender The address which will spend the funds.
|
||||||
|
* @param _value The amount of tokens to be spent.
|
||||||
|
*/
|
||||||
|
function approve(address _from, address _spender, uint256 _value) internal returns (bool) {
|
||||||
|
|
||||||
|
// To change the approve amount you first have to reduce the addresses`
|
||||||
|
// allowance to zero by calling `approve(_spender, 0)` if it is not
|
||||||
|
// already 0 to mitigate the race condition described here:
|
||||||
|
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
|
||||||
|
require((_value == 0) || (allowed[_from][_spender] == 0), "Bad usage");
|
||||||
|
|
||||||
|
allowed[_from][_spender] = _value;
|
||||||
|
emit Approval(_from, _spender, _value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function mint(
|
function mint(
|
||||||
address _to,
|
address _to,
|
||||||
uint256 _amount
|
uint256 _amount
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
pragma solidity ^0.4.23;
|
pragma solidity ^0.4.24;
|
||||||
|
|
||||||
import "./StandardToken.sol";
|
import "./StandardToken.sol";
|
||||||
import './ApprovalReceiver.sol';
|
import "./ApproveAndCallFallBack.sol";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @notice ERC20Token for test scripts, can be minted by anyone.
|
* @notice ERC20Token for test scripts, can be minted by anyone.
|
||||||
|
@ -18,9 +18,13 @@ contract TestToken is StandardToken {
|
||||||
mint(msg.sender, _amount);
|
mint(msg.sender, _amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) {
|
function approveAndCall(address _spender, uint256 _value, bytes _extraData)
|
||||||
assert(approve(_spender, _value));
|
external
|
||||||
return ApprovalReceiver(_spender).receiveApproval(msg.sender, _value, this, _extraData);
|
returns (bool success)
|
||||||
|
{
|
||||||
|
approve(msg.sender, _spender, _value);
|
||||||
|
ApproveAndCallFallBack(_spender).receiveApproval(msg.sender, _value, this, _extraData);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/status-im/contracts#readme",
|
"homepage": "https://github.com/status-im/contracts#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@babel/runtime": "7.0.0-beta.55",
|
||||||
"@material-ui/core": "^1.2.1",
|
"@material-ui/core": "^1.2.1",
|
||||||
"@material-ui/icons": "^1.1.0",
|
"@material-ui/icons": "^1.1.0",
|
||||||
"bignumber.js": "^5.0.0",
|
"bignumber.js": "^5.0.0",
|
||||||
|
|
|
@ -1,723 +0,0 @@
|
||||||
const utils = require('../utils/testUtils.js');
|
|
||||||
const web3Utils = require('web3-utils');
|
|
||||||
const namehash = require('eth-ens-namehash');
|
|
||||||
const TestToken = require('Embark/contracts/TestToken');
|
|
||||||
const ENSRegistry = require('Embark/contracts/ENSRegistry');
|
|
||||||
const PublicResolver = require('Embark/contracts/PublicResolver');
|
|
||||||
const ENSSubdomainRegistry = require('Embark/contracts/ENSSubdomainRegistry');
|
|
||||||
const { MerkleTree } = require('../utils/merkleTree.js');
|
|
||||||
|
|
||||||
const domains = {
|
|
||||||
free : {
|
|
||||||
name: 'freedomain.eth',
|
|
||||||
price: 0,
|
|
||||||
namehash: namehash.hash('freedomain.eth')
|
|
||||||
},
|
|
||||||
paid : {
|
|
||||||
name: 'stateofus.eth',
|
|
||||||
price: 100000000,
|
|
||||||
namehash: namehash.hash('stateofus.eth')
|
|
||||||
},
|
|
||||||
temp : {
|
|
||||||
name: 'temporary.eth',
|
|
||||||
price: 100000000,
|
|
||||||
namehash: namehash.hash('temporary.eth')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const reservedNames = [
|
|
||||||
'administrator',
|
|
||||||
'support',
|
|
||||||
'status',
|
|
||||||
'network',
|
|
||||||
]
|
|
||||||
|
|
||||||
// TODO: load file of reserved names and balance array lenght to be even
|
|
||||||
|
|
||||||
const merkleTree = new MerkleTree(reservedNames);
|
|
||||||
const merkleRoot = merkleTree.getHexRoot();
|
|
||||||
|
|
||||||
var contractsConfig = {
|
|
||||||
"TestToken": {
|
|
||||||
|
|
||||||
},
|
|
||||||
"ENSRegistry": {
|
|
||||||
"onDeploy": [
|
|
||||||
"ENSRegistry.methods.setSubnodeOwner('0x0000000000000000000000000000000000000000000000000000000000000000', '0x4f5b812789fc606be1b3b16908db13fc7a9adf7ca72641f84d75b47069d3d7f0', web3.eth.defaultAccount).send()"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"PublicResolver": {
|
|
||||||
"args": [
|
|
||||||
"$ENSRegistry"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ENSSubdomainRegistry": {
|
|
||||||
"args": [
|
|
||||||
"$TestToken",
|
|
||||||
"$ENSRegistry",
|
|
||||||
"$PublicResolver",
|
|
||||||
"3",
|
|
||||||
[merkleRoot],
|
|
||||||
"0x0"
|
|
||||||
],
|
|
||||||
"onDeploy": [
|
|
||||||
"ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '0xbd99f8d5e7f81d2d7c1da34b67a2bb3a94dd8c9b0ab40ddc077621b98405983b', ENSSubdomainRegistry.address).send()",
|
|
||||||
"ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '0x7b4768a525e733422bf968587a91b4036e5176d36f44a9fb5b29d0bca03ab3a3', ENSSubdomainRegistry.address).send()",
|
|
||||||
"ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '0x44fa953dda0aa9a41a27e14a13ed7e08a0d7fc72873516f5b3bf9b50718610df', ENSSubdomainRegistry.address).send()"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"UpdatedENSSubdomainRegistry": {
|
|
||||||
"instanceOf" : "ENSSubdomainRegistry",
|
|
||||||
"args": [
|
|
||||||
"$TestToken",
|
|
||||||
"$ENSRegistry",
|
|
||||||
"$PublicResolver",
|
|
||||||
"3",
|
|
||||||
[merkleRoot],
|
|
||||||
"$ENSSubdomainRegistry"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
config({ contracts: contractsConfig });
|
|
||||||
|
|
||||||
contract('ENSSubdomainRegistry', function () {
|
|
||||||
let ens;
|
|
||||||
let accountsArr;
|
|
||||||
|
|
||||||
before(function(done) {
|
|
||||||
web3.eth.getAccounts().then(async (accounts) => {
|
|
||||||
ens = ENSRegistry;
|
|
||||||
accountsArr = accounts;
|
|
||||||
await utils.increaseTime(1 * utils.timeUnits.days) //time cannot start zero
|
|
||||||
await utils.increaseTime(1000)
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('setDomainPrice()', function() {
|
|
||||||
it('should add free domain', async () => {
|
|
||||||
const domain = domains.free;
|
|
||||||
const resultSetDomainPrice = await ENSSubdomainRegistry.methods.setDomainPrice(domain.namehash, domain.price).send({from: accountsArr[0]});
|
|
||||||
assert.equal(resultSetDomainPrice.events.DomainPrice.returnValues.price, domain.price, "event DomainPrice wrong price");
|
|
||||||
assert.equal(resultSetDomainPrice.events.DomainPrice.returnValues.namehash, domain.namehash, "event DomainPrice wrong namehash");
|
|
||||||
assert.equal(await ENSSubdomainRegistry.methods.getPrice(domain.namehash).call(), domain.price, "getPrice() wrong price");
|
|
||||||
const resultDomainAccount = await ENSSubdomainRegistry.methods.domains(domain.namehash).call()
|
|
||||||
assert.equal(resultDomainAccount.state, 1, "Wrong domain state")
|
|
||||||
assert.equal(resultDomainAccount.price, domain.price, "Wrong domain price")
|
|
||||||
});
|
|
||||||
it('should add paid domain', async () => {
|
|
||||||
const initialPrice = 100
|
|
||||||
const domain = domains.paid;
|
|
||||||
const resultSetDomainPrice = await ENSSubdomainRegistry.methods.setDomainPrice(domain.namehash, initialPrice).send({from: accountsArr[0]});
|
|
||||||
assert.equal(resultSetDomainPrice.events.DomainPrice.returnValues.price, initialPrice, "event DomainPrice wrong price");
|
|
||||||
assert.equal(resultSetDomainPrice.events.DomainPrice.returnValues.namehash, domain.namehash, "event DomainPrice wrong namehash");
|
|
||||||
assert.equal(await ENSSubdomainRegistry.methods.getPrice(domain.namehash).call(), initialPrice, "getPrice() wrong price");
|
|
||||||
const resultDomainAccount = await ENSSubdomainRegistry.methods.domains(domain.namehash).call()
|
|
||||||
assert.equal(resultDomainAccount.state, 1, "Wrong domain state")
|
|
||||||
assert.equal(resultDomainAccount.price, initialPrice, "Wrong domain price")
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('updateDomainPrice()', function() {
|
|
||||||
it('should change paid domain price', async () => {
|
|
||||||
const newPrice = domains.paid.price;
|
|
||||||
const resultUpdateDomainPrice = await ENSSubdomainRegistry.methods.updateDomainPrice(domains.paid.namehash, newPrice).send({from: accountsArr[0]});
|
|
||||||
assert.equal(resultUpdateDomainPrice.events.DomainPrice.returnValues.price, domains.paid.price, "event DomainPrice wrong price");
|
|
||||||
assert.equal(resultUpdateDomainPrice.events.DomainPrice.returnValues.namehash, domains.paid.namehash, "event DomainPrice wrong namehash");
|
|
||||||
assert.equal(await ENSSubdomainRegistry.methods.getPrice(domains.paid.namehash).call(), newPrice, "Wrong return value at getPrice");
|
|
||||||
const resultDomainAccount= await ENSSubdomainRegistry.methods.domains(domains.paid.namehash).call()
|
|
||||||
assert.equal(resultDomainAccount.state, 1, "Wrong domain state")
|
|
||||||
assert.equal(resultDomainAccount.price, newPrice, "Wrong domain price")
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('register()', function() {
|
|
||||||
it('should register free subdomain', async () => {
|
|
||||||
const subdomain = 'alice';
|
|
||||||
const subdomainHash = namehash.hash(subdomain + '.' + domains.free.name);
|
|
||||||
const registrant = accountsArr[1];
|
|
||||||
const label = web3Utils.sha3(subdomain);
|
|
||||||
const node = domains.free.namehash;
|
|
||||||
const resultRegister = await ENSSubdomainRegistry.methods.register(
|
|
||||||
label,
|
|
||||||
node,
|
|
||||||
utils.zeroAddress,
|
|
||||||
utils.zeroBytes32,
|
|
||||||
utils.zeroBytes32
|
|
||||||
).send({from: registrant});
|
|
||||||
assert.equal(resultRegister.events['0'].raw.topics[0], '0xce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e82', "Wrong Event");
|
|
||||||
assert.equal(resultRegister.events['0'].raw.topics[1], node, "Wrong Node");
|
|
||||||
assert.equal(resultRegister.events['0'].raw.topics[2], label, "Wrong Label");
|
|
||||||
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['0'].raw.data.substring(26)), registrant, "Wrong subnode owner");
|
|
||||||
assert.equal(resultRegister.events.SubdomainOwner.returnValues.accountOwner, registrant, "event SubdomainOwner accountOwner mismatch");
|
|
||||||
assert.equal(resultRegister.events.SubdomainOwner.returnValues.subdomainHash, subdomainHash, "event SubdomainOwner subdomainHash mismatch");
|
|
||||||
assert.equal(await ens.methods.owner(subdomainHash).call(), registrant, "ENSRegistry owner mismatch");
|
|
||||||
assert.equal(await ens.methods.resolver(subdomainHash).call(), utils.zeroAddress, "Resolver wrongly defined");
|
|
||||||
assert.equal(await ENSSubdomainRegistry.methods.getAccountBalance(subdomainHash).call(), 0, "Free domain accounts shouldn't have balance");
|
|
||||||
assert.equal(await ENSSubdomainRegistry.methods.getAccountOwner(subdomainHash).call(), registrant, "Account owner mismatch");
|
|
||||||
});
|
|
||||||
it('should register free address only resolver-defined subdomain', async () => {
|
|
||||||
const registrant = accountsArr[2];
|
|
||||||
const subdomain = 'bob';
|
|
||||||
const subdomainHash = namehash.hash(subdomain + '.' + domains.free.name);
|
|
||||||
const label = web3Utils.sha3(subdomain);
|
|
||||||
const node = domains.free.namehash;
|
|
||||||
const resultRegister = await ENSSubdomainRegistry.methods.register(
|
|
||||||
web3Utils.sha3(subdomain),
|
|
||||||
domains.free.namehash,
|
|
||||||
registrant,
|
|
||||||
utils.zeroBytes32,
|
|
||||||
utils.zeroBytes32
|
|
||||||
).send({from: registrant});
|
|
||||||
assert.equal(resultRegister.events['0'].raw.topics[0], '0xce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e82', "Wrong Event");
|
|
||||||
assert.equal(resultRegister.events['0'].raw.topics[1], node, "Wrong Node");
|
|
||||||
assert.equal(resultRegister.events['0'].raw.topics[2], label, "Wrong Label");
|
|
||||||
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['0'].raw.data.substring(26)), ENSSubdomainRegistry.address, "Wrong subnode owner");
|
|
||||||
assert.equal(resultRegister.events['1'].raw.topics[0], '0x335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0', "Wrong Event");
|
|
||||||
assert.equal(resultRegister.events['1'].raw.topics[1], subdomainHash, "Wrong Subdomain");
|
|
||||||
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['1'].raw.data.substring(26)), PublicResolver.address, "Wrong Resolver");
|
|
||||||
assert.equal(resultRegister.events['2'].raw.topics[0], '0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2', "Wrong Event");
|
|
||||||
assert.equal(resultRegister.events['2'].raw.topics[1], subdomainHash, "Wrong Subdomain");
|
|
||||||
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['2'].raw.data.substring(26)), registrant, "Wrong address to resolve");
|
|
||||||
assert.equal(resultRegister.events['3'].raw.topics[0], '0xd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266', "Wrong Event");
|
|
||||||
assert.equal(resultRegister.events['3'].raw.topics[1], subdomainHash, "Wrong Subdomain");
|
|
||||||
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['3'].raw.data.substring(26)), registrant, "Wrong node owner");
|
|
||||||
assert.equal(resultRegister.events.SubdomainOwner.returnValues.accountOwner, registrant, "event SubdomainOwner accountOwner mismatch");
|
|
||||||
assert.equal(resultRegister.events.SubdomainOwner.returnValues.subdomainHash, subdomainHash, "event SubdomainOwner subdomainHash mismatch");
|
|
||||||
assert.equal(await ens.methods.owner(subdomainHash).call(), registrant, "ENSRegistry owner mismatch");
|
|
||||||
assert.equal(await ens.methods.resolver(subdomainHash).call(), PublicResolver.address, "Resolver wrongly defined");
|
|
||||||
assert.equal(await ENSSubdomainRegistry.methods.getAccountBalance(subdomainHash).call(), 0, "Free domain accounts shouldn't have balance");
|
|
||||||
assert.equal(await ENSSubdomainRegistry.methods.getAccountOwner(subdomainHash).call(), registrant, "Account owner mismatch");
|
|
||||||
assert.equal(await PublicResolver.methods.addr(subdomainHash).call(), registrant, "Resolved address not set");
|
|
||||||
const resolverPubKey = await PublicResolver.methods.pubkey(subdomainHash).call();
|
|
||||||
assert.equal(resolverPubKey[0], utils.zeroBytes32 , "Unexpected resolved pubkey[0]");
|
|
||||||
assert.equal(resolverPubKey[1], utils.zeroBytes32 , "Unexpected resolved pubkey[1]");
|
|
||||||
});
|
|
||||||
it('should register free status contact code and address resolver-defined subdomain', async () => {
|
|
||||||
const registrant = accountsArr[2];
|
|
||||||
const subdomain = 'bob2';
|
|
||||||
const subdomainHash = namehash.hash(subdomain + '.' + domains.free.name);
|
|
||||||
const contactCode = '0x04dbb31252d9bddb4e4d362c7b9c80cba74732280737af97971f42ccbdc716f3f3efb1db366880e52d09b1bfd59842e833f3004088892b7d14b9ce9e957cea9a82';
|
|
||||||
const points = utils.generateXY(contactCode);
|
|
||||||
const label = web3Utils.sha3(subdomain);
|
|
||||||
const node = domains.free.namehash;
|
|
||||||
const resultRegister = await ENSSubdomainRegistry.methods.register(
|
|
||||||
label,
|
|
||||||
node,
|
|
||||||
registrant,
|
|
||||||
points.x,
|
|
||||||
points.y
|
|
||||||
).send({from: registrant});
|
|
||||||
assert.equal(resultRegister.events['0'].raw.topics[0], '0xce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e82', "Wrong Event");
|
|
||||||
assert.equal(resultRegister.events['0'].raw.topics[1], node, "Wrong Node");
|
|
||||||
assert.equal(resultRegister.events['0'].raw.topics[2], label, "Wrong Label");
|
|
||||||
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['0'].raw.data.substring(26)), ENSSubdomainRegistry.address, "Wrong subnode owner");
|
|
||||||
assert.equal(resultRegister.events['1'].raw.topics[0], '0x335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0', "Wrong Event");
|
|
||||||
assert.equal(resultRegister.events['1'].raw.topics[1], subdomainHash, "Wrong Subdomain");
|
|
||||||
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['1'].raw.data.substring(26)), await ENSSubdomainRegistry.methods.resolver().call(), "Wrong Resolver");
|
|
||||||
assert.equal(resultRegister.events['2'].raw.topics[0], '0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2', "Wrong Event");
|
|
||||||
assert.equal(resultRegister.events['2'].raw.topics[1], subdomainHash, "Wrong Subdomain");
|
|
||||||
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['2'].raw.data.substring(26)), registrant, "Wrong address to resolve");
|
|
||||||
assert.equal(resultRegister.events['3'].raw.topics[0], '0x1d6f5e03d3f63eb58751986629a5439baee5079ff04f345becb66e23eb154e46', "Wrong Event");
|
|
||||||
assert.equal(resultRegister.events['3'].raw.topics[1], subdomainHash, "Wrong Subdomain");
|
|
||||||
assert.equal(resultRegister.events['3'].raw.data, points.x.concat(points.y.substr(2)))
|
|
||||||
assert.equal(resultRegister.events['4'].raw.topics[0], '0xd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266', "Wrong Event");
|
|
||||||
assert.equal(resultRegister.events['4'].raw.topics[1], subdomainHash, "Wrong Subdomain");
|
|
||||||
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['4'].raw.data.substring(26)), registrant, "Wrong node owner");
|
|
||||||
assert.equal(resultRegister.events.SubdomainOwner.returnValues.accountOwner, registrant, "event SubdomainOwner accountOwner mismatch");
|
|
||||||
assert.equal(resultRegister.events.SubdomainOwner.returnValues.subdomainHash, subdomainHash, "event SubdomainOwner subdomainHash mismatch");
|
|
||||||
assert.equal(await ens.methods.owner(subdomainHash).call(), registrant, "ENSRegistry owner mismatch");
|
|
||||||
assert.equal(await ens.methods.resolver(subdomainHash).call(), PublicResolver.address, "Resolver wrongly defined");
|
|
||||||
assert.equal(await ENSSubdomainRegistry.methods.getAccountBalance(subdomainHash).call(), 0, "Free domain accounts shouldn't have balance");
|
|
||||||
assert.equal(await ENSSubdomainRegistry.methods.getAccountOwner(subdomainHash).call(), registrant, "Account owner mismatch");
|
|
||||||
assert.equal(await PublicResolver.methods.addr(subdomainHash).call(), registrant, "Resolved address not set");
|
|
||||||
const resolverPubKey = await PublicResolver.methods.pubkey(subdomainHash).call();
|
|
||||||
const pubKey = utils.keyFromXY(resolverPubKey[0], resolverPubKey[1]);
|
|
||||||
assert.equal(pubKey, contactCode, "pubKey does not match contract code");
|
|
||||||
});
|
|
||||||
it('should register free pubkey only resolver-defined subdomain', async () => {
|
|
||||||
const subdomain = 'carlos';
|
|
||||||
const registrant = accountsArr[3];
|
|
||||||
const subdomainHash = namehash.hash(subdomain + '.' + domains.free.name);
|
|
||||||
const contactCode = '0x04dbb31252d9bddb4e4d362c7b9c80cba74732280737af97971f42ccbdc716f3f3efb1db366880e52d09b1bfd59842e833f3004088892b7d14b9ce9e957cea9a82';
|
|
||||||
const points = utils.generateXY(contactCode);
|
|
||||||
const label = web3Utils.sha3(subdomain);
|
|
||||||
const node = domains.free.namehash;
|
|
||||||
const resultRegister = await ENSSubdomainRegistry.methods.register(
|
|
||||||
web3Utils.sha3(subdomain),
|
|
||||||
domains.free.namehash,
|
|
||||||
utils.zeroAddress,
|
|
||||||
points.x,
|
|
||||||
points.y
|
|
||||||
).send({from: registrant});
|
|
||||||
assert.equal(resultRegister.events['0'].raw.topics[0], '0xce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e82', "Wrong Event");
|
|
||||||
assert.equal(resultRegister.events['0'].raw.topics[1], node, "Wrong Node");
|
|
||||||
assert.equal(resultRegister.events['0'].raw.topics[2], label, "Wrong Label");
|
|
||||||
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['0'].raw.data.substring(26)), ENSSubdomainRegistry.address, "Wrong subnode owner");
|
|
||||||
assert.equal(resultRegister.events['1'].raw.topics[0], '0x335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0', "Wrong Event");
|
|
||||||
assert.equal(resultRegister.events['1'].raw.topics[1], subdomainHash, "Wrong Subdomain");
|
|
||||||
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['1'].raw.data.substring(26)), PublicResolver.address, "Wrong Resolver");
|
|
||||||
assert.equal(resultRegister.events['2'].raw.topics[0], '0x1d6f5e03d3f63eb58751986629a5439baee5079ff04f345becb66e23eb154e46', "Wrong Event");
|
|
||||||
assert.equal(resultRegister.events['2'].raw.topics[1], subdomainHash, "Wrong Subdomain");
|
|
||||||
assert.equal(resultRegister.events['2'].raw.data, points.x.concat(points.y.substr(2)))
|
|
||||||
assert.equal(resultRegister.events['3'].raw.topics[0], '0xd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266', "Wrong Event");
|
|
||||||
assert.equal(resultRegister.events['3'].raw.topics[1], subdomainHash, "Wrong Subdomain");
|
|
||||||
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['3'].raw.data.substring(26)), registrant, "Wrong node owner");
|
|
||||||
assert.equal(resultRegister.events.SubdomainOwner.returnValues.accountOwner, registrant, "event SubdomainOwner accountOwner mismatch");
|
|
||||||
assert.equal(resultRegister.events.SubdomainOwner.returnValues.subdomainHash, subdomainHash, "event SubdomainOwner subdomainHash mismatch");
|
|
||||||
assert.equal(await ens.methods.owner(subdomainHash).call(), registrant, "ENSRegistry owner mismatch");
|
|
||||||
assert.equal(await ens.methods.resolver(subdomainHash).call(), PublicResolver.address, "Resolver wrongly defined");
|
|
||||||
assert.equal(await ENSSubdomainRegistry.methods.getAccountBalance(subdomainHash).call(), 0, "Free domain accounts shouldn't have balance");
|
|
||||||
assert.equal(await ENSSubdomainRegistry.methods.getAccountOwner(subdomainHash).call(), registrant, "Account owner mismatch");
|
|
||||||
assert.equal(await PublicResolver.methods.addr(subdomainHash).call(), utils.zeroAddress, "Resolved address not set");
|
|
||||||
const resolverPubKey = await PublicResolver.methods.pubkey(subdomainHash).call();
|
|
||||||
const pubKey = utils.keyFromXY(resolverPubKey[0], resolverPubKey[1]);
|
|
||||||
assert.equal(pubKey, contactCode, "pubKey does not match contract code");
|
|
||||||
});
|
|
||||||
it('should register empty subdomain with token cost', async () => {
|
|
||||||
const registrant = accountsArr[5];
|
|
||||||
const subdomain = 'erin';
|
|
||||||
const subdomainHash = namehash.hash(subdomain + '.' + domains.paid.name);
|
|
||||||
const domainPrice = await ENSSubdomainRegistry.methods.getPrice(domains.paid.namehash).call()
|
|
||||||
const label = web3Utils.sha3(subdomain);
|
|
||||||
const node = domains.paid.namehash;
|
|
||||||
await TestToken.methods.mint(domainPrice).send({from: registrant});
|
|
||||||
const initialRegistrantBalance = await TestToken.methods.balanceOf(registrant).call();
|
|
||||||
const initialRegistryBalance = await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call();
|
|
||||||
await TestToken.methods.approve(ENSSubdomainRegistry.address, domainPrice).send({from: registrant});
|
|
||||||
const resultRegister = await ENSSubdomainRegistry.methods.register(
|
|
||||||
web3Utils.sha3(subdomain),
|
|
||||||
domains.paid.namehash,
|
|
||||||
utils.zeroAddress,
|
|
||||||
utils.zeroBytes32,
|
|
||||||
utils.zeroBytes32
|
|
||||||
).send({from: registrant});
|
|
||||||
assert.equal(resultRegister.events['0'].raw.topics[0], '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', "Wrong Event");
|
|
||||||
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['0'].raw.topics[1].substring(26)), registrant, "Wrong subnode owner");
|
|
||||||
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['0'].raw.topics[2].substring(26)), ENSSubdomainRegistry.address, "Wrong subnode owner");
|
|
||||||
assert.equal(resultRegister.events['1'].raw.topics[0], '0xce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e82', "Wrong Event");
|
|
||||||
assert.equal(resultRegister.events['1'].raw.topics[1], node, "Wrong Node");
|
|
||||||
assert.equal(resultRegister.events['1'].raw.topics[2], label, "Wrong Label");
|
|
||||||
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['1'].raw.data.substring(26)), registrant, "Wrong subnode owner");
|
|
||||||
assert.equal(resultRegister.events.SubdomainOwner.returnValues.accountOwner, registrant, "event SubdomainOwner accountOwner mismatch");
|
|
||||||
assert.equal(resultRegister.events.SubdomainOwner.returnValues.subdomainHash, subdomainHash, "event SubdomainOwner subdomainHash mismatch");
|
|
||||||
assert.equal(await ens.methods.owner(subdomainHash).call(), registrant, "ENSRegistry owner mismatch");
|
|
||||||
assert.equal(await ens.methods.resolver(subdomainHash).call(), utils.zeroAddress, "Resolver wrongly defined");
|
|
||||||
assert.equal(await ENSSubdomainRegistry.methods.getAccountBalance(subdomainHash).call(), domainPrice, "Registry subdomain account balance wrong");
|
|
||||||
assert.equal(await ENSSubdomainRegistry.methods.getAccountOwner(subdomainHash).call(), registrant, "Account owner mismatch");
|
|
||||||
assert.equal(await TestToken.methods.balanceOf(registrant).call(), +initialRegistrantBalance-domainPrice, "User final balance wrong")
|
|
||||||
assert.equal(await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call(), (+initialRegistryBalance)+(+domainPrice), "Registry final balance wrong")
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('release()', function() {
|
|
||||||
it('should not release subdomain due delay', async () => {
|
|
||||||
let registrant = accountsArr[6];
|
|
||||||
let subdomain = 'mistaker';
|
|
||||||
await ENSSubdomainRegistry.methods.register(
|
|
||||||
web3Utils.sha3(subdomain),
|
|
||||||
domains.free.namehash,
|
|
||||||
utils.zeroAddress,
|
|
||||||
utils.zeroBytes32,
|
|
||||||
utils.zeroBytes32
|
|
||||||
).send({from: registrant});
|
|
||||||
let failed;
|
|
||||||
try{
|
|
||||||
await ENSSubdomainRegistry.methods.release(
|
|
||||||
web3Utils.sha3(subdomain),
|
|
||||||
domains.free.namehash
|
|
||||||
).send({from: registrant});
|
|
||||||
failed = false;
|
|
||||||
} catch(e){
|
|
||||||
failed = true;
|
|
||||||
}
|
|
||||||
assert(failed, "Released after delay period");
|
|
||||||
});
|
|
||||||
it('should release free subdomain', async () => {
|
|
||||||
let registrant = accountsArr[6];
|
|
||||||
let subdomain = 'frank';
|
|
||||||
let subdomainHash = namehash.hash(subdomain + '.' + domains.free.name);
|
|
||||||
|
|
||||||
await ENSSubdomainRegistry.methods.register(
|
|
||||||
web3Utils.sha3(subdomain),
|
|
||||||
domains.free.namehash,
|
|
||||||
utils.zeroAddress,
|
|
||||||
utils.zeroBytes32,
|
|
||||||
utils.zeroBytes32
|
|
||||||
).send({from: registrant});
|
|
||||||
const releaseDelay = await ENSSubdomainRegistry.methods.releaseDelay().call();
|
|
||||||
await utils.increaseTime(releaseDelay)
|
|
||||||
await utils.increaseTime(1000)
|
|
||||||
const initialRegistrantBalance = await TestToken.methods.balanceOf(registrant).call();
|
|
||||||
const initialRegistryBalance = await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call();
|
|
||||||
await utils.increaseTime(1000)
|
|
||||||
const resultRelease = await ENSSubdomainRegistry.methods.release(
|
|
||||||
web3Utils.sha3(subdomain),
|
|
||||||
domains.free.namehash
|
|
||||||
).send({from: registrant});
|
|
||||||
//TODO: check events
|
|
||||||
assert.equal(await ens.methods.owner(subdomainHash).call(), utils.zeroAddress, "Not released name ownship");
|
|
||||||
assert.equal(await TestToken.methods.balanceOf(registrant).call(), initialRegistrantBalance, "Registrant token balance unexpectectly changed")
|
|
||||||
assert.equal(await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call(), initialRegistryBalance, "Registry token balance unexpectectly changed")
|
|
||||||
});
|
|
||||||
it('should release subdomain with cost', async () => {;
|
|
||||||
const registrant = accountsArr[6];
|
|
||||||
const subdomain = 'frank';
|
|
||||||
const subdomainHash = namehash.hash(subdomain + '.' + domains.paid.name);
|
|
||||||
const labelHash = web3Utils.sha3(subdomain);
|
|
||||||
const domainPrice = await ENSSubdomainRegistry.methods.getPrice(domains.paid.namehash).call()
|
|
||||||
await TestToken.methods.mint(domainPrice).send({from: registrant});
|
|
||||||
await TestToken.methods.approve(ENSSubdomainRegistry.address, domainPrice).send({from: registrant});
|
|
||||||
await ENSSubdomainRegistry.methods.register(
|
|
||||||
labelHash,
|
|
||||||
domains.paid.namehash,
|
|
||||||
utils.zeroAddress,
|
|
||||||
utils.zeroBytes32,
|
|
||||||
utils.zeroBytes32
|
|
||||||
).send({from: registrant});
|
|
||||||
const releaseDelay = await ENSSubdomainRegistry.methods.releaseDelay().call();
|
|
||||||
await utils.increaseTime(releaseDelay)
|
|
||||||
await utils.increaseTime(1000)
|
|
||||||
const initialAccountBalance = await ENSSubdomainRegistry.methods.getAccountBalance(subdomainHash).call();
|
|
||||||
const initialRegistrantBalance = await TestToken.methods.balanceOf(registrant).call();
|
|
||||||
const initialRegistryBalance = await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call();
|
|
||||||
await utils.increaseTime(1000)
|
|
||||||
const resultRelease = await ENSSubdomainRegistry.methods.release(
|
|
||||||
web3Utils.sha3(subdomain),
|
|
||||||
domains.paid.namehash
|
|
||||||
).send({from: registrant});
|
|
||||||
//TODO: check events
|
|
||||||
assert.equal(await ENSSubdomainRegistry.methods.getAccountBalance(subdomainHash).call(), 0, "Final balance didnt zeroed");
|
|
||||||
assert.equal(await TestToken.methods.balanceOf(registrant).call(), (+initialRegistrantBalance)+(+initialAccountBalance), "Releaser token balance didnt increase")
|
|
||||||
assert.equal(await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call(), (+initialRegistryBalance)-(+initialAccountBalance), "Registry token balance didnt decrease")
|
|
||||||
});
|
|
||||||
it('should release transfered subdomain with cost', async () => {
|
|
||||||
let registrant = accountsArr[7];
|
|
||||||
let subdomain = 'grace';
|
|
||||||
let subdomainHash = namehash.hash(subdomain + '.' + domains.paid.name);
|
|
||||||
let labelHash = web3Utils.sha3(subdomain);
|
|
||||||
let newOwner = accountsArr[8];
|
|
||||||
let domainPrice = await ENSSubdomainRegistry.methods.getPrice(domains.paid.namehash).call()
|
|
||||||
await TestToken.methods.mint(domainPrice).send({from: registrant});
|
|
||||||
await TestToken.methods.approve(ENSSubdomainRegistry.address, domainPrice).send({from: registrant});
|
|
||||||
await ENSSubdomainRegistry.methods.register(
|
|
||||||
labelHash,
|
|
||||||
domains.paid.namehash,
|
|
||||||
utils.zeroAddress,
|
|
||||||
utils.zeroBytes32,
|
|
||||||
utils.zeroBytes32
|
|
||||||
).send({from: registrant});
|
|
||||||
await ens.methods.setOwner(subdomainHash, newOwner).send({from: registrant});
|
|
||||||
let releaseDelay = await ENSSubdomainRegistry.methods.releaseDelay().call();
|
|
||||||
await utils.increaseTime(releaseDelay)
|
|
||||||
await utils.increaseTime(1000)
|
|
||||||
let initialAccountBalance = await ENSSubdomainRegistry.methods.getAccountBalance(subdomainHash).call();
|
|
||||||
let initialRegistrantBalance = await TestToken.methods.balanceOf(newOwner).call();
|
|
||||||
let initialRegistryBalance = await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call();
|
|
||||||
await utils.increaseTime(1000)
|
|
||||||
let resultRelease = await ENSSubdomainRegistry.methods.release(
|
|
||||||
web3Utils.sha3(subdomain),
|
|
||||||
domains.paid.namehash
|
|
||||||
).send({from: newOwner});
|
|
||||||
//TODO: check events
|
|
||||||
assert.equal(await ENSSubdomainRegistry.methods.getAccountBalance(subdomainHash).call(), 0, "Final balance didnt zeroed");
|
|
||||||
assert.equal(await TestToken.methods.balanceOf(newOwner).call(), (+initialRegistrantBalance)+(+initialAccountBalance), "New owner token balance didnt increase")
|
|
||||||
assert.equal(await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call(), (+initialRegistryBalance)-(+initialAccountBalance), "Registry token balance didnt decrease")
|
|
||||||
});
|
|
||||||
it('should release moved subdomain account balance by funds owner', async () => {
|
|
||||||
const domain = domains.temp;
|
|
||||||
await ENSSubdomainRegistry.methods.setDomainPrice(domain.namehash, domain.price).send({from: accountsArr[0]});
|
|
||||||
const registrant = accountsArr[5];
|
|
||||||
const subdomain = 'hardhead';
|
|
||||||
const subdomainHash = namehash.hash(subdomain + '.' + domain.name);
|
|
||||||
const domainPrice = await ENSSubdomainRegistry.methods.getPrice(domain.namehash).call()
|
|
||||||
const label = web3Utils.sha3(subdomain);
|
|
||||||
const node = domain.namehash;
|
|
||||||
await TestToken.methods.mint(domainPrice).send({from: registrant});
|
|
||||||
await TestToken.methods.approve(ENSSubdomainRegistry.address, domainPrice).send({from: registrant});
|
|
||||||
await ENSSubdomainRegistry.methods.register(
|
|
||||||
label,
|
|
||||||
node,
|
|
||||||
utils.zeroAddress,
|
|
||||||
utils.zeroBytes32,
|
|
||||||
utils.zeroBytes32
|
|
||||||
).send({from: registrant});
|
|
||||||
let initialAccountBalance = await ENSSubdomainRegistry.methods.getAccountBalance(subdomainHash).call();
|
|
||||||
const initialRegistrantBalance = await TestToken.methods.balanceOf(registrant).call();
|
|
||||||
const initialRegistryBalance = await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call();
|
|
||||||
await ENSSubdomainRegistry.methods.moveDomain(UpdatedENSSubdomainRegistry.address, domain.namehash).send();
|
|
||||||
const resultRelease = await ENSSubdomainRegistry.methods.release(
|
|
||||||
label,
|
|
||||||
node
|
|
||||||
).send({from: registrant});
|
|
||||||
//TODO: verify events
|
|
||||||
assert.equal(await TestToken.methods.balanceOf(registrant).call(), (+initialRegistrantBalance)+(+initialAccountBalance), "New owner token balance didnt increase")
|
|
||||||
assert.equal(await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call(), (+initialRegistryBalance)-(+initialAccountBalance), "Registry token balance didnt decrease")
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('updateAccountOwner()', function() {
|
|
||||||
it('should update subdomain funds owner', async () => {
|
|
||||||
let subdomain = 'heidi';
|
|
||||||
let labelHash = web3Utils.sha3(subdomain);
|
|
||||||
let registrant = accountsArr[8];
|
|
||||||
let newOwner = accountsArr[9];
|
|
||||||
let subdomainHash = namehash.hash(subdomain + '.' + domains.paid.name);
|
|
||||||
let domainPrice = await ENSSubdomainRegistry.methods.getPrice(domains.paid.namehash).call()
|
|
||||||
await TestToken.methods.mint(domainPrice).send({from: registrant});
|
|
||||||
await TestToken.methods.approve(ENSSubdomainRegistry.address, domainPrice).send({from: registrant});
|
|
||||||
await ENSSubdomainRegistry.methods.register(
|
|
||||||
labelHash,
|
|
||||||
domains.paid.namehash,
|
|
||||||
utils.zeroAddress,
|
|
||||||
utils.zeroBytes32,
|
|
||||||
utils.zeroBytes32
|
|
||||||
).send({from: registrant});
|
|
||||||
await ens.methods.setOwner(subdomainHash, newOwner).send({from: registrant});
|
|
||||||
let resultUpdateOwner = await ENSSubdomainRegistry.methods.updateAccountOwner(
|
|
||||||
labelHash,
|
|
||||||
domains.paid.namehash
|
|
||||||
).send({from: newOwner});
|
|
||||||
//TODO: check events
|
|
||||||
assert.equal(await ENSSubdomainRegistry.methods.getAccountOwner(subdomainHash).call(), newOwner, "Backup owner not updated");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('slashInvalidSubdomain()', function() {
|
|
||||||
it('should slash invalid subdomain', async () => {
|
|
||||||
let subdomain = 'alicé';
|
|
||||||
let subdomainHash = namehash.hash(subdomain + '.' + domains.free.name);
|
|
||||||
let registrant = accountsArr[1];
|
|
||||||
await ENSSubdomainRegistry.methods.register(
|
|
||||||
web3Utils.sha3(subdomain),
|
|
||||||
domains.free.namehash,
|
|
||||||
utils.zeroAddress,
|
|
||||||
utils.zeroBytes32,
|
|
||||||
utils.zeroBytes32
|
|
||||||
).send({from: registrant});
|
|
||||||
assert.equal(await ens.methods.owner(subdomainHash).call(), registrant);
|
|
||||||
assert.notEqual(await ENSSubdomainRegistry.methods.getCreationTime(subdomainHash).call(), 0);
|
|
||||||
await ENSSubdomainRegistry.methods.slashInvalidSubdomain(web3Utils.toHex(subdomain), domains.free.namehash, 4).send()
|
|
||||||
//TODO: check events
|
|
||||||
assert.equal(await ENSSubdomainRegistry.methods.getCreationTime(subdomainHash).call(), 0);
|
|
||||||
assert.equal(await ens.methods.owner(subdomainHash).call(), utils.zeroAddress);
|
|
||||||
});
|
|
||||||
it('should not slash valid subdomain', async () => {
|
|
||||||
let subdomain = 'legituser';
|
|
||||||
let subdomainHash = namehash.hash(subdomain + '.' + domains.free.name);
|
|
||||||
let registrant = accountsArr[1];
|
|
||||||
await ENSSubdomainRegistry.methods.register(
|
|
||||||
web3Utils.sha3(subdomain),
|
|
||||||
domains.free.namehash,
|
|
||||||
utils.zeroAddress,
|
|
||||||
utils.zeroBytes32,
|
|
||||||
utils.zeroBytes32
|
|
||||||
).send({from: registrant});
|
|
||||||
let failed;
|
|
||||||
try{
|
|
||||||
await ENSSubdomainRegistry.methods.slashInvalidSubdomain(web3Utils.toHex(subdomain), domains.free.namehash, 4).send()
|
|
||||||
failed = false;
|
|
||||||
} catch(e){
|
|
||||||
failed = true;
|
|
||||||
}
|
|
||||||
assert(failed, "Was slashed anyway");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('slashReservedSubdomain()', function() {
|
|
||||||
it('should slash reserved name subdomain', async () => {
|
|
||||||
let subdomain = reservedNames[0];
|
|
||||||
let subdomainHash = namehash.hash(subdomain + '.' + domains.free.name);
|
|
||||||
let registrant = accountsArr[1];
|
|
||||||
await ENSSubdomainRegistry.methods.register(
|
|
||||||
web3Utils.sha3(subdomain),
|
|
||||||
domains.free.namehash,
|
|
||||||
utils.zeroAddress,
|
|
||||||
utils.zeroBytes32,
|
|
||||||
utils.zeroBytes32
|
|
||||||
).send({from: registrant});
|
|
||||||
assert.equal(await ens.methods.owner(subdomainHash).call(), registrant);
|
|
||||||
const proof = merkleTree.getHexProof(reservedNames[0]);
|
|
||||||
result = await ENSSubdomainRegistry.methods.slashReservedSubdomain(web3Utils.toHex(subdomain), domains.free.namehash, 0, proof).send()
|
|
||||||
assert.equal(await ens.methods.owner(subdomainHash).call(), utils.zeroAddress);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('slashSmallSubdomain()', function() {
|
|
||||||
it('should not slash big subdomain', async() =>{
|
|
||||||
let subdomain = '1234567890';
|
|
||||||
let subdomainHash = namehash.hash(subdomain + '.' + domains.free.name);
|
|
||||||
let registrant = accountsArr[1];
|
|
||||||
await ENSSubdomainRegistry.methods.register(
|
|
||||||
web3Utils.sha3(subdomain),
|
|
||||||
domains.free.namehash,
|
|
||||||
utils.zeroAddress,
|
|
||||||
utils.zeroBytes32,
|
|
||||||
utils.zeroBytes32
|
|
||||||
).send({from: registrant});
|
|
||||||
let failed;
|
|
||||||
try{
|
|
||||||
await ENSSubdomainRegistry.methods.slashSmallSubdomain(web3Utils.toHex(subdomain), domains.free.namehash).send()
|
|
||||||
failed = false;
|
|
||||||
} catch(e){
|
|
||||||
failed = true;
|
|
||||||
}
|
|
||||||
assert(failed, "Was slashed anyway");
|
|
||||||
})
|
|
||||||
it('should slash small subdomain', async () => {
|
|
||||||
let subdomain = 'a';
|
|
||||||
let subdomainHash = namehash.hash(subdomain + '.' + domains.free.name);
|
|
||||||
let registrant = accountsArr[1];
|
|
||||||
await ENSSubdomainRegistry.methods.register(
|
|
||||||
web3Utils.sha3(subdomain),
|
|
||||||
domains.free.namehash,
|
|
||||||
utils.zeroAddress,
|
|
||||||
utils.zeroBytes32,
|
|
||||||
utils.zeroBytes32
|
|
||||||
).send({from: registrant});
|
|
||||||
assert.equal(await ens.methods.owner(subdomainHash).call(), registrant);
|
|
||||||
result = await ENSSubdomainRegistry.methods.slashSmallSubdomain(web3Utils.toHex(subdomain), domains.free.namehash).send()
|
|
||||||
assert.equal(await ens.methods.owner(subdomainHash).call(), utils.zeroAddress);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('slashAddressLikeSubdomain()', function() {
|
|
||||||
it('should slash subdomain that starts with 0x and is 12 of lenght or bigger', async () => {
|
|
||||||
let subdomain = "0xc6b95bd26123";
|
|
||||||
let userlabelHash = "0xe311c0592b075c30277c679f0daea74ee1727547efa522fd28d20a8f2c3e435b"; //sha3("0xc6b95bd26123")
|
|
||||||
let subdomainHash = "0x5fcd61e83fda60beb7c9bff8e0e26a6f975a5154ff2e6f5464dc97571c95cdd4"; //namehash("0xc6b95bd26123.freedomain.eth")
|
|
||||||
let domainnameHash = "0x297836a76312224372ac04e26dd23d1294bb8256598ec113ecc52735f826beff"; //namehash("freedomain.eth")
|
|
||||||
let registrant = accountsArr[1];
|
|
||||||
let result = await ENSSubdomainRegistry.methods.register(
|
|
||||||
userlabelHash,
|
|
||||||
domainnameHash,
|
|
||||||
utils.zeroAddress,
|
|
||||||
utils.zeroBytes32,
|
|
||||||
utils.zeroBytes32
|
|
||||||
).send({from: registrant});
|
|
||||||
assert.equal(await ens.methods.owner(subdomainHash).call(), registrant);
|
|
||||||
result = await ENSSubdomainRegistry.methods.slashAddressLikeSubdomain(subdomain, domainnameHash).send()
|
|
||||||
assert.equal(await ens.methods.owner(subdomainHash).call(), utils.zeroAddress);
|
|
||||||
});
|
|
||||||
it('should not slash subdomain that starts with 0x but is smaller then 12', async () => {
|
|
||||||
let subdomain = "0xc6b95bd26";
|
|
||||||
let userlabelHash = "0x59bf8d16c517a40a5dacc3471abd002f3bc0850a13e930e4bee49070a58517e8"; //sha3("0xc6b95bd26")
|
|
||||||
let subdomainHash = "0x6f15e192c1c4537c2d774431219ed42efc6be95efe362104ba546eb574f3f1e5"; //namehash("0xc6b95bd26.freedomain.eth")
|
|
||||||
let domainnameHash = "0x297836a76312224372ac04e26dd23d1294bb8256598ec113ecc52735f826beff"; //namehash("freedomain.eth")
|
|
||||||
let registrant = accountsArr[1];
|
|
||||||
await ENSSubdomainRegistry.methods.register(
|
|
||||||
userlabelHash,
|
|
||||||
domainnameHash,
|
|
||||||
utils.zeroAddress,
|
|
||||||
utils.zeroBytes32,
|
|
||||||
utils.zeroBytes32
|
|
||||||
).send({from: registrant});
|
|
||||||
let failed;
|
|
||||||
try{
|
|
||||||
result = await ENSSubdomainRegistry.methods.slashAddressLikeSubdomain(subdomain, domainnameHash).send()
|
|
||||||
failed = false;
|
|
||||||
} catch(e){
|
|
||||||
failed = true;
|
|
||||||
}
|
|
||||||
assert(failed, "Was slashed anyway");
|
|
||||||
});
|
|
||||||
it('should not slash subdomain that dont starts 0x and is bigger than 12', async () => {
|
|
||||||
const subdomain = "0a002322c6b95bd26";
|
|
||||||
const userlabelHash = "0xe4769e5c31ff61ac50dce20559a4411a4ca45d94c733cbeda7ab9f28ed75cef1"; //sha3("0a002322c6b95bd26")
|
|
||||||
const subdomainHash = "0x549a8b62103d19b66f70bee21176514340094253a92123609c1df25b0812d40c"; //namehash("0a002322c6b95bd26.freedomain.eth")
|
|
||||||
const domainnameHash = "0x297836a76312224372ac04e26dd23d1294bb8256598ec113ecc52735f826beff"; //namehash("freedomain.eth")
|
|
||||||
const registrant = accountsArr[1];
|
|
||||||
await ENSSubdomainRegistry.methods.register(
|
|
||||||
userlabelHash,
|
|
||||||
domainnameHash,
|
|
||||||
utils.zeroAddress,
|
|
||||||
utils.zeroBytes32,
|
|
||||||
utils.zeroBytes32
|
|
||||||
).send({from: registrant});
|
|
||||||
let failed;
|
|
||||||
try{
|
|
||||||
await ENSSubdomainRegistry.methods.slashAddressLikeSubdomain(subdomain, domainnameHash).send()
|
|
||||||
failed = false;
|
|
||||||
} catch(e){
|
|
||||||
failed = true;
|
|
||||||
}
|
|
||||||
assert(failed, "Was slashed anyway");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('slashSubdomain()', function() {
|
|
||||||
it('should slash a paid subdomain and get funds from registrant', async () => {
|
|
||||||
const subdomain = 'b';
|
|
||||||
const subdomainHash = namehash.hash(subdomain + '.' + domains.paid.name);
|
|
||||||
const registrant = accountsArr[1];
|
|
||||||
const slasher = accountsArr[2];
|
|
||||||
const domainPrice = await ENSSubdomainRegistry.methods.getPrice(domains.paid.namehash).call()
|
|
||||||
await TestToken.methods.mint(domainPrice).send({from: registrant});
|
|
||||||
await TestToken.methods.approve(ENSSubdomainRegistry.address, domainPrice).send({from: registrant});
|
|
||||||
await ENSSubdomainRegistry.methods.register(
|
|
||||||
web3Utils.sha3(subdomain),
|
|
||||||
domains.paid.namehash,
|
|
||||||
utils.zeroAddress,
|
|
||||||
utils.zeroBytes32,
|
|
||||||
utils.zeroBytes32
|
|
||||||
).send({from: registrant});
|
|
||||||
assert.equal(await ens.methods.owner(subdomainHash).call(), registrant);
|
|
||||||
const initialSlasherBalance = await TestToken.methods.balanceOf(slasher).call();
|
|
||||||
await ENSSubdomainRegistry.methods.slashSmallSubdomain(web3Utils.toHex(subdomain), domains.paid.namehash).send({from: slasher})
|
|
||||||
//TODO: check events
|
|
||||||
assert.equal(await TestToken.methods.balanceOf(slasher).call(), (+initialSlasherBalance)+(+domainPrice));
|
|
||||||
assert.equal(await ens.methods.owner(subdomainHash).call(), utils.zeroAddress);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('moveDomain()', function() {
|
|
||||||
it('should move free domain to new registry and migrate', async () => {
|
|
||||||
const resultMoveDomain = await ENSSubdomainRegistry.methods.moveDomain(UpdatedENSSubdomainRegistry.address, domains.free.namehash).send();
|
|
||||||
//TODO: check events
|
|
||||||
assert.equal(await ens.methods.owner(domains.free.namehash).call(), UpdatedENSSubdomainRegistry.address, "domain ownership not moved correctly")
|
|
||||||
});
|
|
||||||
it('should move paid domain to new registry and migrate', async () => {
|
|
||||||
const price = await ENSSubdomainRegistry.methods.getPrice(domains.paid.namehash).call()
|
|
||||||
const result = await ENSSubdomainRegistry.methods.moveDomain(UpdatedENSSubdomainRegistry.address, domains.paid.namehash).send();
|
|
||||||
//TODO: check events
|
|
||||||
assert.equal(await ens.methods.owner(domains.paid.namehash).call(), UpdatedENSSubdomainRegistry.address, "domain ownership not moved correctly")
|
|
||||||
assert.equal(await UpdatedENSSubdomainRegistry.methods.getPrice(domains.paid.namehash).call(), price, "updated registry didnt migrated price")
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('moveAccount()', function() {
|
|
||||||
it('should move free subdomain to new registry by funds owner', async () => {
|
|
||||||
const subdomain = 'alice';
|
|
||||||
const subdomainHash = namehash.hash(subdomain + '.' + domains.free.name);
|
|
||||||
const registrant = accountsArr[1];
|
|
||||||
const label = web3Utils.sha3(subdomain);
|
|
||||||
const node = domains.free.namehash;
|
|
||||||
const creationTime = await ENSSubdomainRegistry.methods.getCreationTime(subdomainHash).call();
|
|
||||||
assert.notEqual(creationTime, 0);
|
|
||||||
assert.equal(await UpdatedENSSubdomainRegistry.methods.getCreationTime(subdomainHash).call(), 0);
|
|
||||||
const result = await ENSSubdomainRegistry.methods.moveAccount(label,node).send({from: registrant});
|
|
||||||
assert.equal(await ENSSubdomainRegistry.methods.getCreationTime(subdomainHash).call(), 0);
|
|
||||||
assert.equal(await UpdatedENSSubdomainRegistry.methods.getCreationTime(subdomainHash).call(), creationTime);
|
|
||||||
});
|
|
||||||
it('should move paid subdomain to new registry by funds owner', async () => {
|
|
||||||
const registrant = accountsArr[5];
|
|
||||||
const subdomain = 'erin';
|
|
||||||
const subdomainHash = namehash.hash(subdomain + '.' + domains.paid.name);
|
|
||||||
const label = web3Utils.sha3(subdomain);
|
|
||||||
const node = domains.paid.namehash;
|
|
||||||
const accountBalance = await ENSSubdomainRegistry.methods.getAccountBalance(subdomainHash).call()
|
|
||||||
assert.notEqual(accountBalance, 0);
|
|
||||||
const initialRegistryBalance = await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call();
|
|
||||||
const initialUpdatedRegistryBalance = await TestToken.methods.balanceOf(UpdatedENSSubdomainRegistry.address).call();
|
|
||||||
const creationTime = await ENSSubdomainRegistry.methods.getCreationTime(subdomainHash).call();
|
|
||||||
assert.notEqual(creationTime, 0);
|
|
||||||
assert.equal(await UpdatedENSSubdomainRegistry.methods.getCreationTime(subdomainHash).call(), 0);
|
|
||||||
const result = await ENSSubdomainRegistry.methods.moveAccount(label,node).send({from: registrant});
|
|
||||||
assert.equal(await ENSSubdomainRegistry.methods.getCreationTime(subdomainHash).call(), 0);
|
|
||||||
assert.equal(await UpdatedENSSubdomainRegistry.methods.getCreationTime(subdomainHash).call(), creationTime);
|
|
||||||
assert.equal(await TestToken.methods.balanceOf(ENSSubdomainRegistry.address).call(), (+initialRegistryBalance)-(+accountBalance))
|
|
||||||
assert.equal(await TestToken.methods.balanceOf(UpdatedENSSubdomainRegistry.address).call(), (+initialUpdatedRegistryBalance)+(+accountBalance))
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
|
@ -0,0 +1,806 @@
|
||||||
|
const utils = require('../utils/testUtils.js');
|
||||||
|
const web3Utils = require('web3-utils');
|
||||||
|
const namehash = require('eth-ens-namehash');
|
||||||
|
const TestToken = require('Embark/contracts/TestToken');
|
||||||
|
const ENSRegistry = require('Embark/contracts/ENSRegistry');
|
||||||
|
const PublicResolver = require('Embark/contracts/PublicResolver');
|
||||||
|
const UsernameRegistrar = require('Embark/contracts/UsernameRegistrar');
|
||||||
|
const { MerkleTree } = require('../utils/merkleTree.js');
|
||||||
|
const { reservedNames } = require('../config/ens-usernames/reservedNames')
|
||||||
|
const registry = {
|
||||||
|
name: 'stateofus',
|
||||||
|
registry: 'stateofus.eth',
|
||||||
|
label: web3Utils.sha3('stateofus'),
|
||||||
|
namehash: namehash.hash('stateofus.eth'),
|
||||||
|
price: 100000000
|
||||||
|
}
|
||||||
|
|
||||||
|
const dummyRegistry = {
|
||||||
|
name: 'dummyreg',
|
||||||
|
registry: 'dummyreg.eth',
|
||||||
|
label: web3Utils.sha3('dummyreg'),
|
||||||
|
namehash: namehash.hash('dummyreg.eth'),
|
||||||
|
price: 100000000
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: load file of reserved names and balance array lenght to be even
|
||||||
|
|
||||||
|
const merkleTree = new MerkleTree(reservedNames);
|
||||||
|
const merkleRoot = merkleTree.getHexRoot();
|
||||||
|
|
||||||
|
var contractsConfig = {
|
||||||
|
"TestToken": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"ENSRegistry": {
|
||||||
|
"onDeploy": [
|
||||||
|
"ENSRegistry.methods.setSubnodeOwner('0x0000000000000000000000000000000000000000000000000000000000000000', '0x4f5b812789fc606be1b3b16908db13fc7a9adf7ca72641f84d75b47069d3d7f0', web3.eth.defaultAccount).send()"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"PublicResolver": {
|
||||||
|
"args": [
|
||||||
|
"$ENSRegistry"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"UsernameRegistrar": {
|
||||||
|
"args": [
|
||||||
|
"$TestToken",
|
||||||
|
"$ENSRegistry",
|
||||||
|
"$PublicResolver",
|
||||||
|
registry.namehash,
|
||||||
|
"3",
|
||||||
|
merkleRoot,
|
||||||
|
"0x0"
|
||||||
|
],
|
||||||
|
"onDeploy": [
|
||||||
|
"ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '"+registry.label+"', UsernameRegistrar.address).send()",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"UpdatedUsernameRegistrar": {
|
||||||
|
"instanceOf" : "UsernameRegistrar",
|
||||||
|
"args": [
|
||||||
|
"$TestToken",
|
||||||
|
"$ENSRegistry",
|
||||||
|
"$PublicResolver",
|
||||||
|
registry.namehash,
|
||||||
|
"3",
|
||||||
|
merkleRoot,
|
||||||
|
"$UsernameRegistrar"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"DummyUsernameRegistrar": {
|
||||||
|
"instanceOf" : "UsernameRegistrar",
|
||||||
|
"args": [
|
||||||
|
"$TestToken",
|
||||||
|
"$ENSRegistry",
|
||||||
|
"$PublicResolver",
|
||||||
|
dummyRegistry.namehash,
|
||||||
|
"3",
|
||||||
|
merkleRoot,
|
||||||
|
"0x0"
|
||||||
|
],
|
||||||
|
"onDeploy": [
|
||||||
|
"ENSRegistry.methods.setSubnodeOwner('0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae', '"+dummyRegistry.label+"', DummyUsernameRegistrar.address).send()",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"UpdatedDummyUsernameRegistrar": {
|
||||||
|
"instanceOf" : "UsernameRegistrar",
|
||||||
|
"args": [
|
||||||
|
"$TestToken",
|
||||||
|
"$ENSRegistry",
|
||||||
|
"$PublicResolver",
|
||||||
|
dummyRegistry.namehash,
|
||||||
|
"3",
|
||||||
|
merkleRoot,
|
||||||
|
"$DummyUsernameRegistrar"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
config({ contracts: contractsConfig });
|
||||||
|
|
||||||
|
contract('UsernameRegistrar', function () {
|
||||||
|
let ens;
|
||||||
|
let accountsArr;
|
||||||
|
|
||||||
|
before(function(done) {
|
||||||
|
web3.eth.getAccounts().then(async (accounts) => {
|
||||||
|
ens = ENSRegistry;
|
||||||
|
accountsArr = accounts;
|
||||||
|
await utils.increaseTime(1 * utils.timeUnits.days) //time cannot start zero
|
||||||
|
await utils.increaseTime(1000)
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('activate(uint256)', function() {
|
||||||
|
it('should activate registry', async () => {
|
||||||
|
const initialPrice = 100
|
||||||
|
const resultSetRegistryPrice = await UsernameRegistrar.methods.activate(initialPrice).send({from: accountsArr[0]});
|
||||||
|
assert.equal(resultSetRegistryPrice.events.RegistryPrice.returnValues.price, initialPrice, "event RegistryPrice wrong price");
|
||||||
|
assert.equal(await UsernameRegistrar.methods.state().call(), 1, "Wrong registry state")
|
||||||
|
assert.equal(await UsernameRegistrar.methods.price().call(), initialPrice, "Wrong registry price")
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('updateRegistryPrice()', function() {
|
||||||
|
it('should change registry price', async () => {
|
||||||
|
const newPrice = registry.price;
|
||||||
|
const resultUpdateRegistryPrice = await UsernameRegistrar.methods.updateRegistryPrice(newPrice).send({from: accountsArr[0]});
|
||||||
|
assert.equal(resultUpdateRegistryPrice.events.RegistryPrice.returnValues.price, registry.price, "event RegistryPrice wrong price");
|
||||||
|
assert.equal(await UsernameRegistrar.methods.state().call(), 1, "Wrong registry state")
|
||||||
|
assert.equal(await UsernameRegistrar.methods.price().call(), newPrice, "Wrong registry price")
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('register()', function() {
|
||||||
|
it('should register username', async () => {
|
||||||
|
const registrant = accountsArr[5];
|
||||||
|
const username = 'erin';
|
||||||
|
const usernameHash = namehash.hash(username + '.' + registry.registry);
|
||||||
|
const label = web3Utils.sha3(username);
|
||||||
|
const registryPrice = await UsernameRegistrar.methods.getPrice().call()
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
const initialRegistrantBalance = await TestToken.methods.balanceOf(registrant).call();
|
||||||
|
const initialRegistryBalance = await TestToken.methods.balanceOf(UsernameRegistrar.address).call();
|
||||||
|
await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant});
|
||||||
|
const resultRegister = await UsernameRegistrar.methods.register(
|
||||||
|
web3Utils.sha3(username),
|
||||||
|
utils.zeroAddress,
|
||||||
|
utils.zeroBytes32,
|
||||||
|
utils.zeroBytes32
|
||||||
|
).send({from: registrant});
|
||||||
|
assert.equal(resultRegister.events['0'].raw.topics[0], '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', "Wrong Event");
|
||||||
|
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['0'].raw.topics[1].substring(26)), registrant, "Wrong subnode owner");
|
||||||
|
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['0'].raw.topics[2].substring(26)), UsernameRegistrar.address, "Wrong subnode owner");
|
||||||
|
assert.equal(resultRegister.events['1'].raw.topics[0], '0xce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e82', "Wrong Event");
|
||||||
|
assert.equal(resultRegister.events['1'].raw.topics[1], registry.namehash, "Wrong Node");
|
||||||
|
assert.equal(resultRegister.events['1'].raw.topics[2], label, "Wrong Label");
|
||||||
|
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['1'].raw.data.substring(26)), registrant, "Wrong subnode owner");
|
||||||
|
assert.equal(resultRegister.events.UsernameOwner.returnValues.owner, registrant, "event UsernameOwner owner mismatch");
|
||||||
|
assert.equal(resultRegister.events.UsernameOwner.returnValues.nameHash, usernameHash, "event UsernameOwner usernameHash mismatch");
|
||||||
|
assert.equal(await ens.methods.owner(usernameHash).call(), registrant, "ENSRegistry owner mismatch");
|
||||||
|
assert.equal(await ens.methods.resolver(usernameHash).call(), utils.zeroAddress, "Resolver wrongly defined");
|
||||||
|
assert.equal(await UsernameRegistrar.methods.getAccountBalance(label).call(), registryPrice, "Registry username account balance wrong");
|
||||||
|
assert.equal(await UsernameRegistrar.methods.getAccountOwner(label).call(), registrant, "Account owner mismatch");
|
||||||
|
assert.equal(await TestToken.methods.balanceOf(registrant).call(), +initialRegistrantBalance-registryPrice, "User final balance wrong")
|
||||||
|
assert.equal(await TestToken.methods.balanceOf(UsernameRegistrar.address).call(), (+initialRegistryBalance)+(+registry.price), "Registry final balance wrong")
|
||||||
|
});
|
||||||
|
it('should register username only resolveing address ', async () => {
|
||||||
|
const registrant = accountsArr[2];
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant});
|
||||||
|
const username = 'bob';
|
||||||
|
const usernameHash = namehash.hash(username + '.' + registry.registry);
|
||||||
|
const label = web3Utils.sha3(username);
|
||||||
|
const resultRegister = await UsernameRegistrar.methods.register(
|
||||||
|
web3Utils.sha3(username),
|
||||||
|
|
||||||
|
registrant,
|
||||||
|
utils.zeroBytes32,
|
||||||
|
utils.zeroBytes32
|
||||||
|
).send({from: registrant});
|
||||||
|
assert.equal(resultRegister.events['1'].raw.topics[0], '0xce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e82', "Wrong Event");
|
||||||
|
assert.equal(resultRegister.events['1'].raw.topics[1], registry.namehash, "Wrong Node");
|
||||||
|
assert.equal(resultRegister.events['1'].raw.topics[2], label, "Wrong Label");
|
||||||
|
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['1'].raw.data.substring(26)), UsernameRegistrar.address, "Wrong subnode owner");
|
||||||
|
assert.equal(resultRegister.events['2'].raw.topics[0], '0x335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0', "Wrong Event");
|
||||||
|
assert.equal(resultRegister.events['2'].raw.topics[1], usernameHash, "Wrong Username");
|
||||||
|
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['2'].raw.data.substring(26)), PublicResolver.address, "Wrong Resolver");
|
||||||
|
assert.equal(resultRegister.events['3'].raw.topics[0], '0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2', "Wrong Event");
|
||||||
|
assert.equal(resultRegister.events['3'].raw.topics[1], usernameHash, "Wrong Username");
|
||||||
|
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['3'].raw.data.substring(26)), registrant, "Wrong address to resolve");
|
||||||
|
assert.equal(resultRegister.events['4'].raw.topics[0], '0xd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266', "Wrong Event");
|
||||||
|
assert.equal(resultRegister.events['4'].raw.topics[1], usernameHash, "Wrong Username");
|
||||||
|
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['4'].raw.data.substring(26)), registrant, "Wrong registry.namehash owner");
|
||||||
|
assert.equal(resultRegister.events.UsernameOwner.returnValues.owner, registrant, "event UsernameOwner owner mismatch");
|
||||||
|
assert.equal(resultRegister.events.UsernameOwner.returnValues.nameHash, usernameHash, "event UsernameOwner usernameHash mismatch");
|
||||||
|
assert.equal(await ens.methods.owner(usernameHash).call(), registrant, "ENSRegistry owner mismatch");
|
||||||
|
assert.equal(await ens.methods.resolver(usernameHash).call(), PublicResolver.address, "Resolver wrongly defined");
|
||||||
|
assert.equal(await UsernameRegistrar.methods.getAccountBalance(label).call(), registry.price, "Wrong account balance");
|
||||||
|
assert.equal(await UsernameRegistrar.methods.getAccountOwner(label).call(), registrant, "Account owner mismatch");
|
||||||
|
assert.equal(await PublicResolver.methods.addr(usernameHash).call(), registrant, "Resolved address not set");
|
||||||
|
const resolverPubKey = await PublicResolver.methods.pubkey(usernameHash).call();
|
||||||
|
assert.equal(resolverPubKey[0], utils.zeroBytes32 , "Unexpected resolved pubkey[0]");
|
||||||
|
assert.equal(resolverPubKey[1], utils.zeroBytes32 , "Unexpected resolved pubkey[1]");
|
||||||
|
});
|
||||||
|
it('should register username with only status contact', async () => {
|
||||||
|
const username = 'carlos';
|
||||||
|
const registrant = accountsArr[3];
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant});
|
||||||
|
const usernameHash = namehash.hash(username + '.' + registry.registry);
|
||||||
|
const contactCode = '0x04dbb31252d9bddb4e4d362c7b9c80cba74732280737af97971f42ccbdc716f3f3efb1db366880e52d09b1bfd59842e833f3004088892b7d14b9ce9e957cea9a82';
|
||||||
|
const points = utils.generateXY(contactCode);
|
||||||
|
const label = web3Utils.sha3(username);
|
||||||
|
const resultRegister = await UsernameRegistrar.methods.register(
|
||||||
|
web3Utils.sha3(username),
|
||||||
|
utils.zeroAddress,
|
||||||
|
points.x,
|
||||||
|
points.y
|
||||||
|
).send({from: registrant});
|
||||||
|
assert.equal(resultRegister.events['1'].raw.topics[0], '0xce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e82', "Wrong Event");
|
||||||
|
assert.equal(resultRegister.events['1'].raw.topics[1], registry.namehash, "Wrong Node");
|
||||||
|
assert.equal(resultRegister.events['1'].raw.topics[2], label, "Wrong Label");
|
||||||
|
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['1'].raw.data.substring(26)), UsernameRegistrar.address, "Wrong subnode owner");
|
||||||
|
assert.equal(resultRegister.events['2'].raw.topics[0], '0x335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0', "Wrong Event");
|
||||||
|
assert.equal(resultRegister.events['2'].raw.topics[1], usernameHash, "Wrong Username");
|
||||||
|
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['2'].raw.data.substring(26)), PublicResolver.address, "Wrong Resolver");
|
||||||
|
assert.equal(resultRegister.events['3'].raw.topics[0], '0x1d6f5e03d3f63eb58751986629a5439baee5079ff04f345becb66e23eb154e46', "Wrong Event");
|
||||||
|
assert.equal(resultRegister.events['3'].raw.topics[1], usernameHash, "Wrong Username");
|
||||||
|
assert.equal(resultRegister.events['3'].raw.data, points.x.concat(points.y.substr(2)))
|
||||||
|
assert.equal(resultRegister.events['4'].raw.topics[0], '0xd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266', "Wrong Event");
|
||||||
|
assert.equal(resultRegister.events['4'].raw.topics[1], usernameHash, "Wrong Username");
|
||||||
|
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['4'].raw.data.substring(26)), registrant, "Wrong registry.namehash owner");
|
||||||
|
assert.equal(resultRegister.events.UsernameOwner.returnValues.owner, registrant, "event UsernameOwner owner mismatch");
|
||||||
|
assert.equal(resultRegister.events.UsernameOwner.returnValues.nameHash, usernameHash, "event UsernameOwner usernameHash mismatch");
|
||||||
|
assert.equal(await ens.methods.owner(usernameHash).call(), registrant, "ENSRegistry owner mismatch");
|
||||||
|
assert.equal(await ens.methods.resolver(usernameHash).call(), PublicResolver.address, "Resolver wrongly defined");
|
||||||
|
assert.equal(await UsernameRegistrar.methods.getAccountBalance(label).call(), registry.price, "Wrong account balance");
|
||||||
|
assert.equal(await UsernameRegistrar.methods.getAccountOwner(label).call(), registrant, "Account owner mismatch");
|
||||||
|
assert.equal(await PublicResolver.methods.addr(usernameHash).call(), utils.zeroAddress, "Resolved address not set");
|
||||||
|
const resolverPubKey = await PublicResolver.methods.pubkey(usernameHash).call();
|
||||||
|
const pubKey = utils.keyFromXY(resolverPubKey[0], resolverPubKey[1]);
|
||||||
|
assert.equal(pubKey, contactCode, "pubKey does not match contract code");
|
||||||
|
});
|
||||||
|
it('should register username with status contact code and address', async () => {
|
||||||
|
const registrant = accountsArr[2];
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant});
|
||||||
|
const username = 'bob2';
|
||||||
|
const usernameHash = namehash.hash(username + '.' + registry.registry);
|
||||||
|
const contactCode = '0x04dbb31252d9bddb4e4d362c7b9c80cba74732280737af97971f42ccbdc716f3f3efb1db366880e52d09b1bfd59842e833f3004088892b7d14b9ce9e957cea9a82';
|
||||||
|
const points = utils.generateXY(contactCode);
|
||||||
|
const label = web3Utils.sha3(username);
|
||||||
|
const resultRegister = await UsernameRegistrar.methods.register(
|
||||||
|
label,
|
||||||
|
registrant,
|
||||||
|
points.x,
|
||||||
|
points.y
|
||||||
|
).send({from: registrant});
|
||||||
|
assert.equal(resultRegister.events['1'].raw.topics[0], '0xce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e82', "Wrong Event");
|
||||||
|
assert.equal(resultRegister.events['1'].raw.topics[1], registry.namehash, "Wrong Node");
|
||||||
|
assert.equal(resultRegister.events['1'].raw.topics[2], label, "Wrong Label");
|
||||||
|
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['1'].raw.data.substring(26)), UsernameRegistrar.address, "Wrong subnode owner");
|
||||||
|
assert.equal(resultRegister.events['2'].raw.topics[0], '0x335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0', "Wrong Event");
|
||||||
|
assert.equal(resultRegister.events['2'].raw.topics[1], usernameHash, "Wrong Username");
|
||||||
|
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['2'].raw.data.substring(26)), await UsernameRegistrar.methods.resolver().call(), "Wrong Resolver");
|
||||||
|
assert.equal(resultRegister.events['3'].raw.topics[0], '0x52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd2', "Wrong Event");
|
||||||
|
assert.equal(resultRegister.events['3'].raw.topics[1], usernameHash, "Wrong Username");
|
||||||
|
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['3'].raw.data.substring(26)), registrant, "Wrong address to resolve");
|
||||||
|
assert.equal(resultRegister.events['4'].raw.topics[0], '0x1d6f5e03d3f63eb58751986629a5439baee5079ff04f345becb66e23eb154e46', "Wrong Event");
|
||||||
|
assert.equal(resultRegister.events['4'].raw.topics[1], usernameHash, "Wrong Username");
|
||||||
|
assert.equal(resultRegister.events['4'].raw.data, points.x.concat(points.y.substr(2)))
|
||||||
|
assert.equal(resultRegister.events['5'].raw.topics[0], '0xd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266', "Wrong Event");
|
||||||
|
assert.equal(resultRegister.events['5'].raw.topics[1], usernameHash, "Wrong Username");
|
||||||
|
assert.equal(web3Utils.toChecksumAddress("0x"+resultRegister.events['5'].raw.data.substring(26)), registrant, "Wrong registry.namehash owner");
|
||||||
|
assert.equal(resultRegister.events.UsernameOwner.returnValues.owner, registrant, "event UsernameOwner owner mismatch");
|
||||||
|
assert.equal(resultRegister.events.UsernameOwner.returnValues.nameHash, usernameHash, "event UsernameOwner usernameHash mismatch");
|
||||||
|
assert.equal(await ens.methods.owner(usernameHash).call(), registrant, "ENSRegistry owner mismatch");
|
||||||
|
assert.equal(await ens.methods.resolver(usernameHash).call(), PublicResolver.address, "Resolver wrongly defined");
|
||||||
|
assert.equal(await UsernameRegistrar.methods.getAccountBalance(label).call(), registry.price, "Wrong account balance");
|
||||||
|
assert.equal(await UsernameRegistrar.methods.getAccountOwner(label).call(), registrant, "Account owner mismatch");
|
||||||
|
assert.equal(await PublicResolver.methods.addr(usernameHash).call(), registrant, "Resolved address not set");
|
||||||
|
const resolverPubKey = await PublicResolver.methods.pubkey(usernameHash).call();
|
||||||
|
const pubKey = utils.keyFromXY(resolverPubKey[0], resolverPubKey[1]);
|
||||||
|
assert.equal(pubKey, contactCode, "pubKey does not match contract code");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('receiveApproval()', function() {
|
||||||
|
it('should register username', async () => {
|
||||||
|
const registrant = accountsArr[5];
|
||||||
|
const username = 'erinauto';
|
||||||
|
const usernameHash = namehash.hash(username + '.' + registry.registry);
|
||||||
|
const label = web3Utils.sha3(username);
|
||||||
|
const registryPrice = await UsernameRegistrar.methods.getPrice().call()
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
const initialRegistrantBalance = await TestToken.methods.balanceOf(registrant).call();
|
||||||
|
const initialRegistryBalance = await TestToken.methods.balanceOf(UsernameRegistrar.address).call();
|
||||||
|
|
||||||
|
const registerCall = UsernameRegistrar.methods.register(
|
||||||
|
web3Utils.sha3(username),
|
||||||
|
utils.zeroAddress,
|
||||||
|
utils.zeroBytes32,
|
||||||
|
utils.zeroBytes32
|
||||||
|
).encodeABI();
|
||||||
|
const approveAndCallResult = await TestToken.methods.approveAndCall(UsernameRegistrar.address, registry.price, registerCall).send({from: registrant});
|
||||||
|
// TODO: check events
|
||||||
|
assert.equal(await ens.methods.owner(usernameHash).call(), registrant, "ENSRegistry owner mismatch");
|
||||||
|
assert.equal(await ens.methods.resolver(usernameHash).call(), utils.zeroAddress, "Resolver wrongly defined");
|
||||||
|
assert.equal(await UsernameRegistrar.methods.getAccountBalance(label).call(), registryPrice, "Registry username account balance wrong");
|
||||||
|
assert.equal(await UsernameRegistrar.methods.getAccountOwner(label).call(), registrant, "Account owner mismatch");
|
||||||
|
assert.equal(await TestToken.methods.balanceOf(registrant).call(), +initialRegistrantBalance-registryPrice, "User final balance wrong")
|
||||||
|
assert.equal(await TestToken.methods.balanceOf(UsernameRegistrar.address).call(), (+initialRegistryBalance)+(+registry.price), "Registry final balance wrong")
|
||||||
|
});
|
||||||
|
it('should register username only resolveing address ', async () => {
|
||||||
|
const registrant = accountsArr[2];
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
const username = 'bobauto';
|
||||||
|
const usernameHash = namehash.hash(username + '.' + registry.registry);
|
||||||
|
const label = web3Utils.sha3(username);
|
||||||
|
const registerCall = UsernameRegistrar.methods.register(
|
||||||
|
web3Utils.sha3(username),
|
||||||
|
|
||||||
|
registrant,
|
||||||
|
utils.zeroBytes32,
|
||||||
|
utils.zeroBytes32
|
||||||
|
).encodeABI();
|
||||||
|
|
||||||
|
const approveAndCallResult = await TestToken.methods.approveAndCall(UsernameRegistrar.address, registry.price, registerCall).send({from: registrant});
|
||||||
|
// TODO: check events
|
||||||
|
|
||||||
|
assert.equal(await ens.methods.owner(usernameHash).call(), registrant, "ENSRegistry owner mismatch");
|
||||||
|
assert.equal(await ens.methods.resolver(usernameHash).call(), PublicResolver.address, "Resolver wrongly defined");
|
||||||
|
assert.equal(await UsernameRegistrar.methods.getAccountBalance(label).call(), registry.price, "Wrong account balance");
|
||||||
|
assert.equal(await UsernameRegistrar.methods.getAccountOwner(label).call(), registrant, "Account owner mismatch");
|
||||||
|
assert.equal(await PublicResolver.methods.addr(usernameHash).call(), registrant, "Resolved address not set");
|
||||||
|
const resolverPubKey = await PublicResolver.methods.pubkey(usernameHash).call();
|
||||||
|
assert.equal(resolverPubKey[0], utils.zeroBytes32 , "Unexpected resolved pubkey[0]");
|
||||||
|
assert.equal(resolverPubKey[1], utils.zeroBytes32 , "Unexpected resolved pubkey[1]");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should register username with only status contact', async () => {
|
||||||
|
const username = 'carlosauto';
|
||||||
|
const registrant = accountsArr[3];
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
const usernameHash = namehash.hash(username + '.' + registry.registry);
|
||||||
|
const contactCode = '0x04dbb31252d9bddb4e4d362c7b9c80cba74732280737af97971f42ccbdc716f3f3efb1db366880e52d09b1bfd59842e833f3004088892b7d14b9ce9e957cea9a82';
|
||||||
|
const points = utils.generateXY(contactCode);
|
||||||
|
const label = web3Utils.sha3(username);
|
||||||
|
const registerCall = UsernameRegistrar.methods.register(
|
||||||
|
web3Utils.sha3(username),
|
||||||
|
utils.zeroAddress,
|
||||||
|
points.x,
|
||||||
|
points.y
|
||||||
|
).encodeABI();
|
||||||
|
|
||||||
|
const approveAndCallResult = await TestToken.methods.approveAndCall(UsernameRegistrar.address, registry.price, registerCall).send({from: registrant});
|
||||||
|
// TODO: check events
|
||||||
|
assert.equal(await ens.methods.owner(usernameHash).call(), registrant, "ENSRegistry owner mismatch");
|
||||||
|
assert.equal(await ens.methods.resolver(usernameHash).call(), PublicResolver.address, "Resolver wrongly defined");
|
||||||
|
assert.equal(await UsernameRegistrar.methods.getAccountBalance(label).call(), registry.price, "Wrong account balance");
|
||||||
|
assert.equal(await UsernameRegistrar.methods.getAccountOwner(label).call(), registrant, "Account owner mismatch");
|
||||||
|
assert.equal(await PublicResolver.methods.addr(usernameHash).call(), utils.zeroAddress, "Resolved address not set");
|
||||||
|
const resolverPubKey = await PublicResolver.methods.pubkey(usernameHash).call();
|
||||||
|
const pubKey = utils.keyFromXY(resolverPubKey[0], resolverPubKey[1]);
|
||||||
|
assert.equal(pubKey, contactCode, "pubKey does not match contract code");
|
||||||
|
});
|
||||||
|
it('should register username with status contact code and address', async () => {
|
||||||
|
const registrant = accountsArr[2];
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
const username = 'bob2auto';
|
||||||
|
const usernameHash = namehash.hash(username + '.' + registry.registry);
|
||||||
|
const contactCode = '0x04dbb31252d9bddb4e4d362c7b9c80cba74732280737af97971f42ccbdc716f3f3efb1db366880e52d09b1bfd59842e833f3004088892b7d14b9ce9e957cea9a82';
|
||||||
|
const points = utils.generateXY(contactCode);
|
||||||
|
const label = web3Utils.sha3(username);
|
||||||
|
const registerCall = UsernameRegistrar.methods.register(
|
||||||
|
label,
|
||||||
|
registrant,
|
||||||
|
points.x,
|
||||||
|
points.y
|
||||||
|
).encodeABI();
|
||||||
|
|
||||||
|
const approveAndCallResult = await TestToken.methods.approveAndCall(UsernameRegistrar.address, registry.price, registerCall).send({from: registrant});
|
||||||
|
// TODO: check events
|
||||||
|
assert.equal(await ens.methods.owner(usernameHash).call(), registrant, "ENSRegistry owner mismatch");
|
||||||
|
assert.equal(await ens.methods.resolver(usernameHash).call(), PublicResolver.address, "Resolver wrongly defined");
|
||||||
|
assert.equal(await UsernameRegistrar.methods.getAccountBalance(label).call(), registry.price, "Wrong account balance");
|
||||||
|
assert.equal(await UsernameRegistrar.methods.getAccountOwner(label).call(), registrant, "Account owner mismatch");
|
||||||
|
assert.equal(await PublicResolver.methods.addr(usernameHash).call(), registrant, "Resolved address not set");
|
||||||
|
const resolverPubKey = await PublicResolver.methods.pubkey(usernameHash).call();
|
||||||
|
const pubKey = utils.keyFromXY(resolverPubKey[0], resolverPubKey[1]);
|
||||||
|
assert.equal(pubKey, contactCode, "pubKey does not match contract code");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('release()', function() {
|
||||||
|
it('should not release username due delay', async () => {
|
||||||
|
let registrant = accountsArr[6];
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant});
|
||||||
|
let username = 'mistaker';
|
||||||
|
await UsernameRegistrar.methods.register(
|
||||||
|
web3Utils.sha3(username),
|
||||||
|
utils.zeroAddress,
|
||||||
|
utils.zeroBytes32,
|
||||||
|
utils.zeroBytes32
|
||||||
|
).send({from: registrant});
|
||||||
|
let failed;
|
||||||
|
try{
|
||||||
|
await UsernameRegistrar.methods.release(
|
||||||
|
web3Utils.sha3(username),
|
||||||
|
).send({from: registrant});
|
||||||
|
failed = false;
|
||||||
|
} catch(e){
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
assert(failed, "Released after delay period");
|
||||||
|
});
|
||||||
|
it('should release username', async () => {;
|
||||||
|
const registrant = accountsArr[6];
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant});
|
||||||
|
const username = 'frank';
|
||||||
|
const label = web3Utils.sha3(username);
|
||||||
|
await UsernameRegistrar.methods.register(
|
||||||
|
label,
|
||||||
|
utils.zeroAddress,
|
||||||
|
utils.zeroBytes32,
|
||||||
|
utils.zeroBytes32
|
||||||
|
).send({from: registrant});
|
||||||
|
const releaseDelay = await UsernameRegistrar.methods.releaseDelay().call();
|
||||||
|
await utils.increaseTime(releaseDelay)
|
||||||
|
await utils.increaseTime(1000)
|
||||||
|
const initialAccountBalance = await UsernameRegistrar.methods.getAccountBalance(label).call();
|
||||||
|
const initialRegistrantBalance = await TestToken.methods.balanceOf(registrant).call();
|
||||||
|
const initialRegistryBalance = await TestToken.methods.balanceOf(UsernameRegistrar.address).call();
|
||||||
|
await utils.increaseTime(1000)
|
||||||
|
const resultRelease = await UsernameRegistrar.methods.release(
|
||||||
|
web3Utils.sha3(username),
|
||||||
|
|
||||||
|
).send({from: registrant});
|
||||||
|
//TODO: check events
|
||||||
|
assert.equal(await UsernameRegistrar.methods.getAccountBalance(label).call(), 0, "Final balance didnt zeroed");
|
||||||
|
assert.equal(await TestToken.methods.balanceOf(registrant).call(), (+initialRegistrantBalance)+(+initialAccountBalance), "Releaser token balance didnt increase")
|
||||||
|
assert.equal(await TestToken.methods.balanceOf(UsernameRegistrar.address).call(), (+initialRegistryBalance)-(+initialAccountBalance), "Registry token balance didnt decrease")
|
||||||
|
});
|
||||||
|
it('should release transfered username', async () => {
|
||||||
|
let registrant = accountsArr[7];
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant});
|
||||||
|
let username = 'grace';
|
||||||
|
let usernameHash = namehash.hash(username + '.' + registry.registry);
|
||||||
|
let label = web3Utils.sha3(username);
|
||||||
|
let newOwner = accountsArr[8];
|
||||||
|
await UsernameRegistrar.methods.register(
|
||||||
|
label,
|
||||||
|
utils.zeroAddress,
|
||||||
|
utils.zeroBytes32,
|
||||||
|
utils.zeroBytes32
|
||||||
|
).send({from: registrant});
|
||||||
|
await ens.methods.setOwner(usernameHash, newOwner).send({from: registrant});
|
||||||
|
let releaseDelay = await UsernameRegistrar.methods.releaseDelay().call();
|
||||||
|
await utils.increaseTime(releaseDelay)
|
||||||
|
await utils.increaseTime(1000)
|
||||||
|
let initialAccountBalance = await UsernameRegistrar.methods.getAccountBalance(label).call();
|
||||||
|
let initialRegistrantBalance = await TestToken.methods.balanceOf(newOwner).call();
|
||||||
|
let initialRegistryBalance = await TestToken.methods.balanceOf(UsernameRegistrar.address).call();
|
||||||
|
await utils.increaseTime(1000)
|
||||||
|
let resultRelease = await UsernameRegistrar.methods.release(
|
||||||
|
label
|
||||||
|
).send({from: newOwner});
|
||||||
|
//TODO: check events
|
||||||
|
assert.equal(await UsernameRegistrar.methods.getAccountBalance(label).call(), 0, "Final balance didnt zeroed");
|
||||||
|
assert.equal(await TestToken.methods.balanceOf(newOwner).call(), (+initialRegistrantBalance)+(+initialAccountBalance), "New owner token balance didnt increase")
|
||||||
|
assert.equal(await TestToken.methods.balanceOf(UsernameRegistrar.address).call(), (+initialRegistryBalance)-(+initialAccountBalance), "Registry token balance didnt decrease")
|
||||||
|
});
|
||||||
|
it('should release moved username account balance by owner', async () => {
|
||||||
|
const registrant = accountsArr[5];
|
||||||
|
UsernameRegistrar
|
||||||
|
UpdatedUsernameRegistrar
|
||||||
|
await TestToken.methods.mint(dummyRegistry.price).send({from: registrant});
|
||||||
|
await DummyUsernameRegistrar.methods.activate(dummyRegistry.price).send({from: accountsArr[0]});
|
||||||
|
await TestToken.methods.approve(DummyUsernameRegistrar.address, dummyRegistry.price).send({from: registrant});
|
||||||
|
|
||||||
|
const username = 'hardhead';
|
||||||
|
const label = web3Utils.sha3(username);
|
||||||
|
const usernameHash = namehash.hash(username + '.' + dummyRegistry.registry);
|
||||||
|
await DummyUsernameRegistrar.methods.register(
|
||||||
|
label,
|
||||||
|
registrant,
|
||||||
|
utils.zeroBytes32,
|
||||||
|
utils.zeroBytes32
|
||||||
|
).send({from: registrant});
|
||||||
|
let initialAccountBalance = await DummyUsernameRegistrar.methods.getAccountBalance(label).call();
|
||||||
|
const initialRegistrantBalance = await TestToken.methods.balanceOf(registrant).call();
|
||||||
|
const initialRegistryBalance = await TestToken.methods.balanceOf(DummyUsernameRegistrar.address).call();
|
||||||
|
await DummyUsernameRegistrar.methods.moveRegistry(UpdatedDummyUsernameRegistrar.address).send();
|
||||||
|
|
||||||
|
assert.equal(await ens.methods.owner(usernameHash).call(), registrant, "ENSRegistry owner mismatch");
|
||||||
|
assert.equal(await ens.methods.resolver(usernameHash).call(), PublicResolver.address, "Resolver wrongly defined");
|
||||||
|
assert.equal(await PublicResolver.methods.addr(usernameHash).call(), registrant, "Resolved address not set");
|
||||||
|
|
||||||
|
const resultRelease = await DummyUsernameRegistrar.methods.release(
|
||||||
|
label
|
||||||
|
).send({from: registrant});
|
||||||
|
//TODO: verify events
|
||||||
|
assert.equal(await TestToken.methods.balanceOf(registrant).call(), (+initialRegistrantBalance)+(+initialAccountBalance), "New owner token balance didnt increase")
|
||||||
|
assert.equal(await TestToken.methods.balanceOf(DummyUsernameRegistrar.address).call(), (+initialRegistryBalance)-(+initialAccountBalance), "Registry token balance didnt decrease")
|
||||||
|
assert.equal(await ens.methods.resolver(usernameHash).call(), utils.zeroAddress, "Resolver not undefined");
|
||||||
|
assert.equal(await ens.methods.owner(usernameHash).call(), utils.zeroAddress, "Owner not removed");
|
||||||
|
//We are not cleaning PublicResolver or any resolver, so the value should remain the same.
|
||||||
|
assert.equal(await PublicResolver.methods.addr(usernameHash).call(), registrant, "Resolved address not set");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('updateAccountOwner()', function() {
|
||||||
|
it('should update username account owner', async () => {
|
||||||
|
let username = 'heidi';
|
||||||
|
let label = web3Utils.sha3(username);
|
||||||
|
let registrant = accountsArr[8];
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant});
|
||||||
|
let newOwner = accountsArr[9];
|
||||||
|
let usernameHash = namehash.hash(username + '.' + registry.registry);
|
||||||
|
|
||||||
|
await UsernameRegistrar.methods.register(
|
||||||
|
label,
|
||||||
|
utils.zeroAddress,
|
||||||
|
utils.zeroBytes32,
|
||||||
|
utils.zeroBytes32
|
||||||
|
).send({from: registrant});
|
||||||
|
await ens.methods.setOwner(usernameHash, newOwner).send({from: registrant});
|
||||||
|
let resultUpdateOwner = await UsernameRegistrar.methods.updateAccountOwner(
|
||||||
|
label
|
||||||
|
).send({from: newOwner});
|
||||||
|
//TODO: check events
|
||||||
|
assert.equal(await UsernameRegistrar.methods.getAccountOwner(label).call(), newOwner, "Backup owner not updated");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('slashInvalidUsername()', function() {
|
||||||
|
it('should slash invalid username', async () => {
|
||||||
|
let username = 'alicé';
|
||||||
|
let label = web3Utils.sha3(username);
|
||||||
|
let usernameHash = namehash.hash(username + '.' + registry.registry);
|
||||||
|
let registrant = accountsArr[1];
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant});
|
||||||
|
await UsernameRegistrar.methods.register(
|
||||||
|
web3Utils.sha3(username),
|
||||||
|
|
||||||
|
utils.zeroAddress,
|
||||||
|
utils.zeroBytes32,
|
||||||
|
utils.zeroBytes32
|
||||||
|
).send({from: registrant});
|
||||||
|
assert.equal(await ens.methods.owner(usernameHash).call(), registrant);
|
||||||
|
assert.notEqual(await UsernameRegistrar.methods.getCreationTime(label).call(), 0);
|
||||||
|
await UsernameRegistrar.methods.slashInvalidUsername(web3Utils.toHex(username), 4).send()
|
||||||
|
//TODO: check events
|
||||||
|
assert.equal(await UsernameRegistrar.methods.getCreationTime(label).call(), 0);
|
||||||
|
assert.equal(await ens.methods.owner(usernameHash).call(), utils.zeroAddress);
|
||||||
|
});
|
||||||
|
it('should not slash valid username', async () => {
|
||||||
|
const username = 'legituser';
|
||||||
|
const registrant = accountsArr[1];
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant});
|
||||||
|
await UsernameRegistrar.methods.register(
|
||||||
|
web3Utils.sha3(username),
|
||||||
|
utils.zeroAddress,
|
||||||
|
utils.zeroBytes32,
|
||||||
|
utils.zeroBytes32
|
||||||
|
).send({from: registrant});
|
||||||
|
let failed;
|
||||||
|
try{
|
||||||
|
await UsernameRegistrar.methods.slashInvalidUsername(web3Utils.toHex(username), 4).send()
|
||||||
|
failed = false;
|
||||||
|
} catch(e){
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
assert(failed, "Was slashed anyway");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('slashReservedUsername()', function() {
|
||||||
|
it('should not slash not reserved name username', async () => {
|
||||||
|
const username = 'somedummyname123';
|
||||||
|
const usernameHash = namehash.hash(username + '.' + registry.registry);
|
||||||
|
const registrant = accountsArr[1];
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant});
|
||||||
|
await UsernameRegistrar.methods.register(
|
||||||
|
web3Utils.sha3(username),
|
||||||
|
utils.zeroAddress,
|
||||||
|
utils.zeroBytes32,
|
||||||
|
utils.zeroBytes32
|
||||||
|
).send({from: registrant});
|
||||||
|
assert.equal(await ens.methods.owner(usernameHash).call(), registrant);
|
||||||
|
let failed;
|
||||||
|
try{
|
||||||
|
await UsernameRegistrar.methods.slashReservedUsername(web3Utils.toHex(username), merkleTree.getHexProof(reservedNames[0])).send()
|
||||||
|
failed = false;
|
||||||
|
} catch(e){
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
assert(failed, "Was slashed anyway");
|
||||||
|
});
|
||||||
|
it('should not slash reserved name username with wrong proof ', async () => {
|
||||||
|
const username = reservedNames[5];
|
||||||
|
const usernameHash = namehash.hash(username + '.' + registry.registry);
|
||||||
|
const registrant = accountsArr[1];
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant});
|
||||||
|
await UsernameRegistrar.methods.register(
|
||||||
|
web3Utils.sha3(username),
|
||||||
|
utils.zeroAddress,
|
||||||
|
utils.zeroBytes32,
|
||||||
|
utils.zeroBytes32
|
||||||
|
).send({from: registrant});
|
||||||
|
assert.equal(await ens.methods.owner(usernameHash).call(), registrant);
|
||||||
|
let failed;
|
||||||
|
try{
|
||||||
|
await UsernameRegistrar.methods.slashReservedUsername(web3Utils.toHex(username), merkleTree.getHexProof(reservedNames[1])).send()
|
||||||
|
failed = false;
|
||||||
|
} catch(e){
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
assert(failed, "Was slashed anyway");
|
||||||
|
});
|
||||||
|
it('should slash reserved name username', async () => {
|
||||||
|
const username = reservedNames[7];
|
||||||
|
const usernameHash = namehash.hash(username + '.' + registry.registry);
|
||||||
|
const registrant = accountsArr[1];
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant});
|
||||||
|
await UsernameRegistrar.methods.register(
|
||||||
|
web3Utils.sha3(username),
|
||||||
|
utils.zeroAddress,
|
||||||
|
utils.zeroBytes32,
|
||||||
|
utils.zeroBytes32
|
||||||
|
).send({from: registrant});
|
||||||
|
assert.equal(await ens.methods.owner(usernameHash).call(), registrant);
|
||||||
|
result = await UsernameRegistrar.methods.slashReservedUsername(web3Utils.toHex(username), merkleTree.getHexProof(username)).send()
|
||||||
|
//TODO: check events
|
||||||
|
assert.equal(await ens.methods.owner(usernameHash).call(), utils.zeroAddress);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('slashSmallUsername()', function() {
|
||||||
|
it('should not slash big username', async() =>{
|
||||||
|
let username = '1234567890';
|
||||||
|
let registrant = accountsArr[1];
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant});
|
||||||
|
await UsernameRegistrar.methods.register(
|
||||||
|
web3Utils.sha3(username),
|
||||||
|
utils.zeroAddress,
|
||||||
|
utils.zeroBytes32,
|
||||||
|
utils.zeroBytes32
|
||||||
|
).send({from: registrant});
|
||||||
|
let failed;
|
||||||
|
try{
|
||||||
|
await UsernameRegistrar.methods.slashSmallUsername(web3Utils.toHex(username)).send()
|
||||||
|
failed = false;
|
||||||
|
} catch(e){
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
assert(failed, "Was slashed anyway");
|
||||||
|
})
|
||||||
|
it('should slash small username', async () => {
|
||||||
|
let username = 'a';
|
||||||
|
let usernameHash = namehash.hash(username + '.' + registry.registry);
|
||||||
|
let registrant = accountsArr[1];
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant});
|
||||||
|
await UsernameRegistrar.methods.register(
|
||||||
|
web3Utils.sha3(username),
|
||||||
|
|
||||||
|
utils.zeroAddress,
|
||||||
|
utils.zeroBytes32,
|
||||||
|
utils.zeroBytes32
|
||||||
|
).send({from: registrant});
|
||||||
|
assert.equal(await ens.methods.owner(usernameHash).call(), registrant);
|
||||||
|
result = await UsernameRegistrar.methods.slashSmallUsername(web3Utils.toHex(username)).send()
|
||||||
|
assert.equal(await ens.methods.owner(usernameHash).call(), utils.zeroAddress);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('slashAddressLikeUsername()', function() {
|
||||||
|
it('should slash username that starts with 0x and is 12 of lenght or bigger', async () => {
|
||||||
|
let username = "0xc6b95bd26123";
|
||||||
|
let userlabelHash = "0xe311c0592b075c30277c679f0daea74ee1727547efa522fd28d20a8f2c3e435b"; //sha3("0xc6b95bd26123")
|
||||||
|
let usernameHash = "0xb707907c2749895522150dd9e6dec4f71f1662ce873f8bf0b8682fa052ff495e"; //namehash("0xc6b95bd26123.stateofus.eth")
|
||||||
|
let registrant = accountsArr[1];
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant});
|
||||||
|
await UsernameRegistrar.methods.register(
|
||||||
|
userlabelHash,
|
||||||
|
utils.zeroAddress,
|
||||||
|
utils.zeroBytes32,
|
||||||
|
utils.zeroBytes32
|
||||||
|
).send({from: registrant});
|
||||||
|
assert.equal(await ens.methods.owner(usernameHash).call(), registrant);
|
||||||
|
result = await UsernameRegistrar.methods.slashAddressLikeUsername(web3Utils.toHex(username)).send()
|
||||||
|
assert.equal(await ens.methods.owner(usernameHash).call(), utils.zeroAddress);
|
||||||
|
});
|
||||||
|
it('should not slash username that starts with 0x but is smaller then 12', async () => {
|
||||||
|
let username = "0xc6b95bd26";
|
||||||
|
let userlabelHash = "0x59bf8d16c517a40a5dacc3471abd002f3bc0850a13e930e4bee49070a58517e8"; //sha3("0xc6b95bd26")
|
||||||
|
let registrant = accountsArr[1];
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant});
|
||||||
|
await UsernameRegistrar.methods.register(
|
||||||
|
userlabelHash,
|
||||||
|
utils.zeroAddress,
|
||||||
|
utils.zeroBytes32,
|
||||||
|
utils.zeroBytes32
|
||||||
|
).send({from: registrant});
|
||||||
|
let failed;
|
||||||
|
try{
|
||||||
|
result = await UsernameRegistrar.methods.slashAddressLikeUsername(username).send()
|
||||||
|
failed = false;
|
||||||
|
} catch(e){
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
assert(failed, "Was slashed anyway");
|
||||||
|
});
|
||||||
|
it('should not slash username that dont starts 0x and is bigger than 12', async () => {
|
||||||
|
const username = "0a002322c6b95bd26";
|
||||||
|
const userlabelHash = "0xe4769e5c31ff61ac50dce20559a4411a4ca45d94c733cbeda7ab9f28ed75cef1"; //sha3("0a002322c6b95bd26")
|
||||||
|
const registrant = accountsArr[1];
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant});
|
||||||
|
await UsernameRegistrar.methods.register(
|
||||||
|
userlabelHash,
|
||||||
|
utils.zeroAddress,
|
||||||
|
utils.zeroBytes32,
|
||||||
|
utils.zeroBytes32
|
||||||
|
).send({from: registrant});
|
||||||
|
let failed;
|
||||||
|
try{
|
||||||
|
await UsernameRegistrar.methods.slashAddressLikeUsername(username).send()
|
||||||
|
failed = false;
|
||||||
|
} catch(e){
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
assert(failed, "Was slashed anyway");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('slashUsername()', function() {
|
||||||
|
it('should slash a username and get funds from registrant', async () => {
|
||||||
|
const username = 'b';
|
||||||
|
const usernameHash = namehash.hash(username + '.' + registry.registry);
|
||||||
|
const registrant = accountsArr[1];
|
||||||
|
const slasher = accountsArr[2];
|
||||||
|
await TestToken.methods.mint(registry.price).send({from: registrant});
|
||||||
|
await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant});
|
||||||
|
await UsernameRegistrar.methods.register(
|
||||||
|
web3Utils.sha3(username),
|
||||||
|
utils.zeroAddress,
|
||||||
|
utils.zeroBytes32,
|
||||||
|
utils.zeroBytes32
|
||||||
|
).send({from: registrant});
|
||||||
|
assert.equal(await ens.methods.owner(usernameHash).call(), registrant);
|
||||||
|
const initialSlasherBalance = await TestToken.methods.balanceOf(slasher).call();
|
||||||
|
await UsernameRegistrar.methods.slashSmallUsername(web3Utils.toHex(username)).send({from: slasher})
|
||||||
|
//TODO: check events
|
||||||
|
assert.equal(await TestToken.methods.balanceOf(slasher).call(), (+initialSlasherBalance)+(+registry.price));
|
||||||
|
assert.equal(await ens.methods.owner(usernameHash).call(), utils.zeroAddress);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('moveRegistry()', function() {
|
||||||
|
it('should move registry to new registry and migrate', async () => {
|
||||||
|
const result = await UsernameRegistrar.methods.moveRegistry(UpdatedUsernameRegistrar.address).send();
|
||||||
|
//TODO: check events
|
||||||
|
assert.equal(await ens.methods.owner(registry.namehash).call(), UpdatedUsernameRegistrar.address, "registry ownership not moved correctly")
|
||||||
|
assert.equal(await UpdatedUsernameRegistrar.methods.getPrice().call(), registry.price, "updated registry didnt migrated price")
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('moveAccount()', function() {
|
||||||
|
it('should move username to new registry by account owner', async () => {
|
||||||
|
const registrant = accountsArr[5];
|
||||||
|
const username = 'erin';
|
||||||
|
const usernameHash = namehash.hash(username + '.' + registry.registry);
|
||||||
|
const label = web3Utils.sha3(username);
|
||||||
|
|
||||||
|
const accountBalance = await UsernameRegistrar.methods.getAccountBalance(label).call()
|
||||||
|
assert.notEqual(accountBalance, 0);
|
||||||
|
const initialRegistryBalance = await TestToken.methods.balanceOf(UsernameRegistrar.address).call();
|
||||||
|
const initialUpdatedRegistryBalance = await TestToken.methods.balanceOf(UpdatedUsernameRegistrar.address).call();
|
||||||
|
const creationTime = await UsernameRegistrar.methods.getCreationTime(label).call();
|
||||||
|
assert.notEqual(creationTime, 0);
|
||||||
|
assert.equal(await UpdatedUsernameRegistrar.methods.getCreationTime(label).call(), 0);
|
||||||
|
const result = await UsernameRegistrar.methods.moveAccount(label).send({from: registrant});
|
||||||
|
assert.equal(await UsernameRegistrar.methods.getCreationTime(label).call(), 0);
|
||||||
|
assert.equal(await UpdatedUsernameRegistrar.methods.getCreationTime(label).call(), creationTime);
|
||||||
|
assert.equal(await TestToken.methods.balanceOf(UsernameRegistrar.address).call(), (+initialRegistryBalance)-(+accountBalance))
|
||||||
|
assert.equal(await TestToken.methods.balanceOf(UpdatedUsernameRegistrar.address).call(), (+initialUpdatedRegistryBalance)+(+accountBalance))
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
Loading…
Reference in New Issue