diff --git a/webapp/package-lock.json b/webapp/package-lock.json
index 648684e121..1ecd9a1ef2 100644
--- a/webapp/package-lock.json
+++ b/webapp/package-lock.json
@@ -4248,31 +4248,6 @@
"@ethersproject/strings": "^5.7.0"
}
},
- "node_modules/@fluentui/react-component-event-listener": {
- "version": "0.63.1",
- "resolved": "https://registry.npmjs.org/@fluentui/react-component-event-listener/-/react-component-event-listener-0.63.1.tgz",
- "integrity": "sha512-gSMdOh6tI3IJKZFqxfQwbTpskpME0CvxdxGM2tdglmf6ZPVDi0L4+KKIm+2dN8nzb8Ya1A8ZT+Ddq0KmZtwVQg==",
- "dependencies": {
- "@babel/runtime": "^7.10.4"
- },
- "peerDependencies": {
- "react": "^16.8.0 || ^17 || ^18",
- "react-dom": "^16.8.0 || ^17 || ^18"
- }
- },
- "node_modules/@fluentui/react-component-ref": {
- "version": "0.63.1",
- "resolved": "https://registry.npmjs.org/@fluentui/react-component-ref/-/react-component-ref-0.63.1.tgz",
- "integrity": "sha512-8MkXX4+R3i80msdbD4rFpEB4WWq2UDvGwG386g3ckIWbekdvN9z2kWAd9OXhRGqB7QeOsoAGWocp6gAMCivRlw==",
- "dependencies": {
- "@babel/runtime": "^7.10.4",
- "react-is": "^16.6.3"
- },
- "peerDependencies": {
- "react": "^16.8.0 || ^17 || ^18",
- "react-dom": "^16.8.0 || ^17 || ^18"
- }
- },
"node_modules/@formatjs/ecma402-abstract": {
"version": "1.11.4",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz",
@@ -5342,19 +5317,6 @@
"@scure/base": "~1.0.0"
}
},
- "node_modules/@semantic-ui-react/event-stack": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/@semantic-ui-react/event-stack/-/event-stack-3.1.3.tgz",
- "integrity": "sha512-FdTmJyWvJaYinHrKRsMLDrz4tTMGdFfds299Qory53hBugiDvGC0tEJf+cHsi5igDwWb/CLOgOiChInHwq8URQ==",
- "dependencies": {
- "exenv": "^1.2.2",
- "prop-types": "^15.6.2"
- },
- "peerDependencies": {
- "react": "^16.0.0 || ^17.0.0 || ^18.0.0",
- "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0"
- }
- },
"node_modules/@sentry-internal/tracing": {
"version": "7.64.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.64.0.tgz",
@@ -10773,19 +10735,6 @@
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz",
"integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
},
- "node_modules/dayzed": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/dayzed/-/dayzed-3.2.3.tgz",
- "integrity": "sha512-qXTIKs+R6ydWwNo+X1wu3lUptyRSGoyY+ZzRcQSM0zUlaG+/Ei+bFjqbQm1T2oJ+WKNkTHURBcGsxnx9N+9kfA==",
- "dependencies": {
- "@babel/runtime": "^7.6.2",
- "date-fns": "^2.0.0"
- },
- "peerDependencies": {
- "prop-types": "^15",
- "react": "^16.8 || ^17.0 || ^18.0"
- }
- },
"node_modules/dcl-catalyst-client": {
"version": "21.5.0",
"resolved": "https://registry.npmjs.org/dcl-catalyst-client/-/dcl-catalyst-client-21.5.0.tgz",
@@ -22551,20 +22500,6 @@
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
- "node_modules/react-popper": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz",
- "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==",
- "dependencies": {
- "react-fast-compare": "^3.0.1",
- "warning": "^4.0.2"
- },
- "peerDependencies": {
- "@popperjs/core": "^2.0.0",
- "react": "^16.8.0 || ^17 || ^18",
- "react-dom": "^16.8.0 || ^17 || ^18"
- }
- },
"node_modules/react-redux": {
"version": "7.2.4",
"license": "MIT",
@@ -22861,6 +22796,19 @@
"node": ">=14.0.0"
}
},
+ "node_modules/react-semantic-ui-datepickers/node_modules/dayzed": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/dayzed/-/dayzed-3.2.3.tgz",
+ "integrity": "sha512-qXTIKs+R6ydWwNo+X1wu3lUptyRSGoyY+ZzRcQSM0zUlaG+/Ei+bFjqbQm1T2oJ+WKNkTHURBcGsxnx9N+9kfA==",
+ "dependencies": {
+ "@babel/runtime": "^7.6.2",
+ "date-fns": "^2.0.0"
+ },
+ "peerDependencies": {
+ "prop-types": "^15",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ }
+ },
"node_modules/react-tile-map": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/react-tile-map/-/react-tile-map-0.4.1.tgz",
@@ -24342,6 +24290,58 @@
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
+ "node_modules/semantic-ui-react/node_modules/@fluentui/react-component-event-listener": {
+ "version": "0.63.1",
+ "resolved": "https://registry.npmjs.org/@fluentui/react-component-event-listener/-/react-component-event-listener-0.63.1.tgz",
+ "integrity": "sha512-gSMdOh6tI3IJKZFqxfQwbTpskpME0CvxdxGM2tdglmf6ZPVDi0L4+KKIm+2dN8nzb8Ya1A8ZT+Ddq0KmZtwVQg==",
+ "dependencies": {
+ "@babel/runtime": "^7.10.4"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17 || ^18",
+ "react-dom": "^16.8.0 || ^17 || ^18"
+ }
+ },
+ "node_modules/semantic-ui-react/node_modules/@fluentui/react-component-ref": {
+ "version": "0.63.1",
+ "resolved": "https://registry.npmjs.org/@fluentui/react-component-ref/-/react-component-ref-0.63.1.tgz",
+ "integrity": "sha512-8MkXX4+R3i80msdbD4rFpEB4WWq2UDvGwG386g3ckIWbekdvN9z2kWAd9OXhRGqB7QeOsoAGWocp6gAMCivRlw==",
+ "dependencies": {
+ "@babel/runtime": "^7.10.4",
+ "react-is": "^16.6.3"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17 || ^18",
+ "react-dom": "^16.8.0 || ^17 || ^18"
+ }
+ },
+ "node_modules/semantic-ui-react/node_modules/@semantic-ui-react/event-stack": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@semantic-ui-react/event-stack/-/event-stack-3.1.3.tgz",
+ "integrity": "sha512-FdTmJyWvJaYinHrKRsMLDrz4tTMGdFfds299Qory53hBugiDvGC0tEJf+cHsi5igDwWb/CLOgOiChInHwq8URQ==",
+ "dependencies": {
+ "exenv": "^1.2.2",
+ "prop-types": "^15.6.2"
+ },
+ "peerDependencies": {
+ "react": "^16.0.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/semantic-ui-react/node_modules/react-popper": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz",
+ "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==",
+ "dependencies": {
+ "react-fast-compare": "^3.0.1",
+ "warning": "^4.0.2"
+ },
+ "peerDependencies": {
+ "@popperjs/core": "^2.0.0",
+ "react": "^16.8.0 || ^17 || ^18",
+ "react-dom": "^16.8.0 || ^17 || ^18"
+ }
+ },
"node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
diff --git a/webapp/src/components/AssetBrowse/AssetBrowse.tsx b/webapp/src/components/AssetBrowse/AssetBrowse.tsx
index 3d2a2823ce..840aff3517 100644
--- a/webapp/src/components/AssetBrowse/AssetBrowse.tsx
+++ b/webapp/src/components/AssetBrowse/AssetBrowse.tsx
@@ -33,7 +33,6 @@ import CollectionList from '../CollectionList'
import StoreSettings from '../StoreSettings'
import Sales from '../Sales'
import { Bids } from '../Bids'
-import { ClaimYourName } from '../ClaimYourName'
import { Props } from './AssetBrowse.types'
import MapTopbar from './MapTopbar'
import MapBrowse from './MapBrowse'
@@ -233,7 +232,6 @@ const AssetBrowse = (props: Props) => {
case DecentralandSection.ENS:
right = (
<>
- {!isAccountOrCurrentAccount && }
>
@@ -257,7 +255,6 @@ const AssetBrowse = (props: Props) => {
Sections.decentraland.LAND,
Sections.decentraland.WEARABLES,
Sections.decentraland.EMOTES,
- Sections.decentraland.ENS,
Sections.decentraland.ON_SALE,
Sections.decentraland.ON_RENT,
Sections.decentraland.SALES,
diff --git a/webapp/src/components/AssetTopbar/AssetTopbar.tsx b/webapp/src/components/AssetTopbar/AssetTopbar.tsx
index f45b7f48ba..2385c0a4b5 100644
--- a/webapp/src/components/AssetTopbar/AssetTopbar.tsx
+++ b/webapp/src/components/AssetTopbar/AssetTopbar.tsx
@@ -159,13 +159,6 @@ export const AssetTopbar = ({
[onBrowse, onlyOnSale, onlyOnRent]
)
- useEffect(() => {
- const option = sortByOptions.find(option => option.value === sortBy)
- if (!option) {
- onBrowse({ sortBy: sortByOptions[0].value })
- }
- }, [onBrowse, sortBy, sortByOptions])
-
const sortByValue = sortByOptions.find(option => option.value === sortBy)
? sortBy
: sortByOptions[0].value
diff --git a/webapp/src/components/BrowsePage/BrowsePage.tsx b/webapp/src/components/BrowsePage/BrowsePage.tsx
index cd8c4fe33f..62fb71590f 100644
--- a/webapp/src/components/BrowsePage/BrowsePage.tsx
+++ b/webapp/src/components/BrowsePage/BrowsePage.tsx
@@ -29,7 +29,7 @@ const BrowsePage = (props: Props) => {
isFullscreen={Boolean(isFullscreen)}
view={View.MARKET}
section={section}
- sections={[Section.WEARABLES, Section.EMOTES, Section.ENS]}
+ sections={[Section.WEARABLES, Section.EMOTES]}
contracts={contracts}
/>
diff --git a/webapp/src/components/ClaimYourName/ClaimYourName.module.css b/webapp/src/components/ClaimYourName/ClaimYourName.module.css
deleted file mode 100644
index fb80ea7e0d..0000000000
--- a/webapp/src/components/ClaimYourName/ClaimYourName.module.css
+++ /dev/null
@@ -1,82 +0,0 @@
-.gradient {
- height: 152px;
- width: 100%;
- margin-bottom: 11px;
- border-radius: 14.482757568359375px;
- display: flex;
- justify-content: center;
- align-items: center;
- overflow: hidden;
- position: relative;
-}
-
-.gradient::before {
- content: '';
- position: absolute;
- aspect-ratio: 1/1;
- width: 105%;
- background-image: radial-gradient(
- 100% 100% at 6% 9%,
- #ffbc5bff 2%,
- #ffffff00 73%
- ),
- linear-gradient(60deg, #ff2d55 18%, #c640cd 100%);
- z-index: -1;
- animation: rotate 4s linear infinite;
-}
-
-@keyframes rotate {
- 0% {
- transform: rotate(0deg);
- }
- 100% {
- transform: rotate(360deg);
- }
-}
-
-@media (max-width: 767px) {
- .gradient {
- display: none;
- }
-}
-
-.container {
- height: 146px;
- width: calc(100% - 6px);
- background: #242129;
- border-radius: 14.482757568359375px;
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 0 33px 0 15px;
-}
-
-.imgTitleAndTextContainer {
- display: flex;
- align-items: center;
-}
-
-.img {
- margin-right: 14px;
-}
-
-.title {
- font-size: 20px;
- font-weight: 700;
- line-height: 24px;
- letter-spacing: 0px;
- text-align: left;
-}
-
-.text {
- font-size: 14px;
- font-weight: 400;
- line-height: 18px;
- letter-spacing: 0em;
- text-align: left;
-}
-
-.btn {
- max-width: 222px;
- margin-left: 21px !important;
-}
diff --git a/webapp/src/components/ClaimYourName/ClaimYourName.spec.tsx b/webapp/src/components/ClaimYourName/ClaimYourName.spec.tsx
deleted file mode 100644
index 55f5eac038..0000000000
--- a/webapp/src/components/ClaimYourName/ClaimYourName.spec.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import { render, fireEvent } from '@testing-library/react'
-import ClaimYourName from './ClaimYourName'
-import { getAnalytics } from 'decentraland-dapps/dist/modules/analytics/utils'
-import * as events from '../../utils/events'
-
-jest.mock('../../lib/environment', () => {
- return {
- builderUrl: 'https://mocked-builder-url.com'
- }
-})
-
-jest.mock('decentraland-dapps/dist/modules/analytics/utils')
-
-const mockGetAnalytics = getAnalytics as jest.MockedFunction<
- typeof getAnalytics
->
-
-describe('ClaimYourName', () => {
- it('should have a link to the builder with the names path', async () => {
- const { findByRole } = render()
- const button = await findByRole('button')
- expect(button.getAttribute('href')).toBe(
- `https://mocked-builder-url.com/claim-name`
- )
- })
-
- describe('when tracking the event that the button was clicked', () => {
- let track: jest.Mock
-
- beforeEach(() => {
- track = jest.fn()
-
- mockGetAnalytics.mockReturnValueOnce({
- track
- })
- })
-
- it('should track an event when the user clicks on the button', async () => {
- const { findByRole } = render()
- // TODO: Fix the "Error: Not implemented: navigation (except hash changes)" that happens because of the href.
- fireEvent.click(await findByRole('button'))
- expect(track).toHaveBeenCalledWith(events.CLICK_CLAIM_NEW_NAME)
- })
-
- it('should track an event when the user right clicks on the button', async () => {
- const { findByRole } = render()
- fireEvent.contextMenu(await findByRole('button'))
- expect(track).toHaveBeenCalledWith(events.CLICK_CLAIM_NEW_NAME)
- })
- })
-})
diff --git a/webapp/src/components/ClaimYourName/ClaimYourName.tsx b/webapp/src/components/ClaimYourName/ClaimYourName.tsx
deleted file mode 100644
index 587ef4b23d..0000000000
--- a/webapp/src/components/ClaimYourName/ClaimYourName.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import React from 'react'
-import { Button } from 'decentraland-ui'
-import { T, t } from 'decentraland-dapps/dist/modules/translation/utils'
-import { getAnalytics } from 'decentraland-dapps/dist/modules/analytics/utils'
-import claimYourOwnNameImg from '../../images/claim-your-own-name.svg'
-import { builderUrl } from '../../lib/environment'
-import * as events from '../../utils/events'
-import { Mana } from '../Mana'
-import styles from './ClaimYourName.module.css'
-
-const ClaimYourName = () => {
- const trackClick = () => {
- getAnalytics().track(events.CLICK_CLAIM_NEW_NAME)
- }
-
- return (
-
-
-
-
-
-
{t('claim_your_own_name.title')}
-
- 100
- }}
- />
-
-
-
-
-
-
- )
-}
-
-export default React.memo(ClaimYourName)
diff --git a/webapp/src/components/ClaimYourName/index.ts b/webapp/src/components/ClaimYourName/index.ts
deleted file mode 100644
index 37a1ab46c5..0000000000
--- a/webapp/src/components/ClaimYourName/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import ClaimYourName from './ClaimYourName'
-
-export { ClaimYourName }
diff --git a/webapp/src/components/Modals/ClaimNameFatFingerModal/ClaimNameFatFingerModal.container.ts b/webapp/src/components/Modals/ClaimNameFatFingerModal/ClaimNameFatFingerModal.container.ts
new file mode 100644
index 0000000000..80a6d51e8b
--- /dev/null
+++ b/webapp/src/components/Modals/ClaimNameFatFingerModal/ClaimNameFatFingerModal.container.ts
@@ -0,0 +1,54 @@
+import { connect } from 'react-redux'
+import { withAuthorizedAction } from 'decentraland-dapps/dist/containers'
+import { AuthorizedAction } from 'decentraland-dapps/dist/containers/withAuthorizedAction/AuthorizationModal'
+import { getAddress } from 'decentraland-dapps/dist/modules/wallet/selectors'
+import { isLoadingType } from 'decentraland-dapps/dist/modules/loading/selectors'
+import { RootState } from '../../../modules/reducer'
+import {
+ getClaimNameStatus,
+ getLoading,
+ isWaitingTxClaimName,
+ getErrorMessage
+} from '../../../modules/ens/selectors'
+import {
+ claimNameRequest,
+ CLAIM_NAME_REQUEST,
+ claimNameClear
+} from '../../../modules/ens/actions'
+import { Contract } from '../../../modules/vendor/services'
+import { getContract } from '../../../modules/contract/selectors'
+import {
+ MapDispatch,
+ MapDispatchProps,
+ MapState
+} from './ClaimNameFatFingerModal.types'
+import ClaimNameFatFingerModal from './ClaimNameFatFingerModal'
+
+const mapState = (state: RootState): MapState => ({
+ isLoading:
+ isLoadingType(getLoading(state), CLAIM_NAME_REQUEST) ||
+ isWaitingTxClaimName(state),
+ address: getAddress(state),
+ getContract: (query: Partial) => getContract(state, query)
+})
+
+const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({
+ onClaim: name => dispatch(claimNameRequest(name)),
+ onClaimNameClear: () => dispatch(claimNameClear())
+})
+
+export default connect(
+ mapState,
+ mapDispatch
+)(
+ withAuthorizedAction(
+ ClaimNameFatFingerModal,
+ AuthorizedAction.CLAIM_NAME,
+ {
+ title_action: 'names_page.claim_name_fat_finger_modal.authorization.title_action',
+ action: 'names_page.claim_name_fat_finger_modal.authorization.action'
+ },
+ getClaimNameStatus,
+ getErrorMessage
+ )
+)
diff --git a/webapp/src/components/Modals/ClaimNameFatFingerModal/ClaimNameFatFingerModal.css b/webapp/src/components/Modals/ClaimNameFatFingerModal/ClaimNameFatFingerModal.css
new file mode 100644
index 0000000000..e2e00b39c2
--- /dev/null
+++ b/webapp/src/components/Modals/ClaimNameFatFingerModal/ClaimNameFatFingerModal.css
@@ -0,0 +1,35 @@
+.ClaimNameFatFingerModal.ui.modal .content .dcl.field,
+.ClaimNameFatFingerModal.ui.modal .actions {
+ margin-top: 30px;
+}
+
+.ClaimNameFatFingerModal.ui.modal .content .dcl.field .message {
+ margin-top: 10px;
+}
+
+.ClaimNameFatFingerModal.ui.modal .dcl.field.error .ui.input > i.icon,
+.ClaimNameFatFingerModal.ui.modal .ui.form .dcl.field.error .ui.input > i.icon {
+ background-position: 0% 100%;
+}
+
+.ClaimNameFatFingerModal.ui.modal .ui.form .dcl.field .ui.input input {
+ border: 1px solid #fcfcfc;
+ border-radius: 6px;
+ padding: 12px;
+}
+
+.ClaimNameFatFingerModal.ui.modal .ui.form .dcl.field.error .ui.input input {
+ border: 1px solid var(--error);
+ border-radius: 6px;
+ padding: 12px;
+}
+
+.ClaimNameFatFingerModal.ui.modal .capsWarning {
+ border: 1px solid #716b7c;
+ padding: 12px 16px;
+ border-radius: 6px;
+ color: #a09ba8;
+ font-size: 14px;
+ font-weight: 700;
+ line-height: 17px;
+}
diff --git a/webapp/src/components/Modals/ClaimNameFatFingerModal/ClaimNameFatFingerModal.spec.tsx b/webapp/src/components/Modals/ClaimNameFatFingerModal/ClaimNameFatFingerModal.spec.tsx
new file mode 100644
index 0000000000..db72cd7be6
--- /dev/null
+++ b/webapp/src/components/Modals/ClaimNameFatFingerModal/ClaimNameFatFingerModal.spec.tsx
@@ -0,0 +1,109 @@
+import { fireEvent, waitFor } from '@testing-library/react'
+import { t } from 'decentraland-dapps/dist/modules/translation/utils'
+import { renderWithProviders } from '../../../utils/test'
+import ClaimNameFatFingerModal from './ClaimNameFatFingerModal'
+
+describe('ClaimNameFatFingerModal', () => {
+ const name = 'aNAME'
+ const onCloseMock = jest.fn()
+ const onClaimMock = jest.fn()
+ const onClaimNameClearMock = jest.fn()
+ const onAuthorizedActionMock = jest.fn()
+ const baseProps = {
+ metadata: { name },
+ isLoading: false,
+ name: 'Modal',
+ onClose: onCloseMock,
+ getContract: jest.fn(),
+ onClaim: onClaimMock,
+ onClaimNameClear: onClaimNameClearMock,
+ onAuthorizedAction: onAuthorizedActionMock,
+ onCloseAuthorization: jest.fn(),
+ isLoadingAuthorization: false
+ }
+
+ afterEach(() => {
+ jest.clearAllMocks()
+ })
+
+ describe('when there is no name typed', () => {
+ it('should have the confirm button disabled', () => {
+ const { getByText } = renderWithProviders(
+
+ )
+
+ const claimButton = getByText(t('global.confirm'))
+ expect(claimButton).toBeDisabled()
+ })
+ })
+
+ describe('when not typing the same name that is given by props', () => {
+ it('should have the confirm button disabled and show error message', () => {
+ const { getByRole, getByText } = renderWithProviders(
+
+ )
+
+ const inputField = getByRole('textbox')
+ fireEvent.change(inputField, { target: { value: 'wrongName' } })
+
+ const claimButton = getByText(t('global.confirm'))
+ expect(claimButton).toBeDisabled()
+
+ const errorMessage = getByText(
+ t('names_page.claim_name_fat_finger_modal.names_different')
+ )
+ expect(errorMessage).toBeInTheDocument()
+ })
+ })
+
+ describe('when typing the correct name', () => {
+ it('should call onClaim when claim button is clicked', async () => {
+ const { getByRole, getByText } = renderWithProviders(
+ {
+ onAuthorized()
+ })}
+ />
+ )
+
+ const inputField = getByRole('textbox')
+ await fireEvent.change(inputField, { target: { value: name } })
+ const claimButton = getByText(t('global.confirm'))
+ await waitFor(() => {
+ expect(claimButton).not.toBeDisabled()
+ })
+ fireEvent.click(claimButton)
+ expect(onClaimMock).toHaveBeenCalledWith(name)
+ })
+ })
+
+ describe('while loading', () => {
+ it('should have the confirm button disabled', () => {
+ const { getByText } = renderWithProviders(
+
+ )
+
+ const claimButton = getByText(t('global.confirm'))
+ expect(claimButton).toBeDisabled()
+ })
+ })
+
+ describe('when the modal is closed', () => {
+ it('should call onClose', () => {
+ const { getByText } = renderWithProviders(
+
+ )
+
+ const closeButton = getByText(t('global.cancel'))
+ fireEvent.click(closeButton)
+
+ expect(onCloseMock).toHaveBeenCalled()
+ })
+ })
+})
diff --git a/webapp/src/components/Modals/ClaimNameFatFingerModal/ClaimNameFatFingerModal.tsx b/webapp/src/components/Modals/ClaimNameFatFingerModal/ClaimNameFatFingerModal.tsx
new file mode 100644
index 0000000000..bd86358590
--- /dev/null
+++ b/webapp/src/components/Modals/ClaimNameFatFingerModal/ClaimNameFatFingerModal.tsx
@@ -0,0 +1,124 @@
+import React, { useCallback, useState } from 'react'
+import { NFTCategory, Network } from '@dcl/schemas'
+import { ModalNavigation, Field, Button, Form, Icon } from 'decentraland-ui'
+import { ContractName } from 'decentraland-transactions'
+import { getChainIdByNetwork } from 'decentraland-dapps/dist/lib/eth'
+import { AuthorizationType } from 'decentraland-dapps/dist/modules/authorization/types'
+import Modal from 'decentraland-dapps/dist/containers/Modal'
+import { t, T } from 'decentraland-dapps/dist/modules/translation/utils'
+import { config } from '../../../config'
+import { getContractNames } from '../../../modules/vendor'
+import { PRICE_IN_WEI } from '../../../modules/ens/utils'
+import { Props } from './ClaimNameFatFingerModal.types'
+import './ClaimNameFatFingerModal.css'
+
+export const CONTROLLER_V2_ADDRESS = config.get(
+ 'CONTROLLER_V2_CONTRACT_ADDRESS',
+ ''
+)
+
+const ClaimNameFatFingerModal = ({
+ name,
+ metadata: { name: originalName },
+ isLoading,
+ onClaim,
+ onAuthorizedAction,
+ onClaimNameClear,
+ getContract,
+ onClose
+}: Props) => {
+ const [currentName, setCurrentName] = useState('')
+
+ const handleClaim = useCallback(() => {
+ const mana = getContract({
+ name: getContractNames().MANA,
+ network: Network.ETHEREUM
+ })
+
+ if (!mana) return
+
+ const manaContract = {
+ name: ContractName.MANAToken,
+ address: mana.address,
+ chainId: getChainIdByNetwork(Network.ETHEREUM),
+ network: Network.ETHEREUM,
+ category: NFTCategory.ENS
+ }
+
+ onClaimNameClear()
+ onAuthorizedAction({
+ authorizedAddress: CONTROLLER_V2_ADDRESS,
+ authorizedContractLabel: 'DCLControllerV2',
+ targetContract: manaContract,
+ targetContractName: ContractName.MANAToken,
+ requiredAllowanceInWei: PRICE_IN_WEI,
+ authorizationType: AuthorizationType.ALLOWANCE,
+ onAuthorized: () => onClaim(currentName)
+ })
+ }, [currentName, getContract, onAuthorizedAction, onClaim, onClaimNameClear])
+
+ const handleChangeName = (event: React.ChangeEvent) => {
+ const name = event.target.value
+ setCurrentName(name.replace(/\s/g, ''))
+ }
+
+ const areNamesDifferent = currentName !== originalName
+ const hasError = areNamesDifferent && currentName.length > 0
+
+ return (
+
+
+
+
+ )
+}
+
+export default ClaimNameFatFingerModal
diff --git a/webapp/src/components/Modals/ClaimNameFatFingerModal/ClaimNameFatFingerModal.types.ts b/webapp/src/components/Modals/ClaimNameFatFingerModal/ClaimNameFatFingerModal.types.ts
new file mode 100644
index 0000000000..1ed6be62f2
--- /dev/null
+++ b/webapp/src/components/Modals/ClaimNameFatFingerModal/ClaimNameFatFingerModal.types.ts
@@ -0,0 +1,21 @@
+import { Dispatch } from 'redux'
+import { ModalProps } from 'decentraland-dapps/dist/providers/ModalProvider/ModalProvider.types'
+import { WithAuthorizedActionProps } from 'decentraland-dapps/dist/containers/withAuthorizedAction'
+import { claimNameClear, claimNameRequest } from '../../../modules/ens/actions'
+import { Contract } from '../../../modules/vendor/services'
+import { getContract } from '../../../modules/contract/selectors'
+
+export type Props = ModalProps & {
+ isLoading: boolean
+ address?: string
+ metadata: {
+ name: string
+ }
+ onClaim: typeof claimNameRequest
+ onClaimNameClear: typeof claimNameClear
+ getContract: (query: Partial) => ReturnType
+} & WithAuthorizedActionProps
+
+export type MapState = Pick
+export type MapDispatch = Dispatch
+export type MapDispatchProps = Pick
diff --git a/webapp/src/components/Modals/ClaimNameFatFingerModal/index.ts b/webapp/src/components/Modals/ClaimNameFatFingerModal/index.ts
new file mode 100644
index 0000000000..f85a85f084
--- /dev/null
+++ b/webapp/src/components/Modals/ClaimNameFatFingerModal/index.ts
@@ -0,0 +1,2 @@
+import ClaimNameFatFingerModal from './ClaimNameFatFingerModal.container'
+export default ClaimNameFatFingerModal
diff --git a/webapp/src/components/Modals/SetNameAsAliasModal/SetNameAsAliasModal.container.ts b/webapp/src/components/Modals/SetNameAsAliasModal/SetNameAsAliasModal.container.ts
new file mode 100644
index 0000000000..23ee7aa639
--- /dev/null
+++ b/webapp/src/components/Modals/SetNameAsAliasModal/SetNameAsAliasModal.container.ts
@@ -0,0 +1,38 @@
+import { connect } from 'react-redux'
+import { getAddress } from 'decentraland-dapps/dist/modules/wallet/selectors'
+import { isLoadingType } from 'decentraland-dapps/dist/modules/loading/selectors'
+import {
+ SET_PROFILE_AVATAR_ALIAS_REQUEST,
+ setProfileAvatarAliasRequest
+} from 'decentraland-dapps/dist/modules/profile/actions'
+import {
+ getProfileOfAddress,
+ getLoading
+} from 'decentraland-dapps/dist/modules/profile/selectors'
+import { RootState } from '../../../modules/reducer'
+import {
+ MapDispatch,
+ MapDispatchProps,
+ MapState
+} from './SetNameAsAliasModal.types'
+import SetNameAsAliasModal from './SetNameAsAliasModal'
+
+const mapState = (state: RootState): MapState => {
+ const address = getAddress(state)
+ const profile = !!address ? getProfileOfAddress(state, address) : undefined
+ return {
+ isLoading: isLoadingType(
+ getLoading(state),
+ SET_PROFILE_AVATAR_ALIAS_REQUEST
+ ),
+ address,
+ profile
+ }
+}
+
+const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({
+ onSubmit: (address, name) =>
+ dispatch(setProfileAvatarAliasRequest(address, name))
+})
+
+export default connect(mapState, mapDispatch)(SetNameAsAliasModal)
diff --git a/webapp/src/components/Modals/SetNameAsAliasModal/SetNameAsAliasModal.css b/webapp/src/components/Modals/SetNameAsAliasModal/SetNameAsAliasModal.css
new file mode 100644
index 0000000000..df0a99e2b9
--- /dev/null
+++ b/webapp/src/components/Modals/SetNameAsAliasModal/SetNameAsAliasModal.css
@@ -0,0 +1,83 @@
+.SetNameAsAliasModal.ui.modal .content .dcl.field,
+.SetNameAsAliasModal.ui.modal .actions {
+ margin-top: 30px;
+}
+
+.SetNameAsAliasModal.ui.modal .content .dcl.field .message {
+ margin-top: 10px;
+}
+
+.SetNameAsAliasModal.ui.modal .details {
+ text-align: center;
+}
+
+.SetNameAsAliasModal .card {
+ display: flex;
+ background-color: var(--card-on-modal);
+ padding: 60px;
+ border-radius: 16px;
+ margin-top: 16px;
+ font-size: 24px;
+ align-items: center;
+ justify-content: center;
+}
+
+.SetNameAsAliasModal .card div {
+ padding: 0 12px;
+ border-radius: 26px;
+ align-items: center;
+ display: flex;
+ padding: 8px 16px;
+}
+
+.SetNameAsAliasModal .card span .verified img,
+.SetNameAsAliasModal .card span .unverified img {
+ margin-left: 6px;
+}
+
+.SetNameAsAliasModal .card div.verified {
+ height: 37px;
+ font-weight: bold;
+ font-size: 18px;
+ background: linear-gradient(116deg, #ffbc5b 0%, #ff2d55 50.52%, #c640cd 100%);
+}
+
+.SetNameAsAliasModal .card div.unverified {
+ height: 37px;
+ font-weight: bold;
+ font-size: 18px;
+ background-color: #fcfcfc;
+ color: black;
+}
+
+.SetNameAsAliasModal .card div.verified span,
+.SetNameAsAliasModal .card div.unverified span {
+ margin-right: 6px;
+}
+
+.SetNameAsAliasModal .chevron.right:first-of-type {
+ color: #56555a;
+ margin-left: 6px;
+}
+
+.SetNameAsAliasModal .chevron.right:nth-of-type(2) {
+ color: #949396;
+}
+
+.SetNameAsAliasModal .chevron.right:last-of-type {
+ margin-right: 2px;
+}
+
+.SetNameAsAliasModal .successContainer {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.SetNameAsAliasModal .successContainer .Profile.massive .dcl.avatar-face,
+.Profile.massive .dcl.logo {
+ height: 110px;
+ width: 110px;
+ border-radius: 60px;
+ margin-bottom: 20px;
+}
diff --git a/webapp/src/components/Modals/SetNameAsAliasModal/SetNameAsAliasModal.spec.tsx b/webapp/src/components/Modals/SetNameAsAliasModal/SetNameAsAliasModal.spec.tsx
new file mode 100644
index 0000000000..c0c96d3ec3
--- /dev/null
+++ b/webapp/src/components/Modals/SetNameAsAliasModal/SetNameAsAliasModal.spec.tsx
@@ -0,0 +1,119 @@
+import { RenderResult, fireEvent } from '@testing-library/react'
+import { AvatarInfo, Profile } from '@dcl/schemas'
+import { t } from 'decentraland-dapps/dist/modules/translation/utils'
+import { renderWithProviders } from '../../../utils/test'
+import SetNameAsAliasModal from './SetNameAsAliasModal'
+
+describe('SetNameAsAliasModal', () => {
+ const previousName = 'previous name'
+ const newName = 'John Doe'
+ const mockProps = {
+ name: 'Modal Name',
+ address: '0x1234567890',
+ profile: {
+ avatars: [
+ {
+ name: previousName,
+ hasClaimedName: false,
+ avatar: ({
+ snapshots: []
+ } as unknown) as AvatarInfo
+ }
+ ]
+ } as Profile,
+ metadata: {
+ name: newName
+ },
+ isLoading: false,
+ onSubmit: jest.fn(),
+ onClose: jest.fn(),
+ onAuthorizedAction: jest.fn(),
+ onCloseAuthorization: jest.fn(),
+ isLoadingAuthorization: false
+ }
+
+ describe('when the alias is not set yet', () => {
+ let screen: RenderResult
+ beforeEach(() => {
+ screen = renderWithProviders()
+ })
+ it('renders the modal with the base title', () => {
+ const { getByText } = screen
+ expect(getByText(t('set_name_as_alias_modal.title'))).toBeInTheDocument()
+ })
+ it('renders both names showing how it will be changed', () => {
+ const { getByText } = screen
+ expect(getByText(newName)).toBeInTheDocument()
+ expect(
+ getByText(`${previousName}#${mockProps.address.slice(-4)}`)
+ ).toBeInTheDocument()
+ })
+ it('renders the correct actions', () => {
+ const { getByText } = screen
+ expect(getByText(t('global.cancel'))).toBeInTheDocument()
+ expect(getByText(t('global.confirm'))).toBeInTheDocument()
+ })
+ it('clicking Confirm should call onSubmit', () => {
+ const { getByText } = screen
+ const confirmButton = getByText(t('global.confirm'))
+ fireEvent.click(confirmButton)
+ expect(mockProps.onSubmit).toHaveBeenCalledWith(
+ mockProps.address,
+ newName
+ )
+ })
+ })
+
+ describe('when the alias is set', () => {
+ let updatedProfile: Profile
+ let screen: RenderResult
+ beforeEach(() => {
+ updatedProfile = {
+ avatars: [
+ {
+ name: newName,
+ hasClaimedName: true,
+ avatar: ({
+ snapshots: []
+ } as unknown) as AvatarInfo
+ }
+ ]
+ } as Profile
+ screen = renderWithProviders(
+
+ )
+ })
+ it('renders the success title when the alias is set', () => {
+ const { getByText } = screen
+ expect(
+ getByText(t('set_name_as_alias_modal.success_title'))
+ ).toBeInTheDocument()
+ })
+ it('should render the new name and the checked icon', () => {
+ const { getAllByText, getByAltText } = screen
+ expect(getAllByText(newName).length).toBeGreaterThan(0)
+ expect(getByAltText('verified icon')).toBeInTheDocument()
+ })
+ it('should render the Finish button', () => {
+ const { getByText } = screen
+ expect(getByText(t('global.finish'))).toBeInTheDocument()
+ })
+ describe('and the Finish button is clicked', () => {
+ it('should call onClose', () => {
+ const { getByText } = screen
+ const finishButton = getByText(t('global.finish'))
+ fireEvent.click(finishButton)
+ expect(mockProps.onClose).toHaveBeenCalled()
+ })
+ })
+ })
+
+ describe('when the user has no profile yet', () => {
+ it('should render the guest name', () => {
+ const { getByText } = renderWithProviders(
+
+ )
+ expect(getByText(`${t('global.guest')}#4567`)).toBeInTheDocument()
+ })
+ })
+})
diff --git a/webapp/src/components/Modals/SetNameAsAliasModal/SetNameAsAliasModal.tsx b/webapp/src/components/Modals/SetNameAsAliasModal/SetNameAsAliasModal.tsx
new file mode 100644
index 0000000000..ffc9083499
--- /dev/null
+++ b/webapp/src/components/Modals/SetNameAsAliasModal/SetNameAsAliasModal.tsx
@@ -0,0 +1,127 @@
+import classNames from 'classnames'
+import { ModalNavigation, Button, Form, Icon, Profile } from 'decentraland-ui'
+import Modal from 'decentraland-dapps/dist/containers/Modal'
+import { t, T } from 'decentraland-dapps/dist/modules/translation/utils'
+import VerifiedIcon from '../../../images/verified.svg'
+import UserIcon from '../../../images/user-circle.svg'
+import { Props } from './SetNameAsAliasModal.types'
+import './SetNameAsAliasModal.css'
+
+const SetNameAsAliasModal = ({
+ address,
+ profile,
+ metadata: { name },
+ isLoading,
+ onSubmit,
+ onClose
+}: Props) => {
+ const successOnSetAlias =
+ name === profile?.avatars[0].name &&
+ profile?.avatars[0].hasClaimedName &&
+ !isLoading
+
+ return (
+
+
+
+
+ )
+}
+
+export default SetNameAsAliasModal
diff --git a/webapp/src/components/Modals/SetNameAsAliasModal/SetNameAsAliasModal.types.ts b/webapp/src/components/Modals/SetNameAsAliasModal/SetNameAsAliasModal.types.ts
new file mode 100644
index 0000000000..aefda7085e
--- /dev/null
+++ b/webapp/src/components/Modals/SetNameAsAliasModal/SetNameAsAliasModal.types.ts
@@ -0,0 +1,19 @@
+import { Dispatch } from 'redux'
+import { Profile } from '@dcl/schemas'
+import { ModalProps } from 'decentraland-dapps/dist/providers/ModalProvider/ModalProvider.types'
+import { WithAuthorizedActionProps } from 'decentraland-dapps/dist/containers/withAuthorizedAction'
+import { setProfileAvatarAliasRequest } from 'decentraland-dapps/dist/modules/profile/actions'
+
+export type Props = ModalProps & {
+ isLoading: boolean
+ address?: string
+ profile: Profile | undefined
+ metadata: {
+ name: string
+ }
+ onSubmit: typeof setProfileAvatarAliasRequest
+} & WithAuthorizedActionProps
+
+export type MapState = Pick
+export type MapDispatch = Dispatch
+export type MapDispatchProps = Pick
diff --git a/webapp/src/components/Modals/SetNameAsAliasModal/index.ts b/webapp/src/components/Modals/SetNameAsAliasModal/index.ts
new file mode 100644
index 0000000000..e059a099ba
--- /dev/null
+++ b/webapp/src/components/Modals/SetNameAsAliasModal/index.ts
@@ -0,0 +1,2 @@
+import SetNameAsAliasModal from './SetNameAsAliasModal.container'
+export default SetNameAsAliasModal
diff --git a/webapp/src/components/Modals/index.ts b/webapp/src/components/Modals/index.ts
index 04669e3beb..44ccf9a3b3 100644
--- a/webapp/src/components/Modals/index.ts
+++ b/webapp/src/components/Modals/index.ts
@@ -21,3 +21,5 @@ export {
BuyNFTWithCryptoModalContainer as BuyNFTWithCryptoModal,
MintNFTWithCryptoModalConatiner as MintNFTWithCryptoModal
} from './BuyWithCryptoModal'
+export { default as ClaimNameFatFingerModal } from './ClaimNameFatFingerModal'
+export { default as SetNameAsAliasModal } from './SetNameAsAliasModal'
diff --git a/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.container.tsx b/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.container.tsx
new file mode 100644
index 0000000000..5818891fb5
--- /dev/null
+++ b/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.container.tsx
@@ -0,0 +1,35 @@
+import { connect } from 'react-redux'
+import { push, replace } from 'connected-react-router'
+import { Network } from '@dcl/schemas'
+import { openModal } from 'decentraland-dapps/dist/modules/modal/actions'
+import {
+ getMana,
+ getWallet,
+ isConnecting
+} from '../../../modules/wallet/selectors'
+import { locations } from '../../../modules/routing/locations'
+import { Section } from '../../../modules/vendor/decentraland'
+import { BrowseOptions } from '../../../modules/routing/types'
+import {
+ MapDispatch,
+ MapDispatchProps,
+ MapStateProps
+} from './ClaimNamePage.types'
+import { RootState } from '../../../modules/reducer'
+import ClaimNamePage from './ClaimNamePage'
+
+const mapState = (state: RootState): MapStateProps => ({
+ currentMana: getMana(state, Network.ETHEREUM),
+ isConnecting: isConnecting(state),
+ wallet: getWallet(state)
+})
+
+const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({
+ onBrowse: (options?: BrowseOptions) =>
+ dispatch(push(locations.names({ ...options, section: Section.ENS }))),
+ onClaim: (name: string) =>
+ dispatch(openModal('ClaimNameFatFingerModal', { name })),
+ onRedirect: path => dispatch(replace(path))
+})
+
+export default connect(mapState, mapDispatch)(ClaimNamePage)
diff --git a/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.module.css b/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.module.css
new file mode 100644
index 0000000000..3e907cf91f
--- /dev/null
+++ b/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.module.css
@@ -0,0 +1,525 @@
+.claimNamePageContainer {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+}
+
+.claimNamePageContainer .card {
+ background-color: var(--card);
+ border-radius: 16px;
+ padding: 24px;
+ margin-top: 12px;
+}
+
+.claimNamePage {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ flex: 1;
+}
+
+.claimNamePage .card.claimContainer :global(.dcl.close) {
+ position: absolute;
+ background: none;
+ right: 12px;
+ top: 12px;
+}
+
+.claimNamePage .buttons {
+ display: flex;
+ height: 100%;
+ padding: 46px 0;
+}
+
+.claimNamePage .buttons > div + div {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ text-align: start;
+ margin-left: 38px;
+ justify-content: center;
+}
+
+.claimNamePage .buttons > div + div :global(.ui.button:hover) {
+ background-color: white;
+ color: black;
+}
+
+.claimNamePage .buttons img {
+ padding-left: 44px;
+}
+
+.claimNamePage .buttons :global(.button) {
+ margin-top: 16px;
+}
+
+.claimNamePage .buttons :global(.ui.button + .ui.button) {
+ margin-left: 0;
+}
+
+.claimNamePage .availableContainer {
+ background-color: rgba(32, 17, 22, 0.75);
+ padding: 16px;
+ border-radius: 4px;
+ display: flex;
+ position: absolute;
+ top: 54px;
+ width: 100%;
+}
+
+.claimNamePage .cardsContainer button,
+.claimNamePage .cardsContainer :global(.ui.button.inverted) {
+ background-color: white;
+ color: #161518;
+ margin-top: 12px;
+}
+
+.claimNamePage .nameTakenCard {
+ display: flex;
+ align-items: center;
+ height: 250px;
+ border-radius: 8px;
+ background: linear-gradient(180deg, #ff2d55 0%, #c640cd 100%);
+ margin-right: 19px;
+ flex: 1;
+}
+
+.claimNamePage .manageNames {
+ display: flex;
+ align-items: center;
+ height: 250px;
+ border-radius: 8px;
+ background: linear-gradient(244deg, #ff2d55 -11.67%, #ffbc5b 88.23%);
+ flex: 1;
+}
+
+.claimNamePage > div + div {
+ display: flex;
+}
+
+.claimNamePage .marketplaceLinkContainer {
+ margin-left: 4px;
+ text-decoration: underline;
+ font-weight: bold;
+ color: white;
+ display: flex;
+}
+
+.claimNamePage .marketplaceLinkContainer :global(.icon) {
+ margin-left: 4px;
+}
+
+.claimNamePage .availableContainer :global(.check.icon::before) {
+ color: #34ce76;
+}
+
+.claimNamePage .availableContainer :global(.close.icon::before) {
+ color: #ff2d55;
+}
+
+.claimNamePage .nameCostNetwork {
+ color: #a09ba8;
+ font-size: 16px;
+ font-style: normal;
+ font-weight: 500;
+ line-height: 24px;
+}
+
+.claimNamePage .nameCost {
+ margin-top: 74px;
+ transition: opacity 1s ease; /* Adjust the duration as needed */
+ position: relative;
+ margin-left: -14px;
+}
+
+.claimNamePage .mainContainer .claimContainer .nameCost img {
+ opacity: 1;
+ left: 100%;
+ top: 6px;
+ cursor: pointer;
+ margin-left: 4px;
+}
+
+.claimNamePage .nameCost :global(.dcl.mana .symbol) {
+ padding: 0;
+}
+
+.claimNamePage .fadeOut {
+ opacity: 0;
+}
+
+.claimNamePage .claimContainer {
+ display: flex;
+ flex-direction: column;
+ border: 1px solid var(--divider);
+ margin-right: 16px;
+ border-radius: 16px;
+ border: 3px solid var(--DCL-Gradients-Highlight-stroke, #ffbc5b);
+ background: linear-gradient(
+ 116deg,
+ rgba(255, 188, 91, 0.1) 0%,
+ rgba(255, 45, 85, 0.1) 50.52%,
+ rgba(198, 64, 205, 0.1) 100%
+ );
+ width: 100%;
+ height: 524px;
+ position: relative;
+ align-items: center;
+}
+
+.claimNamePage .mainContainer .claimContainer {
+ position: relative;
+}
+
+.claimNamePage .mainContainer .imageContainer {
+ position: relative;
+ height: 192px;
+ display: flex;
+}
+
+.claimNamePage .mainContainer .imagePassportContainer {
+ display: flex;
+ flex-direction: column;
+ margin-top: auto;
+}
+
+.claimNamePage .mainContainer .claimContainer img {
+ position: absolute;
+ top: 0;
+ left: calc(50% - 45px);
+ opacity: 0;
+ transition: opacity 0.5s ease;
+}
+
+.claimNamePage .mainContainer .claimContainer img.banner {
+ left: 90px;
+}
+
+.claimNamePage .claimContainer .banner {
+ height: 200px;
+}
+
+.claimNamePage .claimContainer img.visible {
+ opacity: 1;
+}
+
+.claimNamePage .claimContainer .passportLogo {
+ margin-top: 10px;
+}
+
+.claimNamePage .claimContainer h2 {
+ color: #fff;
+ font-size: 48px;
+ font-weight: 700;
+ line-height: normal;
+ margin-bottom: 8px;
+}
+
+.claimNamePage .claimContainer .subtitle {
+ font-size: 24px;
+ line-height: 36px;
+}
+
+.claimNamePage .claimInput {
+ position: relative;
+ display: flex;
+ margin-top: 40px;
+ border-top-left-radius: 6px;
+ border-bottom-left-radius: 6px;
+ height: 46px;
+ cursor: pointer;
+}
+
+.claimNamePage .claimInput :global(.ui.input) {
+ align-items: baseline;
+}
+
+.claimNamePage .claimInput :global(.dcl.field.simple) {
+ display: flex;
+ align-items: center;
+ padding-left: 24px;
+ border: 1px solid var(--bluish-steel);
+ border-top-left-radius: 6px;
+ border-bottom-left-radius: 6px;
+ border-right: none;
+}
+
+.claimNamePage .claimContainer :global(.dcl.field .ui.input input::after) {
+ content: '.dcl.eth';
+ position: absolute;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ display: flex;
+ align-items: center;
+ color: red; /* Suffix color */
+ background: white; /* Match input field background color */
+ padding: 0 12px; /* Suffix padding */
+ pointer-events: none; /* Ignore pointer events */
+}
+
+.claimNamePage .claimContainer :global(.dcl.field .ui.input input) {
+ border-bottom: 0;
+ padding: 0;
+}
+
+.claimNamePage .claimContainer .remainingCharactersContainer {
+ position: absolute;
+ right: 170px;
+ top: 13px;
+}
+
+.claimNamePage
+ .claimContainer
+ .remainingCharactersContainer
+ :global(.ui.tiny.active.inline.loader) {
+ margin-right: 12px;
+}
+
+.claimNamePage .claimContainer .remainingCharacters {
+ color: #a09ba8;
+ font-size: 12px;
+ font-weight: 700;
+ font-style: normal;
+ line-height: normal;
+ top: 13px;
+}
+
+.claimNamePage .claimContainer .inputSuffix {
+ color: #a09ba8;
+ font-size: 16px;
+ font-weight: 400;
+ line-height: normal;
+}
+
+.claimNamePage .claimContainer :global(.ui.primary.button) {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.claimNamePage .claimContainer :global(.message) {
+ display: none;
+}
+
+.claimNamePage .mainContainer {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+}
+
+.claimNamePage .ctasContainer {
+ text-align: center;
+ width: 100%;
+ margin-top: 40px;
+}
+
+.claimNamePage .ctasContainer h1 {
+ margin-bottom: 16px;
+}
+
+.claimNamePage .ctasContainer .cardsContainer {
+ display: grid;
+ grid-gap: 20px;
+ grid-auto-flow: column;
+ margin-bottom: 52px;
+}
+
+.claimNamePage .ctasContainer .card {
+ background-color: #242129;
+ height: 209px;
+ width: 305px;
+ padding: 0;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+}
+
+.claimNamePage .ctasContainer .card span {
+ font-size: 16px;
+ font-weight: 700;
+}
+
+.claimNamePage .ctasContainer .learnMore {
+ color: #ffa25a;
+}
+
+.claimNamePage .ctasContainer .voting {
+ color: #ecebed;
+}
+
+.claimNamePage .ctasContainer .nameLink {
+ color: #ecebed;
+ text-decoration: underline;
+}
+
+.claimNamePage .ctasContainer .card p {
+ color: #a09ba8;
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: 20px;
+ padding-top: 2px;
+}
+
+.claimNamePage .ctasContainer .whyImgContainer {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 103px;
+ background-color: #2d2c2f;
+}
+
+.claimNamePage .ctasContainer .whyImgContainer img {
+ height: 100%;
+}
+
+.claimNamePage .ctasContainer .whyTextContainer {
+ height: 116px;
+ text-align: left;
+ padding: 16px 12px;
+}
+
+.claimNamePage .ctasContainer .whyImgContainer.standOut {
+ align-items: flex-end;
+}
+
+.claimNamePage .warningIcon {
+ color: #ffa25a;
+ margin-right: 8px;
+}
+
+.claimNamePage .informationTooltip {
+ width: 14px;
+ height: 14px;
+}
+
+@media (max-width: 768px) {
+ .claimNamePage .claimContainer {
+ height: 587px;
+ margin: 0;
+ padding: 12px;
+ }
+
+ .claimNamePage .mainContainer .imageContainer {
+ height: 222px;
+ }
+
+ .claimNamePage .mainContainer:global(.ui.container) {
+ max-width: 100% !important;
+ margin: 0 !important;
+ padding: 0 12px;
+ }
+
+ .claimNamePage .claimContainer h2 {
+ font-size: 30px;
+ text-align: center;
+ }
+
+ .claimNamePage .claimContainer .subtitle {
+ font-size: 20px;
+ }
+
+ .claimNamePage .ctasContainer .cardsContainer {
+ width: 100%;
+ overflow-x: scroll;
+ padding: 0 16px;
+ }
+
+ .claimNamePage .ctasContainer .cardsContainer:last-of-type {
+ grid-auto-flow: row;
+ overflow: visible;
+ }
+
+ .claimNamePage .nameTakenCard {
+ margin-right: 0;
+ margin-bottom: 16px;
+ justify-content: center;
+ height: unset;
+ padding: 0 12px;
+ }
+
+ .claimNamePage .nameTakenCard .buttons,
+ .claimNamePage .manageNames .buttons {
+ flex-direction: column;
+ }
+
+ .claimNamePage .manageNames .buttons,
+ .claimNamePage .nameTakenCard .buttons div {
+ align-items: center;
+ }
+
+ .claimNamePage .buttons > div + div {
+ margin-left: 0;
+ }
+
+ .claimNamePage .nameTakenCard .buttons div button {
+ margin-top: 28px;
+ }
+
+ .claimNamePage .claimInput {
+ flex-direction: column;
+ width: 100%;
+ }
+
+ .claimNamePage .claimContainer .remainingCharactersContainer {
+ right: 12px;
+ top: 6px;
+ }
+
+ .claimNamePage .claimContainer :global(.ui.primary.button) {
+ min-height: 48px;
+ border-radius: 6px;
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
+ }
+
+ .claimNamePage .claimInput :global(.dcl.field.simple) {
+ min-height: 100%;
+ min-width: 100%;
+ border: 1px solid var(--bluish-steel);
+ border-top-right-radius: 6px;
+ border-top-left-radius: 6px;
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+ border-bottom: none;
+ }
+
+ .claimNamePage .nameCost {
+ text-align: center;
+ margin-top: 72px;
+ margin-left: 0;
+ }
+
+ .claimNamePage .mainContainer .claimContainer .nameCost img {
+ left: 92%;
+ }
+
+ .claimNamePage .mainContainer .claimContainer img.banner {
+ left: 0;
+ width: 340px;
+ height: 129px;
+ top: 50px;
+ }
+
+ .claimNamePage .mainContainer .ctasContainer .card {
+ margin-right: 20px;
+ }
+
+ .claimNamePage .manageNames {
+ justify-content: center;
+ height: unset;
+ padding: 0 12px;
+ }
+
+ .claimNamePage .buttons img {
+ padding-left: 0;
+ }
+
+ .claimNamePage .availableContainer {
+ top: 97px;
+ }
+
+ .claimNamePage .ctasContainer .card {
+ min-width: 305px;
+ }
+}
diff --git a/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.spec.tsx b/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.spec.tsx
new file mode 100644
index 0000000000..7f15a650ab
--- /dev/null
+++ b/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.spec.tsx
@@ -0,0 +1,157 @@
+import { fireEvent, waitFor } from '@testing-library/react'
+import { t } from 'decentraland-dapps/dist/modules/translation/utils'
+import { Wallet } from 'decentraland-dapps/dist/modules/wallet/types'
+import { renderWithProviders } from '../../../utils/test'
+import { MAX_NAME_SIZE, isNameAvailable } from '../../../modules/ens/utils'
+import { Props } from './ClaimNamePage.types'
+import ClaimNamePage from './ClaimNamePage'
+
+jest.mock('../../../modules/ens/utils', () => ({
+ ...jest.requireActual('../../../modules/ens/utils'),
+ isNameAvailable: jest.fn()
+}))
+
+describe('ClaimNamePage', () => {
+ let walletMock: Wallet
+ let currentManaMock: number
+ let onBrowseMock: Props['onBrowse']
+ let onClaimMock: Props['onClaim']
+ let onRedirectMock: Props['onRedirect']
+
+ const renderAndTypeText = async (text: string) => {
+ const matchers = renderWithProviders(
+
+ )
+ const { getByDisplayValue, getByText } = matchers
+ const nameInput = getByDisplayValue(
+ t('names_page.your_name')
+ ) as HTMLInputElement
+ fireEvent.change(nameInput, { target: { value: text } })
+
+ await waitFor(() => {
+ expect(nameInput.value).toBe(text)
+ })
+
+ const claimButton = getByText(t('names_page.claim_a_name'))
+ expect(claimButton).toHaveAttribute('disabled')
+
+ return matchers
+ }
+
+ describe('when typing an invalid NAME', () => {
+ let invalidName: string
+ describe('and the name has a not supported character', () => {
+ beforeEach(() => {
+ invalidName = 'test!'
+ })
+ it('should have the claim name disabled and show the proper warning message', async () => {
+ const { getByText } = await renderAndTypeText(invalidName)
+
+ expect(
+ getByText(t('names_page.invalid_characters'))
+ ).toBeInTheDocument()
+ })
+ })
+ describe('and the name has a space', () => {
+ beforeEach(() => {
+ invalidName = 'te st'
+ })
+ it('should have the claim name disabled and show the proper warning message', async () => {
+ const { getByText } = await renderAndTypeText(invalidName)
+
+ expect(getByText(t('names_page.has_spaces'))).toBeInTheDocument()
+ })
+ })
+ describe('and the name is too short', () => {
+ beforeEach(() => {
+ invalidName = 't'
+ })
+ it('should have the claim name disabled and show the proper warning message', async () => {
+ const { getByText } = await renderAndTypeText(invalidName)
+
+ expect(getByText(t('names_page.name_too_short'))).toBeInTheDocument()
+ })
+ })
+ describe('and the name is too long', () => {
+ beforeEach(() => {
+ invalidName = Array(MAX_NAME_SIZE + 1)
+ .fill('t')
+ .toString()
+ })
+ it('should have the claim name disabled and show the proper warning message', async () => {
+ const { getByText } = await renderAndTypeText(invalidName)
+
+ expect(getByText(t('names_page.name_too_long'))).toBeInTheDocument()
+ })
+ })
+ })
+
+ describe('when typing a valid NAME', () => {
+ let validName: string
+ beforeEach(() => {
+ validName = 'test'
+ })
+
+ describe('and its available', () => {
+ beforeEach(() => {
+ ;(isNameAvailable as jest.Mock).mockResolvedValue(true)
+ })
+ describe('and has enough funds to claim the NAME', () => {
+ beforeEach(() => {
+ currentManaMock = 100
+ walletMock = {} as Wallet
+ onClaimMock = jest.fn()
+ })
+ it('should have the claim name enabled and call the onClaim when clicking it', async () => {
+ const { getByText } = await renderAndTypeText(validName)
+ const claimButton = getByText(t('names_page.claim_a_name'))
+ await waitFor(() =>
+ expect(claimButton).not.toHaveAttribute('disabled')
+ )
+ fireEvent.click(claimButton)
+ await waitFor(() =>
+ expect(onClaimMock).toHaveBeenCalledWith(validName)
+ )
+ })
+ })
+ describe('and does not have enough funds to claim the NAME', () => {
+ beforeEach(() => {
+ currentManaMock = 99 // 100 is the mana needed
+ walletMock = {} as Wallet
+ onClaimMock = jest.fn()
+ })
+ it('should have the claim name disabled and not call the onClaim when clicking it', async () => {
+ const { getByText } = await renderAndTypeText(validName)
+ const claimButton = getByText(t('names_page.claim_a_name'))
+ await waitFor(() => expect(claimButton).toHaveAttribute('disabled'))
+ fireEvent.click(claimButton)
+ await waitFor(() =>
+ expect(onClaimMock).not.toHaveBeenCalledWith(validName)
+ )
+ })
+ })
+ })
+
+ describe('and its not available', () => {
+ beforeEach(() => {
+ ;(isNameAvailable as jest.Mock).mockResolvedValue(false)
+ })
+ it('should have the claim name disabled', async () => {
+ const { getByText } = await renderAndTypeText(validName)
+
+ await waitFor(() =>
+ expect(getByText(t('names_page.claim_a_name'))).toHaveAttribute(
+ 'disabled'
+ )
+ )
+ })
+ })
+ })
+})
diff --git a/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.tsx b/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.tsx
new file mode 100644
index 0000000000..d8b08ace40
--- /dev/null
+++ b/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.tsx
@@ -0,0 +1,513 @@
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
+import { useLocation } from 'react-router-dom'
+import classNames from 'classnames'
+import {
+ Button,
+ Close,
+ Container,
+ Field,
+ Icon,
+ Loader,
+ Popup
+} from 'decentraland-ui'
+import { t } from 'decentraland-dapps/dist/modules/translation/utils'
+import infoIcon from '../../../images/infoIcon.png'
+import ClaimNameImage from '../../../images/claim-name.svg'
+import ClaimNameBanner from '../../../images/claim-name-banner.png'
+import CreateImg from '../../../images/names/create.png'
+import GovernanceImg from '../../../images/names/governance.png'
+import LandmarkImg from '../../../images/names/landmark.png'
+import OwnSpaceImg from '../../../images/names/own-space.png'
+import Chest from '../../../images/names/chest.png'
+import Passports from '../../../images/names/passports.png'
+import { lists } from '../../../modules/vendor/decentraland/lists/api'
+import { SortBy } from '../../../modules/routing/types'
+import {
+ MAX_NAME_SIZE,
+ NameInvalidType,
+ getNameInvalidType,
+ hasNameMinLength,
+ isEnoughClaimMana,
+ isNameAvailable,
+ isNameValid
+} from '../../../modules/ens/utils'
+import { locations } from '../../../modules/routing/locations'
+import { Section } from '../../../modules/vendor/decentraland'
+import { NavigationTab } from '../../Navigation/Navigation.types'
+import { builderUrl } from '../../../lib/environment'
+import { Mana } from '../../Mana'
+import { Props } from './ClaimNamePage.types'
+import styles from './ClaimNamePage.module.css'
+import { PageLayout } from '../../PageLayout'
+
+const PLACEHOLDER_WIDTH = '94px'
+
+const ClaimNamePage = (props: Props) => {
+ const PLACEHOLDER_NAME = t('names_page.your_name')
+ const {
+ wallet,
+ isConnecting,
+ currentMana,
+ onClaim,
+ onBrowse,
+ onRedirect
+ } = props
+ const location = useLocation()
+ const [isLoadingStatus, setIsLoadingStatus] = useState(false)
+ const [bannedNames, setBannedNames] = useState()
+ const [isAvailable, setIsAvailable] = useState(undefined)
+ const debounceRef = useRef | null>(null)
+
+ useEffect(() => {
+ ;(async () => {
+ try {
+ const bannedNames = await lists.fetchBannedNames()
+ setBannedNames(bannedNames)
+ } catch (error) {}
+ })()
+ }, [])
+
+ const [name, setName] = useState(PLACEHOLDER_NAME)
+
+ const handleNameChange = useCallback(
+ async text => {
+ const valid = isNameValid(text)
+ const minLength = hasNameMinLength(text)
+ if (valid && minLength) {
+ try {
+ if (bannedNames?.includes(text.toLocaleLowerCase())) {
+ setIsAvailable(undefined)
+ } else {
+ const isAvailable = await isNameAvailable(text)
+ setIsAvailable(isAvailable)
+ }
+ setIsLoadingStatus(false)
+ } catch (error) {
+ console.log('error: ', error)
+ setIsLoadingStatus(false)
+ }
+ }
+ },
+ [bannedNames]
+ )
+
+ const handleDebouncedChange = useCallback(
+ text => {
+ setName(text)
+ const timeoutId = setTimeout(() => {
+ if (debounceRef.current === timeoutId) {
+ handleNameChange(text)
+ }
+ }, 1000)
+ if (debounceRef.current) {
+ clearTimeout(debounceRef.current)
+ }
+ debounceRef.current = timeoutId
+ },
+ [handleNameChange]
+ )
+
+ useEffect(() => {
+ if (
+ name !== PLACEHOLDER_NAME &&
+ name.length &&
+ hasNameMinLength(name) &&
+ isNameValid(name)
+ ) {
+ setIsLoadingStatus(true)
+ } else if (!isNameValid(name)) {
+ // turn off loading if an invalid character is typed
+ setIsLoadingStatus(false)
+ }
+ }, [PLACEHOLDER_NAME, name])
+
+ const handleClaim = useCallback(() => {
+ if (!isConnecting && !wallet) {
+ onRedirect(locations.signIn(`${location.pathname}`))
+ } else {
+ const isValid = isNameValid(name)
+ const isEnoughMana =
+ wallet && currentMana && isEnoughClaimMana(currentMana)
+
+ if (!isValid || !isEnoughMana) return
+
+ onClaim(name)
+ }
+ }, [
+ isConnecting,
+ wallet,
+ name,
+ currentMana,
+ onClaim,
+ onRedirect,
+ location.pathname
+ ])
+
+ const inputRef = useRef(null)
+
+ const [inputWidth, setInputWidth] = useState(PLACEHOLDER_WIDTH)
+
+ // this fn is used to update the width of the input field so it has the suffix in the right place
+ const updateWidth = (value: string) => {
+ if (inputRef.current) {
+ // Use a temporary span to measure the width of the input's content
+ const tempSpan = document.createElement('span')
+ tempSpan.innerHTML = value
+ // Apply same font properties to the span
+ tempSpan.style.fontSize = getComputedStyle(inputRef.current).fontSize
+ tempSpan.style.fontFamily = getComputedStyle(inputRef.current).fontFamily
+ tempSpan.style.visibility = 'hidden' // Hide the span element
+ document.body.appendChild(tempSpan)
+ // Update the width state to the width of the content plus a little extra space
+ setInputWidth(`${tempSpan.offsetWidth + 2}px`)
+ document.body.removeChild(tempSpan) // Clean up
+ }
+ }
+
+ const [isInputFocus, setIsInputFocus] = useState(false)
+
+ const onFieldClick = useCallback(() => {
+ inputRef.current?.focus()
+ setIsInputFocus(true)
+ }, [])
+
+ const onFieldFocus = useCallback(() => {
+ const inputValue = inputRef.current?.value
+ if (inputValue === PLACEHOLDER_NAME) {
+ setName('')
+ }
+ }, [PLACEHOLDER_NAME])
+
+ const renderRemainingCharacters = useCallback(() => {
+ if (name !== PLACEHOLDER_NAME) {
+ return (
+ {`${name.length}/${MAX_NAME_SIZE}`}
+ )
+ }
+ }, [PLACEHOLDER_NAME, name])
+
+ const nameInvalidType = useMemo(() => {
+ return getNameInvalidType(name)
+ }, [name])
+
+ const onFieldChange = useCallback(
+ (event: React.ChangeEvent) => {
+ handleDebouncedChange(event.target.value)
+ updateWidth(event.target.value)
+ },
+ [handleDebouncedChange]
+ )
+
+ const cards = useMemo(() => {
+ return [
+ {
+ image: CreateImg,
+ title: t('names_page.why.stand_out.title'),
+ description: t('names_page.why.stand_out.description'),
+ className: styles.standOut
+ },
+ {
+ image: OwnSpaceImg,
+ title: t('names_page.why.unlock.title'),
+ description: t('names_page.why.unlock.description', {
+ link: (
+
+ {t('global.learn_more')}
+
+ )
+ })
+ },
+ {
+ image: GovernanceImg,
+ title: t('names_page.why.governance.title'),
+ description: t('names_page.why.governance.description', {
+ b: (children: React.ReactChildren) => (
+ {children}
+ ),
+ link: (
+
+ {t('global.learn_more')}
+
+ )
+ })
+ },
+ {
+ image: LandmarkImg,
+ title: t('names_page.why.get_url.title'),
+ description: t('names_page.why.get_url.description', {
+ b: (children: React.ReactChildren) => (
+ {children}
+ )
+ })
+ }
+ ]
+ }, [])
+
+ return (
+
+
+
+
+
+ {isInputFocus ? (
+
setIsInputFocus(false)} />
+ ) : null}
+
+
+
+
+ {t('names_page.title')}
+
+
+
+
+
+
+ {t('names_page.subtitle')}
+
+
+ }
+ disabled={
+ !!(wallet && currentMana && isEnoughClaimMana(currentMana))
+ }
+ hideOnScroll={true}
+ on="hover"
+ inverted
+ />
+
+ {name &&
+ hasNameMinLength(name) &&
+ isNameValid(name) &&
+ isInputFocus &&
+ name !== PLACEHOLDER_NAME &&
+ isAvailable !== undefined &&
+ bannedNames !== undefined &&
+ !isLoadingStatus ? (
+
+ {isAvailable ? (
+ <>
+
+ {t('names_page.available')}
+ >
+ ) : (
+ <>
+
+ {t('names_page.not_available', {
+ link: (
+
+ {t('names_page.marketplace')}
+
+
+ )
+ })}
+ >
+ )}
+
+ ) : name && (!hasNameMinLength(name) || !isNameValid(name)) ? (
+
+
+ {nameInvalidType === NameInvalidType.TOO_SHORT
+ ? t('names_page.name_too_short')
+ : nameInvalidType === NameInvalidType.TOO_LONG
+ ? t('names_page.name_too_long')
+ : nameInvalidType === NameInvalidType.HAS_SPACES
+ ? t('names_page.has_spaces')
+ : t('names_page.invalid_characters')}
+
+ ) : null}
+
+
+
+ {t('names_page.name_cost', {
+ mana: (
+ <>
+ 100 MANA
+ >
+ ),
+ network: (
+
+ {t('names_page.ethereum_mainnet_network')}
+
+ )
+ })}
+
+ {t('global.learn_more')}
+
+ )
+ })}
+ position="top center"
+ hoverable
+ mouseLeaveDelay={500}
+ trigger={
+
+ }
+ on="hover"
+ />
+
+
+
+
{t('names_page.why_names')}
+
+ {cards.map((card, index) => (
+
+
+
+
+
+
{card.title}
+
{card.description}
+
+
+ ))}
+
+
+
+
+
+
+
+
+
{t('names_page.ctas.name_taken.title')}
+
+ {' '}
+ {t('names_page.ctas.name_taken.description')}
+
+
+
+
+
+
+
+
+
+
+
+
{t('names_page.ctas.manage.title')}
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default React.memo(ClaimNamePage)
diff --git a/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.types.ts b/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.types.ts
new file mode 100644
index 0000000000..16c0865a8e
--- /dev/null
+++ b/webapp/src/components/NamesPage/ClaimNamePage/ClaimNamePage.types.ts
@@ -0,0 +1,24 @@
+import { Dispatch } from 'react'
+import { CallHistoryMethodAction } from 'connected-react-router'
+import { Wallet } from 'decentraland-dapps/dist/modules/wallet/types'
+import { OpenModalAction } from 'decentraland-dapps/dist/modules/modal/actions'
+import { BrowseOptions } from '../../../modules/routing/types'
+
+export type Props = {
+ currentMana: number | undefined
+ wallet: Wallet | null
+ isConnecting: boolean
+ onBrowse: (options?: BrowseOptions) => void
+ onClaim: (name: string) => void
+ onRedirect: (path: string) => void
+}
+
+export type MapStateProps = Pick<
+ Props,
+ 'currentMana' | 'wallet' | 'isConnecting'
+>
+export type MapDispatchProps = Pick<
+ Props,
+ 'onBrowse' | 'onClaim' | 'onRedirect'
+>
+export type MapDispatch = Dispatch
diff --git a/webapp/src/components/NamesPage/ClaimNamePage/index.ts b/webapp/src/components/NamesPage/ClaimNamePage/index.ts
new file mode 100644
index 0000000000..5c7c6cb58f
--- /dev/null
+++ b/webapp/src/components/NamesPage/ClaimNamePage/index.ts
@@ -0,0 +1,2 @@
+import ClaimNamePage from './ClaimNamePage.container'
+export { ClaimNamePage }
diff --git a/webapp/src/components/NamesPage/NamesPage.module.css b/webapp/src/components/NamesPage/NamesPage.module.css
new file mode 100644
index 0000000000..9394069b8c
--- /dev/null
+++ b/webapp/src/components/NamesPage/NamesPage.module.css
@@ -0,0 +1,4 @@
+.namesPageContainer {
+ display: flex;
+ flex-direction: column;
+}
diff --git a/webapp/src/components/NamesPage/NamesPage.tsx b/webapp/src/components/NamesPage/NamesPage.tsx
new file mode 100644
index 0000000000..16663816a9
--- /dev/null
+++ b/webapp/src/components/NamesPage/NamesPage.tsx
@@ -0,0 +1,22 @@
+import React from 'react'
+import { VendorName } from '../../modules/vendor/types'
+import { View } from '../../modules/ui/types'
+import { NavigationTab } from '../Navigation/Navigation.types'
+import { Section } from '../../modules/vendor/decentraland'
+import { AssetBrowse } from '../AssetBrowse'
+import { PageLayout } from '../PageLayout'
+
+const NamesPage = () => {
+ return (
+
+
+
+ )
+}
+
+export default React.memo(NamesPage)
diff --git a/webapp/src/components/NamesPage/NamesPage.types.ts b/webapp/src/components/NamesPage/NamesPage.types.ts
new file mode 100644
index 0000000000..808d48c15c
--- /dev/null
+++ b/webapp/src/components/NamesPage/NamesPage.types.ts
@@ -0,0 +1,7 @@
+export type Props = {
+ isFullscreen: boolean
+ isMap: boolean
+ onBrowse: () => void
+}
+
+export type MapStateProps = Pick
diff --git a/webapp/src/components/NamesPage/index.ts b/webapp/src/components/NamesPage/index.ts
new file mode 100644
index 0000000000..7a8f04630a
--- /dev/null
+++ b/webapp/src/components/NamesPage/index.ts
@@ -0,0 +1,2 @@
+import NamesPage from './NamesPage'
+export { NamesPage }
diff --git a/webapp/src/components/Navigation/Navigation.tsx b/webapp/src/components/Navigation/Navigation.tsx
index 6b6e93fd6e..5471080873 100644
--- a/webapp/src/components/Navigation/Navigation.tsx
+++ b/webapp/src/components/Navigation/Navigation.tsx
@@ -83,6 +83,11 @@ const Navigation = (props: Props) => {
{t('navigation.land')}
+
+
+ {t('navigation.names')}
+
+
{t('navigation.my_assets')}
diff --git a/webapp/src/components/Navigation/Navigation.types.ts b/webapp/src/components/Navigation/Navigation.types.ts
index b7496fbd08..b7fba98853 100644
--- a/webapp/src/components/Navigation/Navigation.types.ts
+++ b/webapp/src/components/Navigation/Navigation.types.ts
@@ -9,6 +9,7 @@ export enum NavigationTab {
OVERVIEW = 'overview',
CAMPAIGN_BROWSER = 'campaign-browser',
LANDS = 'lands',
+ NAMES = 'names',
BROWSE = 'browse',
COLLECTIBLES = 'collectibles',
MY_STORE = 'my_store',
diff --git a/webapp/src/components/Routes/Routes.tsx b/webapp/src/components/Routes/Routes.tsx
index 64b5dfeac6..0898856702 100644
--- a/webapp/src/components/Routes/Routes.tsx
+++ b/webapp/src/components/Routes/Routes.tsx
@@ -21,6 +21,7 @@ import { ActivityPage } from '../ActivityPage'
import { HomePage } from '../HomePage'
import { LegacyNFTPage } from '../LegacyNFTPage'
import { LandsPage } from '../LandsPage'
+import { NamesPage } from '../NamesPage'
import { CollectionPage } from '../CollectionPage'
import { Navbar } from '../Navbar'
import { ManageAssetPage } from '../ManageAssetPage'
@@ -31,6 +32,7 @@ import { StatusPage } from '../BuyPage/StatusPage'
import { ListPage } from '../ListPage'
import { ProtectedRoute } from '../ProtectedRoute'
import { ListsPage } from '../ListsPage'
+import { ClaimNamePage } from '../NamesPage/ClaimNamePage'
import { Props } from './Routes.types'
const Routes = ({ inMaintenance }: Props) => {
@@ -61,6 +63,8 @@ const Routes = ({ inMaintenance }: Props) => {
<>
+
+
{
const search = new URLSearchParams(getSearch(state))
const transaction = getTransaction(state, search.get('txHash') || '')
+ const address = getAddress(state)
return {
isLoading: Boolean(
transaction && transaction.status !== TransactionStatus.CONFIRMED
),
- mintedTokenId: getTokenIdFromLogs(ChainId.MATIC_MUMBAI, transaction?.receipt?.logs)
+ mintedTokenId: getTokenIdFromLogs(
+ ChainId.MATIC_MUMBAI,
+ transaction?.receipt?.logs
+ ),
+ profile: !!address ? getProfileOfAddress(state, address) : undefined
}
}
-export default connect(mapState)(SuccessPage)
+const mapDispatch = (dispatch: Dispatch): MapDispatchProps => ({
+ onSetNameAsAlias: (name: string) =>
+ dispatch(openModal('SetNameAsAliasModal', { name }))
+})
+
+export default connect(mapState, mapDispatch)(SuccessPage)
diff --git a/webapp/src/components/SuccessPage/SuccessPage.spec.tsx b/webapp/src/components/SuccessPage/SuccessPage.spec.tsx
index 9a25b21127..12d70e51b7 100644
--- a/webapp/src/components/SuccessPage/SuccessPage.spec.tsx
+++ b/webapp/src/components/SuccessPage/SuccessPage.spec.tsx
@@ -1,5 +1,5 @@
import { RenderResult } from '@testing-library/react'
-import { NFTCategory, Rarity } from '@dcl/schemas'
+import { NFTCategory, Profile, Rarity } from '@dcl/schemas'
import { t } from 'decentraland-dapps/dist/modules/translation/utils'
import {
renderWithProviders,
@@ -22,20 +22,23 @@ jest.mock('react-router-dom', () => {
jest.mock('lottie-react', () => () => LOTTIE
)
-function renderSuccessPage(props: Partial = {}): RenderResult {
+function renderSuccessPage(
+ props: Partial = {},
+ preloadedNFTData?: Record
+): RenderResult {
return renderWithProviders(
,
{
preloadedState: {
nft: {
- data: {
+ data: preloadedNFTData || {
'address-1': {
data: {
wearable: {
rarity: Rarity.COMMON
}
},
- category: NFTCategory.WEARABLE,
+ category: NFTCategory.WEARABLE
} as NFT
},
loading: [],
@@ -64,15 +67,83 @@ describe('when transaction is still loading', () => {
})
describe('when transaction finishes successfully', () => {
- beforeEach(async () => {
+ beforeEach(() => {
props = { isLoading: false }
- screen = renderSuccessPage(props)
- await waitForComponentToFinishLoading(screen)
})
- it('should render transaction confirmed message', () => {
- expect(
- screen.getByText(t('success_page.success_state.status'))
- ).toBeInTheDocument()
+ describe('and its an ENS type of asset', () => {
+ describe('and the user has a profile set', () => {
+ beforeEach(async () => {
+ screen = renderSuccessPage(
+ {
+ ...props,
+ profile: {} as Profile
+ },
+ {
+ 'address-1': {
+ data: {
+ ens: {
+ subdomain: 'bondi'
+ }
+ },
+ category: NFTCategory.ENS
+ } as NFT
+ }
+ )
+ await waitForComponentToFinishLoading(screen)
+ })
+ it('should show the CTAs to mint more names and set as primary name', () => {
+ expect(
+ screen.getByText(t('success_page.success_state.mint_more_names'))
+ ).toBeInTheDocument()
+ expect(
+ screen.getByText(t('success_page.success_state.set_as_primary_name'))
+ ).toBeInTheDocument()
+ })
+ })
+ describe('and the user has not profile set yet', () => {
+ beforeEach(async () => {
+ screen = renderSuccessPage(
+ {
+ ...props,
+ profile: undefined
+ },
+ {
+ 'address-1': {
+ data: {
+ ens: {
+ subdomain: 'bondi'
+ }
+ },
+ category: NFTCategory.ENS
+ } as NFT
+ }
+ )
+ await waitForComponentToFinishLoading(screen)
+ })
+ it('should show the CTAs to mint more names only', () => {
+ expect(
+ screen.getByText(t('success_page.success_state.mint_more_names'))
+ ).toBeInTheDocument()
+ expect(
+ screen.queryByText(
+ t('success_page.success_state.set_as_primary_name')
+ )
+ ).not.toBeInTheDocument()
+ })
+ })
+ })
+
+ describe('and its not an ENS type of asset', () => {
+ beforeEach(async () => {
+ props = { isLoading: false }
+ screen = renderSuccessPage(props)
+ await waitForComponentToFinishLoading(screen)
+ })
+ it('should render transaction confirmed message', () => {
+ expect(
+ screen.getByText(t('success_page.success_state.status'))
+ ).toBeInTheDocument()
+ })
})
})
diff --git a/webapp/src/components/SuccessPage/SuccessPage.tsx b/webapp/src/components/SuccessPage/SuccessPage.tsx
index 6400c96654..8579482e66 100644
--- a/webapp/src/components/SuccessPage/SuccessPage.tsx
+++ b/webapp/src/components/SuccessPage/SuccessPage.tsx
@@ -18,7 +18,7 @@ import styles from './SuccessPage.module.css'
const EXPLORER_URL = config.get('EXPLORER_URL', '')
export function SuccessPage(props: Props) {
- const { isLoading, mintedTokenId } = props
+ const { isLoading, mintedTokenId, profile, onSetNameAsAlias } = props
const search = new URLSearchParams(useLocation().search)
const contractAddress = search.get('contractAddress')
const tokenId = search.get('tokenId')
@@ -84,7 +84,9 @@ export function SuccessPage(props: Props) {
{t('success_page.success_state.status')}
- {assetType === AssetType.ITEM && !isLoading && mintedTokenId ? (
+ {assetType === AssetType.ITEM &&
+ !isLoading &&
+ mintedTokenId ? (
) : (
-
+ <>
+ {asset.category === NFTCategory.ENS ? (
+ <>
+
+ {!!profile && (
+
+ )}
+ >
+ ) : (
+
+ )}
+ >
)}
{(asset.category === NFTCategory.WEARABLE ||
diff --git a/webapp/src/components/SuccessPage/SuccessPage.types.ts b/webapp/src/components/SuccessPage/SuccessPage.types.ts
index 0ee247f01c..a5c01e211f 100644
--- a/webapp/src/components/SuccessPage/SuccessPage.types.ts
+++ b/webapp/src/components/SuccessPage/SuccessPage.types.ts
@@ -1,8 +1,12 @@
-import { BigNumber } from "ethers"
+import { BigNumber } from 'ethers'
+import { Profile } from '@dcl/schemas'
export type Props = {
isLoading: boolean
mintedTokenId: BigNumber | null
+ onSetNameAsAlias: (name: string) => void
+ profile: Profile | undefined
}
-export type MapStateProps = Pick
+export type MapStateProps = Pick
+export type MapDispatchProps = Pick
diff --git a/webapp/src/config/env/dev.json b/webapp/src/config/env/dev.json
index 0b8ca45e2b..868a666d28 100644
--- a/webapp/src/config/env/dev.json
+++ b/webapp/src/config/env/dev.json
@@ -29,6 +29,8 @@
"MAX_PRICE_INCREASE_PERCENTAGE": "15",
"MANA_ADDRESS": "0x2a8fd99c19271f4f04b1b7b9c4f7cf264b626edb",
"CONVERTER_ADDRESS": "0x2782eb28Dcb1eF4E7632273cd4e347e130Ce4646",
+ "REGISTRAR_CONTRACT_ADDRESS": "0x7518456ae93eb98f3e64571b689c626616bb7f30",
+ "CONTROLLER_V2_CONTRACT_ADDRESS": "0xd2046364317c21fa8d121d84185c39e6e910cf89",
"CONVERTER_EXCHANGE": "uniswap_v2",
"INTERCOM_APP_ID": "z0h94kay",
"SEGMENT_API_KEY": "gMnkewUBzA6J879kAtMvp2WhohEk36uy",
@@ -42,5 +44,6 @@
"SUBGRAPH_WORKER": "https://subgraph.decentraland.zone",
"SENTRY_DSN": "https://1dc401149e1c819b8477565c9cdd9b70@o4504361728212992.ingest.sentry.io/4505743351676928",
"SQUID_API_URL": "https://testnet.v2.api.squidrouter.com/",
+ "DCL_LISTS_SERVER": "https://dcl-lists.decentraland.zone",
"AUTH_URL": "/auth"
}
diff --git a/webapp/src/config/env/prod.json b/webapp/src/config/env/prod.json
index 4c99ebf0d5..f630e038be 100644
--- a/webapp/src/config/env/prod.json
+++ b/webapp/src/config/env/prod.json
@@ -29,6 +29,8 @@
"MAX_PRICE_INCREASE_PERCENTAGE": "15",
"MANA_ADDRESS": "0x0f5d2fb29fb7d3cfee444a200298f468908cc942",
"CONVERTER_ADDRESS": "0x2859581da59bd4e16a866dd06b461b76d8e489a4",
+ "REGISTRAR_CONTRACT_ADDRESS": "0x2a187453064356c898cae034eaed119e1663acb8",
+ "CONTROLLER_V2_CONTRACT_ADDRESS": "0xbe92b49aee993adea3a002adcda189a2b7dec56c",
"CONVERTER_EXCHANGE": "uniswap_v2",
"INTERCOM_APP_ID": "z0h94kay",
"SEGMENT_API_KEY": "WijgHCmOGJjV04XBGghZGEadMD4454R3",
@@ -42,5 +44,6 @@
"SUBGRAPH_WORKER": "https://subgraph.decentraland.org",
"SENTRY_DSN": "https://1dc401149e1c819b8477565c9cdd9b70@o4504361728212992.ingest.sentry.io/4505743351676928",
"SQUID_API_URL": "https://v2.api.squidrouter.com",
+ "DCL_LISTS_SERVER": "https://dcl-lists.decentraland.org",
"AUTH_URL": "/auth"
}
diff --git a/webapp/src/config/env/stg.json b/webapp/src/config/env/stg.json
index 3488adb55f..1ca5b8216b 100644
--- a/webapp/src/config/env/stg.json
+++ b/webapp/src/config/env/stg.json
@@ -29,6 +29,8 @@
"MAX_PRICE_INCREASE_PERCENTAGE": "15",
"MANA_ADDRESS": "0x0f5d2fb29fb7d3cfee444a200298f468908cc942",
"CONVERTER_ADDRESS": "0x2859581da59bd4e16a866dd06b461b76d8e489a4",
+ "REGISTRAR_CONTRACT_ADDRESS": "0x7518456ae93eb98f3e64571b689c626616bb7f30",
+ "CONTROLLER_V2_CONTRACT_ADDRESS": "0xbe92b49aee993adea3a002adcda189a2b7dec56c",
"CONVERTER_EXCHANGE": "uniswap_v2",
"INTERCOM_APP_ID": "z0h94kay",
"SEGMENT_API_KEY": "WijgHCmOGJjV04XBGghZGEadMD4454R3",
@@ -42,5 +44,6 @@
"SUBGRAPH_WORKER": "https://subgraph.decentraland.today",
"SENTRY_DSN": "https://1dc401149e1c819b8477565c9cdd9b70@o4504361728212992.ingest.sentry.io/4505743351676928",
"SQUID_API_URL": "https://v2.api.squidrouter.com",
+ "DCL_LISTS_SERVER": "https://dcl-lists.decentraland.today",
"AUTH_URL": "/auth"
}
diff --git a/webapp/src/contracts/DCLController.ts b/webapp/src/contracts/DCLController.ts
new file mode 100644
index 0000000000..05f1a787b9
--- /dev/null
+++ b/webapp/src/contracts/DCLController.ts
@@ -0,0 +1,385 @@
+/* Autogenerated file. Do not edit manually. */
+/* tslint:disable */
+/* eslint-disable */
+import type {
+ BaseContract,
+ BigNumber,
+ BigNumberish,
+ BytesLike,
+ CallOverrides,
+ ContractTransaction,
+ Overrides,
+ PopulatedTransaction,
+ Signer,
+ utils,
+} from "ethers";
+import type {
+ FunctionFragment,
+ Result,
+ EventFragment,
+} from "@ethersproject/abi";
+import type { Listener, Provider } from "@ethersproject/providers";
+import type {
+ TypedEventFilter,
+ TypedEvent,
+ TypedListener,
+ OnEvent,
+ PromiseOrValue,
+} from "./common";
+
+export interface DCLControllerInterface extends utils.Interface {
+ functions: {
+ "PRICE()": FunctionFragment;
+ "acceptedToken()": FunctionFragment;
+ "isOwner()": FunctionFragment;
+ "maxGasPrice()": FunctionFragment;
+ "owner()": FunctionFragment;
+ "register(string,address)": FunctionFragment;
+ "registrar()": FunctionFragment;
+ "renounceOwnership()": FunctionFragment;
+ "transferOwnership(address)": FunctionFragment;
+ "updateMaxGasPrice(uint256)": FunctionFragment;
+ };
+
+ getFunction(
+ nameOrSignatureOrTopic:
+ | "PRICE"
+ | "acceptedToken"
+ | "isOwner"
+ | "maxGasPrice"
+ | "owner"
+ | "register"
+ | "registrar"
+ | "renounceOwnership"
+ | "transferOwnership"
+ | "updateMaxGasPrice"
+ ): FunctionFragment;
+
+ encodeFunctionData(functionFragment: "PRICE", values?: undefined): string;
+ encodeFunctionData(
+ functionFragment: "acceptedToken",
+ values?: undefined
+ ): string;
+ encodeFunctionData(functionFragment: "isOwner", values?: undefined): string;
+ encodeFunctionData(
+ functionFragment: "maxGasPrice",
+ values?: undefined
+ ): string;
+ encodeFunctionData(functionFragment: "owner", values?: undefined): string;
+ encodeFunctionData(
+ functionFragment: "register",
+ values: [PromiseOrValue, PromiseOrValue]
+ ): string;
+ encodeFunctionData(functionFragment: "registrar", values?: undefined): string;
+ encodeFunctionData(
+ functionFragment: "renounceOwnership",
+ values?: undefined
+ ): string;
+ encodeFunctionData(
+ functionFragment: "transferOwnership",
+ values: [PromiseOrValue]
+ ): string;
+ encodeFunctionData(
+ functionFragment: "updateMaxGasPrice",
+ values: [PromiseOrValue]
+ ): string;
+
+ decodeFunctionResult(functionFragment: "PRICE", data: BytesLike): Result;
+ decodeFunctionResult(
+ functionFragment: "acceptedToken",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(functionFragment: "isOwner", data: BytesLike): Result;
+ decodeFunctionResult(
+ functionFragment: "maxGasPrice",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(functionFragment: "owner", data: BytesLike): Result;
+ decodeFunctionResult(functionFragment: "register", data: BytesLike): Result;
+ decodeFunctionResult(functionFragment: "registrar", data: BytesLike): Result;
+ decodeFunctionResult(
+ functionFragment: "renounceOwnership",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(
+ functionFragment: "transferOwnership",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(
+ functionFragment: "updateMaxGasPrice",
+ data: BytesLike
+ ): Result;
+
+ events: {
+ "MaxGasPriceChanged(uint256,uint256)": EventFragment;
+ "NameBought(address,address,uint256,string)": EventFragment;
+ "OwnershipTransferred(address,address)": EventFragment;
+ };
+
+ getEvent(nameOrSignatureOrTopic: "MaxGasPriceChanged"): EventFragment;
+ getEvent(nameOrSignatureOrTopic: "NameBought"): EventFragment;
+ getEvent(nameOrSignatureOrTopic: "OwnershipTransferred"): EventFragment;
+}
+
+export interface MaxGasPriceChangedEventObject {
+ _oldMaxGasPrice: BigNumber;
+ _newMaxGasPrice: BigNumber;
+}
+export type MaxGasPriceChangedEvent = TypedEvent<
+ [BigNumber, BigNumber],
+ MaxGasPriceChangedEventObject
+>;
+
+export type MaxGasPriceChangedEventFilter =
+ TypedEventFilter;
+
+export interface NameBoughtEventObject {
+ _caller: string;
+ _beneficiary: string;
+ _price: BigNumber;
+ _name: string;
+}
+export type NameBoughtEvent = TypedEvent<
+ [string, string, BigNumber, string],
+ NameBoughtEventObject
+>;
+
+export type NameBoughtEventFilter = TypedEventFilter;
+
+export interface OwnershipTransferredEventObject {
+ previousOwner: string;
+ newOwner: string;
+}
+export type OwnershipTransferredEvent = TypedEvent<
+ [string, string],
+ OwnershipTransferredEventObject
+>;
+
+export type OwnershipTransferredEventFilter =
+ TypedEventFilter;
+
+export interface DCLController extends BaseContract {
+ connect(signerOrProvider: Signer | Provider | string): this;
+ attach(addressOrName: string): this;
+ deployed(): Promise;
+
+ interface: DCLControllerInterface;
+
+ queryFilter(
+ event: TypedEventFilter,
+ fromBlockOrBlockhash?: string | number | undefined,
+ toBlock?: string | number | undefined
+ ): Promise>;
+
+ listeners(
+ eventFilter?: TypedEventFilter
+ ): Array>;
+ listeners(eventName?: string): Array;
+ removeAllListeners(
+ eventFilter: TypedEventFilter
+ ): this;
+ removeAllListeners(eventName?: string): this;
+ off: OnEvent;
+ on: OnEvent;
+ once: OnEvent;
+ removeListener: OnEvent;
+
+ functions: {
+ PRICE(overrides?: CallOverrides): Promise<[BigNumber]>;
+
+ acceptedToken(overrides?: CallOverrides): Promise<[string]>;
+
+ isOwner(overrides?: CallOverrides): Promise<[boolean]>;
+
+ maxGasPrice(overrides?: CallOverrides): Promise<[BigNumber]>;
+
+ owner(overrides?: CallOverrides): Promise<[string]>;
+
+ register(
+ _name: PromiseOrValue,
+ _beneficiary: PromiseOrValue,
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+
+ registrar(overrides?: CallOverrides): Promise<[string]>;
+
+ renounceOwnership(
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+
+ transferOwnership(
+ newOwner: PromiseOrValue,
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+
+ updateMaxGasPrice(
+ _maxGasPrice: PromiseOrValue,
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+ };
+
+ PRICE(overrides?: CallOverrides): Promise;
+
+ acceptedToken(overrides?: CallOverrides): Promise;
+
+ isOwner(overrides?: CallOverrides): Promise;
+
+ maxGasPrice(overrides?: CallOverrides): Promise;
+
+ owner(overrides?: CallOverrides): Promise;
+
+ register(
+ _name: PromiseOrValue,
+ _beneficiary: PromiseOrValue,
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+
+ registrar(overrides?: CallOverrides): Promise;
+
+ renounceOwnership(
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+
+ transferOwnership(
+ newOwner: PromiseOrValue,
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+
+ updateMaxGasPrice(
+ _maxGasPrice: PromiseOrValue,
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+
+ callStatic: {
+ PRICE(overrides?: CallOverrides): Promise;
+
+ acceptedToken(overrides?: CallOverrides): Promise;
+
+ isOwner(overrides?: CallOverrides): Promise;
+
+ maxGasPrice(overrides?: CallOverrides): Promise;
+
+ owner(overrides?: CallOverrides): Promise;
+
+ register(
+ _name: PromiseOrValue,
+ _beneficiary: PromiseOrValue,
+ overrides?: CallOverrides
+ ): Promise;
+
+ registrar(overrides?: CallOverrides): Promise;
+
+ renounceOwnership(overrides?: CallOverrides): Promise;
+
+ transferOwnership(
+ newOwner: PromiseOrValue,
+ overrides?: CallOverrides
+ ): Promise;
+
+ updateMaxGasPrice(
+ _maxGasPrice: PromiseOrValue,
+ overrides?: CallOverrides
+ ): Promise;
+ };
+
+ filters: {
+ "MaxGasPriceChanged(uint256,uint256)"(
+ _oldMaxGasPrice?: PromiseOrValue | null,
+ _newMaxGasPrice?: PromiseOrValue | null
+ ): MaxGasPriceChangedEventFilter;
+ MaxGasPriceChanged(
+ _oldMaxGasPrice?: PromiseOrValue | null,
+ _newMaxGasPrice?: PromiseOrValue | null
+ ): MaxGasPriceChangedEventFilter;
+
+ "NameBought(address,address,uint256,string)"(
+ _caller?: PromiseOrValue | null,
+ _beneficiary?: PromiseOrValue | null,
+ _price?: null,
+ _name?: null
+ ): NameBoughtEventFilter;
+ NameBought(
+ _caller?: PromiseOrValue | null,
+ _beneficiary?: PromiseOrValue | null,
+ _price?: null,
+ _name?: null
+ ): NameBoughtEventFilter;
+
+ "OwnershipTransferred(address,address)"(
+ previousOwner?: PromiseOrValue | null,
+ newOwner?: PromiseOrValue | null
+ ): OwnershipTransferredEventFilter;
+ OwnershipTransferred(
+ previousOwner?: PromiseOrValue | null,
+ newOwner?: PromiseOrValue | null
+ ): OwnershipTransferredEventFilter;
+ };
+
+ estimateGas: {
+ PRICE(overrides?: CallOverrides): Promise;
+
+ acceptedToken(overrides?: CallOverrides): Promise;
+
+ isOwner(overrides?: CallOverrides): Promise;
+
+ maxGasPrice(overrides?: CallOverrides): Promise;
+
+ owner(overrides?: CallOverrides): Promise;
+
+ register(
+ _name: PromiseOrValue,
+ _beneficiary: PromiseOrValue,
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+
+ registrar(overrides?: CallOverrides): Promise;
+
+ renounceOwnership(
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+
+ transferOwnership(
+ newOwner: PromiseOrValue,
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+
+ updateMaxGasPrice(
+ _maxGasPrice: PromiseOrValue,
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+ };
+
+ populateTransaction: {
+ PRICE(overrides?: CallOverrides): Promise;
+
+ acceptedToken(overrides?: CallOverrides): Promise;
+
+ isOwner(overrides?: CallOverrides): Promise;
+
+ maxGasPrice(overrides?: CallOverrides): Promise;
+
+ owner(overrides?: CallOverrides): Promise;
+
+ register(
+ _name: PromiseOrValue,
+ _beneficiary: PromiseOrValue,
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+
+ registrar(overrides?: CallOverrides): Promise;
+
+ renounceOwnership(
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+
+ transferOwnership(
+ newOwner: PromiseOrValue,
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+
+ updateMaxGasPrice(
+ _maxGasPrice: PromiseOrValue,
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+ };
+}
diff --git a/webapp/src/contracts/DCLRegistrar.ts b/webapp/src/contracts/DCLRegistrar.ts
new file mode 100644
index 0000000000..1d9c1f9712
--- /dev/null
+++ b/webapp/src/contracts/DCLRegistrar.ts
@@ -0,0 +1,1799 @@
+/* Autogenerated file. Do not edit manually. */
+/* tslint:disable */
+/* eslint-disable */
+import type {
+ BaseContract,
+ BigNumber,
+ BigNumberish,
+ BytesLike,
+ CallOverrides,
+ ContractTransaction,
+ Overrides,
+ PopulatedTransaction,
+ Signer,
+ utils,
+} from "ethers";
+import type {
+ FunctionFragment,
+ Result,
+ EventFragment,
+} from "@ethersproject/abi";
+import type { Listener, Provider } from "@ethersproject/providers";
+import type {
+ TypedEventFilter,
+ TypedEvent,
+ TypedListener,
+ OnEvent,
+ PromiseOrValue,
+} from "./common";
+
+export interface DCLRegistrarInterface extends utils.Interface {
+ functions: {
+ "ERC721_RECEIVED()": FunctionFragment;
+ "addController(address)": FunctionFragment;
+ "approve(address,uint256)": FunctionFragment;
+ "available(string)": FunctionFragment;
+ "balanceOf(address)": FunctionFragment;
+ "base()": FunctionFragment;
+ "baseURI()": FunctionFragment;
+ "controllers(address)": FunctionFragment;
+ "domain()": FunctionFragment;
+ "domainNameHash()": FunctionFragment;
+ "getApproved(uint256)": FunctionFragment;
+ "getOwnerOf(string)": FunctionFragment;
+ "getTokenId(string)": FunctionFragment;
+ "isApprovedForAll(address,address)": FunctionFragment;
+ "isOwner()": FunctionFragment;
+ "migrateNames(bytes32[],address[],uint256[])": FunctionFragment;
+ "migrated()": FunctionFragment;
+ "migrationFinished()": FunctionFragment;
+ "name()": FunctionFragment;
+ "onERC721Received(address,address,uint256,bytes)": FunctionFragment;
+ "owner()": FunctionFragment;
+ "ownerOf(uint256)": FunctionFragment;
+ "reclaim(uint256,address)": FunctionFragment;
+ "reclaimDomain(uint256)": FunctionFragment;
+ "register(string,address)": FunctionFragment;
+ "registry()": FunctionFragment;
+ "removeController(address)": FunctionFragment;
+ "renounceOwnership()": FunctionFragment;
+ "safeTransferFrom(address,address,uint256)": FunctionFragment;
+ "safeTransferFrom(address,address,uint256,bytes)": FunctionFragment;
+ "setApprovalForAll(address,bool)": FunctionFragment;
+ "subdomains(bytes32)": FunctionFragment;
+ "supportsInterface(bytes4)": FunctionFragment;
+ "symbol()": FunctionFragment;
+ "tokenByIndex(uint256)": FunctionFragment;
+ "tokenOfOwnerByIndex(address,uint256)": FunctionFragment;
+ "tokenURI(uint256)": FunctionFragment;
+ "topdomain()": FunctionFragment;
+ "topdomainNameHash()": FunctionFragment;
+ "totalSupply()": FunctionFragment;
+ "transferDomainOwnership(address,uint256)": FunctionFragment;
+ "transferFrom(address,address,uint256)": FunctionFragment;
+ "transferOwnership(address)": FunctionFragment;
+ "updateBase(address)": FunctionFragment;
+ "updateBaseURI(string)": FunctionFragment;
+ "updateRegistry(address)": FunctionFragment;
+ };
+
+ getFunction(
+ nameOrSignatureOrTopic:
+ | "ERC721_RECEIVED"
+ | "addController"
+ | "approve"
+ | "available"
+ | "balanceOf"
+ | "base"
+ | "baseURI"
+ | "controllers"
+ | "domain"
+ | "domainNameHash"
+ | "getApproved"
+ | "getOwnerOf"
+ | "getTokenId"
+ | "isApprovedForAll"
+ | "isOwner"
+ | "migrateNames"
+ | "migrated"
+ | "migrationFinished"
+ | "name"
+ | "onERC721Received"
+ | "owner"
+ | "ownerOf"
+ | "reclaim"
+ | "reclaimDomain"
+ | "register"
+ | "registry"
+ | "removeController"
+ | "renounceOwnership"
+ | "safeTransferFrom(address,address,uint256)"
+ | "safeTransferFrom(address,address,uint256,bytes)"
+ | "setApprovalForAll"
+ | "subdomains"
+ | "supportsInterface"
+ | "symbol"
+ | "tokenByIndex"
+ | "tokenOfOwnerByIndex"
+ | "tokenURI"
+ | "topdomain"
+ | "topdomainNameHash"
+ | "totalSupply"
+ | "transferDomainOwnership"
+ | "transferFrom"
+ | "transferOwnership"
+ | "updateBase"
+ | "updateBaseURI"
+ | "updateRegistry"
+ ): FunctionFragment;
+
+ encodeFunctionData(
+ functionFragment: "ERC721_RECEIVED",
+ values?: undefined
+ ): string;
+ encodeFunctionData(
+ functionFragment: "addController",
+ values: [PromiseOrValue]
+ ): string;
+ encodeFunctionData(
+ functionFragment: "approve",
+ values: [PromiseOrValue, PromiseOrValue]
+ ): string;
+ encodeFunctionData(
+ functionFragment: "available",
+ values: [PromiseOrValue]
+ ): string;
+ encodeFunctionData(
+ functionFragment: "balanceOf",
+ values: [PromiseOrValue]
+ ): string;
+ encodeFunctionData(functionFragment: "base", values?: undefined): string;
+ encodeFunctionData(functionFragment: "baseURI", values?: undefined): string;
+ encodeFunctionData(
+ functionFragment: "controllers",
+ values: [PromiseOrValue]
+ ): string;
+ encodeFunctionData(functionFragment: "domain", values?: undefined): string;
+ encodeFunctionData(
+ functionFragment: "domainNameHash",
+ values?: undefined
+ ): string;
+ encodeFunctionData(
+ functionFragment: "getApproved",
+ values: [PromiseOrValue]
+ ): string;
+ encodeFunctionData(
+ functionFragment: "getOwnerOf",
+ values: [PromiseOrValue]
+ ): string;
+ encodeFunctionData(
+ functionFragment: "getTokenId",
+ values: [PromiseOrValue]
+ ): string;
+ encodeFunctionData(
+ functionFragment: "isApprovedForAll",
+ values: [PromiseOrValue, PromiseOrValue]
+ ): string;
+ encodeFunctionData(functionFragment: "isOwner", values?: undefined): string;
+ encodeFunctionData(
+ functionFragment: "migrateNames",
+ values: [
+ PromiseOrValue[],
+ PromiseOrValue[],
+ PromiseOrValue[]
+ ]
+ ): string;
+ encodeFunctionData(functionFragment: "migrated", values?: undefined): string;
+ encodeFunctionData(
+ functionFragment: "migrationFinished",
+ values?: undefined
+ ): string;
+ encodeFunctionData(functionFragment: "name", values?: undefined): string;
+ encodeFunctionData(
+ functionFragment: "onERC721Received",
+ values: [
+ PromiseOrValue,
+ PromiseOrValue,
+ PromiseOrValue,
+ PromiseOrValue
+ ]
+ ): string;
+ encodeFunctionData(functionFragment: "owner", values?: undefined): string;
+ encodeFunctionData(
+ functionFragment: "ownerOf",
+ values: [PromiseOrValue]
+ ): string;
+ encodeFunctionData(
+ functionFragment: "reclaim",
+ values: [PromiseOrValue, PromiseOrValue]
+ ): string;
+ encodeFunctionData(
+ functionFragment: "reclaimDomain",
+ values: [PromiseOrValue]
+ ): string;
+ encodeFunctionData(
+ functionFragment: "register",
+ values: [PromiseOrValue, PromiseOrValue]
+ ): string;
+ encodeFunctionData(functionFragment: "registry", values?: undefined): string;
+ encodeFunctionData(
+ functionFragment: "removeController",
+ values: [PromiseOrValue]
+ ): string;
+ encodeFunctionData(
+ functionFragment: "renounceOwnership",
+ values?: undefined
+ ): string;
+ encodeFunctionData(
+ functionFragment: "safeTransferFrom(address,address,uint256)",
+ values: [
+ PromiseOrValue,
+ PromiseOrValue,
+ PromiseOrValue
+ ]
+ ): string;
+ encodeFunctionData(
+ functionFragment: "safeTransferFrom(address,address,uint256,bytes)",
+ values: [
+ PromiseOrValue,
+ PromiseOrValue,
+ PromiseOrValue,
+ PromiseOrValue
+ ]
+ ): string;
+ encodeFunctionData(
+ functionFragment: "setApprovalForAll",
+ values: [PromiseOrValue, PromiseOrValue]
+ ): string;
+ encodeFunctionData(
+ functionFragment: "subdomains",
+ values: [PromiseOrValue]
+ ): string;
+ encodeFunctionData(
+ functionFragment: "supportsInterface",
+ values: [PromiseOrValue]
+ ): string;
+ encodeFunctionData(functionFragment: "symbol", values?: undefined): string;
+ encodeFunctionData(
+ functionFragment: "tokenByIndex",
+ values: [PromiseOrValue]
+ ): string;
+ encodeFunctionData(
+ functionFragment: "tokenOfOwnerByIndex",
+ values: [PromiseOrValue, PromiseOrValue]
+ ): string;
+ encodeFunctionData(
+ functionFragment: "tokenURI",
+ values: [PromiseOrValue]
+ ): string;
+ encodeFunctionData(functionFragment: "topdomain", values?: undefined): string;
+ encodeFunctionData(
+ functionFragment: "topdomainNameHash",
+ values?: undefined
+ ): string;
+ encodeFunctionData(
+ functionFragment: "totalSupply",
+ values?: undefined
+ ): string;
+ encodeFunctionData(
+ functionFragment: "transferDomainOwnership",
+ values: [PromiseOrValue, PromiseOrValue]
+ ): string;
+ encodeFunctionData(
+ functionFragment: "transferFrom",
+ values: [
+ PromiseOrValue,
+ PromiseOrValue,
+ PromiseOrValue
+ ]
+ ): string;
+ encodeFunctionData(
+ functionFragment: "transferOwnership",
+ values: [PromiseOrValue]
+ ): string;
+ encodeFunctionData(
+ functionFragment: "updateBase",
+ values: [PromiseOrValue]
+ ): string;
+ encodeFunctionData(
+ functionFragment: "updateBaseURI",
+ values: [PromiseOrValue]
+ ): string;
+ encodeFunctionData(
+ functionFragment: "updateRegistry",
+ values: [PromiseOrValue]
+ ): string;
+
+ decodeFunctionResult(
+ functionFragment: "ERC721_RECEIVED",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(
+ functionFragment: "addController",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(functionFragment: "approve", data: BytesLike): Result;
+ decodeFunctionResult(functionFragment: "available", data: BytesLike): Result;
+ decodeFunctionResult(functionFragment: "balanceOf", data: BytesLike): Result;
+ decodeFunctionResult(functionFragment: "base", data: BytesLike): Result;
+ decodeFunctionResult(functionFragment: "baseURI", data: BytesLike): Result;
+ decodeFunctionResult(
+ functionFragment: "controllers",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(functionFragment: "domain", data: BytesLike): Result;
+ decodeFunctionResult(
+ functionFragment: "domainNameHash",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(
+ functionFragment: "getApproved",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(functionFragment: "getOwnerOf", data: BytesLike): Result;
+ decodeFunctionResult(functionFragment: "getTokenId", data: BytesLike): Result;
+ decodeFunctionResult(
+ functionFragment: "isApprovedForAll",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(functionFragment: "isOwner", data: BytesLike): Result;
+ decodeFunctionResult(
+ functionFragment: "migrateNames",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(functionFragment: "migrated", data: BytesLike): Result;
+ decodeFunctionResult(
+ functionFragment: "migrationFinished",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(functionFragment: "name", data: BytesLike): Result;
+ decodeFunctionResult(
+ functionFragment: "onERC721Received",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(functionFragment: "owner", data: BytesLike): Result;
+ decodeFunctionResult(functionFragment: "ownerOf", data: BytesLike): Result;
+ decodeFunctionResult(functionFragment: "reclaim", data: BytesLike): Result;
+ decodeFunctionResult(
+ functionFragment: "reclaimDomain",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(functionFragment: "register", data: BytesLike): Result;
+ decodeFunctionResult(functionFragment: "registry", data: BytesLike): Result;
+ decodeFunctionResult(
+ functionFragment: "removeController",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(
+ functionFragment: "renounceOwnership",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(
+ functionFragment: "safeTransferFrom(address,address,uint256)",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(
+ functionFragment: "safeTransferFrom(address,address,uint256,bytes)",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(
+ functionFragment: "setApprovalForAll",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(functionFragment: "subdomains", data: BytesLike): Result;
+ decodeFunctionResult(
+ functionFragment: "supportsInterface",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(functionFragment: "symbol", data: BytesLike): Result;
+ decodeFunctionResult(
+ functionFragment: "tokenByIndex",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(
+ functionFragment: "tokenOfOwnerByIndex",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(functionFragment: "tokenURI", data: BytesLike): Result;
+ decodeFunctionResult(functionFragment: "topdomain", data: BytesLike): Result;
+ decodeFunctionResult(
+ functionFragment: "topdomainNameHash",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(
+ functionFragment: "totalSupply",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(
+ functionFragment: "transferDomainOwnership",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(
+ functionFragment: "transferFrom",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(
+ functionFragment: "transferOwnership",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(functionFragment: "updateBase", data: BytesLike): Result;
+ decodeFunctionResult(
+ functionFragment: "updateBaseURI",
+ data: BytesLike
+ ): Result;
+ decodeFunctionResult(
+ functionFragment: "updateRegistry",
+ data: BytesLike
+ ): Result;
+
+ events: {
+ "Approval(address,address,uint256)": EventFragment;
+ "ApprovalForAll(address,address,bool)": EventFragment;
+ "BaseURI(string,string)": EventFragment;
+ "BaseUpdated(address,address)": EventFragment;
+ "ControllerAdded(address)": EventFragment;
+ "ControllerRemoved(address)": EventFragment;
+ "DomainReclaimed(uint256)": EventFragment;
+ "DomainTransferred(address,uint256)": EventFragment;
+ "MigrationFinished()": EventFragment;
+ "NameRegistered(address,address,bytes32,string,uint256)": EventFragment;
+ "OwnershipTransferred(address,address)": EventFragment;
+ "Reclaimed(address,address,uint256)": EventFragment;
+ "RegistryUpdated(address,address)": EventFragment;
+ "Transfer(address,address,uint256)": EventFragment;
+ };
+
+ getEvent(nameOrSignatureOrTopic: "Approval"): EventFragment;
+ getEvent(nameOrSignatureOrTopic: "ApprovalForAll"): EventFragment;
+ getEvent(nameOrSignatureOrTopic: "BaseURI"): EventFragment;
+ getEvent(nameOrSignatureOrTopic: "BaseUpdated"): EventFragment;
+ getEvent(nameOrSignatureOrTopic: "ControllerAdded"): EventFragment;
+ getEvent(nameOrSignatureOrTopic: "ControllerRemoved"): EventFragment;
+ getEvent(nameOrSignatureOrTopic: "DomainReclaimed"): EventFragment;
+ getEvent(nameOrSignatureOrTopic: "DomainTransferred"): EventFragment;
+ getEvent(nameOrSignatureOrTopic: "MigrationFinished"): EventFragment;
+ getEvent(nameOrSignatureOrTopic: "NameRegistered"): EventFragment;
+ getEvent(nameOrSignatureOrTopic: "OwnershipTransferred"): EventFragment;
+ getEvent(nameOrSignatureOrTopic: "Reclaimed"): EventFragment;
+ getEvent(nameOrSignatureOrTopic: "RegistryUpdated"): EventFragment;
+ getEvent(nameOrSignatureOrTopic: "Transfer"): EventFragment;
+}
+
+export interface ApprovalEventObject {
+ owner: string;
+ approved: string;
+ tokenId: BigNumber;
+}
+export type ApprovalEvent = TypedEvent<
+ [string, string, BigNumber],
+ ApprovalEventObject
+>;
+
+export type ApprovalEventFilter = TypedEventFilter;
+
+export interface ApprovalForAllEventObject {
+ owner: string;
+ operator: string;
+ approved: boolean;
+}
+export type ApprovalForAllEvent = TypedEvent<
+ [string, string, boolean],
+ ApprovalForAllEventObject
+>;
+
+export type ApprovalForAllEventFilter = TypedEventFilter;
+
+export interface BaseURIEventObject {
+ _oldBaseURI: string;
+ _newBaseURI: string;
+}
+export type BaseURIEvent = TypedEvent<[string, string], BaseURIEventObject>;
+
+export type BaseURIEventFilter = TypedEventFilter;
+
+export interface BaseUpdatedEventObject {
+ _previousBase: string;
+ _newBase: string;
+}
+export type BaseUpdatedEvent = TypedEvent<
+ [string, string],
+ BaseUpdatedEventObject
+>;
+
+export type BaseUpdatedEventFilter = TypedEventFilter;
+
+export interface ControllerAddedEventObject {
+ _controller: string;
+}
+export type ControllerAddedEvent = TypedEvent<
+ [string],
+ ControllerAddedEventObject
+>;
+
+export type ControllerAddedEventFilter = TypedEventFilter;
+
+export interface ControllerRemovedEventObject {
+ _controller: string;
+}
+export type ControllerRemovedEvent = TypedEvent<
+ [string],
+ ControllerRemovedEventObject
+>;
+
+export type ControllerRemovedEventFilter =
+ TypedEventFilter;
+
+export interface DomainReclaimedEventObject {
+ _tokenId: BigNumber;
+}
+export type DomainReclaimedEvent = TypedEvent<
+ [BigNumber],
+ DomainReclaimedEventObject
+>;
+
+export type DomainReclaimedEventFilter = TypedEventFilter;
+
+export interface DomainTransferredEventObject {
+ _newOwner: string;
+ _tokenId: BigNumber;
+}
+export type DomainTransferredEvent = TypedEvent<
+ [string, BigNumber],
+ DomainTransferredEventObject
+>;
+
+export type DomainTransferredEventFilter =
+ TypedEventFilter;
+
+export interface MigrationFinishedEventObject {}
+export type MigrationFinishedEvent = TypedEvent<
+ [],
+ MigrationFinishedEventObject
+>;
+
+export type MigrationFinishedEventFilter =
+ TypedEventFilter;
+
+export interface NameRegisteredEventObject {
+ _caller: string;
+ _beneficiary: string;
+ _labelHash: string;
+ _subdomain: string;
+ _createdDate: BigNumber;
+}
+export type NameRegisteredEvent = TypedEvent<
+ [string, string, string, string, BigNumber],
+ NameRegisteredEventObject
+>;
+
+export type NameRegisteredEventFilter = TypedEventFilter;
+
+export interface OwnershipTransferredEventObject {
+ previousOwner: string;
+ newOwner: string;
+}
+export type OwnershipTransferredEvent = TypedEvent<
+ [string, string],
+ OwnershipTransferredEventObject
+>;
+
+export type OwnershipTransferredEventFilter =
+ TypedEventFilter;
+
+export interface ReclaimedEventObject {
+ _caller: string;
+ _owner: string;
+ _tokenId: BigNumber;
+}
+export type ReclaimedEvent = TypedEvent<
+ [string, string, BigNumber],
+ ReclaimedEventObject
+>;
+
+export type ReclaimedEventFilter = TypedEventFilter;
+
+export interface RegistryUpdatedEventObject {
+ _previousRegistry: string;
+ _newRegistry: string;
+}
+export type RegistryUpdatedEvent = TypedEvent<
+ [string, string],
+ RegistryUpdatedEventObject
+>;
+
+export type RegistryUpdatedEventFilter = TypedEventFilter;
+
+export interface TransferEventObject {
+ from: string;
+ to: string;
+ tokenId: BigNumber;
+}
+export type TransferEvent = TypedEvent<
+ [string, string, BigNumber],
+ TransferEventObject
+>;
+
+export type TransferEventFilter = TypedEventFilter;
+
+export interface DCLRegistrar extends BaseContract {
+ connect(signerOrProvider: Signer | Provider | string): this;
+ attach(addressOrName: string): this;
+ deployed(): Promise;
+
+ interface: DCLRegistrarInterface;
+
+ queryFilter(
+ event: TypedEventFilter,
+ fromBlockOrBlockhash?: string | number | undefined,
+ toBlock?: string | number | undefined
+ ): Promise>;
+
+ listeners(
+ eventFilter?: TypedEventFilter
+ ): Array>;
+ listeners(eventName?: string): Array;
+ removeAllListeners(
+ eventFilter: TypedEventFilter
+ ): this;
+ removeAllListeners(eventName?: string): this;
+ off: OnEvent;
+ on: OnEvent;
+ once: OnEvent;
+ removeListener: OnEvent;
+
+ functions: {
+ ERC721_RECEIVED(overrides?: CallOverrides): Promise<[string]>;
+
+ addController(
+ controller: PromiseOrValue,
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+
+ approve(
+ to: PromiseOrValue,
+ tokenId: PromiseOrValue,
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+
+ available(
+ _subdomain: PromiseOrValue,
+ overrides?: CallOverrides
+ ): Promise<[boolean]>;
+
+ balanceOf(
+ owner: PromiseOrValue,
+ overrides?: CallOverrides
+ ): Promise<[BigNumber]>;
+
+ base(overrides?: CallOverrides): Promise<[string]>;
+
+ baseURI(overrides?: CallOverrides): Promise<[string]>;
+
+ controllers(
+ arg0: PromiseOrValue,
+ overrides?: CallOverrides
+ ): Promise<[boolean]>;
+
+ domain(overrides?: CallOverrides): Promise<[string]>;
+
+ domainNameHash(overrides?: CallOverrides): Promise<[string]>;
+
+ getApproved(
+ tokenId: PromiseOrValue,
+ overrides?: CallOverrides
+ ): Promise<[string]>;
+
+ getOwnerOf(
+ _subdomain: PromiseOrValue,
+ overrides?: CallOverrides
+ ): Promise<[string]>;
+
+ getTokenId(
+ _subdomain: PromiseOrValue,
+ overrides?: CallOverrides
+ ): Promise<[BigNumber]>;
+
+ isApprovedForAll(
+ owner: PromiseOrValue,
+ operator: PromiseOrValue,
+ overrides?: CallOverrides
+ ): Promise<[boolean]>;
+
+ isOwner(overrides?: CallOverrides): Promise<[boolean]>;
+
+ migrateNames(
+ _names: PromiseOrValue[],
+ _beneficiaries: PromiseOrValue[],
+ _createdDates: PromiseOrValue[],
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+
+ migrated(overrides?: CallOverrides): Promise<[boolean]>;
+
+ migrationFinished(
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+
+ name(overrides?: CallOverrides): Promise<[string]>;
+
+ onERC721Received(
+ arg0: PromiseOrValue,
+ arg1: PromiseOrValue,
+ _tokenId: PromiseOrValue,
+ arg3: PromiseOrValue,
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+
+ owner(overrides?: CallOverrides): Promise<[string]>;
+
+ ownerOf(
+ tokenId: PromiseOrValue,
+ overrides?: CallOverrides
+ ): Promise<[string]>;
+
+ reclaim(
+ _tokenId: PromiseOrValue,
+ _owner: PromiseOrValue,
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+
+ reclaimDomain(
+ _tokenId: PromiseOrValue,
+ overrides?: Overrides & { from?: PromiseOrValue }
+ ): Promise;
+
+ register(
+ _subdomain: PromiseOrValue