├── src
├── components
│ ├── NetworkAlert
│ │ ├── NetworkAlert.module.css
│ │ └── index.tsx
│ ├── StandalonePart
│ │ ├── StandalonePart.module.css
│ │ └── index.tsx
│ ├── TightStackedCircleNouns
│ │ ├── TightStackedCircleNouns.module.css
│ │ └── index.tsx
│ ├── MobileProfileActivityFeed
│ │ └── MobileProfileActivityFeed.module.css
│ ├── ModalLabel
│ │ ├── ModalLabel.module.css
│ │ └── index.tsx
│ ├── Tooltip
│ │ ├── Tooltip.module.css
│ │ └── index.tsx
│ ├── ModalSubtitle
│ │ ├── ModalSubtitle.module.css
│ │ └── index.tsx
│ ├── ModalTitle
│ │ ├── ModalTitle.module.css
│ │ └── index.tsx
│ ├── GrayCircle
│ │ ├── GrayCircle.module.css
│ │ └── index.tsx
│ ├── StandaloneNoun
│ │ └── StandaloneNoun.module.css
│ ├── NounImageVoteTable
│ │ └── NounImageVoteTable.module.css
│ ├── ModalBottomButtonRow
│ │ ├── ModalBottomButtonRow.module.css
│ │ └── index.tsx
│ ├── Link
│ │ ├── Link.module.css
│ │ └── index.tsx
│ ├── AuctionActivityWrapper
│ │ ├── AuctionActivityWrapper.module.css
│ │ └── index.tsx
│ ├── ModalTextPrimary
│ │ ├── ModalTextPrimary.module.css
│ │ └── index.tsx
│ ├── DelegateGroupedNounImageVoteTable
│ │ └── DelegateGroupedNounImageVoteTable.module.css
│ ├── ProposalActionsModal
│ │ └── steps
│ │ │ ├── TransferFundsReviewStep
│ │ │ └── TransferFundsReviewStep.module.css
│ │ │ ├── StreamPaymentsReviewStep
│ │ │ └── StreamPaymentsReviewStep.module.css
│ │ │ ├── FunctionCallReviewStep
│ │ │ └── FunctionCallReviewStep.module.css
│ │ │ └── FunctionCallEnterArgsStep
│ │ │ └── FunctionCallEnterArgsStep.module.css
│ ├── AuctionActivityDateHeadline
│ │ ├── AuctionActivityDateHeadline.module.css
│ │ └── index.tsx
│ ├── ProfileActivityFeedToggle
│ │ ├── ProfileActivityFeedToggle.module.css
│ │ └── index.tsx
│ ├── HorizontalStackedNouns
│ │ ├── HorizontalStackedNouns.module.css
│ │ └── index.tsx
│ ├── MinBid
│ │ ├── MinBid.module.css
│ │ └── index.tsx
│ ├── AuctionActivityNounTitle
│ │ ├── AuctionActivityNounTitle.module.css
│ │ └── index.tsx
│ ├── DesktopProfileActivityFeed
│ │ └── DesktopProfileActivityFeed.module.css
│ ├── profileEvent
│ │ ├── event
│ │ │ ├── MobileProposalVoteEvent
│ │ │ │ └── MobileProposalVoteEvent.module.css
│ │ │ ├── MobileNounWinEvent
│ │ │ │ └── MobileNounWinEvent.module.css
│ │ │ ├── MobileTransferEvent
│ │ │ │ └── MobileTransferEvent.module.css
│ │ │ ├── MobileDelegationEvent
│ │ │ │ ├── MobileDelegationEvent.module.css
│ │ │ │ └── index.tsx
│ │ │ ├── DesktopProposalVoteEvent
│ │ │ │ └── DesktopProposalVoteEvent.module.css
│ │ │ ├── DesktopTransferEvent
│ │ │ │ └── DesktopTransferEvent.module.css
│ │ │ ├── DesktopDelegationEvent
│ │ │ │ └── DesktopDelegationEvent.module.css
│ │ │ └── DesktopNounWinEvent
│ │ │ │ └── DesktopNounWinEvent.module.css
│ │ ├── eventData
│ │ │ ├── ProposalVoteHeadline
│ │ │ │ └── ProposalVoteHeadline.module.css
│ │ │ ├── ProposalVoteInfoPillsContainer
│ │ │ │ ├── ProposalVoteInfoPills.module.css
│ │ │ │ └── index.tsx
│ │ │ └── infoPills
│ │ │ │ ├── TransactionHashPill
│ │ │ │ ├── TransactionHashPill.module.css
│ │ │ │ └── index.tsx
│ │ │ │ └── DelegatePill
│ │ │ │ ├── index.tsx
│ │ │ │ └── DelegatePill.module.css
│ │ └── activityRow
│ │ │ ├── MobileNounActivityRow
│ │ │ ├── MobileNounActivityRow.module.css
│ │ │ └── index.tsx
│ │ │ └── DesktopNounActivityRow
│ │ │ ├── DesktopNounActivityRow.module.css
│ │ │ └── index.tsx
│ ├── ShortAddress
│ │ ├── ShortAddress.module.css
│ │ └── index.tsx
│ ├── WalletConnectModal
│ │ └── WalletConnectModal.module.css
│ ├── ABIUpload
│ │ ├── ABIUpload.module.css
│ │ └── index.tsx
│ ├── BrandSpinner
│ │ ├── BrandSpinner.module.css
│ │ └── index.tsx
│ ├── Banner
│ │ ├── Banner.module.css
│ │ └── index.tsx
│ ├── AuctionTitleAndNavWrapper
│ │ ├── index.tsx
│ │ └── AuctionTitleAndNavWrapper.module.css
│ ├── NounInfoRowBirthday
│ │ └── NounInfoRowBirthday.module.css
│ ├── TruncatedAmount
│ │ └── index.tsx
│ ├── NavBar
│ │ ├── NavBarItem
│ │ │ ├── index.tsx
│ │ │ └── NavBarItem.module.css
│ │ └── NavBarLink
│ │ │ ├── index.tsx
│ │ │ └── NavBarLink.module.css
│ ├── NounderNounContent
│ │ └── NounderNounContent.module.css
│ ├── DelegationCandidateVoteCountInfo
│ │ ├── DelegationCandidateVoteCountInfo.module.css
│ │ └── index.tsx
│ ├── HoverCard
│ │ ├── HoverCard.module.css
│ │ └── index.tsx
│ ├── ProposalTransactions
│ │ └── ProposalTransactions.module.css
│ ├── WalletButton
│ │ └── WalletButton.module.css
│ ├── BrandTextEntry
│ │ ├── BrandTextEntry.module.css
│ │ └── index.tsx
│ ├── BrandDatePicker
│ │ ├── BrandDatePicker.module.css
│ │ └── index.tsx
│ ├── BrandNumericEntry
│ │ ├── BrandNumericEntry.module.css
│ │ └── index.tsx
│ ├── SettleManuallyBtn
│ │ └── SettleManuallyBtn.module.css
│ ├── VoteCardPager
│ │ └── VoteCardPager.module.css
│ ├── EnsOrLongAddress
│ │ └── index.tsx
│ ├── VoteStatusPill
│ │ ├── index.tsx
│ │ └── VoteStatusPill.module.css
│ ├── DelegationCandidateInfo
│ │ └── DelegationCandidateInfo.module.css
│ ├── HistoryCollection
│ │ └── HistoryCollection.module.css
│ ├── ProposalStatus
│ │ └── ProposalStatus.module.css
│ ├── VoteProgressBar
│ │ ├── VoteProgressBar.module.css
│ │ └── index.tsx
│ ├── BidHistoryBtn
│ │ └── index.tsx
│ ├── LanguageSelectionModal
│ │ ├── LanguageSelectionModal.module.css
│ │ └── index.tsx
│ ├── NavWallet
│ │ └── WalletConnectButton
│ │ │ └── index.tsx
│ ├── NounInfoCard
│ │ └── NounInfoCard.module.css
│ ├── NavDropdown
│ │ └── NavDropdown.module.css
│ ├── ByLineHoverCard
│ │ └── ByLineHoverCard.module.css
│ ├── BidHistoryModalRow
│ │ └── BidHistoryModalRow.module.css
│ ├── BrandDropdown
│ │ ├── BrandDropdown.module.css
│ │ └── index.tsx
│ ├── StartOrEndTime
│ │ └── index.tsx
│ ├── Noun
│ │ ├── index.tsx
│ │ └── Noun.module.css
│ ├── NounInfoRowButton
│ │ └── index.tsx
│ ├── CurrentBid
│ │ └── CurrentBid.module.css
│ ├── Auction
│ │ └── Auction.module.css
│ ├── Footer
│ │ ├── index.tsx
│ │ └── Footer.module.css
│ ├── ProfileActivityFeed
│ │ └── ProfileActivityFeed.module.css
│ ├── NounInfoRowHolder
│ │ └── NounInfoRowHolder.module.css
│ ├── ProposalStatusCopy
│ │ └── index.tsx
│ ├── ProposalContent
│ │ └── ProposalContent.module.css
│ ├── DelegateHoverCard
│ │ └── DelegateHoverCard.module.css
│ ├── NounProfileVoteRow
│ │ └── NounProfileVoteRow.module.css
│ ├── ProposalEditor
│ │ └── ProposalEditor.module.css
│ ├── Holder
│ │ └── Holder.module.css
│ ├── Modal
│ │ └── index.tsx
│ ├── TightStackedCircleNoun
│ │ └── index.tsx
│ ├── CurrentDelegatePannel
│ │ └── CurrentDelegatePannel.module.css
│ ├── ExploreGrid
│ │ └── ExploreGridItem
│ │ │ └── index.tsx
│ ├── NavLocaleSwitcher
│ │ └── NavLocalSwitcher.module.css
│ ├── Identicon
│ │ └── index.tsx
│ ├── CreateProposalButton
│ │ └── index.tsx
│ ├── NavBarTreasury
│ │ └── index.tsx
│ └── NounHoverCard
│ │ └── NounHoverCard.module.css
├── react-app-env.d.ts
├── utils
│ ├── history.ts
│ ├── vote.ts
│ ├── isMobile.ts
│ ├── nounBgColors.ts
│ ├── numberUtils.ts
│ ├── moderation
│ │ ├── moderationRegexes.json
│ │ └── containsBlockedText.ts
│ ├── pickByState.ts
│ ├── ResponsiveUIUtils.module.css
│ ├── compareBids.ts
│ ├── processProposalDescriptionText.ts
│ ├── timeUtils.ts
│ ├── resolveNounsContractAddress.ts
│ ├── usdcUtils.ts
│ ├── externalURL.ts
│ ├── grayBackgroundSVG.ts
│ ├── bidSorter.ts
│ ├── addressAndENSDisplayUtils.ts
│ ├── getNounsVotes.ts
│ ├── tokenBuyerContractUtils
│ │ └── tokenBuyer.ts
│ ├── pseudoRandomPredictableShuffle.ts
│ ├── types.ts
│ ├── nounActivity
│ │ └── getProposalVoteIcon.ts
│ ├── getAddressFromQueryParams.ts
│ ├── colorResponsiveUIUtils.ts
│ ├── logParsing.ts
│ ├── lookupNNSOrENS.ts
│ ├── ensAvatar.ts
│ ├── cssTransitionUtils.ts
│ ├── constants.ts
│ └── nounderNoun.ts
├── layout
│ └── Section
│ │ ├── Section.module.css
│ │ └── index.tsx
├── assets
│ ├── 404noun.png
│ ├── x-icon.png
│ ├── heart-noun.png
│ ├── nouns-ios.gif
│ ├── calendar_noun.png
│ ├── noun-pointer.png
│ ├── testnet-noun.png
│ ├── loading-skull-noun.gif
│ ├── nounder-pfps
│ │ ├── 4156.png
│ │ ├── 9999.png
│ │ ├── dom.png
│ │ ├── kai.png
│ │ ├── timpers.png
│ │ ├── vapeape.png
│ │ ├── devcarrot.png
│ │ ├── gremplin.png
│ │ ├── solimander.png
│ │ └── cryptoseneca.png
│ ├── panel-animation2.gif
│ ├── icons
│ │ ├── Abstain.svg
│ │ ├── Noggles.svg
│ │ ├── PendingVote.svg
│ │ ├── YesVote.svg
│ │ ├── Heart.svg
│ │ ├── Bids.svg
│ │ ├── Clock.svg
│ │ ├── Info.svg
│ │ └── NoVote.svg
│ └── wallet-brand-assets
│ │ ├── fortmatic.svg
│ │ ├── coinbase-wallet-dot.svg
│ │ └── ledger.svg
├── pages
│ ├── NotFound
│ │ ├── NotFound.module.css
│ │ └── index.tsx
│ ├── Explore
│ │ └── Explore.module.css
│ ├── Inscribe
│ │ └── inscribe.css
│ ├── DelegatePage
│ │ └── index.tsx
│ ├── Playground
│ │ ├── NounModal
│ │ │ └── NounModal.module.css
│ │ └── Playground.module.css
│ ├── Nounders
│ │ └── NoundersPage.module.css
│ └── CreateProposal
│ │ └── CreateProposal.module.css
├── fonts
│ ├── PT_Root_UI
│ │ ├── PT-Root-UI_Bold.woff
│ │ ├── PT-Root-UI_Bold.woff2
│ │ ├── PT-Root-UI_Light.woff
│ │ ├── PT-Root-UI_Light.woff2
│ │ ├── PT-Root-UI_Medium.woff
│ │ ├── PT-Root-UI_Medium.woff2
│ │ ├── PT-Root-UI_Regular.woff
│ │ └── PT-Root-UI_Regular.woff2
│ └── Londrina_Solid
│ │ ├── LondrinaSolid-Black.ttf
│ │ ├── LondrinaSolid-Light.ttf
│ │ ├── LondrinaSolid-Thin.ttf
│ │ └── LondrinaSolid-Regular.ttf
├── framer-motion.d.ts
├── store
│ ├── index.js
│ ├── reducers
│ │ ├── index.js
│ │ └── user.js
│ ├── selectors
│ │ └── index.js
│ └── actions
│ │ └── index.js
├── css
│ ├── globals.css
│ └── colors.css
├── setupTests.ts
├── libs
│ └── abi
│ │ └── ERC20.json
├── usedapp-core.d.ts
├── App.test.tsx
├── hooks.ts
├── App.module.css
├── i18n
│ ├── setLocale.ts
│ ├── LanguageProvider.tsx
│ └── locales.ts
├── hooks
│ ├── useProposalStatus.ts
│ ├── useReadonlyProvider.ts
│ ├── useBlockTimestamp.ts
│ ├── useLidoBalance.ts
│ ├── useTreasuryBalance.ts
│ └── useKeyPress.ts
├── reportWebVitals.ts
├── state
│ └── slices
│ │ ├── account.ts
│ │ ├── contracts.ts
│ │ ├── application.ts
│ │ └── onDisplayAuction.ts
├── wrappers
│ └── nounsStream.ts
└── index.css
├── declarations.d.ts
├── .babelrc
├── public
├── robots.txt
├── favicon.ico
├── logo192.png
├── logo512.png
├── mstile-150x150.png
├── apple-touch-icon.png
├── _redirects
├── android-chrome-192x192.png
├── android-chrome-384x384.png
├── browserconfig.xml
├── site.webmanifest.json
├── logo_.svg
└── safari-pinned-tab.svg
├── tsconfig.build.json
├── functions
├── utils.ts
├── v0-is-noun-owner
│ └── index.ts
├── v0-is-noun-delegate
│ └── index.ts
├── README.md
├── v0-noun-seeds
│ └── index.ts
├── v0-noun-owners
│ └── index.ts
├── v0-noun-votes
│ └── index.ts
└── v0-proposal-votes
│ └── index.ts
├── .env.example.local
├── .env.example
├── tailwind.config.js
├── .gitignore
├── lingui.config.ts
├── tsconfig.json
├── tsconfig.base.json
└── README.md
/src/components/NetworkAlert/NetworkAlert.module.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/StandalonePart/StandalonePart.module.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/declarations.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'bs-custom-file-input';
2 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "macros"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/components/TightStackedCircleNouns/TightStackedCircleNouns.module.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/MobileProfileActivityFeed/MobileProfileActivityFeed.module.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/ModalLabel/ModalLabel.module.css:
--------------------------------------------------------------------------------
1 | .label {
2 | opacity: 0.5;
3 | }
4 |
--------------------------------------------------------------------------------
/src/utils/history.ts:
--------------------------------------------------------------------------------
1 | export const nounPath = (nounId: Number) => `/noun/${nounId}`;
2 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/components/Tooltip/Tooltip.module.css:
--------------------------------------------------------------------------------
1 | .hover {
2 | border-radius: 8px !important;
3 | }
4 |
--------------------------------------------------------------------------------
/src/layout/Section/Section.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | padding: 2rem 0rem 0rem 0rem;
3 | }
4 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/public/logo512.png
--------------------------------------------------------------------------------
/src/components/ModalSubtitle/ModalSubtitle.module.css:
--------------------------------------------------------------------------------
1 | .subtitle {
2 | font-weight: 500;
3 | }
4 |
--------------------------------------------------------------------------------
/src/components/ModalTitle/ModalTitle.module.css:
--------------------------------------------------------------------------------
1 | .title {
2 | font-family: 'Londrina Solid';
3 | }
4 |
--------------------------------------------------------------------------------
/src/utils/vote.ts:
--------------------------------------------------------------------------------
1 | export enum Vote {
2 | SUPPORT = 0,
3 | FOR = 1,
4 | ABSTAIN = 2,
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/GrayCircle/GrayCircle.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | height: 55px;
3 | width: 55px;
4 | }
5 |
--------------------------------------------------------------------------------
/src/components/StandaloneNoun/StandaloneNoun.module.css:
--------------------------------------------------------------------------------
1 | .clickableNoun {
2 | cursor: pointer;
3 | }
4 |
--------------------------------------------------------------------------------
/src/utils/isMobile.ts:
--------------------------------------------------------------------------------
1 | export const isMobileScreen = () => {
2 | return window.innerWidth < 992;
3 | };
4 |
--------------------------------------------------------------------------------
/src/assets/404noun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/assets/404noun.png
--------------------------------------------------------------------------------
/src/assets/x-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/assets/x-icon.png
--------------------------------------------------------------------------------
/src/components/NounImageVoteTable/NounImageVoteTable.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | margin-left: 0.25rem;
3 | }
4 |
--------------------------------------------------------------------------------
/public/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/public/mstile-150x150.png
--------------------------------------------------------------------------------
/src/assets/heart-noun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/assets/heart-noun.png
--------------------------------------------------------------------------------
/src/assets/nouns-ios.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/assets/nouns-ios.gif
--------------------------------------------------------------------------------
/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/src/assets/calendar_noun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/assets/calendar_noun.png
--------------------------------------------------------------------------------
/src/assets/noun-pointer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/assets/noun-pointer.png
--------------------------------------------------------------------------------
/src/assets/testnet-noun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/assets/testnet-noun.png
--------------------------------------------------------------------------------
/public/_redirects:
--------------------------------------------------------------------------------
1 | /docs https://nouns.notion.site/Explore-Nouns-a2a9dceeb1d54e10b9cbf3f931c2266f 302
2 | /* /index.html 200
--------------------------------------------------------------------------------
/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/public/android-chrome-384x384.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/public/android-chrome-384x384.png
--------------------------------------------------------------------------------
/src/assets/loading-skull-noun.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/assets/loading-skull-noun.gif
--------------------------------------------------------------------------------
/src/assets/nounder-pfps/4156.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/assets/nounder-pfps/4156.png
--------------------------------------------------------------------------------
/src/assets/nounder-pfps/9999.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/assets/nounder-pfps/9999.png
--------------------------------------------------------------------------------
/src/assets/nounder-pfps/dom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/assets/nounder-pfps/dom.png
--------------------------------------------------------------------------------
/src/assets/nounder-pfps/kai.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/assets/nounder-pfps/kai.png
--------------------------------------------------------------------------------
/src/assets/panel-animation2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/assets/panel-animation2.gif
--------------------------------------------------------------------------------
/src/assets/nounder-pfps/timpers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/assets/nounder-pfps/timpers.png
--------------------------------------------------------------------------------
/src/assets/nounder-pfps/vapeape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/assets/nounder-pfps/vapeape.png
--------------------------------------------------------------------------------
/src/pages/NotFound/NotFound.module.css:
--------------------------------------------------------------------------------
1 | .heading {
2 | display: inline-block;
3 | font-weight: bold;
4 | font-size: 3rem;
5 | }
6 |
--------------------------------------------------------------------------------
/src/utils/nounBgColors.ts:
--------------------------------------------------------------------------------
1 | // export const grey = '#d5d7e1';
2 | export const grey = '#5ec1f3';
3 | export const beige = '#e1d7d5';
4 |
--------------------------------------------------------------------------------
/src/assets/nounder-pfps/devcarrot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/assets/nounder-pfps/devcarrot.png
--------------------------------------------------------------------------------
/src/assets/nounder-pfps/gremplin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/assets/nounder-pfps/gremplin.png
--------------------------------------------------------------------------------
/src/assets/nounder-pfps/solimander.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/assets/nounder-pfps/solimander.png
--------------------------------------------------------------------------------
/src/assets/nounder-pfps/cryptoseneca.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/assets/nounder-pfps/cryptoseneca.png
--------------------------------------------------------------------------------
/src/fonts/PT_Root_UI/PT-Root-UI_Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/fonts/PT_Root_UI/PT-Root-UI_Bold.woff
--------------------------------------------------------------------------------
/src/fonts/PT_Root_UI/PT-Root-UI_Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/fonts/PT_Root_UI/PT-Root-UI_Bold.woff2
--------------------------------------------------------------------------------
/src/fonts/PT_Root_UI/PT-Root-UI_Light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/fonts/PT_Root_UI/PT-Root-UI_Light.woff
--------------------------------------------------------------------------------
/src/fonts/PT_Root_UI/PT-Root-UI_Light.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/fonts/PT_Root_UI/PT-Root-UI_Light.woff2
--------------------------------------------------------------------------------
/src/fonts/PT_Root_UI/PT-Root-UI_Medium.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/fonts/PT_Root_UI/PT-Root-UI_Medium.woff
--------------------------------------------------------------------------------
/src/fonts/PT_Root_UI/PT-Root-UI_Medium.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/fonts/PT_Root_UI/PT-Root-UI_Medium.woff2
--------------------------------------------------------------------------------
/src/fonts/PT_Root_UI/PT-Root-UI_Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/fonts/PT_Root_UI/PT-Root-UI_Regular.woff
--------------------------------------------------------------------------------
/src/fonts/PT_Root_UI/PT-Root-UI_Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/fonts/PT_Root_UI/PT-Root-UI_Regular.woff2
--------------------------------------------------------------------------------
/src/fonts/Londrina_Solid/LondrinaSolid-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/fonts/Londrina_Solid/LondrinaSolid-Black.ttf
--------------------------------------------------------------------------------
/src/fonts/Londrina_Solid/LondrinaSolid-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/fonts/Londrina_Solid/LondrinaSolid-Light.ttf
--------------------------------------------------------------------------------
/src/fonts/Londrina_Solid/LondrinaSolid-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/fonts/Londrina_Solid/LondrinaSolid-Thin.ttf
--------------------------------------------------------------------------------
/src/fonts/Londrina_Solid/LondrinaSolid-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Crypto27dev/ordauction-frontend/HEAD/src/fonts/Londrina_Solid/LondrinaSolid-Regular.ttf
--------------------------------------------------------------------------------
/src/framer-motion.d.ts:
--------------------------------------------------------------------------------
1 | // workaround for framer-motion ts conflict
2 | declare module 'framer-motion/dist/framer-motion' {
3 | export * from 'framer-motion';
4 | }
5 |
--------------------------------------------------------------------------------
/src/components/ModalBottomButtonRow/ModalBottomButtonRow.module.css:
--------------------------------------------------------------------------------
1 | .buttonWrapper {
2 | display: flex;
3 | justify-content: space-between;
4 | margin-top: 2rem;
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/Link/Link.module.css:
--------------------------------------------------------------------------------
1 | .link,
2 | .link:hover,
3 | .link:active {
4 | color: var(--brand-dark-red);
5 | }
6 | .link:hover {
7 | text-decoration: underline;
8 | }
9 |
--------------------------------------------------------------------------------
/src/components/AuctionActivityWrapper/AuctionActivityWrapper.module.css:
--------------------------------------------------------------------------------
1 | @media (max-width: 992px) {
2 | .wrapper {
3 | margin-left: 1rem;
4 | margin-right: 1rem;
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/utils/numberUtils.ts:
--------------------------------------------------------------------------------
1 | export function countDecimals(n: number) {
2 | if (Math.floor(n.valueOf()) === n.valueOf()) return 0;
3 | return n.toString().split('.')[1].length || 0;
4 | }
5 |
--------------------------------------------------------------------------------
/src/components/ModalTextPrimary/ModalTextPrimary.module.css:
--------------------------------------------------------------------------------
1 | .text {
2 | font-size: 22px;
3 | font-weight: bold;
4 | color: var(--brand-cool-dark-text);
5 | margin-bottom: 0.5rem;
6 | }
7 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import { applyMiddleware, createStore } from 'redux'
2 | import { rootReducer } from './reducers'
3 |
4 | const store = createStore(rootReducer);
5 |
6 | export default store;
--------------------------------------------------------------------------------
/src/utils/moderation/moderationRegexes.json:
--------------------------------------------------------------------------------
1 | {
2 | "en": [
3 | {
4 | "regex": "kill.{0,3}all.{0,3}jew"
5 | },
6 | {
7 | "regex": "hate.{0,3}jew"
8 | }
9 | ],
10 | "jp": []
11 | }
12 |
--------------------------------------------------------------------------------
/src/css/globals.css:
--------------------------------------------------------------------------------
1 | @media (min-width: 1400px) {
2 | .container,
3 | .container-lg,
4 | .container-xl,
5 | .container-xxl {
6 | max-width: 1140px !important;
7 | }
8 | }
9 |
10 | body {
11 | overflow-x: hidden;
12 | }
13 |
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.base.json",
3 | "compilerOptions": {
4 | "rootDirs": ["src", "typechain", "abi/**/*.json"],
5 | "outDir": "dist"
6 | },
7 | "include": ["src", "typechain", "abi/**/*.json"]
8 | }
9 |
--------------------------------------------------------------------------------
/src/utils/pickByState.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Given state, picks the stateResult that corresponses to state
3 | */
4 | export const usePickByState = (state: any, states: any[], stateResults: any[]): any => {
5 | return stateResults[states.indexOf(state)];
6 | };
7 |
--------------------------------------------------------------------------------
/src/components/DelegateGroupedNounImageVoteTable/DelegateGroupedNounImageVoteTable.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | margin-left: auto;
3 | margin-right: auto;
4 | margin-top: -20px;
5 | }
6 |
7 | .nounCell {
8 | width: 55px;
9 | text-align: center;
10 | }
11 |
--------------------------------------------------------------------------------
/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/src/components/ModalSubtitle/index.tsx:
--------------------------------------------------------------------------------
1 | import classes from './ModalSubtitle.module.css';
2 | const ModalSubTitle = (props: { children: React.ReactNode }) => {
3 | return
{props.children}
;
4 | };
5 |
6 | export default ModalSubTitle;
7 |
--------------------------------------------------------------------------------
/src/assets/icons/Abstain.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/components/ProposalActionsModal/steps/TransferFundsReviewStep/TransferFundsReviewStep.module.css:
--------------------------------------------------------------------------------
1 | .label {
2 | opacity: 0.5;
3 | }
4 |
5 | .text {
6 | font-size: 22px;
7 | font-weight: bold;
8 | color: var(--brand-cool-dark-text);
9 | margin-bottom: 0.5rem;
10 | }
11 |
--------------------------------------------------------------------------------
/src/components/AuctionActivityWrapper/index.tsx:
--------------------------------------------------------------------------------
1 | import classes from './AuctionActivityWrapper.module.css';
2 |
3 | const AuctionActivityWrapper: React.FC<{}> = props => {
4 | return {props.children}
;
5 | };
6 | export default AuctionActivityWrapper;
7 |
--------------------------------------------------------------------------------
/src/assets/icons/Noggles.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/functions/utils.ts:
--------------------------------------------------------------------------------
1 | import { BigNumber, BigNumberish } from '@ethersproject/bignumber';
2 |
3 | export const bigNumbersEqual = (a: BigNumberish, b: BigNumberish) =>
4 | BigNumber.from(a).eq(BigNumber.from(b));
5 |
6 | export const sharedResponseHeaders = {
7 | 'Access-Control-Allow-Origin': '*',
8 | };
9 |
--------------------------------------------------------------------------------
/public/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #2b5797
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/components/AuctionActivityDateHeadline/AuctionActivityDateHeadline.module.css:
--------------------------------------------------------------------------------
1 | .date {
2 | font-family: 'PT Root UI';
3 | font-weight: bold;
4 | font-size: 17px;
5 | line-height: 27px;
6 | margin-top: 0.22rem;
7 | }
8 |
9 | .wrapper {
10 | margin-left: 5rem;
11 | width: auto;
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/ModalLabel/index.tsx:
--------------------------------------------------------------------------------
1 | import classes from './ModalLabel.module.css';
2 |
3 | interface ModalLabelProps {
4 | children?: React.ReactNode;
5 | }
6 |
7 | export default function ModalLabel({ children }: ModalLabelProps) {
8 | return {children}
;
9 | }
10 |
--------------------------------------------------------------------------------
/src/components/ProfileActivityFeedToggle/ProfileActivityFeedToggle.module.css:
--------------------------------------------------------------------------------
1 | .expandCollapseCopy {
2 | color: var(--brand-cool-light-text);
3 | font-family: 'PT Root UI';
4 | font-weight: bold;
5 | font-size: 20px;
6 | text-align: center;
7 | cursor: pointer;
8 | margin-top: 1rem;
9 | }
10 |
--------------------------------------------------------------------------------
/src/components/HorizontalStackedNouns/HorizontalStackedNouns.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | position: relative !important;
3 | margin-bottom: 50px;
4 | margin-top: 10px;
5 | margin-left: auto;
6 | margin-right: auto;
7 | width: 100%;
8 | }
9 |
10 | .nounWrapper {
11 | position: absolute;
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/MinBid/MinBid.module.css:
--------------------------------------------------------------------------------
1 | .minBidWrapper {
2 | margin-top: 1rem;
3 | display: flex;
4 | }
5 |
6 | .minBid {
7 | font-family: 'Londrina Solid';
8 | margin-left: 1rem;
9 | color: var(--brand-black);
10 | font-weight: normal;
11 | font-size: 1.25rem;
12 | cursor: pointer;
13 | }
14 |
--------------------------------------------------------------------------------
/src/libs/abi/ERC20.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "constant": true,
4 | "inputs": [{ "name": "_account", "type": "address" }],
5 | "name": "balanceOf",
6 | "outputs": [{ "name": "", "type": "uint256" }],
7 | "payable": false,
8 | "stateMutability": "view",
9 | "type": "function"
10 | }
11 | ]
12 |
--------------------------------------------------------------------------------
/src/usedapp-core.d.ts:
--------------------------------------------------------------------------------
1 | import '@usedapp/core';
2 |
3 | declare module '@usedapp/core' {
4 | function useContractCall(call: ContractCall | Falsy): T | undefined;
5 |
6 | function useContractCalls(
7 | calls: (ContractCall | Falsy)[],
8 | ): (T | undefined)[];
9 | }
10 |
--------------------------------------------------------------------------------
/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, screen } from '@testing-library/react';
3 | import App from './App';
4 |
5 | test('renders learn react link', () => {
6 | render();
7 | const linkElement = screen.getByText(/learn react/i);
8 | expect(linkElement).toBeInTheDocument();
9 | });
10 |
--------------------------------------------------------------------------------
/src/components/ProposalActionsModal/steps/StreamPaymentsReviewStep/StreamPaymentsReviewStep.module.css:
--------------------------------------------------------------------------------
1 | .hover {
2 | border-radius: 8px !important;
3 | background-color: var(--brand-gray-dark-text) !important;
4 | color: white;
5 | opacity: 0.75 !important;
6 | font-weight: 500;
7 | transition: ease-in-out 125ms;
8 | }
9 |
--------------------------------------------------------------------------------
/.env.example.local:
--------------------------------------------------------------------------------
1 | # Active Chain ID
2 | REACT_APP_CHAIN_ID=31337
3 |
4 | # If you have an Infura Project ID
5 | # REACT_APP_INFURA_PROJECT_ID=
6 |
7 | # If you have an explicit Rinkeby JSON-RPC endpoint
8 | # REACT_APP_RINKEBY_JSONRPC=
9 |
10 | # If you have an explicit Mainnet JSON-RPC endpoint
11 | # REACT_APP_MAINNET_JSONRPC=
12 |
--------------------------------------------------------------------------------
/src/components/ModalTextPrimary/index.tsx:
--------------------------------------------------------------------------------
1 | import classes from './ModalTextPrimary.module.css';
2 |
3 | interface ModalTextPrimaryProps {
4 | children?: React.ReactNode;
5 | }
6 |
7 | export default function ModalTextPrimary({ children }: ModalTextPrimaryProps) {
8 | return {children}
;
9 | }
10 |
--------------------------------------------------------------------------------
/src/components/AuctionActivityNounTitle/AuctionActivityNounTitle.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | display: inline-block;
3 | }
4 |
5 | .wrapper h1 {
6 | font-family: 'Londrina Solid';
7 | font-size: 68px;
8 | margin-bottom: 10px;
9 | }
10 |
11 | @media (max-width: 992px) {
12 | .wrapper h1 {
13 | font-size: 56px;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/DesktopProfileActivityFeed/DesktopProfileActivityFeed.module.css:
--------------------------------------------------------------------------------
1 | .aboveTheFoldEventsTable {
2 | margin-bottom: 0rem;
3 | }
4 |
5 | .aboveTheFoldEventsTable td {
6 | border-top: 1px solid rgba(0, 0, 0, 0.1);
7 | padding-top: 12px;
8 | }
9 |
10 | .nounInfoPadding {
11 | padding-bottom: 1rem;
12 | font-size: 18px;
13 | }
14 |
--------------------------------------------------------------------------------
/src/components/ModalTitle/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classes from './ModalTitle.module.css';
3 |
4 | const ModalTitle = (props: { children: React.ReactNode }) => {
5 | return (
6 |
7 |
{props.children}
8 |
9 | );
10 | };
11 |
12 | export default ModalTitle;
13 |
--------------------------------------------------------------------------------
/src/components/profileEvent/event/MobileProposalVoteEvent/MobileProposalVoteEvent.module.css:
--------------------------------------------------------------------------------
1 | .voteIcon {
2 | width: 38px;
3 | margin-right: 0px;
4 | margin-bottom: 4px;
5 | margin-left: 6px;
6 | }
7 |
8 | .proposalTitle {
9 | text-decoration: none;
10 | color: black;
11 | font-family: 'PT Root UI';
12 | font-weight: bold;
13 | }
14 |
--------------------------------------------------------------------------------
/src/store/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from "redux";
2 | import global from "./global";
3 | import user from "./user";
4 |
5 | export const rootReducer = combineReducers({
6 | global: global,
7 | userInfo: user
8 | })
9 |
10 | const reducers = (state, action) => rootReducer(state, action);
11 |
12 | export default reducers;
--------------------------------------------------------------------------------
/src/components/ShortAddress/ShortAddress.module.css:
--------------------------------------------------------------------------------
1 | .shortAddress {
2 | display: flex;
3 | flex-flow: row nowrap;
4 | gap: 6px;
5 | align-items: center;
6 | }
7 |
8 | .shortAddress > div {
9 | padding-top: -2px;
10 | }
11 |
12 | .shortAddress span {
13 | letter-spacing: 0.2px;
14 | font-family: 'PT Root UI';
15 | font-weight: bold;
16 | }
17 |
--------------------------------------------------------------------------------
/src/utils/ResponsiveUIUtils.module.css:
--------------------------------------------------------------------------------
1 | .mobileOnly {
2 | display: none !important;
3 | }
4 |
5 | @media (max-width: 1200px) {
6 | .desktopOnly {
7 | display: none;
8 | }
9 |
10 | .mobileOnly {
11 | display: inherit !important;
12 | }
13 | }
14 |
15 | @media (max-width: 414px) {
16 | .disableSmallScreens {
17 | display: none;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/hooks.ts:
--------------------------------------------------------------------------------
1 | import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
2 | import type { RootState, AppDispatch } from './index';
3 |
4 | // Use throughout your app instead of plain `useDispatch` and `useSelector`
5 | export const useAppDispatch = () => useDispatch();
6 | export const useAppSelector: TypedUseSelectorHook = useSelector;
7 |
--------------------------------------------------------------------------------
/src/components/WalletConnectModal/WalletConnectModal.module.css:
--------------------------------------------------------------------------------
1 | .clickable {
2 | cursor: pointer;
3 | }
4 |
5 | .walletConnectData {
6 | font-size: 15px;
7 | padding-top: 1.5rem;
8 | padding-bottom: 0rem;
9 | margin-bottom: -1.25rem;
10 | opacity: 0.5;
11 | transition: all 0.15s ease-in-out;
12 | }
13 |
14 | .walletConnectData:hover {
15 | opacity: 1;
16 | }
17 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | # Active Chain ID
2 | REACT_APP_CHAIN_ID=4
3 |
4 | # If you have an Infura Project ID
5 | # REACT_APP_INFURA_PROJECT_ID=
6 |
7 | # If you have an explicit Rinkeby JSON-RPC endpoint
8 | # REACT_APP_RINKEBY_JSONRPC=
9 |
10 | # If you have an explicit Mainnet JSON-RPC endpoint
11 | # REACT_APP_MAINNET_JSONRPC=
12 |
13 | # Etherscan API Key
14 | REACT_APP_ETHERSCAN_API_KEY=
15 |
--------------------------------------------------------------------------------
/src/components/ABIUpload/ABIUpload.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | margin-top: 1rem;
3 | }
4 |
5 | .label {
6 | opacity: 0.5;
7 | }
8 |
9 | .form {
10 | border-radius: 15px;
11 | width: 100%;
12 | height: 3rem;
13 | font-size: 22px;
14 | font-weight: bold;
15 | padding: 0.5rem 1rem;
16 | border: 1px solid rgba(0, 0, 0, 0.1);
17 | color: var(--brand-cool-dark-text);
18 | }
19 |
--------------------------------------------------------------------------------
/src/components/BrandSpinner/BrandSpinner.module.css:
--------------------------------------------------------------------------------
1 | .spinner {
2 | animation: rotate 1.25s linear infinite;
3 | transform-origin: center center !important;
4 | }
5 |
6 | @keyframes rotate {
7 | 0% {
8 | -webkit-transform: rotate(0deg);
9 | transform: rotate(0deg);
10 | }
11 | 100% {
12 | -webkit-transform: rotate(360deg);
13 | transform: rotate(360deg);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/Banner/Banner.module.css:
--------------------------------------------------------------------------------
1 | .wrapper h1 {
2 | font-family: 'Londrina Solid';
3 | font-size: 5rem;
4 | }
5 |
6 | @media (max-width: 992px) {
7 | .wrapper {
8 | padding: 2rem;
9 | }
10 |
11 | .wrapper h1 {
12 | font-size: 3.75rem;
13 | }
14 | }
15 |
16 | @media (min-width: 992px) {
17 | .wrapper h1 {
18 | font-size: 6rem;
19 | margin-left: 2rem;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/components/profileEvent/eventData/ProposalVoteHeadline/ProposalVoteHeadline.module.css:
--------------------------------------------------------------------------------
1 | .voterLink {
2 | font-weight: bold;
3 | cursor: pointer;
4 | }
5 |
6 | .delegateHover {
7 | border-radius: 8px !important;
8 | background-color: var(--brand-gray-dark-text) !important;
9 | color: white;
10 | opacity: 0.75 !important;
11 | font-weight: 500;
12 | transition: ease-in-out 125ms;
13 | }
14 |
--------------------------------------------------------------------------------
/src/components/AuctionTitleAndNavWrapper/index.tsx:
--------------------------------------------------------------------------------
1 | import { Col } from 'react-bootstrap';
2 | import classes from './AuctionTitleAndNavWrapper.module.css';
3 |
4 | const AuctionTitleAndNavWrapper: React.FC<{}> = props => {
5 | return (
6 |
7 | {props.children}
8 |
9 | );
10 | };
11 | export default AuctionTitleAndNavWrapper;
12 |
--------------------------------------------------------------------------------
/src/components/NounInfoRowBirthday/NounInfoRowBirthday.module.css:
--------------------------------------------------------------------------------
1 | .nounInfoRowBirthday {
2 | font-family: 'PT Root UI';
3 | font-weight: bold;
4 | margin-left: 5px;
5 | }
6 |
7 | .birthdayInfoContainer {
8 | display: inline;
9 | width: 350px;
10 | height: 25px;
11 | }
12 |
13 | .birthdayIcon {
14 | margin-bottom: 4px;
15 | margin-right: 7px;
16 | }
17 |
18 | img {
19 | display: inline;
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/TruncatedAmount/index.tsx:
--------------------------------------------------------------------------------
1 | import BigNumber from 'bignumber.js';
2 | import { utils } from 'ethers';
3 | import React from 'react';
4 |
5 | const TruncatedAmount: React.FC<{ amount: BigNumber }> = props => {
6 | const { amount } = props;
7 |
8 | const eth = new BigNumber(utils.formatEther(amount.toString())).toFixed(2);
9 | return <>Ξ {`${eth}`}>;
10 | };
11 | export default TruncatedAmount;
12 |
--------------------------------------------------------------------------------
/src/components/NavBar/NavBarItem/index.tsx:
--------------------------------------------------------------------------------
1 | import classes from './NavBarItem.module.css';
2 |
3 | const NavBarItem: React.FC<{ onClick?: () => void; className?: string }> = props => {
4 | const { onClick, children, className } = props;
5 | return (
6 |
7 | {children}
8 |
9 | );
10 | };
11 | export default NavBarItem;
12 |
--------------------------------------------------------------------------------
/src/App.module.css:
--------------------------------------------------------------------------------
1 | @import './css/colors.css';
2 |
3 | .wrapper {
4 | display: flex;
5 | flex-direction: column;
6 | min-height: 100vh;
7 | max-width: 100vw;
8 | }
9 |
10 | /* solution to allow for sticky sidebar on Explore page */
11 | @media (min-width: 993px) {
12 | .wrapper {
13 | contain: paint;
14 | }
15 | }
16 | @media (max-width: 992px) {
17 | .wrapper {
18 | overflow-x: hidden;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/NounderNounContent/NounderNounContent.module.css:
--------------------------------------------------------------------------------
1 | .link,
2 | .link:visited,
3 | .link:active {
4 | color: var(--brand-dark-green);
5 | text-decoration: underline;
6 | }
7 |
8 | .link:hover {
9 | color: var(--brand-dark-red);
10 | }
11 |
12 | .bidRow {
13 | font-weight: 500;
14 | font-size: 15.5px;
15 | line-height: 21px;
16 | }
17 |
18 | .currentBidCol {
19 | border-right: 0 !important;
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/profileEvent/eventData/ProposalVoteInfoPillsContainer/ProposalVoteInfoPills.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | display: flex;
3 | flex-direction: row;
4 | justify-content: flex-end;
5 | }
6 |
7 | @media (max-width: 992px) {
8 | .wrapper {
9 | justify-content: flex-start;
10 | }
11 | }
12 |
13 | .voteStatusWrapper {
14 | display: flex;
15 | flex-flow: row nowrap;
16 | justify-content: flex-end;
17 | align-items: center;
18 | }
19 |
--------------------------------------------------------------------------------
/src/components/AuctionTitleAndNavWrapper/AuctionTitleAndNavWrapper.module.css:
--------------------------------------------------------------------------------
1 | .auctionTitleAndNavContainer {
2 | display: flex;
3 | }
4 |
5 | @media (max-width: 992px) {
6 | .auctionTitleAndNavContainer h1 {
7 | font-size: 2.75rem;
8 | }
9 | }
10 |
11 | /* Fix Firefox navigation arrow alignment issues */
12 | @-moz-document url-prefix() {
13 | .auctionTitleAndNavContainer {
14 | display: flex;
15 | align-items: center;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/i18n/setLocale.ts:
--------------------------------------------------------------------------------
1 | import { dynamicActivate } from './NounsI18nProvider';
2 |
3 | /**
4 | * Sets locale in local storage
5 | *
6 | * Note: this value will persist across sessions
7 | *
8 | * @param locale Locale we wish to use for this user
9 | */
10 | export const setLocale = (locale: string) => {
11 | if (localStorage.getItem('lang') !== locale) {
12 | localStorage.setItem('lang', locale);
13 | dynamicActivate(locale);
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/src/components/DelegationCandidateVoteCountInfo/DelegationCandidateVoteCountInfo.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | display: flex;
3 | }
4 |
5 | .spinner {
6 | margin-right: 0.5rem;
7 | margin-top: 0.5rem;
8 | }
9 |
10 | .voteInfoWrapper {
11 | display: flex;
12 | flex-direction: column;
13 | font-weight: normal;
14 | color: var(--brand-gray-light-text);
15 | text-align: right;
16 | }
17 |
18 | .voteCount {
19 | color: black;
20 | font-weight: bold;
21 | }
22 |
--------------------------------------------------------------------------------
/src/utils/compareBids.ts:
--------------------------------------------------------------------------------
1 | import { BigNumber } from 'ethers';
2 |
3 | const blockMultiple = BigNumber.from(1_000_000);
4 |
5 | const generateBidScore = (bid: any) =>
6 | BigNumber.from(bid.blockNumber).mul(blockMultiple).add(BigNumber.from(bid.txIndex));
7 |
8 | export const compareBids = (bidA: any, bidB: any): number => {
9 | const aScore = generateBidScore(bidA);
10 | const bScore = generateBidScore(bidB);
11 | return aScore.sub(bScore).toNumber();
12 | };
13 |
--------------------------------------------------------------------------------
/src/store/reducers/user.js:
--------------------------------------------------------------------------------
1 | import { getType } from "typesafe-actions";
2 | import * as actions from "../actions"
3 |
4 | export const initialState = {
5 | user: ""
6 | };
7 |
8 | const states = (state = initialState, action) => {
9 | switch(action.type) {
10 | case getType(actions.setUserInfo):
11 | return {...state, user: action.payload};
12 | default:
13 | return state;
14 | }
15 | }
16 |
17 | export default states;
--------------------------------------------------------------------------------
/src/components/HoverCard/HoverCard.module.css:
--------------------------------------------------------------------------------
1 | .hover {
2 | box-sizing: border-box;
3 | min-width: 217px;
4 | width: max-content;
5 | height: fit-content;
6 | left: 301px;
7 | top: 396px;
8 |
9 | /* White */
10 | background-color: #ffffff !important;
11 |
12 | /* Gray/Border */
13 | border: 1px solid #e2e3e8 !important;
14 | box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.01) !important;
15 | border-radius: 14px !important;
16 | color: black !important;
17 | }
18 |
--------------------------------------------------------------------------------
/public/site.webmanifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "OrdAuction",
3 | "short_name": "OrdAuction",
4 | "icons": [
5 | {
6 | "src": "/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/android-chrome-384x384.png",
12 | "sizes": "384x384",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#ffffff",
17 | "background_color": "#ffffff",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/Link/index.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react';
2 | import classes from './Link.module.css';
3 |
4 | const Link: React.FC<{ text: ReactNode; url: string; leavesPage: boolean }> = props => {
5 | const { text, url, leavesPage } = props;
6 | return (
7 |
13 | {text}
14 |
15 | );
16 | };
17 | export default Link;
18 |
--------------------------------------------------------------------------------
/src/components/ProposalTransactions/ProposalTransactions.module.css:
--------------------------------------------------------------------------------
1 | .popover {
2 | max-width: 600px;
3 | }
4 |
5 | .transactionDetails {
6 | margin-top: 1rem;
7 | padding: 0.5rem 1rem;
8 | border: 1px solid #aaa !important;
9 | border-radius: 8px !important;
10 | }
11 |
12 | .removeTransactionButton {
13 | border: none;
14 | background: none;
15 | outline: none !important;
16 | box-shadow: none !important;
17 | }
18 |
19 | .removeTransactionButton img {
20 | width: 1rem;
21 | }
22 |
--------------------------------------------------------------------------------
/src/hooks/useProposalStatus.ts:
--------------------------------------------------------------------------------
1 | import { Proposal, ProposalState } from '../wrappers/nounsDao';
2 |
3 | export const useProposalStatus = (proposal: Proposal): string => {
4 | switch (proposal.status) {
5 | case ProposalState.SUCCEEDED:
6 | case ProposalState.EXECUTED:
7 | case ProposalState.QUEUED:
8 | return 'success';
9 | case ProposalState.DEFEATED:
10 | case ProposalState.VETOED:
11 | return 'failure';
12 | default:
13 | return 'pending';
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/src/reportWebVitals.ts:
--------------------------------------------------------------------------------
1 | import { ReportHandler } from 'web-vitals';
2 |
3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => {
4 | if (onPerfEntry && onPerfEntry instanceof Function) {
5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
6 | getCLS(onPerfEntry);
7 | getFID(onPerfEntry);
8 | getFCP(onPerfEntry);
9 | getLCP(onPerfEntry);
10 | getTTFB(onPerfEntry);
11 | });
12 | }
13 | };
14 |
15 | export default reportWebVitals;
16 |
--------------------------------------------------------------------------------
/src/components/profileEvent/event/MobileNounWinEvent/MobileNounWinEvent.module.css:
--------------------------------------------------------------------------------
1 | .iconWrapper {
2 | display: flex;
3 | justify-content: center;
4 | align-items: center;
5 | background-color: var(--brand-gray-light-text-translucent);
6 | color: var(--brand-gray-light-text);
7 | border-radius: 100%;
8 | height: 38px;
9 | width: 38px;
10 | margin-left: 0.4rem;
11 | }
12 |
13 | .switchIcon {
14 | height: 22px;
15 | width: 22px;
16 | }
17 |
18 | .bold {
19 | font-weight: bold;
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/profileEvent/event/MobileTransferEvent/MobileTransferEvent.module.css:
--------------------------------------------------------------------------------
1 | .switchIconWrapper {
2 | display: flex;
3 | justify-content: center;
4 | align-items: center;
5 | background-color: var(--brand-gray-light-text-translucent);
6 | color: var(--brand-gray-light-text);
7 | border-radius: 100%;
8 | height: 38px;
9 | width: 38px;
10 | margin-left: 0.4rem;
11 | }
12 |
13 | .switchIcon {
14 | height: 22px;
15 | width: 22px;
16 | }
17 |
18 | .bold {
19 | font-weight: bold;
20 | }
21 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | // content: ["./src/**/*.{html,js}"],
4 | // content: ["./src/pages/Incribe/*.{html,js}"],
5 | content: [
6 | "./src/**/*.{js,jsx,ts,tsx}",
7 | "./node_modules/react-tailwindcss-select/dist/index.esm.js"
8 | ]
9 | theme: {
10 | extend: {
11 | boxShadow: {
12 | 'shadow-profile': '0 4px 6px -1px #110528, 0 2px 4px -2px #110528',
13 | }
14 | },
15 | },
16 | plugins: [],
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/profileEvent/event/MobileDelegationEvent/MobileDelegationEvent.module.css:
--------------------------------------------------------------------------------
1 | .scaleIconWrapper {
2 | display: flex;
3 | justify-content: center;
4 | align-items: center;
5 | background-color: var(--brand-gray-light-text-translucent);
6 | color: var(--brand-gray-light-text);
7 | border-radius: 100%;
8 | height: 38px;
9 | width: 38px;
10 | margin-left: 0.4rem;
11 | }
12 |
13 | .scaleIcon {
14 | height: 22px;
15 | width: 22px;
16 | }
17 |
18 | .bold {
19 | font-weight: bold;
20 | }
21 |
--------------------------------------------------------------------------------
/src/utils/processProposalDescriptionText.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Removes first occurence of proposalTitle from descriptionText
3 | * @param descriptionText The description field of a proposal object
4 | * @param proposalTitle The title of the corresponding proposal
5 | * @returns The proposal description with the first occurence of the title string removed
6 | */
7 | export const processProposalDescriptionText = (descriptionText: string, proposalTitle: string) => {
8 | return descriptionText.replace(proposalTitle, '');
9 | };
10 |
--------------------------------------------------------------------------------
/src/assets/wallet-brand-assets/fortmatic.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/utils/timeUtils.ts:
--------------------------------------------------------------------------------
1 | import dayjs from 'dayjs';
2 |
3 | export const currentUnixEpoch = () => {
4 | return Math.floor(new Date().getTime() / 1000);
5 | };
6 |
7 | /**
8 | * Converts date string to unix timestamp
9 | * @param dateString
10 | */
11 | export const toUnixEpoch = (dateString: string) => {
12 | return new Date(dateString).valueOf() / 1000;
13 | };
14 |
15 | export const unixToDateString = (timestamp?: number) => {
16 | return dayjs
17 | .unix(timestamp ?? 0)
18 | .utc()
19 | .format('MMMM DD, YYYY');
20 | };
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
25 | .env
26 | .vercel
27 |
28 | # i18n
29 | /src/locales/**/*.js
30 | /src/locales/**/en-US.po
31 | /src/locales/**/pseudo.po
32 |
--------------------------------------------------------------------------------
/src/components/ProposalActionsModal/steps/FunctionCallReviewStep/FunctionCallReviewStep.module.css:
--------------------------------------------------------------------------------
1 | .label {
2 | opacity: 0.5;
3 | }
4 |
5 | .argument {
6 | display: flex;
7 | justify-content: space-between;
8 | margin-bottom: 0.25rem;
9 | }
10 |
11 | .row {
12 | display: flex;
13 | }
14 |
15 | .value {
16 | font-size: 22px;
17 | font-weight: bold;
18 | color: var(--brand-cool-dark-text);
19 | margin-bottom: 0.5rem;
20 | word-break: break-all;
21 | }
22 |
23 | .argValue {
24 | max-width: 50%;
25 | word-break: break-all;
26 | }
27 |
--------------------------------------------------------------------------------
/functions/v0-is-noun-owner/index.ts:
--------------------------------------------------------------------------------
1 | import { Handler } from '@netlify/functions';
2 | import { isNounOwner, nounsQuery } from '../theGraph';
3 | import { sharedResponseHeaders } from '../utils';
4 |
5 | const handler: Handler = async (event, context) => {
6 | const nouns = await nounsQuery();
7 | return {
8 | statusCode: 200,
9 | headers: {
10 | 'Content-Type': 'application/json',
11 | ...sharedResponseHeaders,
12 | },
13 | body: JSON.stringify(isNounOwner(event.body, nouns)),
14 | };
15 | };
16 |
17 | export { handler };
18 |
--------------------------------------------------------------------------------
/src/components/profileEvent/activityRow/MobileNounActivityRow/MobileNounActivityRow.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | border-bottom: 1px solid var(--brand-gray-border);
3 | padding-bottom: 0.5rem;
4 | font-family: PT Root UI;
5 | font-size: 18px;
6 | margin-top: 1rem;
7 | display: flex;
8 | }
9 |
10 | .icon {
11 | width: 38px;
12 | margin-right: 0px;
13 | margin-top: 0.25rem;
14 | margin-left: 6px;
15 | }
16 |
17 | .content {
18 | margin-left: 1.5rem;
19 | display: flex;
20 | justify-content: center;
21 | flex-direction: column;
22 | }
23 |
--------------------------------------------------------------------------------
/src/utils/resolveNounsContractAddress.ts:
--------------------------------------------------------------------------------
1 | import config from '../config';
2 |
3 | export const resolveNounContractAddress = (address: string) => {
4 | switch (address.toLowerCase()) {
5 | case config.addresses.nounsDAOProxy.toLowerCase():
6 | return 'Nouns DAO Proxy';
7 | case config.addresses.nounsAuctionHouseProxy.toLowerCase():
8 | return 'Nouns Auction House Proxy';
9 | case config.addresses.nounsDaoExecutor.toLowerCase():
10 | return 'Nouns DAO Treasury';
11 | default:
12 | return undefined;
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/src/utils/usdcUtils.ts:
--------------------------------------------------------------------------------
1 | export const human2ContractUSDCFormat = (humanReadableUSDCAmt: string | number) => {
2 | return Math.round(parseFloat(humanReadableUSDCAmt.toString()) * 1_000_000).toString();
3 | };
4 |
5 | export const contract2humanUSDCFormat = (
6 | contractUSCDAmt: string | number,
7 | allDecimals?: boolean,
8 | ) => {
9 | if (allDecimals === true) {
10 | return (parseFloat(contractUSCDAmt.toString()) / 1_000_000).toString();
11 | }
12 |
13 | return (parseFloat(contractUSCDAmt.toString()) / 1_000_000).toFixed(3).toString();
14 | };
15 |
--------------------------------------------------------------------------------
/functions/v0-is-noun-delegate/index.ts:
--------------------------------------------------------------------------------
1 | import { Handler } from '@netlify/functions';
2 | import { isNounDelegate, nounsQuery } from '../theGraph';
3 | import { sharedResponseHeaders } from '../utils';
4 |
5 | const handler: Handler = async (event, context) => {
6 | const nouns = await nounsQuery();
7 | return {
8 | statusCode: 200,
9 | headers: {
10 | 'Content-Type': 'application/json',
11 | ...sharedResponseHeaders,
12 | },
13 | body: JSON.stringify(isNounDelegate(event.body, nouns)),
14 | };
15 | };
16 |
17 | export { handler };
18 |
--------------------------------------------------------------------------------
/src/components/NavBar/NavBarItem/NavBarItem.module.css:
--------------------------------------------------------------------------------
1 | .navBarItem {
2 | display: flex;
3 | align-items: center;
4 | justify-content: center;
5 | margin-right: 1rem;
6 | }
7 |
8 | .navBarItem a {
9 | color: var(--brand-black);
10 | }
11 | .navBarItem a:hover {
12 | color: var(--brand-dark-red);
13 | }
14 |
15 | @media (max-width: 992px) {
16 | .navBarItem {
17 | background-color: transparent !important;
18 | color: var(--brand-black) !important;
19 | }
20 | .navBarItem:hover {
21 | background-color: transparent !important;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/utils/externalURL.ts:
--------------------------------------------------------------------------------
1 | export enum ExternalURL {
2 | twitter,
3 | notion,
4 | discourse,
5 | nounsCenter,
6 | }
7 |
8 | export const externalURL = (externalURL: ExternalURL) => {
9 | switch (externalURL) {
10 | case ExternalURL.twitter:
11 | return 'https://twitter.com/nounsdao';
12 | case ExternalURL.notion:
13 | return 'https://nouns.notion.site/Explore-Nouns-a2a9dceeb1d54e10b9cbf3f931c2266f';
14 | case ExternalURL.discourse:
15 | return 'https://discourse.nouns.wtf/';
16 | case ExternalURL.nounsCenter:
17 | return 'https://nouns.center/';
18 | }
19 | };
20 |
--------------------------------------------------------------------------------
/src/components/profileEvent/event/DesktopProposalVoteEvent/DesktopProposalVoteEvent.module.css:
--------------------------------------------------------------------------------
1 | .voteIcon {
2 | width: 38px;
3 | margin-right: 0px;
4 | margin-bottom: 4px;
5 | margin-left: 6px;
6 | }
7 |
8 | .proposalTitle {
9 | text-decoration: none;
10 | color: black;
11 | font-family: 'PT Root UI';
12 | font-weight: bold;
13 | cursor: pointer;
14 | }
15 |
16 | .delegateHover {
17 | border-radius: 8px !important;
18 | background-color: var(--brand-gray-dark-text) !important;
19 | color: white;
20 | opacity: 0.75 !important;
21 | font-weight: 500;
22 | transition: ease-in-out 125ms;
23 | }
24 |
--------------------------------------------------------------------------------
/src/utils/grayBackgroundSVG.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Returns an SVG that is just a light gray background that fills the screen
3 | * @returns base64 encoded data URI format SVG image of viewport filling light background
4 | */
5 | export const getGrayBackgroundSVG = () => {
6 | return 'data:image/svg+xml;base64,PHN2ZyB4bWx ucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHByZXNlcnZlQX NwZWN0UmF0aW89InhNaW5ZTWluIG1lZXQiIHZpZXdCb3g9IjAgMCAzN TAgMzUwIj48c3R5bGU+LmJhc2UgeyBmaWxsOiB3aGl0ZTsgZm9udC1m YW1pbHk6IHNlcmlmOyBmb250LXNpemU6IDE0cHg7IH08L3N0eWxlPjx yZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNlMG UwZTc4MCIgLz48L3N2Zz4=';
7 | };
8 |
--------------------------------------------------------------------------------
/src/components/WalletButton/WalletButton.module.css:
--------------------------------------------------------------------------------
1 | .walletButton {
2 | border: none;
3 | margin: 5px;
4 | padding: 5px 20px;
5 | border-radius: 5px;
6 | background-color: rgba(211, 211, 211, 0.664);
7 | color: var(--brand-black);
8 | border: none;
9 | }
10 | .walletButton img {
11 | margin-right: 10px;
12 | border-radius: 5px;
13 | }
14 | .walletButton:hover,
15 | .walletButton:active,
16 | .walletButton:focus,
17 | .walletButton:disabled {
18 | outline: none !important;
19 | box-shadow: none !important;
20 | background-color: lightgray !important;
21 | color: var(--brand-dark-green);
22 | color: white;
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/AuctionActivityNounTitle/index.tsx:
--------------------------------------------------------------------------------
1 | import { BigNumber } from 'ethers';
2 | import classes from './AuctionActivityNounTitle.module.css';
3 | import { Trans } from '@lingui/macro';
4 |
5 | const AuctionActivityNounTitle: React.FC<{ nounId: BigNumber; isCool?: boolean }> = props => {
6 | const { nounId, isCool } = props;
7 | return (
8 |
9 |
10 | Ord {nounId.toString()}
11 |
12 |
13 | );
14 | };
15 | export default AuctionActivityNounTitle;
16 |
--------------------------------------------------------------------------------
/src/hooks/useReadonlyProvider.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 | import { useConfig } from '@usedapp/core';
3 | import { providers } from 'ethers';
4 | import { CHAIN_ID } from '../config';
5 |
6 | /**
7 | * Returns a provider that's constructed using the readonly RPC URL.
8 | */
9 | export function useReadonlyProvider(): providers.JsonRpcProvider | undefined {
10 | const config = useConfig();
11 | const rpcURL = config?.readOnlyUrls?.[CHAIN_ID] as string | undefined;
12 | return useMemo(() => {
13 | if (!rpcURL) {
14 | return;
15 | }
16 | return new providers.JsonRpcProvider(rpcURL);
17 | }, [rpcURL]);
18 | }
19 |
--------------------------------------------------------------------------------
/src/components/BrandTextEntry/BrandTextEntry.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | width: 100%;
3 | margin-top: 1rem;
4 | position: relative;
5 | }
6 |
7 | .label {
8 | opacity: 0.5;
9 | }
10 |
11 | .entry {
12 | border-radius: 15px;
13 | width: 100%;
14 | height: 3rem;
15 | font-size: 22px;
16 | font-weight: bold;
17 | padding: 0.5rem 1rem;
18 | border: 1px solid rgba(0, 0, 0, 0.1);
19 | color: var(--brand-cool-dark-text);
20 | margin-top: 0.25rem;
21 | margin-bottom: 0.5rem;
22 | outline: none;
23 | transition: all ease-in-out 125;
24 | }
25 |
26 | .invalid {
27 | border: 2px solid var(--brand-color-red) !important;
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/BrandDatePicker/BrandDatePicker.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | width: 100%;
3 | margin-top: 1rem;
4 | position: relative;
5 | }
6 |
7 | .label {
8 | opacity: 0.5;
9 | }
10 |
11 | .entry {
12 | border-radius: 15px;
13 | width: 100%;
14 | height: 3rem;
15 | font-size: 22px;
16 | font-weight: bold;
17 | padding: 0.5rem 1rem;
18 | border: 1px solid rgba(0, 0, 0, 0.1);
19 | color: var(--brand-cool-dark-text);
20 | margin-top: 0.25rem;
21 | margin-bottom: 0.5rem;
22 | outline: none;
23 | transition: all ease-in-out 125;
24 | }
25 |
26 | .invalid {
27 | border: 2px solid var(--brand-color-red) !important;
28 | }
29 |
--------------------------------------------------------------------------------
/src/pages/Explore/Explore.module.css:
--------------------------------------------------------------------------------
1 | .exploreWrap {
2 | padding: 0;
3 | border: 1px solid rgba(0, 0, 0, 0.1);
4 | margin: 0 3vw;
5 | border-radius: 10px;
6 | }
7 |
8 | .gridWrap {
9 | width: 100%;
10 | position: relative;
11 | z-index: 2;
12 | }
13 |
14 | .detailBlock {
15 | max-width: 500px;
16 | flex: 0 0 33%;
17 | }
18 |
19 | .contentWrap {
20 | display: flex;
21 | justify-content: space-between;
22 | }
23 |
24 | @media (max-width: 991px) {
25 | .exploreWrap {
26 | border: 0;
27 | margin: 0 0.5rem;
28 | }
29 | .contentWrap {
30 | contain: none;
31 | position: relative;
32 | z-index: 2;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/components/BrandNumericEntry/BrandNumericEntry.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | width: 100%;
3 | margin-top: 1rem;
4 | position: relative;
5 | }
6 |
7 | .label {
8 | opacity: 0.5;
9 | }
10 |
11 | .entry {
12 | border-radius: 15px;
13 | width: 100%;
14 | height: 3rem;
15 | font-size: 22px;
16 | font-weight: bold;
17 | padding: 0.5rem 1rem;
18 | border: 1px solid rgba(0, 0, 0, 0.1);
19 | color: var(--brand-cool-dark-text);
20 | margin-top: 0.25rem;
21 | margin-bottom: 0.5rem;
22 | outline: none;
23 | transition: all ease-in-out 125;
24 | }
25 |
26 | .invalid {
27 | border: 2px solid var(--brand-color-red) !important;
28 | }
29 |
--------------------------------------------------------------------------------
/src/layout/Section/index.tsx:
--------------------------------------------------------------------------------
1 | import classes from './Section.module.css';
2 | import { Container, Row } from 'react-bootstrap';
3 | import { CSSProperties } from 'react';
4 |
5 | const Section: React.FC<{
6 | fullWidth: boolean;
7 | className?: string;
8 | style?: CSSProperties;
9 | }> = props => {
10 | const { fullWidth, className, children, style } = props;
11 | return (
12 |
13 |
14 | {children}
15 |
16 |
17 | );
18 | };
19 | export default Section;
20 |
--------------------------------------------------------------------------------
/src/components/SettleManuallyBtn/SettleManuallyBtn.module.css:
--------------------------------------------------------------------------------
1 | @media (max-width: 992px) {
2 | .emergencySettleWrapper {
3 | text-align: center;
4 | margin-left: 0.5rem;
5 | }
6 | }
7 |
8 | @media (max-width: 660px) {
9 | .emergencySettleWrapper {
10 | text-align: center;
11 | margin-left: 0rem;
12 | }
13 | }
14 |
15 | .emergencySettleButton {
16 | background-color: transparent;
17 | border: none;
18 | cursor: pointer;
19 | text-decoration: underline;
20 | display: inline;
21 | margin: 0;
22 | padding: 0;
23 | }
24 | .emergencySettleButton:disabled {
25 | color: #8c8d92;
26 | text-decoration: none;
27 | cursor: default;
28 | }
29 |
--------------------------------------------------------------------------------
/src/utils/bidSorter.ts:
--------------------------------------------------------------------------------
1 | import { BigNumber } from '@ethersproject/bignumber';
2 | import { IBid } from '../wrappers/subgraph';
3 |
4 | /**
5 | * Sorts bids chronologically using block timestamp and
6 | * transaction index within the block.
7 | * @param a First bid
8 | * @param b Second bid
9 | */
10 | export const compareBidsChronologically = (a: IBid, b: IBid): number => {
11 | const adjustedTimes = {
12 | a: BigNumber.from(a.blockTimestamp).mul(1_000_000).add(BigNumber.from(a.txIndex)),
13 | b: BigNumber.from(b.blockTimestamp).mul(1_000_000).add(BigNumber.from(b.txIndex)),
14 | };
15 | return adjustedTimes.b.sub(adjustedTimes.a).toNumber();
16 | };
17 |
--------------------------------------------------------------------------------
/src/state/slices/account.ts:
--------------------------------------------------------------------------------
1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit';
2 |
3 | interface AccountState {
4 | activeAccount?: string;
5 | }
6 |
7 | const initialState: AccountState = {
8 | activeAccount: undefined,
9 | };
10 |
11 | export const accountSlice = createSlice({
12 | name: 'account',
13 | initialState,
14 | reducers: {
15 | setActiveAccount: (state, action: PayloadAction) => {
16 | state.activeAccount = action.payload === null ? undefined : action.payload;
17 | },
18 | },
19 | });
20 |
21 | export const { setActiveAccount } = accountSlice.actions;
22 |
23 | export default accountSlice.reducer;
24 |
--------------------------------------------------------------------------------
/src/state/slices/contracts.ts:
--------------------------------------------------------------------------------
1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit';
2 |
3 | interface AccountState {
4 | activeAccount?: string;
5 | }
6 |
7 | const initialState: AccountState = {
8 | activeAccount: undefined,
9 | };
10 |
11 | export const accountSlice = createSlice({
12 | name: 'account',
13 | initialState,
14 | reducers: {
15 | setActiveAccount: (state, action: PayloadAction) => {
16 | state.activeAccount = action.payload === null ? undefined : action.payload;
17 | },
18 | },
19 | });
20 |
21 | export const { setActiveAccount } = accountSlice.actions;
22 |
23 | export default accountSlice.reducer;
24 |
--------------------------------------------------------------------------------
/src/store/selectors/index.js:
--------------------------------------------------------------------------------
1 | // global
2 | export const getAlertMessage = (state) => state.global.alertMessage;
3 | export const getNotifications = (state) => state.global.notifications;
4 | export const getCollections = (state) => state.global.collections;
5 | export const getUpdateCollectionFlag = (state) => state.global.updateCollectionFlag;
6 | export const getInscriberId = (state) => state.global.inscriberId;
7 | export const getUserState = (state) => state.global.user;
8 | export const getUserProfile = (state) => state.global.userProfile;
9 | export const getWalletConnected = (state) => state.global.connected;
10 | export const getSignInfo = (state) => state.global.signInfo;
11 |
--------------------------------------------------------------------------------
/src/components/VoteCardPager/VoteCardPager.module.css:
--------------------------------------------------------------------------------
1 | .pageDots {
2 | font-size: 24px;
3 | font-weight: bold;
4 | text-align: center;
5 | color: var(--brand-gray-light-text);
6 | }
7 |
8 | .disabledPageDot {
9 | opacity: 0.5;
10 | }
11 |
12 | .paginationArrowsWrapper {
13 | display: flex;
14 | justify-content: center;
15 | }
16 |
17 | .paginationArrowBtnWrapper {
18 | border: none;
19 | background-color: transparent;
20 | }
21 |
22 | .paginationArrowBtnWrapper:disabled {
23 | opacity: 0.5;
24 | }
25 |
26 | .paginationArrow {
27 | height: 28px;
28 | width: 28px;
29 | color: var(--brand-gray-light-text);
30 | }
31 |
32 | .disabled {
33 | opacity: 0.25;
34 | }
35 |
--------------------------------------------------------------------------------
/src/pages/Inscribe/inscribe.css:
--------------------------------------------------------------------------------
1 | div.Inscribe {
2 | background: linear-gradient(90deg, #5ec1f3, #5ec1f3);
3 | color: #ffffff;
4 | }
5 |
6 | h1.page-header {
7 | font-family: "Power Grotesk";
8 | font-weight: 700;
9 | line-height: 1.2;
10 | text-transform: unset;
11 | }
12 |
13 | .Inscribe .label {
14 | font-family: system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
15 | }
16 |
17 | .Inscribe .react-calendar button, select {
18 | color: rgb(54 149 247);
19 | }
20 |
21 | .input-fee-rate {
22 | border-color: #4da6d3 !important;
23 | }
--------------------------------------------------------------------------------
/src/utils/addressAndENSDisplayUtils.ts:
--------------------------------------------------------------------------------
1 | export const veryShortENS = (ens: string) => {
2 | return [ens.substr(0, 1), ens.substr(ens.length - 3, 3)].join('...');
3 | };
4 |
5 | export const veryShortAddress = (address: string) => {
6 | return [address.substr(0, 3), address.substr(address.length - 1, 1)].join('...');
7 | };
8 |
9 | export const shortENS = (ens: string) => {
10 | if (ens.length < 15 || window.innerWidth > 480) {
11 | return ens;
12 | }
13 | return [ens.substr(0, 4), ens.substr(ens.length - 8, 8)].join('...');
14 | };
15 |
16 | export const useShortAddress = (address: string) => {
17 | return address && [address.substr(0, 4), address.substr(38, 4)].join('...');
18 | };
--------------------------------------------------------------------------------
/src/utils/getNounsVotes.ts:
--------------------------------------------------------------------------------
1 | interface Vote {
2 | supportDetailed: 0 | 1 | 2;
3 | nounsRepresented: string[];
4 | }
5 |
6 | /**
7 | * Helper function to transform response from graph into flat list of nounIds that voted supportDetailed for the given prop
8 | *
9 | * @param votes - Graph response for noun vote query
10 | * @param supportDetailed - The integer support value: against (0), for (1), or abstain (2)
11 | * @returns - flat list of nounIds that voted supportDetailed for the given prop
12 | */
13 | export const getNounVotes = (votes: Vote[], supportDetailed: number) => {
14 | return votes.filter(v => v.supportDetailed === supportDetailed).flatMap(v => v.nounsRepresented);
15 | };
16 |
--------------------------------------------------------------------------------
/src/components/MinBid/index.tsx:
--------------------------------------------------------------------------------
1 | import nounPointerImg from '../../assets/noun-pointer.png';
2 | import classes from './MinBid.module.css';
3 | import BigNumber from 'bignumber.js';
4 | import TruncatedAmount from '../TruncatedAmount';
5 |
6 | const MinBid: React.FC<{ minBid: BigNumber; onClick: () => void }> = props => {
7 | const { minBid, onClick } = props;
8 |
9 | return (
10 |
11 |

12 |
13 | You must bid at least {minBid && }
14 |
15 |
16 | );
17 | };
18 | export default MinBid;
19 |
--------------------------------------------------------------------------------
/src/pages/NotFound/index.tsx:
--------------------------------------------------------------------------------
1 | import { Col, Image } from 'react-bootstrap';
2 | import Section from '../../layout/Section';
3 | import classes from './NotFound.module.css';
4 | import _404img from '../../assets/404noun.png';
5 | import { Trans } from '@lingui/macro';
6 |
7 | const NotFoundPage = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 | 404: This is not the person, place, or thing you're looking for...
16 |
17 |
18 |
19 | );
20 | };
21 | export default NotFoundPage;
22 |
--------------------------------------------------------------------------------
/src/components/EnsOrLongAddress/index.tsx:
--------------------------------------------------------------------------------
1 | import { useReverseENSLookUp } from '../../utils/ensLookup';
2 | import { containsBlockedText } from '../../utils/moderation/containsBlockedText';
3 |
4 | interface EnsOrLongAddressProps {
5 | address: string;
6 | }
7 |
8 | /**
9 | * Resolves ENS for address if one exists, otherwise falls back to full address
10 | */
11 | const EnsOrLongAddress: React.FC = props => {
12 | const { address } = props;
13 | const ens = useReverseENSLookUp(address);
14 | const ensMatchesBlocklistRegex = containsBlockedText(ens || '', 'en');
15 | return <>{ens && !ensMatchesBlocklistRegex ? ens : address}>;
16 | };
17 |
18 | export default EnsOrLongAddress;
19 |
--------------------------------------------------------------------------------
/src/components/VoteStatusPill/index.tsx:
--------------------------------------------------------------------------------
1 | import classes from './VoteStatusPill.module.css';
2 |
3 | interface VoteStatusPillProps {
4 | status: string;
5 | text: React.ReactNode;
6 | }
7 |
8 | const VoteStatusPill: React.FC = props => {
9 | const { status, text } = props;
10 | switch (status) {
11 | case 'success':
12 | return {text}
;
13 | case 'failure':
14 | return {text}
;
15 | default:
16 | return {text}
;
17 | }
18 | };
19 |
20 | export default VoteStatusPill;
21 |
--------------------------------------------------------------------------------
/src/components/DelegationCandidateInfo/DelegationCandidateInfo.module.css:
--------------------------------------------------------------------------------
1 | .spinner {
2 | display: flex;
3 | justify-content: center;
4 | }
5 |
6 | .wrapper {
7 | display: flex;
8 | flex-direction: row;
9 | margin-top: 0.75rem;
10 | justify-content: space-between;
11 | padding-left: 0.5rem;
12 | padding-right: 0.5rem;
13 | }
14 |
15 | .delegateCandidateInfoWrapper {
16 | display: flex;
17 | }
18 |
19 | .avatarWrapper {
20 | margin-right: 1rem;
21 | }
22 |
23 | .ensText {
24 | color: var(--brand-cool-dark-text);
25 | font-weight: bold;
26 | font-size: 22px;
27 | }
28 |
29 | .shortAddress {
30 | font-weight: 500;
31 | font-size: 13px;
32 | color: var(--brand-cool-light-text);
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/HistoryCollection/HistoryCollection.module.css:
--------------------------------------------------------------------------------
1 | .historyCollection {
2 | display: grid;
3 | grid-auto-flow: column dense;
4 | column-gap: 8px;
5 | width: 100%;
6 | max-height: 175px;
7 | padding: 8px;
8 | overflow-y: hidden;
9 | overflow-x: hidden;
10 | overflow: hidden;
11 | max-width: 1700px;
12 | }
13 | .historyCollection img:hover {
14 | cursor: pointer;
15 | transform: scale(1.025);
16 | }
17 |
18 | .rtl {
19 | direction: ltr;
20 | }
21 |
22 | .historyCollection div {
23 | width: 146px;
24 | margin: 2px;
25 | }
26 |
27 | @media (max-width: 992px) {
28 | .historyCollection {
29 | grid-template-columns: repeat(10, 150px);
30 | overflow-x: auto;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/assets/wallet-brand-assets/coinbase-wallet-dot.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/components/NavBar/NavBarLink/index.tsx:
--------------------------------------------------------------------------------
1 | import { Link } from 'react-router-dom';
2 | import classes from './NavBarLink.module.css';
3 |
4 | const NavBarLink: React.FC<{ to: string; className?: string }> = props => {
5 | const { to, children, className } = props;
6 | // hacks to make React Router work with external links
7 | const onClick = () => (/http/.test(to) ? (window.location.href = to) : null);
8 | const target = /http/.test(to) ? '_blank' : '';
9 | return (
10 |
16 | {children}
17 |
18 | );
19 | };
20 | export default NavBarLink;
21 |
--------------------------------------------------------------------------------
/src/components/ProposalStatus/ProposalStatus.module.css:
--------------------------------------------------------------------------------
1 | .proposalStatus {
2 | font-family: 'PT Root UI';
3 | font-weight: bold;
4 | color: white;
5 | border-radius: 8px;
6 | font-size: 14px;
7 | border: 0rem;
8 | padding-left: 0.65rem;
9 | padding-right: 0.65rem;
10 | padding-top: 0.36rem;
11 | padding-bottom: 0.36rem;
12 | }
13 |
14 | .primary {
15 | background-color: var(--brand-color-green);
16 | }
17 |
18 | .success {
19 | background-color: var(--brand-color-blue);
20 | }
21 |
22 | .danger {
23 | background-color: var(--brand-color-red);
24 | }
25 |
26 | .secondary {
27 | background-color: var(--brand-gray-light-text);
28 | }
29 |
30 | .votePageProposalStatus {
31 | margin-top: 0.8rem;
32 | }
33 |
--------------------------------------------------------------------------------
/lingui.config.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Taken from https://github.com/Uniswap/interface/blob/main/lingui.config.ts with minor modifications
3 | */
4 | const linguiConfig = {
5 | catalogs: [
6 | {
7 | path: '/src/locales/{locale}',
8 | include: ['/src'],
9 | },
10 | ],
11 | compileNamespace: 'cjs',
12 | fallbackLocales: {
13 | default: 'en-US',
14 | pseudo: 'en-US',
15 | },
16 | format: 'po',
17 | formatOptions: {
18 | lineNumbers: false,
19 | },
20 | locales: ['en-US', 'ja-JP', 'pseudo'],
21 | orderBy: 'messageId',
22 | rootDir: '.',
23 | runtimeConfigModule: ['@lingui/core', 'i18n'],
24 | sourceLocale: 'en-US',
25 | pseudoLocale: 'pseudo',
26 | };
27 |
28 | export default linguiConfig;
29 |
--------------------------------------------------------------------------------
/src/components/VoteProgressBar/VoteProgressBar.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | border-radius: 6px;
3 | height: 1rem;
4 | }
5 |
6 | .progressBar {
7 | height: 100%;
8 | border-radius: 6px;
9 | }
10 |
11 | .forWrapper {
12 | background-color: var(--brand-color-green-translucent);
13 | }
14 |
15 | .forProgressBar {
16 | background-color: var(--brand-color-green);
17 | }
18 |
19 | .againstWrapper {
20 | background-color: var(--brand-color-red-translucent);
21 | }
22 |
23 | .againstProgressBar {
24 | background-color: var(--brand-color-red);
25 | }
26 |
27 | .abstainWrapper {
28 | background-color: var(--brand-gray-light-text-translucent);
29 | }
30 |
31 | .abstainProgressBar {
32 | background-color: var(--brand-gray-light-text);
33 | }
34 |
--------------------------------------------------------------------------------
/public/logo_.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/src/components/BidHistoryBtn/index.tsx:
--------------------------------------------------------------------------------
1 | import bidBtnClasses from './BidHistoryBtn.module.css';
2 |
3 | import { useAppSelector } from '../../hooks';
4 | import { Trans } from '@lingui/macro';
5 |
6 | const BidHistoryBtn: React.FC<{ onClick: () => void }> = props => {
7 | const { onClick } = props;
8 |
9 | const isCool = useAppSelector(state => state.application.stateBackgroundColor) === '#d5d7e1';
10 |
11 | return (
12 |
16 |
17 | View all bids
18 |
19 |
20 | );
21 | };
22 | export default BidHistoryBtn;
23 |
--------------------------------------------------------------------------------
/src/components/BrandSpinner/index.tsx:
--------------------------------------------------------------------------------
1 | import classes from './BrandSpinner.module.css';
2 |
3 | const BrandSpinner = () => {
4 | return (
5 |
15 | );
16 | };
17 |
18 | export default BrandSpinner;
19 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.base.json",
3 | "compilerOptions": {
4 | "strict": true,
5 | "esModuleInterop": true,
6 | "target": "es5",
7 | "lib": [
8 | "dom",
9 | "dom.iterable",
10 | "esnext",
11 | "es2015"
12 | ],
13 | "allowJs": true,
14 | "composite": false,
15 | "declaration": false,
16 | "forceConsistentCasingInFileNames": true,
17 | "noFallthroughCasesInSwitch": true,
18 | "module": "esnext",
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src",
25 | "./src/**/*"
26 | ],
27 | "references": [
28 | {
29 | "path": "../nouns-contracts/tsconfig.build.json"
30 | }
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/src/assets/icons/PendingVote.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/utils/tokenBuyerContractUtils/tokenBuyer.ts:
--------------------------------------------------------------------------------
1 | import { useContractCall } from '@usedapp/core';
2 | import { Interface } from 'ethers/lib/utils';
3 | import tokenBuyerABI from './tokenBuyerABI.json';
4 | import { BigNumber as EthersBN } from 'ethers';
5 |
6 | const abi = new Interface(tokenBuyerABI);
7 | const BUFFER_BPS = 5_000;
8 |
9 | export const useEthNeeded = (address: string, additionalTokens: number, skip?: boolean) => {
10 | const request = () => {
11 | if (skip) return false;
12 | return {
13 | abi,
14 | address,
15 | method: 'ethNeeded',
16 | args: [additionalTokens, BUFFER_BPS],
17 | };
18 | };
19 |
20 | const [ethNeeded] = useContractCall<[EthersBN]>(request()) || [];
21 |
22 | return ethNeeded?.toString();
23 | };
24 |
--------------------------------------------------------------------------------
/src/assets/icons/YesVote.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/LanguageSelectionModal/LanguageSelectionModal.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | max-height: 40vh;
3 | overflow-y: scroll;
4 | }
5 |
6 | .languageButton {
7 | border: none;
8 | margin: 5px;
9 | padding: 5px 20px;
10 | border-radius: 5px;
11 | background-color: rgba(211, 211, 211, 0.664);
12 | color: var(--brand-black);
13 | border: none;
14 | display: flex;
15 | justify-content: space-between;
16 | }
17 |
18 | .languageButton:hover,
19 | .languageButton:active,
20 | .languageButton:focus,
21 | .languageButton:disabled {
22 | outline: none !important;
23 | box-shadow: none !important;
24 | background-color: lightgray !important;
25 | color: var(--brand-dark-green);
26 | color: white;
27 | }
28 |
29 | .icon {
30 | margin-top: 0.25rem;
31 | }
32 |
--------------------------------------------------------------------------------
/src/components/Tooltip/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactTooltip from 'react-tooltip';
3 | import classes from './Tooltip.module.css';
4 |
5 | interface TooltipProps {
6 | tooltipContent: (dataTip: string) => React.ReactNode;
7 | tip: string;
8 | id: string;
9 | }
10 |
11 | const Tooltip: React.FC = props => {
12 | const { tooltipContent, tip, id } = props;
13 |
14 | return (
15 | <>
16 | {
20 | return tooltipContent(dataTip);
21 | }}
22 | />
23 |
24 | {props.children}
25 |
26 | >
27 | );
28 | };
29 |
30 | export default Tooltip;
31 |
--------------------------------------------------------------------------------
/src/components/NavWallet/WalletConnectButton/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Nav } from 'react-bootstrap';
3 | import NavBarButton, { NavBarButtonStyle } from '../../NavBarButton';
4 | import { Trans } from '@lingui/macro';
5 |
6 | interface WalletConnectButtonProps {
7 | className: string;
8 | onClickHandler: () => void;
9 | buttonStyle: NavBarButtonStyle;
10 | }
11 |
12 | const WalletConnectButton: React.FC = props => {
13 | const { className, onClickHandler, buttonStyle } = props;
14 | return (
15 |
16 | Connect} />
17 |
18 | );
19 | };
20 |
21 | export default WalletConnectButton;
22 |
--------------------------------------------------------------------------------
/src/components/ProposalActionsModal/steps/FunctionCallEnterArgsStep/FunctionCallEnterArgsStep.module.css:
--------------------------------------------------------------------------------
1 | .inputGroupText {
2 | border-radius: 15px 0 0 15px;
3 | border-right: none;
4 | height: 3rem;
5 | margin-top: 0.25rem;
6 | margin-bottom: 0.5rem;
7 | font-weight: 500;
8 | opacity: 0.5;
9 | }
10 |
11 | .label {
12 | opacity: 0.5;
13 | }
14 |
15 | .inputGroup {
16 | margin-bottom: 1rem;
17 | border-radius: 15px;
18 | width: 100%;
19 | height: 3rem;
20 | font-size: 22px;
21 | font-weight: bold;
22 | padding: 0.5rem 1rem;
23 | border: 1px solid rgba(0, 0, 0, 0.1);
24 | color: var(--brand-cool-dark-text);
25 | margin-top: 0.25rem;
26 | margin-bottom: 0.5rem;
27 | outline: none;
28 | }
29 |
30 | .invalid {
31 | color: var(--brand-color-rd);
32 | }
33 |
--------------------------------------------------------------------------------
/functions/README.md:
--------------------------------------------------------------------------------
1 | # Nouns Serverless API
2 |
3 | `nouns.wtf` provides a serverless API to make fetching data about the Nouns ecosystem easier. [An Insomnia manifest is provided for example.](./docs/insomnia.json)
4 |
5 | ## API Convention
6 |
7 | `https://nouns.wtf/.netlify/functions//`
8 |
9 | ## `V0`
10 |
11 | The `V0` namespace is an incubator for serverless functions before the next stable version release. Functions within this namespace may change before becoming promoted. Once promoted to "stable" they'll get their final function names and will be locked in. Any further function changes will result in new function names on breaking changes.
12 |
13 | ## Endpoints and Schema
14 |
15 | See the [OpenAPI Spec File for detailed information about the API](docs/swagger.yaml).
16 |
--------------------------------------------------------------------------------
/src/assets/icons/Heart.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/functions/v0-noun-seeds/index.ts:
--------------------------------------------------------------------------------
1 | import { Handler } from '@netlify/functions';
2 | import { nounsQuery, Seed } from '../theGraph';
3 | import * as R from 'ramda';
4 | import { sharedResponseHeaders } from '../utils';
5 |
6 | interface SeededNoun {
7 | id: number;
8 | seed: Seed;
9 | }
10 |
11 | const buildSeededNoun = R.pick(['id', 'seed']);
12 |
13 | const buildSeededNouns = R.map(buildSeededNoun);
14 |
15 | const handler: Handler = async (event, context) => {
16 | const nouns = await nounsQuery();
17 | const seededNouns: SeededNoun[] = buildSeededNouns(nouns);
18 | return {
19 | statusCode: 200,
20 | headers: {
21 | 'Content-Type': 'application/json',
22 | ...sharedResponseHeaders,
23 | },
24 | body: JSON.stringify(seededNouns),
25 | };
26 | };
27 |
28 | export { handler };
29 |
--------------------------------------------------------------------------------
/src/pages/DelegatePage/index.tsx:
--------------------------------------------------------------------------------
1 | import { useHistory, useLocation } from 'react-router-dom';
2 | import DelegationModal from '../../components/DelegationModal';
3 | import { getAddressFromQueryParams } from '../../utils/getAddressFromQueryParams';
4 |
5 | const DelegatePage = () => {
6 | const { search } = useLocation();
7 | const delegateTo = getAddressFromQueryParams('to', search);
8 |
9 | const history = useHistory();
10 |
11 | if (!delegateTo || delegateTo.length === 0) {
12 | return (
13 | <>
14 | history.push('/vote')} />
15 | >
16 | );
17 | }
18 |
19 | return (
20 | <>
21 | history.push('/vote')} delegateTo={delegateTo} />
22 | >
23 | );
24 | };
25 |
26 | export default DelegatePage;
27 |
--------------------------------------------------------------------------------
/src/components/NounInfoCard/NounInfoCard.module.css:
--------------------------------------------------------------------------------
1 | .nounInfoHeader {
2 | display: inline-block;
3 | padding-left: 0rem;
4 | font-family: 'Londrina Solid';
5 | }
6 |
7 | @media only screen and (max-width: 600px) {
8 | .nounInfoHeader {
9 | padding-top: 1.5rem;
10 | margin-bottom: 0;
11 | }
12 | }
13 |
14 | .nounInfoHeader h2 {
15 | font-size: 56px;
16 | margin-bottom: 18px;
17 | }
18 |
19 | .nounInfoHeader h3 {
20 | font-size: 24px;
21 | margin-bottom: '0px';
22 | color: #79809c;
23 | }
24 |
25 | .nounInfoRow {
26 | font-family: 'PT Root UI';
27 | font-weight: 500;
28 | padding-bottom: 0.5rem;
29 | padding-top: 0.5rem;
30 | font-size: 18px;
31 | display: flex;
32 | }
33 |
34 | .nounInfoRowText {
35 | padding-left: 4;
36 | font-family: 'PT Root UI';
37 | font-family: bold;
38 | }
39 |
--------------------------------------------------------------------------------
/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /*
4 | * Uncomment for small accuracy reduction & vast build time reduction for
5 | * --watch mode
6 | */
7 | // "assumeChangesOnlyAffectDirectDependencies": true,
8 | "allowSyntheticDefaultImports": true,
9 | "alwaysStrict": true,
10 | "declaration": true,
11 | "esModuleInterop": true,
12 | "resolveJsonModule": true,
13 | "forceConsistentCasingInFileNames": true,
14 | "incremental": true,
15 | "composite": true,
16 | "lib": ["esnext"],
17 | "module": "CommonJS",
18 | "moduleResolution": "node",
19 | "noImplicitThis":true,
20 | "outDir": "dist",
21 | "skipLibCheck": true,
22 | "strict": true,
23 | "strictNullChecks": true,
24 | "target": "es6",
25 | },
26 | "exclude": ["**/dist"]
27 | }
28 |
--------------------------------------------------------------------------------
/functions/v0-noun-owners/index.ts:
--------------------------------------------------------------------------------
1 | import { Handler } from '@netlify/functions';
2 | import { nounsQuery } from '../theGraph';
3 | import * as R from 'ramda';
4 | import { sharedResponseHeaders } from '../utils';
5 |
6 | export interface LiteNoun {
7 | id: number;
8 | owner: string;
9 | delegatedTo: null | string;
10 | }
11 |
12 | const lightenNoun = R.pick(['id', 'owner', 'delegatedTo']);
13 |
14 | const lightenNouns = R.map(lightenNoun);
15 |
16 | const handler: Handler = async (event, context) => {
17 | const nouns = await nounsQuery();
18 | const liteNouns: LiteNoun[] = lightenNouns(nouns);
19 | return {
20 | statusCode: 200,
21 | headers: {
22 | 'Content-Type': 'application/json',
23 | ...sharedResponseHeaders,
24 | },
25 | body: JSON.stringify(liteNouns),
26 | };
27 | };
28 |
29 | export { handler };
30 |
--------------------------------------------------------------------------------
/src/components/TightStackedCircleNouns/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TightStackedCircleNoun from '../TightStackedCircleNoun';
3 |
4 | interface StackedCircleNounsProps {
5 | nounIds: Array;
6 | }
7 |
8 | const MAX_NOUNS_PER_STACK = 3;
9 |
10 | const TightStackedCircleNouns: React.FC = props => {
11 | const { nounIds } = props;
12 |
13 | const shift = 3;
14 |
15 | const square = 55;
16 |
17 | return (
18 |
26 | );
27 | };
28 |
29 | export default TightStackedCircleNouns;
30 |
--------------------------------------------------------------------------------
/src/store/actions/index.js:
--------------------------------------------------------------------------------
1 | import { createAction as action } from "typesafe-actions"
2 |
3 | // Global
4 | export const setUserInfo = action("global/SET_USER_INFO")();
5 | export const setWalletConnected = action("global/SET_WALLET_CONNECTED")();
6 | export const setSignedMessage = action("global/SET_SIGNED_MESSAGE")();
7 | export const setUserProfile = action("global/SET_USER_PROFILE")();
8 |
9 | export const setAlertMessage = action("global/SET_ALERT_MESSAGE")();
10 | export const setNotifications = action("global/SET_NOTIFICATIONS")();
11 | export const setClearNotifications = action("global/CLEAR_NOTIFICATIONS")();
12 |
13 | export const setCollections = action("global/SET_COLLETIONS")();
14 | export const setUpdateCollectionFlag = action("global/SET_UPDATE_COLLECTION_FLAG")();
15 |
16 | export const setInscriberId = action("global/SET_INSCRIBER_ID")();
--------------------------------------------------------------------------------
/src/utils/pseudoRandomPredictableShuffle.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Pseudorandomly shuffle elements on input conditioned on seed
3 | *
4 | * h/t to 9999 for this code
5 | *
6 | * @param input array of elements to be shuffeled
7 | * @param seed random seed
8 | * @returns elements in inputs pseudorandomly shuffled as a function of seed
9 | */
10 | export const pseudoRandomPredictableShuffle = (input: Array = [], seed: number = 1) => {
11 | input = [...input];
12 | const output = [];
13 |
14 | while (input.length) {
15 | if (seed === 0) seed++;
16 | // adapted from: https://stackoverflow.com/a/19303725
17 | let rand = Math.sin(seed++) * 10000;
18 | rand -= Math.floor(rand);
19 | const index = Math.floor(input.length * rand);
20 | output.push(input[index]);
21 | input.splice(index, 1);
22 | }
23 |
24 | return output;
25 | };
26 |
--------------------------------------------------------------------------------
/public/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
18 |
--------------------------------------------------------------------------------
/src/components/profileEvent/activityRow/MobileNounActivityRow/index.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react';
2 | import classes from './MobileNounActivityRow.module.css';
3 |
4 | interface MobileNounActivityRowProps {
5 | onClick: () => void;
6 | icon: ReactNode;
7 | primaryContent: ReactNode;
8 | secondaryContent?: ReactNode;
9 | }
10 |
11 | const MobileNounActivityRow: React.FC = props => {
12 | const { onClick, icon, primaryContent, secondaryContent } = props;
13 |
14 | return (
15 |
16 |
{icon}
17 |
18 |
19 |
{primaryContent}
20 |
{secondaryContent}
21 |
22 |
23 | );
24 | };
25 |
26 | export default MobileNounActivityRow;
27 |
--------------------------------------------------------------------------------
/src/components/HoverCard/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactTooltip from 'react-tooltip';
3 | import classes from './HoverCard.module.css';
4 |
5 | interface HoverCardProps {
6 | hoverCardContent: (dataTip: string) => React.ReactNode;
7 | tip: string;
8 | id: string;
9 | }
10 |
11 | const HoverCard: React.FC = props => {
12 | const { hoverCardContent, tip, id } = props;
13 |
14 | return (
15 | <>
16 | {
22 | return hoverCardContent(dataTip);
23 | }}
24 | />
25 |
26 | {props.children}
27 |
28 | >
29 | );
30 | };
31 |
32 | export default HoverCard;
33 |
--------------------------------------------------------------------------------
/src/components/NavBar/NavBarLink/NavBarLink.module.css:
--------------------------------------------------------------------------------
1 | .navBarLink {
2 | display: flex;
3 | align-items: center;
4 | justify-content: center;
5 | margin-right: 1rem;
6 | padding: 1.75rem;
7 | height: 2rem;
8 | background-color: white;
9 | color: var(--brand-black);
10 | border-radius: 50px;
11 | border: 0 none;
12 | font-size: large;
13 | font-weight: normal;
14 | cursor: pointer;
15 | text-decoration: none !important;
16 | }
17 | .navBarLink:hover {
18 | color: var(--brand-dark-green) !important;
19 | background-color: #f2f2f2;
20 | }
21 |
22 | @media (max-width: 992px) {
23 | .navBarLink {
24 | background-color: transparent !important;
25 | color: var(--brand-black) !important;
26 | }
27 | .navBarLink:hover {
28 | background-color: transparent !important;
29 | color: var(--brand-dark-green) !important;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/components/BrandDatePicker/index.tsx:
--------------------------------------------------------------------------------
1 | import clsx from 'clsx';
2 | import classes from './BrandDatePicker.module.css';
3 |
4 | interface BrandDatePickerProps {
5 | onChange: (e: React.ChangeEvent) => void;
6 | value?: string | number;
7 | placeholder?: string;
8 | label?: string;
9 | isInvalid?: boolean;
10 | }
11 |
12 | const BrandDatePicker: React.FC = props => {
13 | const { onChange, value, label, isInvalid = false } = props;
14 |
15 | return (
16 |
17 | {label && {label}}
18 |
24 |
25 | );
26 | };
27 |
28 | export default BrandDatePicker;
29 |
--------------------------------------------------------------------------------
/src/i18n/LanguageProvider.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * LanguageProvider.tsx is a modified version of https://github.com/Uniswap/interface/blob/main/src/lib/i18n.tsx
3 | */
4 | import { SupportedLocale } from './locales';
5 | import { initialLocale, useActiveLocale } from '../hooks/useActivateLocale';
6 | import { dynamicActivate, NounsI18nProvider } from './NounsI18nProvider';
7 | import { ReactNode, useCallback } from 'react';
8 |
9 | dynamicActivate(initialLocale);
10 |
11 | export function LanguageProvider({ children }: { children: ReactNode }) {
12 | const locale = useActiveLocale();
13 |
14 | const onActivate = useCallback((locale: SupportedLocale) => {
15 | dynamicActivate(locale);
16 | }, []);
17 |
18 | return (
19 |
20 | {children}
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/NavDropdown/NavDropdown.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | height: 40px;
3 | border-radius: 10px;
4 | font-size: 16px;
5 | line-height: 16px;
6 | font-family: 'PT Root UI';
7 | font-weight: bold;
8 | padding: 0;
9 | transition: all 0.125s ease-in-out;
10 | box-shadow: none;
11 | }
12 |
13 | .menu {
14 | border-radius: 10px;
15 | border: 1px solid rgba(0, 0, 0, 0.1);
16 | margin: 0;
17 | padding: 0;
18 | overflow: hidden;
19 | }
20 |
21 | .menu a {
22 | color: rgb(95, 95, 95);
23 | padding: 0.5rem 1rem;
24 | text-decoration: none;
25 | font-weight: bold;
26 | display: block;
27 | /* background-color: #f4f4f8; */
28 | border: 0;
29 | border-bottom: 1.5px solid #e2e3e8;
30 | border-radius: 0;
31 | }
32 | .menu a:last-child {
33 | border: none;
34 | }
35 |
36 | .menu a:hover {
37 | /* color: var(--brand-black); */
38 | background-color: #fff;
39 | }
40 |
--------------------------------------------------------------------------------
/functions/v0-noun-votes/index.ts:
--------------------------------------------------------------------------------
1 | import { Handler } from '@netlify/functions';
2 | import { NormalizedVote, nounsQuery } from '../theGraph';
3 | import * as R from 'ramda';
4 | import { sharedResponseHeaders } from '../utils';
5 |
6 | interface NounVote {
7 | id: number;
8 | owner: string;
9 | delegatedTo: null | string;
10 | votes: NormalizedVote[];
11 | }
12 |
13 | const buildNounVote = R.pick(['id', 'owner', 'delegatedTo', 'votes']);
14 |
15 | const buildNounVotes = R.map(buildNounVote);
16 |
17 | const handler: Handler = async (event, context) => {
18 | const nouns = await nounsQuery();
19 | const nounVotes: NounVote[] = buildNounVotes(nouns);
20 | return {
21 | statusCode: 200,
22 | headers: {
23 | 'Content-Type': 'application/json',
24 | ...sharedResponseHeaders,
25 | },
26 | body: JSON.stringify(nounVotes),
27 | };
28 | };
29 |
30 | export { handler };
31 |
--------------------------------------------------------------------------------
/src/utils/types.ts:
--------------------------------------------------------------------------------
1 | import { BigNumber, BigNumberish } from '@ethersproject/bignumber';
2 |
3 | export interface BidEvent {
4 | nounId: BigNumberish;
5 | sender: string;
6 | value: BigNumberish;
7 | extended: boolean;
8 | transactionHash: string;
9 | timestamp: BigNumberish;
10 | }
11 |
12 | export interface AuctionCreateEvent {
13 | nounId: BigNumberish;
14 | startTime: BigNumberish;
15 | endTime: BigNumberish;
16 | settled: boolean;
17 | }
18 |
19 | export interface AuctionSettledEvent {
20 | nounId: BigNumberish;
21 | winner: string;
22 | amount: BigNumberish;
23 | }
24 |
25 | export interface AuctionExtendedEvent {
26 | nounId: BigNumberish;
27 | endTime: BigNumberish;
28 | }
29 |
30 | export interface Bid {
31 | nounId: BigNumber;
32 | sender: string;
33 | value: BigNumber;
34 | extended: boolean;
35 | transactionHash: string;
36 | timestamp: BigNumber;
37 | }
38 |
--------------------------------------------------------------------------------
/src/i18n/locales.ts:
--------------------------------------------------------------------------------
1 | import en, { Locale as DaysJSLocale } from 'dayjs/locale/en';
2 | import ja from 'dayjs/locale/ja';
3 |
4 | export const SUPPORTED_LOCALES = [
5 | // order as they appear in the language dropdown
6 | 'en-US',
7 | 'ja-JP',
8 | ];
9 | export type SupportedLocale = typeof SUPPORTED_LOCALES[number] | 'pseudo';
10 |
11 | export const DEFAULT_LOCALE: SupportedLocale = 'en-US';
12 |
13 | export const LOCALE_LABEL: { [locale in SupportedLocale]: string } = {
14 | 'en-US': 'English',
15 | 'ja-JP': '日本語',
16 | pseudo: 'ƥƨèúδô',
17 | };
18 |
19 | export enum Locales {
20 | en_US = 'en-US',
21 | ja_JP = 'ja-JP',
22 | }
23 |
24 | // Map SupportedLocale string to DaysJS locale object (used for locale aware time formatting)
25 | export const SUPPORTED_LOCALE_TO_DAYSJS_LOCALE: { [locale in SupportedLocale]: DaysJSLocale } = {
26 | 'en-US': en,
27 | 'ja-JP': ja,
28 | pseudo: en,
29 | };
30 |
--------------------------------------------------------------------------------
/src/assets/wallet-brand-assets/ledger.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
--------------------------------------------------------------------------------
/src/components/GrayCircle/index.tsx:
--------------------------------------------------------------------------------
1 | import { getGrayBackgroundSVG } from '../../utils/grayBackgroundSVG';
2 | import nounClasses from '../Noun/Noun.module.css';
3 | import Noun from '../Noun';
4 | import classes from './GrayCircle.module.css';
5 |
6 | interface GrayCircleProps {
7 | isDelegateView?: boolean;
8 | }
9 |
10 | export const GrayCircle: React.FC = props => {
11 | const { isDelegateView } = props;
12 | return (
13 |
14 |
24 |
25 | );
26 | };
27 |
--------------------------------------------------------------------------------
/src/components/VoteStatusPill/VoteStatusPill.module.css:
--------------------------------------------------------------------------------
1 | .nounButton {
2 | height: 28px;
3 | border-radius: 8px;
4 | margin-top: 5px;
5 | margin-bottom: 5px;
6 | font-family: 'PT Root UI';
7 | font-weight: bold;
8 | vertical-align: middle;
9 | text-align: center;
10 | display: flex;
11 | flex-direction: row;
12 | justify-content: center;
13 | align-items: center;
14 | padding: 0px 8px;
15 | color: var(--brand-color-green);
16 | background-color: var(--brand-color-green-translucent);
17 | font-size: 14px;
18 | width: fit-content;
19 | }
20 |
21 | .pass {
22 | color: var(--brand-color-blue);
23 | background-color: var(--brand-color-blue-translucent);
24 | }
25 |
26 | .fail {
27 | color: var(--brand-color-red);
28 | background-color: var(--brand-color-red-translucent);
29 | }
30 |
31 | .pending {
32 | color: var(--brand-color-green);
33 | background-color: var(--brand-color-green-translucent);
34 | }
35 |
--------------------------------------------------------------------------------
/src/components/ByLineHoverCard/ByLineHoverCard.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | display: flex;
3 | flex-direction: column;
4 | }
5 |
6 | .stackedNounWrapper {
7 | display: flex;
8 | }
9 |
10 | .address {
11 | font-family: 'Londrina Solid';
12 | font-size: 24px;
13 | width: 100%;
14 | text-align: left;
15 | }
16 |
17 | .nounsRepresented {
18 | color: var(--brand-gray-dark-text);
19 | display: flex;
20 | margin-top: 0.25rem;
21 | font-weight: 500;
22 | font-size: 15px;
23 | max-width: 11rem;
24 | }
25 |
26 | .bold {
27 | font-weight: bold;
28 | }
29 |
30 | .spinnerWrapper {
31 | color: var(--brand-gray-light-text);
32 | width: 100%;
33 | height: 120px;
34 | display: flex;
35 | flex-direction: column;
36 | justify-content: center;
37 | }
38 |
39 | .spinner {
40 | display: flex;
41 | width: 100%;
42 | justify-content: center;
43 | }
44 |
45 | .icon {
46 | margin-bottom: 5px;
47 | margin-right: 6px;
48 | }
49 |
--------------------------------------------------------------------------------
/src/components/BidHistoryModalRow/BidHistoryModalRow.module.css:
--------------------------------------------------------------------------------
1 | .bidRow {
2 | border-radius: 14px;
3 | height: 72px;
4 | width: 100%;
5 | padding: 1rem;
6 | margin-top: 0.75rem;
7 | border-bottom: 0px;
8 | background-color: white;
9 | }
10 |
11 | .bidderInfoWrapper {
12 | display: flex;
13 | }
14 |
15 | .bidderInfoText {
16 | margin-left: 0.5rem;
17 | display: inline-block;
18 | padding: 0;
19 | line-height: 23px;
20 | }
21 |
22 | .bidDate {
23 | color: rgba(140, 141, 146, 1);
24 | font-weight: 500;
25 | font-family: 'PT Root UI';
26 | font-size: 13px;
27 | }
28 |
29 | .trophy {
30 | margin-left: 0.5rem;
31 | margin-bottom: 0.1rem;
32 | }
33 |
34 | .linkIcon {
35 | color: var(--brand-gray-light-text);
36 | transition: all 0.125s ease-in-out;
37 | margin-bottom: 0.1rem;
38 | }
39 |
40 | .linkIcon:hover {
41 | color: black;
42 | cursor: pointer;
43 | }
44 |
45 | .bidAmount {
46 | white-space: nowrap;
47 | }
48 |
--------------------------------------------------------------------------------
/src/components/profileEvent/activityRow/DesktopNounActivityRow/DesktopNounActivityRow.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | transition: all 0.2s ease-in-out;
3 | }
4 |
5 | .pointer {
6 | cursor: pointer;
7 | }
8 |
9 | .wrapper:hover {
10 | --bs-table-hover-bg: rgba(0, 0, 0, 0.03);
11 | }
12 |
13 | .icon {
14 | width: 38px;
15 | margin-right: 0px;
16 | margin-bottom: 4px;
17 | margin-left: 6px;
18 | }
19 |
20 | .activityTableCell {
21 | max-width: 50vw;
22 | }
23 |
24 | .infoContainer {
25 | display: inline-block;
26 | margin-top: 4px;
27 | width: 100%;
28 | }
29 | @media (max-width: 992px) {
30 | .infoContainer {
31 | overflow: hidden;
32 | white-space: nowrap;
33 | text-overflow: ellipsis;
34 | }
35 | }
36 |
37 | .secondaryContentWrapper {
38 | display: flex;
39 | flex-direction: row;
40 | justify-content: flex-end;
41 | }
42 |
43 | .secondaryContentContainer {
44 | text-align: right;
45 | width: max-content;
46 | }
47 |
--------------------------------------------------------------------------------
/src/hooks/useBlockTimestamp.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 | import { useEthers } from '@usedapp/core';
3 |
4 | /**
5 | * A function that takes a block number from the chain and returns the timestamp of when the block occurred.
6 | * @param blockNumber target block number to retrieve the timestamp for
7 | * @returns unix timestamp of block number
8 | */
9 | export function useBlockTimestamp(blockNumber: number | undefined): number | undefined {
10 | const { library } = useEthers();
11 | const [blockTimestamp, setBlockTimestamp] = useState();
12 |
13 | useEffect(() => {
14 | async function updateBlockTimestamp() {
15 | if (!blockNumber) return;
16 | const blockData = await library?.getBlock(blockNumber);
17 | setBlockTimestamp(blockData?.timestamp || undefined);
18 | }
19 |
20 | updateBlockTimestamp();
21 | }, [blockNumber, library]);
22 |
23 | return blockTimestamp;
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/BrandDropdown/BrandDropdown.module.css:
--------------------------------------------------------------------------------
1 | .chevron {
2 | height: 1.75rem;
3 | width: 1.75rem;
4 | color: var(--brand-cool-dark-text);
5 | }
6 |
7 | .chevronWrapper {
8 | position: absolute;
9 | }
10 |
11 | .dropdownContainer {
12 | width: 100%;
13 | margin-top: 1rem;
14 | position: relative;
15 | cursor: pointer;
16 | }
17 |
18 | .select {
19 | border-radius: 15px;
20 | width: 100%;
21 | height: 3rem;
22 | font-size: 22px;
23 | font-weight: bold;
24 | padding: 0.5rem 1rem;
25 | border: 1px solid rgba(0, 0, 0, 0.1);
26 | color: var(--brand-cool-dark-text);
27 | }
28 |
29 | .dropdownContainer select {
30 | /* for Firefox */
31 | -moz-appearance: none;
32 | /* for Safari, Chrome, Opera */
33 | -webkit-appearance: none;
34 | outline: none;
35 | cursor: pointer;
36 | }
37 |
38 | /* for IE10 */
39 | .dropdownContainer select::-ms-expand {
40 | display: none;
41 | }
42 |
43 | .label {
44 | opacity: 0.5;
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/Banner/index.tsx:
--------------------------------------------------------------------------------
1 | import classes from './Banner.module.css';
2 | import Section from '../../layout/Section';
3 | import { Col } from 'react-bootstrap';
4 | import calendar_noun from '../../assets/calendar_noun.png';
5 | import Noun from '../Noun';
6 | import { Trans } from '@lingui/macro';
7 |
8 | const Banner = () => {
9 | return (
10 |
11 |
12 |
13 |
14 | ONE NOUN,
15 |
16 | EVERY DAY,
17 |
18 | FOREVER.
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | );
29 | };
30 |
31 | export default Banner;
32 |
--------------------------------------------------------------------------------
/src/components/StartOrEndTime/index.tsx:
--------------------------------------------------------------------------------
1 | import dayjs from 'dayjs';
2 | import { Trans } from '@lingui/macro';
3 | import relativeTime from 'dayjs/plugin/relativeTime';
4 |
5 | dayjs.extend(relativeTime);
6 |
7 | const getCountdownCopy = (startTime: number, endTime: number) => {
8 | const startDate = dayjs.unix(startTime);
9 | const endDate = dayjs.unix(endTime);
10 |
11 | const now = dayjs();
12 |
13 | if (now?.isBefore(startDate)) {
14 | return starts {endDate.fromNow()};
15 | } else if (now?.isBefore(endDate)) {
16 | return ends {dayjs(endDate).fromNow()};
17 | } else {
18 | return ended {dayjs(endDate).fromNow()};
19 | }
20 | };
21 |
22 | export interface StartOrEndTimeProps {
23 | startTime?: number;
24 | endTime?: number;
25 | }
26 |
27 | export default function StartOrEndTime({ startTime, endTime }: StartOrEndTimeProps) {
28 | return <>{getCountdownCopy(startTime ?? 0, endTime ?? 0)}>;
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/Noun/index.tsx:
--------------------------------------------------------------------------------
1 | import classes from './Noun.module.css';
2 | import React from 'react';
3 | import loadingNoun from '../../assets/loading-skull-noun.gif';
4 | import Image from 'react-bootstrap/Image';
5 |
6 | export const LoadingNoun = () => {
7 | return (
8 |
9 |
10 |
11 | );
12 | };
13 |
14 | const Noun: React.FC<{
15 | imgPath: string;
16 | alt: string;
17 | className?: string;
18 | wrapperClassName?: string;
19 | }> = props => {
20 | const { imgPath, alt, className, wrapperClassName } = props;
21 | return (
22 |
23 |
29 |
30 | );
31 | };
32 |
33 | export default Noun;
34 |
--------------------------------------------------------------------------------
/src/components/profileEvent/eventData/infoPills/TransactionHashPill/TransactionHashPill.module.css:
--------------------------------------------------------------------------------
1 | .transactionHashWrapper {
2 | background-color: var(--brand-gray-light-text-translucent);
3 | color: var(--brand-gray-light-text);
4 | height: 28px;
5 | border-radius: 8px;
6 | margin-top: 5px;
7 | margin-bottom: 5px;
8 | font-family: 'PT Root UI';
9 | font-weight: bold;
10 | vertical-align: middle;
11 | text-align: center;
12 | display: flex;
13 | flex-direction: row;
14 | justify-content: center;
15 | align-items: center;
16 | padding: 0px 8px;
17 | font-size: 14px;
18 | width: fit-content;
19 | transition: all 0.15s ease-in-out;
20 | cursor: pointer;
21 | }
22 | .transactionHashWrapper:hover {
23 | transform: scale(0.95);
24 | }
25 |
26 | .externalLinkIcon {
27 | height: 16px;
28 | width: 16px;
29 | margin-right: 0.3rem;
30 | }
31 |
32 | @media (max-width: 992px) {
33 | .externalLinkIcon {
34 | margin-bottom: 0.135rem;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/components/NounInfoRowButton/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Image } from 'react-bootstrap';
3 | import classes from './NounInfoRowButton.module.css';
4 | import { useAppSelector } from '../../hooks';
5 |
6 | interface NounInfoRowButtonProps {
7 | iconImgSource: string;
8 | btnText: React.ReactNode;
9 | onClickHandler: () => void;
10 | }
11 |
12 | const NounInfoRowButton: React.FC = props => {
13 | const { iconImgSource, btnText, onClickHandler } = props;
14 | const isCool = useAppSelector(state => state.application.isCoolBackground);
15 | return (
16 |
20 |
21 |
22 | {btnText}
23 |
24 |
25 | );
26 | };
27 |
28 | export default NounInfoRowButton;
29 |
--------------------------------------------------------------------------------
/src/components/profileEvent/eventData/infoPills/TransactionHashPill/index.tsx:
--------------------------------------------------------------------------------
1 | import { ExternalLinkIcon } from '@heroicons/react/solid';
2 | import React from 'react';
3 | import { buildEtherscanTxLink } from '../../../../../utils/etherscan';
4 | import classes from './TransactionHashPill.module.css';
5 |
6 | interface TransactionHashPillProps {
7 | transactionHash: string;
8 | }
9 |
10 | const TransactionHashPill: React.FC = props => {
11 | const { transactionHash } = props;
12 |
13 | return (
14 | window.open(buildEtherscanTxLink(transactionHash), '_blank')}
16 | className={classes.transactionHashWrapper}
17 | >
18 |
19 | {transactionHash.substring(0, 4) +
20 | '...' +
21 | transactionHash.substring(transactionHash.length - 4, transactionHash.length)}
22 |
23 | );
24 | };
25 |
26 | export default TransactionHashPill;
27 |
--------------------------------------------------------------------------------
/src/utils/nounActivity/getProposalVoteIcon.ts:
--------------------------------------------------------------------------------
1 | import { Proposal, ProposalState, Vote } from '../../wrappers/nounsDao';
2 | import _YesVoteIcon from '../../assets/icons/YesVote.svg';
3 | import _NoVoteIcon from '../../assets/icons/NoVote.svg';
4 | import _AbsentVoteIcon from '../../assets/icons/AbsentVote.svg';
5 | import _AbstainVoteIcon from '../../assets/icons/Abstain.svg';
6 | import _PendingVoteIcon from '../../assets/icons/PendingVote.svg';
7 |
8 | export const getProposalVoteIcon = (proposal: Proposal, supportDetailed: 0 | 1 | 2 | undefined) => {
9 | if (supportDetailed === undefined) {
10 | if (proposal.status === ProposalState.PENDING || proposal.status === ProposalState.ACTIVE) {
11 | return _PendingVoteIcon;
12 | }
13 | return _AbsentVoteIcon;
14 | }
15 |
16 | switch (supportDetailed) {
17 | case Vote.FOR:
18 | return _YesVoteIcon;
19 | case Vote.ABSTAIN:
20 | return _AbstainVoteIcon;
21 | default:
22 | return _NoVoteIcon;
23 | }
24 | };
25 |
--------------------------------------------------------------------------------
/src/components/BrandTextEntry/index.tsx:
--------------------------------------------------------------------------------
1 | import clsx from 'clsx';
2 | import classes from './BrandTextEntry.module.css';
3 |
4 | interface BrandTextEntryProps {
5 | onChange: (e: React.ChangeEvent) => void;
6 | value?: string | number;
7 | placeholder?: string;
8 | type?: string;
9 | min?: string;
10 | label?: string;
11 | isInvalid?: boolean;
12 | }
13 |
14 | const BrandTextEntry: React.FC = props => {
15 | const { onChange, value, placeholder, type, min, label, isInvalid = false } = props;
16 |
17 | return (
18 |
19 | {label && {label}}
20 |
28 |
29 | );
30 | };
31 |
32 | export default BrandTextEntry;
33 |
--------------------------------------------------------------------------------
/src/components/ABIUpload/index.tsx:
--------------------------------------------------------------------------------
1 | import classes from './ABIUpload.module.css';
2 | import React from 'react';
3 | import { Form } from 'react-bootstrap';
4 |
5 | interface ABIUploadProps {
6 | abiFileName?: string;
7 | isValid: boolean | undefined;
8 | isInvalid: boolean;
9 | onChange: (e: React.ChangeEvent) => void;
10 | }
11 |
12 | const ABIUpload: React.FC = props => {
13 | const { abiFileName, isValid, isInvalid, onChange } = props;
14 | return (
15 |
16 |
17 | {abiFileName === 'etherscan-abi-download.json' ? abiFileName : 'ABI'}
18 |
19 |
29 |
30 | );
31 | };
32 |
33 | export default ABIUpload;
34 |
--------------------------------------------------------------------------------
/src/components/CurrentBid/CurrentBid.module.css:
--------------------------------------------------------------------------------
1 | .section span {
2 | font-size: 3rem;
3 | font-weight: bold;
4 | font-family: 'Londrina Solid';
5 | }
6 |
7 | .section h4 {
8 | font-family: 'PT Root UI';
9 | font-weight: bold;
10 | font-size: 18px;
11 | }
12 |
13 | .section h2 {
14 | font-family: 'PT Root UI';
15 | font-weight: bold;
16 | font-size: 32px;
17 | margin-bottom: 0px !important;
18 | margin-top: 3px;
19 | }
20 |
21 | .wrapper {
22 | padding-left: 0rem;
23 | padding-right: 0rem;
24 | }
25 |
26 | @media (max-width: 992px) {
27 | .section h4 {
28 | font-size: 18px;
29 | margin-bottom: 0px;
30 | margin-top: 6px;
31 | }
32 |
33 | .section h2 {
34 | font-size: 23px;
35 | }
36 |
37 | .section {
38 | justify-content: space-between;
39 | }
40 |
41 | .currentBid {
42 | margin-right: 0.5rem;
43 | }
44 |
45 | .wrapper {
46 | width: 100%;
47 | margin-left: 0;
48 | margin-right: 0;
49 | }
50 |
51 | .leftCol {
52 | padding-left: 0.5rem;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/assets/icons/Bids.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/components/BrandNumericEntry/index.tsx:
--------------------------------------------------------------------------------
1 | import clsx from 'clsx';
2 | import classes from './BrandNumericEntry.module.css';
3 | import { NumericFormat, OnValueChange } from 'react-number-format';
4 |
5 | interface BrandNumericEntryProps {
6 | onValueChange?: OnValueChange;
7 | value?: string | number;
8 | placeholder?: string;
9 | label?: string;
10 | isInvalid?: boolean;
11 | }
12 |
13 | const BrandNumericEntry: React.FC = props => {
14 | const { onValueChange, value, placeholder, label, isInvalid = false } = props;
15 |
16 | return (
17 |
18 | {label && {label}}
19 |
27 |
28 | );
29 | };
30 |
31 | export default BrandNumericEntry;
32 |
--------------------------------------------------------------------------------
/src/pages/Playground/NounModal/NounModal.module.css:
--------------------------------------------------------------------------------
1 | .modal {
2 | position: fixed;
3 | top: 15vh;
4 | z-index: 100;
5 | padding: 2rem;
6 | text-align: center;
7 | border-radius: 15px;
8 | left: calc(50% - 12.5rem);
9 | width: 25rem;
10 | }
11 |
12 | .displayNounFooter {
13 | display: flex;
14 | flex-direction: column;
15 | align-items: center;
16 | }
17 | .displayNounFooter span {
18 | color: white;
19 | font-weight: bold;
20 | margin-bottom: 1rem;
21 | }
22 | .displayNounFooter button {
23 | width: 50%;
24 | background: rgba(255, 255, 255, 0.3);
25 | border: none;
26 | }
27 |
28 | .displayNounFooter button:hover,
29 | .displayNounFooter button:active,
30 | .displayNounFooter button:focus {
31 | background: rgba(255, 255, 255, 0.4) !important;
32 | box-shadow: none !important;
33 | }
34 |
35 | .nounImg {
36 | border-radius: 16px;
37 | }
38 | .nounWrapper {
39 | margin-bottom: 1rem;
40 | }
41 |
42 | @media (max-width: 992px) {
43 | .modal {
44 | width: 80% !important;
45 | left: 10% !important;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/hooks/useLidoBalance.ts:
--------------------------------------------------------------------------------
1 | import { useMemo, useEffect, useState } from 'react';
2 | import { Contract } from '@ethersproject/contracts';
3 | import { useEthers } from '@usedapp/core';
4 | import { utils, BigNumber } from 'ethers';
5 | import config from '../config';
6 | import ERC20 from '../libs/abi/ERC20.json';
7 |
8 | const { addresses } = config;
9 |
10 | const erc20Interface = new utils.Interface(ERC20);
11 |
12 | function useLidoBalance(): BigNumber | undefined {
13 | const { library } = useEthers();
14 |
15 | const [balance, setBalance] = useState(undefined);
16 |
17 | const lidoContract = useMemo((): Contract | undefined => {
18 | if (!library || !addresses.lidoToken) return;
19 | return new Contract(addresses.lidoToken, erc20Interface, library);
20 | }, [library]);
21 |
22 | useEffect(() => {
23 | if (!lidoContract || !addresses.nounsDaoExecutor) return;
24 | lidoContract.balanceOf(addresses.nounsDaoExecutor).then(setBalance);
25 | }, [lidoContract]);
26 |
27 | return balance;
28 | }
29 |
30 | export default useLidoBalance;
31 |
--------------------------------------------------------------------------------
/src/components/Auction/Auction.module.css:
--------------------------------------------------------------------------------
1 | .nounWrapper {
2 | align-self: flex-end;
3 | width: 100%;
4 | }
5 |
6 | .nounContentCol {
7 | display: flex;
8 | }
9 |
10 | .auctionActivityCol {
11 | padding-right: 5rem;
12 | padding-bottom: 0rem;
13 | min-height: 558px;
14 | align-self: flex-end !important;
15 | }
16 |
17 | @media (max-width: 992px) {
18 | .nounWrapper {
19 | width: 70%;
20 | margin-left: 15%;
21 | margin-right: 15%;
22 | }
23 | .auctionActivityCol {
24 | padding-top: 5%;
25 | padding-right: 5%;
26 | padding-left: 5%;
27 | width: 100%;
28 | /* background-color: white; */
29 | }
30 | }
31 |
32 | @media (max-width: 568px) {
33 | .auctionActivityCol {
34 | width: 100%;
35 | margin-left: unset;
36 | margin-right: unset;
37 | padding-top: 2rem;
38 | padding-right: unset;
39 | padding-left: 0;
40 | }
41 |
42 | .nounWrapper {
43 | width: 80%;
44 | margin-left: 10%;
45 | margin-right: 10%;
46 | margin-top: 2rem;
47 | }
48 |
49 | .nounContentCol {
50 | padding: 0rem;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/components/AuctionActivityDateHeadline/index.tsx:
--------------------------------------------------------------------------------
1 | import { BigNumber } from 'ethers';
2 | import dayjs from 'dayjs';
3 | import utc from 'dayjs/plugin/utc';
4 | import classes from './AuctionActivityDateHeadline.module.css';
5 | import { useAppSelector } from '../../hooks';
6 | import { i18n } from '@lingui/core';
7 |
8 | dayjs.extend(utc);
9 |
10 | const AuctionActivityDateHeadline: React.FC<{ startTime: BigNumber }> = props => {
11 | const { startTime } = props;
12 | const isCool = useAppSelector(state => state.application.isCoolBackground);
13 | const auctionStartTimeUTC = dayjs(startTime.toNumber() * 1000)
14 | .utc()
15 | .format('MMMM DD, YYYY');
16 | return (
17 |
18 |
22 | {i18n.date(auctionStartTimeUTC, { month: 'long', year: 'numeric', day: '2-digit' })}
23 |
24 |
25 | );
26 | };
27 |
28 | export default AuctionActivityDateHeadline;
29 |
--------------------------------------------------------------------------------
/src/components/DelegationCandidateVoteCountInfo/index.tsx:
--------------------------------------------------------------------------------
1 | import classes from './DelegationCandidateVoteCountInfo.module.css';
2 | import { Trans } from '@lingui/macro';
3 | import React from 'react';
4 | import BrandSpinner from '../BrandSpinner';
5 |
6 | interface DelegationCandidateVoteCountInfoProps {
7 | text: React.ReactNode;
8 | voteCount: number;
9 | isLoading: boolean;
10 | }
11 |
12 | const DelegationCandidateVoteCountInfo: React.FC = props => {
13 | const { text, voteCount, isLoading } = props;
14 |
15 | return (
16 |
17 | {isLoading && (
18 |
19 |
20 |
21 | )}
22 |
23 |
{text}
24 |
25 | {voteCount === 1 ? {voteCount} Vote : {voteCount} Votes}
26 |
27 |
28 |
29 | );
30 | };
31 |
32 | export default DelegationCandidateVoteCountInfo;
33 |
--------------------------------------------------------------------------------
/src/assets/icons/Clock.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/components/BrandDropdown/index.tsx:
--------------------------------------------------------------------------------
1 | import { ChevronDownIcon } from '@heroicons/react/solid';
2 | import React from 'react';
3 | import classes from './BrandDropdown.module.css';
4 |
5 | interface BrandDropdownProps {
6 | onChange: (e: any) => void;
7 | label?: string;
8 | value: any;
9 | chevonRight?: number;
10 | chevronTop?: number;
11 | }
12 |
13 | const BrandDropdown: React.FC = props => {
14 | const { children, onChange, value, label, chevonRight = 10, chevronTop = 10 } = props;
15 |
16 | return (
17 |
18 | {label &&
{label}}
19 |
22 |
29 |
30 |
31 |
32 | );
33 | };
34 |
35 | export default BrandDropdown;
36 |
--------------------------------------------------------------------------------
/src/components/HorizontalStackedNouns/index.tsx:
--------------------------------------------------------------------------------
1 | import { BigNumber } from 'ethers';
2 | import React from 'react';
3 | import { StandaloneNounCircular } from '../StandaloneNoun';
4 | import classes from './HorizontalStackedNouns.module.css';
5 |
6 | interface HorizontalStackedNounsProps {
7 | nounIds: string[];
8 | }
9 |
10 | const HorizontalStackedNouns: React.FC = props => {
11 | const { nounIds } = props;
12 | return (
13 |
14 | {nounIds
15 | .slice(0, 6)
16 | .map((nounId: string, i: number) => {
17 | return (
18 |
26 |
27 |
28 | );
29 | })
30 | .reverse()}
31 |
32 | );
33 | };
34 |
35 | export default HorizontalStackedNouns;
36 |
--------------------------------------------------------------------------------
/src/assets/icons/Info.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/components/Footer/index.tsx:
--------------------------------------------------------------------------------
1 | import classes from './Footer.module.css';
2 | import { Container } from 'react-bootstrap';
3 | import { buildEtherscanAddressLink } from '../../utils/etherscan';
4 | import { externalURL, ExternalURL } from '../../utils/externalURL';
5 | import config from '../../config';
6 | import Link from '../Link';
7 | import { Trans } from '@lingui/macro';
8 |
9 | const Footer = () => {
10 | const twitterURL = externalURL(ExternalURL.twitter);
11 | const etherscanURL = buildEtherscanAddressLink(config.addresses.nounsToken);
12 | const discourseURL = externalURL(ExternalURL.discourse);
13 |
14 | return (
15 |
16 |
17 |
22 |
23 |
24 | );
25 | };
26 | export default Footer;
27 |
--------------------------------------------------------------------------------
/src/utils/getAddressFromQueryParams.ts:
--------------------------------------------------------------------------------
1 | import { ethers } from 'ethers';
2 |
3 | /**
4 | * Get address from query param
5 | *
6 | * @param paramName query param name i.e. for the URL nouns.wtf/delegate?to=0xabv... to would be the paramName
7 | * @param useLocationResult string returned by react-router-v5 useLocation
8 | */
9 | export const getAddressFromQueryParams = (
10 | paramName: string,
11 | useLocationResult: string,
12 | ): string | undefined => {
13 | console.log(useLocationResult);
14 | const splitLocationResult = useLocationResult
15 | .split('=')
16 | .map((s: string) => s.replace('?', '').replace('&', ''));
17 |
18 | const paramIndex = splitLocationResult.indexOf(paramName);
19 | if (paramIndex < 0 || paramIndex === splitLocationResult.length - 1) {
20 | return undefined;
21 | }
22 |
23 | const maybeAddress = splitLocationResult[paramIndex + 1];
24 |
25 | if (maybeAddress.endsWith('.eth') || maybeAddress.endsWith(encodeURIComponent('.⌐◨-◨'))) {
26 | return decodeURIComponent(maybeAddress);
27 | }
28 |
29 | return ethers.utils.isAddress(maybeAddress) ? maybeAddress : undefined;
30 | };
31 |
--------------------------------------------------------------------------------
/src/components/Footer/Footer.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | display: flex;
3 | flex: 1;
4 | }
5 |
6 | .container {
7 | display: flex;
8 | }
9 |
10 | .footerSignature {
11 | margin: auto auto 0 auto;
12 | font-family: 'PT Root UI' !important;
13 | font-weight: 500;
14 | font-size: 18px;
15 | padding-bottom: 4rem;
16 | padding-top: 2rem;
17 | color: var(--brand-black) !important;
18 | display: flex;
19 | flex-flow: row wrap;
20 | align-items: center;
21 | justify-content: flex-start;
22 | }
23 |
24 | .footerSignature a {
25 | text-decoration: none;
26 | color: black;
27 | margin: 8px 14px;
28 | transition: all 0.15s ease-in-out;
29 | }
30 |
31 | .footerSignature a:hover {
32 | text-decoration: none;
33 | font-weight: bold;
34 | }
35 |
36 | .footerSignature img {
37 | width: 32px;
38 | height: 32px;
39 | margin: 0 8px;
40 | }
41 |
42 | @media (max-width: 992px) {
43 | .footerSignature a {
44 | font-size: 16px;
45 | }
46 | }
47 |
48 | @keyframes slidein {
49 | from {
50 | margin-left: 100%;
51 | width: 300%;
52 | }
53 |
54 | to {
55 | margin-left: 0%;
56 | width: 100%;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/components/ProfileActivityFeedToggle/index.tsx:
--------------------------------------------------------------------------------
1 | import { faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons';
2 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
3 | import { Trans } from '@lingui/macro';
4 | import React from 'react';
5 | import classes from './ProfileActivityFeedToggle.module.css';
6 |
7 | interface ProfileActivityFeedToggleProps {
8 | isExpanded: boolean;
9 | numEvents: number;
10 | toggleCallback: () => void;
11 | }
12 |
13 | const ProfileActivityFeedToggle: React.FC = props => {
14 | const { isExpanded, numEvents, toggleCallback } = props;
15 |
16 | if (isExpanded) {
17 | return (
18 |
19 | Show fewer
20 |
21 | );
22 | }
23 |
24 | return (
25 |
26 | Show all {numEvents} events
27 |
28 | );
29 | };
30 |
31 | export default ProfileActivityFeedToggle;
32 |
--------------------------------------------------------------------------------
/src/components/profileEvent/eventData/ProposalVoteInfoPillsContainer/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useProposalStatus } from '../../../../hooks/useProposalStatus';
3 | import { Proposal } from '../../../../wrappers/nounsDao';
4 | import ProposalStatusCopy from '../../../ProposalStatusCopy';
5 | import VoteStatusPill from '../../../VoteStatusPill';
6 | import classes from './ProposalVoteInfoPills.module.css';
7 |
8 | interface ProposalVoteInfoPillsContainerProps {
9 | proposal: Proposal;
10 | }
11 |
12 | const ProposalVoteInfoPillsContainer: React.FC = props => {
13 | const { proposal } = props;
14 |
15 | const proposalStatus = useProposalStatus(proposal);
16 |
17 | return (
18 |
19 |
20 |
21 | }
24 | />
25 |
26 |
27 |
28 | );
29 | };
30 |
31 | export default ProposalVoteInfoPillsContainer;
32 |
--------------------------------------------------------------------------------
/src/utils/colorResponsiveUIUtils.ts:
--------------------------------------------------------------------------------
1 | import { useAppSelector } from '../hooks';
2 |
3 | export const shouldUseStateBg = (history: any) => {
4 | return (
5 | history.location.pathname === '/' ||
6 | history.location.pathname.includes('/noun') ||
7 | history.location.pathname.includes('/auction')
8 | );
9 | };
10 |
11 | /**
12 | * Utility function that takes three items and returns whichever one corresponds to the current
13 | * page state (white, cool, warm)
14 | * @param whiteState What to return if the state is white
15 | * @param coolState What to return if the state is cool
16 | * @param warmState What to return is the state is warm
17 | * @param history History object from useHistory
18 | * @returns item corresponding to current state
19 | */
20 | export const usePickByState = (whiteState: any, coolState: any, warmState: any, history: any) => {
21 | const useStateBg = shouldUseStateBg(history);
22 | const isCoolState = useAppSelector(state => state.application.isCoolBackground);
23 |
24 | if (!useStateBg) {
25 | return whiteState;
26 | }
27 | if (isCoolState) {
28 | return coolState;
29 | }
30 | return warmState;
31 | };
32 |
--------------------------------------------------------------------------------
/src/components/ProfileActivityFeed/ProfileActivityFeed.module.css:
--------------------------------------------------------------------------------
1 | .headerWrapper h1 {
2 | font-family: 'Londrina Solid';
3 | font-size: 32px;
4 | }
5 |
6 | .headerWrapper p {
7 | margin: 3rem 0;
8 | }
9 |
10 | @media (min-width: 992px) {
11 | .headerWrapper {
12 | padding: 1rem;
13 | display: block;
14 | padding-bottom: 16px;
15 | }
16 | }
17 |
18 | .nounInfoPadding {
19 | padding-bottom: 1rem;
20 | font-size: 18px;
21 | }
22 |
23 | .expandCollapseCopy {
24 | color: var(--brand-cool-light-text);
25 | font-family: 'PT Root UI';
26 | font-weight: bold;
27 | font-size: 20px;
28 | text-align: center;
29 | cursor: pointer;
30 | margin-top: 1rem;
31 | }
32 |
33 | .aboveTheFoldEventsTable {
34 | margin-bottom: 0rem;
35 | }
36 |
37 | .aboveTheFoldEventsTable td {
38 | border-top: 1px solid rgba(0, 0, 0, 0.1);
39 | padding-top: 12px;
40 | }
41 |
42 | .nullStateCopy {
43 | opacity: 0.5;
44 | margin-left: 1rem;
45 | font-family: 'PT Root UI';
46 | font-weight: 500;
47 | font-size: 18px;
48 | }
49 |
50 | .spinner {
51 | display: flex;
52 | justify-content: center;
53 | margin-top: 3rem;
54 | color: var(--brand-gray-light-text);
55 | }
56 |
--------------------------------------------------------------------------------
/src/components/profileEvent/eventData/infoPills/DelegatePill/index.tsx:
--------------------------------------------------------------------------------
1 | import { ScaleIcon } from '@heroicons/react/solid';
2 | import React from 'react';
3 | import ReactTooltip from 'react-tooltip';
4 | import ShortAddress from '../../../../ShortAddress';
5 | import classes from './DelegatePill.module.css';
6 |
7 | interface DelegatePillProps {
8 | address: string;
9 | proposalId: string;
10 | }
11 |
12 | const DelegatePill: React.FC = props => {
13 | const { address, proposalId } = props;
14 |
15 | return (
16 |
17 |
{
22 | return {dataTip}
;
23 | }}
24 | />
25 |
30 |
31 |
32 |
33 |
34 | );
35 | };
36 |
37 | export default DelegatePill;
38 |
--------------------------------------------------------------------------------
/src/components/NounInfoRowHolder/NounInfoRowHolder.module.css:
--------------------------------------------------------------------------------
1 | .nounHolderEtherscanLinkCool {
2 | font-family: 'PT Root UI';
3 | font-weight: bold;
4 | color: var(--brand-color-blue);
5 | margin-left: 5px;
6 | text-decoration: none;
7 | transition: all 0.15s ease-in-out;
8 | }
9 |
10 | .nounHolderEtherscanLinkWarm {
11 | font-family: 'PT Root UI';
12 | font-weight: bold;
13 | color: var(--brand-color-red);
14 | margin-left: 5px;
15 | text-decoration: none;
16 | transition: all 0.15s ease-in-out;
17 | }
18 |
19 | .nounHolderEtherscanLinkWarm:hover {
20 | color: var(--brand-color-red) !important;
21 | filter: brightness(110%);
22 | }
23 |
24 | .nounHolderEtherscanLinkCool:hover {
25 | color: var(--brand-color-blue) !important;
26 | filter: brightness(110%);
27 | }
28 |
29 | .nounHolderInfoContainer {
30 | display: inline;
31 | width: 350px;
32 | height: 35px;
33 | }
34 |
35 | .nounHolderLoading {
36 | opacity: 0.5;
37 | }
38 |
39 | .linkIconSpan {
40 | margin-left: 5px;
41 | }
42 |
43 | .linkIcon {
44 | margin-bottom: 5px;
45 | }
46 |
47 | .heartIcon {
48 | margin-bottom: 4px;
49 | margin-right: 7px;
50 | }
51 |
52 | img {
53 | display: inline;
54 | }
55 |
--------------------------------------------------------------------------------
/src/utils/logParsing.ts:
--------------------------------------------------------------------------------
1 | export interface EventFilter {
2 | address?: string;
3 | topics?: Array | null>;
4 | fromBlock?: number;
5 | }
6 |
7 | export interface Log {
8 | topics: Array;
9 | transactionHash: string;
10 | data: string;
11 | }
12 |
13 | /**
14 | * Converts a filter to the corresponding string key
15 | * @param filter the filter to convert
16 | */
17 | export const filterToKey = (filter: EventFilter): string => {
18 | return `${filter.address ?? ''}:${
19 | filter.topics
20 | ?.map(topic => (topic ? (Array.isArray(topic) ? topic.join(';') : topic) : '\0'))
21 | ?.join('-') ?? ''
22 | }`;
23 | };
24 |
25 | /**
26 | * Convert a filter key to the corresponding filter
27 | * @param key key to convert
28 | */
29 | export const keyToFilter = (key: string): EventFilter => {
30 | const pcs = key.split(':');
31 | const address = pcs[0];
32 | const topics = pcs[1].split('-').map(topic => {
33 | const parts = topic.split(';');
34 | if (parts.length === 1) return parts[0];
35 | return parts;
36 | });
37 |
38 | return {
39 | address: address.length === 0 ? undefined : address,
40 | topics,
41 | };
42 | };
43 |
--------------------------------------------------------------------------------
/src/components/VoteProgressBar/index.tsx:
--------------------------------------------------------------------------------
1 | import clsx from 'clsx';
2 | import { VoteCardVariant } from '../VoteCard';
3 | import classes from './VoteProgressBar.module.css';
4 |
5 | const VoteProgressBar: React.FC<{
6 | variant: VoteCardVariant;
7 | percentage: number;
8 | }> = props => {
9 | const { variant, percentage } = props;
10 |
11 | let progressBarClass;
12 | let wrapperClass;
13 | switch (variant) {
14 | case VoteCardVariant.FOR:
15 | progressBarClass = classes.forProgressBar;
16 | wrapperClass = classes.forWrapper;
17 | break;
18 | case VoteCardVariant.AGAINST:
19 | progressBarClass = classes.againstProgressBar;
20 | wrapperClass = classes.againstWrapper;
21 | break;
22 | default:
23 | progressBarClass = classes.abstainProgressBar;
24 | wrapperClass = classes.abstainWrapper;
25 | break;
26 | }
27 |
28 | return (
29 |
37 | );
38 | };
39 |
40 | export default VoteProgressBar;
41 |
--------------------------------------------------------------------------------
/src/pages/Nounders/NoundersPage.module.css:
--------------------------------------------------------------------------------
1 | .noundersPage h1,
2 | .noundersPage h2,
3 | .noundersPage h3 {
4 | font-family: 'Londrina Solid';
5 | }
6 |
7 | .noundersPage h2 {
8 | margin-bottom: 2rem;
9 | }
10 |
11 | .noundersPage a {
12 | color: var(--brand-black);
13 | }
14 | .noundersPage a:hover {
15 | color: var(--brand-dark-red);
16 | }
17 |
18 | .bioGroup {
19 | padding-right: 5px;
20 | padding-left: 5px;
21 | }
22 | .bioGroup a {
23 | font-size: 1rem;
24 | }
25 |
26 | .noundersPage img {
27 | border-radius: 50%;
28 | width: 50%;
29 | }
30 |
31 | .marginBottom {
32 | margin-bottom: 0rem;
33 | }
34 |
35 | .bioGroup {
36 | margin-bottom: 2rem;
37 | text-align: center;
38 | }
39 | .bioGroup a:hover svg {
40 | fill: var(--brand-dark-red);
41 | }
42 | .bioGroup img {
43 | margin-bottom: 1rem;
44 | }
45 |
46 | .twitterIcon {
47 | width: 20px;
48 | color: #aaa;
49 | margin-right: 5px;
50 | }
51 |
52 | .card {
53 | border: none;
54 | }
55 |
56 | .card,
57 | .headerWrapper {
58 | font-size: 1.3rem;
59 | }
60 | .cardHeader {
61 | border: none;
62 | background-color: transparent;
63 | font-family: 'Londrina Solid';
64 | font-size: 2.5rem;
65 | cursor: pointer;
66 | }
67 |
--------------------------------------------------------------------------------
/src/wrappers/nounsStream.ts:
--------------------------------------------------------------------------------
1 | import { useContractCall, useContractFunction, useEthers } from '@usedapp/core';
2 | import { utils, BigNumber, Contract } from 'ethers';
3 | import streamABI from '../utils/streamingPaymentUtils/stream.abi.json';
4 |
5 | const abi = new utils.Interface(streamABI);
6 |
7 | export const useStreamRemainingBalance = (streamAddress: string) => {
8 | const [balance] =
9 | useContractCall<[BigNumber]>({
10 | abi,
11 | address: streamAddress,
12 | method: 'recipientBalance',
13 | }) || [];
14 |
15 | return balance?.toString();
16 | };
17 |
18 | export const useWithdrawTokens = (streamAddress: string) => {
19 | const { library } = useEthers();
20 | const { send: withdrawTokens, state: withdrawTokensState } = useContractFunction(
21 | new Contract(streamAddress, abi, library),
22 | 'withdraw',
23 | );
24 | return { withdrawTokens, withdrawTokensState };
25 | };
26 |
27 | export const useElapsedTime = (streamAddress: string) => {
28 | const [elapsedTime] =
29 | useContractCall<[BigNumber]>({
30 | abi,
31 | address: streamAddress,
32 | method: 'elapsedTime',
33 | }) || [];
34 |
35 | return elapsedTime?.toNumber();
36 | };
37 |
--------------------------------------------------------------------------------
/src/components/Noun/Noun.module.css:
--------------------------------------------------------------------------------
1 | .img {
2 | image-rendering: pixelated;
3 | image-rendering: -moz-crisp-edges;
4 | position: absolute;
5 | top: 0;
6 | left: 0;
7 | width: 100%;
8 | height: auto;
9 | vertical-align: middle;
10 | border-radius: 50%;
11 | /* border: thick solid #7293ff;
12 | filter: drop-shadow(rgb(255, 255, 255) 0px 0px 9px); */
13 | border: 8px solid rgb(221, 216, 231);
14 | filter: drop-shadow(rgb(221, 216, 231) 0px 0px 15px);
15 | }
16 |
17 | .imgWrapper {
18 | position: relative;
19 | padding-top: 100%;
20 | width: 100%;
21 | height: 0;
22 | }
23 |
24 | .circular {
25 | border-radius: 50%;
26 | }
27 |
28 | .delegateViewCircular {
29 | border-radius: 50%;
30 | margin-top: 13px;
31 | }
32 |
33 | .circleWithBorder {
34 | border-radius: 50%;
35 | border: 2px solid #ffffff;
36 | }
37 |
38 | .rounded {
39 | border-radius: 15px;
40 | }
41 |
42 | .circularNounWrapper {
43 | width: 42px;
44 | height: 42px;
45 | }
46 |
47 | .delegateViewCircularNounWrapper {
48 | width: 42px;
49 | height: 42px;
50 | margin-left: 12px;
51 | }
52 |
53 | @media (max-width: 1200px) {
54 | .circularNounWrapper {
55 | height: 70%;
56 | width: 70%;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/components/ProposalStatusCopy/index.tsx:
--------------------------------------------------------------------------------
1 | import { Trans } from '@lingui/macro';
2 | import React from 'react';
3 | import { Proposal, ProposalState } from '../../wrappers/nounsDao';
4 |
5 | interface ProposalStatusCopyProps {
6 | proposal: Proposal;
7 | }
8 |
9 | const ProposalStatusCopy: React.FC = props => {
10 | const { proposal } = props;
11 | switch (proposal.status) {
12 | case ProposalState.PENDING:
13 | return Pending;
14 | case ProposalState.ACTIVE:
15 | return Active;
16 | case ProposalState.SUCCEEDED:
17 | return Succeeded;
18 | case ProposalState.EXECUTED:
19 | return Executed;
20 | case ProposalState.DEFEATED:
21 | return Defeated;
22 | case ProposalState.QUEUED:
23 | return Queued;
24 | case ProposalState.CANCELLED:
25 | return Canceled;
26 | case ProposalState.VETOED:
27 | return Vetoed;
28 | case ProposalState.EXPIRED:
29 | return Expired;
30 | default:
31 | return Undetermined;
32 | }
33 | };
34 |
35 | export default ProposalStatusCopy;
36 |
--------------------------------------------------------------------------------
/src/state/slices/application.ts:
--------------------------------------------------------------------------------
1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit';
2 | import { ReactNode } from 'react';
3 | import { grey } from '../../utils/nounBgColors';
4 |
5 | export interface AlertModal {
6 | show: boolean;
7 | title?: ReactNode;
8 | message?: ReactNode;
9 | }
10 |
11 | interface ApplicationState {
12 | stateBackgroundColor: string;
13 | isCoolBackground: boolean;
14 | alertModal: AlertModal;
15 | }
16 |
17 | const initialState: ApplicationState = {
18 | stateBackgroundColor: grey,
19 | isCoolBackground: true,
20 | alertModal: {
21 | show: false,
22 | },
23 | };
24 |
25 | export const applicationSlice = createSlice({
26 | name: 'application',
27 | initialState,
28 | reducers: {
29 | setStateBackgroundColor: (state, action: PayloadAction) => {
30 | state.stateBackgroundColor = grey;
31 | state.isCoolBackground = /* action.payload === grey */true;
32 | },
33 | setAlertModal: (state, action: PayloadAction) => {
34 | state.alertModal = action.payload;
35 | },
36 | },
37 | });
38 |
39 | export const { setStateBackgroundColor, setAlertModal } = applicationSlice.actions;
40 |
41 | export default applicationSlice.reducer;
42 |
--------------------------------------------------------------------------------
/src/utils/lookupNNSOrENS.ts:
--------------------------------------------------------------------------------
1 | import { Web3Provider } from '@ethersproject/providers';
2 | import { BigNumber as EthersBN, utils } from 'ethers';
3 |
4 | /**
5 | * Look up either NNS or ENS (using NNS contract to resolve NNS with ENS fallback)
6 | * @param library provider
7 | * @param address Address to resolve
8 | * @returns NNS or ENS or null (if neither resolve)
9 | */
10 | export async function lookupNNSOrENS(
11 | library: Web3Provider,
12 | address: string,
13 | ): Promise {
14 | try {
15 | // Call resolver contract
16 | const res = await library.call({
17 | to: '0x849f92178950f6254db5d16d1ba265e70521ac1b', // see https://etherscan.io/address/0x849f92178950f6254db5d16d1ba265e70521ac1b
18 | data: `0x55ea6c47000000000000000000000000${address.substring(2)}`, // call .resolve(address) method
19 | });
20 | // Parse result into a string.
21 | const offset = EthersBN.from(utils.hexDataSlice(res, 0, 32)).toNumber();
22 | const length = EthersBN.from(utils.hexDataSlice(res, offset, offset + 32)).toNumber();
23 | const data = utils.hexDataSlice(res, offset + 32, offset + 32 + length);
24 | return utils.toUtf8String(data) || null;
25 | } catch (e) {
26 | return null;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/utils/moderation/containsBlockedText.ts:
--------------------------------------------------------------------------------
1 | import moderationRegexes from './moderationRegexes.json';
2 | import Filter from 'bad-words';
3 |
4 | /**
5 | * Check if text matches a blocked phrase in a given langugae
6 | *
7 | * @param text
8 | * @param language
9 | * @returns boolean true iif text matches a blocked word of phrase
10 | */
11 | export const containsBlockedText = (text: string, language: string) => {
12 | // Get modearation regexes for language
13 | const regexesForLanguage = new Map(Object.entries(moderationRegexes)).get(language);
14 | // Default to letting the string through if the language is unsupprted
15 | if (regexesForLanguage === undefined) {
16 | console.log(`Unsupported language ${language} requested`);
17 | return false;
18 | }
19 |
20 | // Filter based on bad-words profanity filters
21 | const filter = new Filter();
22 | if (filter.isProfane(text)) {
23 | return true;
24 | }
25 |
26 | // Filter based on custom regexes
27 | return (
28 | regexesForLanguage
29 | .map((entry: { regex: string }) => {
30 | const regex = entry.regex;
31 | return text.match(regex) !== null;
32 | })
33 | .filter((isRegexMatch: boolean) => isRegexMatch).length > 0
34 | );
35 | };
36 |
--------------------------------------------------------------------------------
/src/utils/ensAvatar.ts:
--------------------------------------------------------------------------------
1 | import { useEthers } from '@usedapp/core';
2 | import { useEffect, useState } from 'react';
3 |
4 | export const useEnsAvatarLookup = (address: string) => {
5 | const { library } = useEthers();
6 | const [ensAvatar, setEnsAvatar] = useState();
7 |
8 | useEffect(() => {
9 | let mounted = true;
10 | if (address && library) {
11 | library
12 | .lookupAddress(address)
13 | .then(name => {
14 | if (!name) return;
15 | library.getResolver(name).then(resolver => {
16 | if (!resolver) return;
17 | resolver
18 | .getText('avatar')
19 | .then(avatar => {
20 | if (mounted) {
21 | setEnsAvatar(avatar);
22 | }
23 | })
24 | .catch(error => {
25 | console.log(`error resolving ens avatar: `, error);
26 | });
27 | });
28 | })
29 | .catch(error => {
30 | console.log(`error resolving reverse ens lookup: `, error);
31 | });
32 | }
33 |
34 | return () => {
35 | setEnsAvatar('');
36 | mounted = false;
37 | };
38 | }, [address, library]);
39 |
40 | return ensAvatar;
41 | };
42 |
--------------------------------------------------------------------------------
/src/assets/icons/NoVote.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/components/profileEvent/activityRow/DesktopNounActivityRow/index.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react-markdown/lib/react-markdown';
2 | import classes from './DesktopNounActivityRow.module.css';
3 | import responsiveUiUtilsClasses from '../../../../utils/ResponsiveUIUtils.module.css';
4 | import { useActiveLocale } from '../../../../hooks/useActivateLocale';
5 |
6 | interface DesktopNounActivityRowProps {
7 | icon: ReactNode;
8 | primaryContent: ReactNode;
9 | secondaryContent?: ReactNode;
10 | }
11 |
12 | const DesktopNounActivityRow: React.FC = props => {
13 | const { icon, primaryContent, secondaryContent } = props;
14 |
15 | const activeLocale = useActiveLocale();
16 |
17 | return (
18 |
19 | | {icon} |
20 |
21 | {primaryContent}
22 | |
23 |
24 |
25 | {secondaryContent}
26 |
27 | |
28 |
29 | );
30 | };
31 |
32 | export default DesktopNounActivityRow;
33 |
--------------------------------------------------------------------------------
/src/css/colors.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --brand-dark-blue-text: #2a7aa1;
3 | --brand-warm-blue-text: #3093c3;
4 | --brand-dark-white-text: #edf2f0;
5 | --brand-warm-white-text: #fff;
6 | --brand-bg-green: #edf2f0;
7 | --brand-dark-red: #d63c5e;
8 | --brand-light-green: #6da886;
9 | --brand-black: #212529;
10 | /* Colors from Figma */
11 | --brand-cool-background: #d5d7e1;
12 | --brand-cool-border: rgb(189, 192, 207);
13 | --brand-cool-dark-text: #151c3b;
14 | --brand-cool-light-text: #79809c;
15 | --brand-cool-accent: #e9ebf3;
16 | --brand-warm-background: #e1d7d5;
17 | --brand-warm-border: rgb(207, 189, 186);
18 | --brand-warm-dark-text: #221b1a;
19 | --brand-warm-light-text: #8f7e7c;
20 | --brand-warm-accent: #f9f1f1;
21 | --brand-gray-dark-text: #14161b;
22 | --brand-gray-border: #e2e3eb;
23 | --brand-gray-background: #f4f4f8;
24 | --brand-gray-light-text: #8c8d92;
25 | --brand-gray-light-text-translucent: rgb(140, 141, 146, 0.1);
26 | --brand-gray-hover: #fafafb;
27 | --brand-color-red: #a54646;
28 | --brand-color-blue: #4965f0;
29 | --brand-color-green: #43b369;
30 | --brand-color-red-translucent: rgba(214, 60, 94, 0.1);
31 | --brand-color-blue-translucent: rgba(73, 101, 240, 0.1);
32 | --brand-color-green-translucent: rgba(67, 179, 105, 0.1);
33 | --brand-color-blue-darker: #3a52c7;
34 | }
35 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ordauction-frontend
2 |
3 | This package contains the source for the Nouns webapp at [nouns.wtf](https://nouns.wtf).
4 |
5 | ## Quickstart
6 |
7 | _From the base of the `nouns-monorepo`_
8 |
9 | In the first shell:
10 |
11 | ```sh
12 | # Install all dependencies and build contract artifacts
13 | yarn
14 | # Switch to nouns-contracts
15 | cd packages/nouns-contracts
16 | # Start local simnet
17 | yarn task:run-local
18 | ```
19 |
20 | In the second shell:
21 |
22 | ```sh
23 | # Switch to nouns-webapp
24 | cd packages/nouns-webapp
25 | # Copy local example environment file
26 | cp .env.example.local .env
27 | # Start local development
28 | yarn start
29 | ```
30 |
31 | ### MetaMask
32 |
33 | Interact with the local simnet by importing the following private key into MetaMask. _Do not use this private key anywhere else_
34 |
35 | ```
36 | Private Key:
37 | 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
38 | ```
39 |
40 | Then add an RPC provider to MetaMask to point to your local simnet.
41 |
42 | 1. Navigate to settings
43 | 2. Select `Networks`
44 | 3. Click `Add Network`
45 | 4. Enter the following:
46 |
47 | Network Name: Hardhat
48 | New RPC URL: http://localhost:8545
49 | Chain ID: 31337
50 |
51 | Select the network and connect to your local Nouns webapp to interact with simnet
52 |
--------------------------------------------------------------------------------
/src/components/ModalBottomButtonRow/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import NavBarButton, { NavBarButtonStyle } from '../NavBarButton';
3 | import classes from './ModalBottomButtonRow.module.css';
4 |
5 | export interface ModalBottomButtonRowProps {
6 | onPrevBtnClick: (e?: any) => void;
7 | onNextBtnClick: (e?: any) => void;
8 | prevBtnText: React.ReactNode;
9 | nextBtnText: React.ReactNode;
10 | isNextBtnDisabled?: boolean;
11 | }
12 |
13 | const ModalBottomButtonRow: React.FC = props => {
14 | const {
15 | onPrevBtnClick,
16 | onNextBtnClick,
17 | prevBtnText,
18 | nextBtnText,
19 | isNextBtnDisabled = false,
20 | } = props;
21 |
22 | return (
23 |
24 |
29 |
39 |
40 | );
41 | };
42 |
43 | export default ModalBottomButtonRow;
44 |
--------------------------------------------------------------------------------
/src/hooks/useTreasuryBalance.ts:
--------------------------------------------------------------------------------
1 | import { useEtherBalance } from '@usedapp/core';
2 | import useLidoBalance from './useLidoBalance';
3 | import useTokenBuyerBalance from './useTokenBuyerBalance';
4 | import { useCoingeckoPrice } from '@usedapp/coingecko';
5 | import config from '../config';
6 | import { BigNumber, ethers } from 'ethers';
7 |
8 | /**
9 | * Computes treasury balance (ETH + Lido)
10 | *
11 | * @returns Total balance of treasury (ETH + Lido) as EthersBN
12 | */
13 | export const useTreasuryBalance = () => {
14 | const ethBalance = useEtherBalance(config.addresses.nounsDaoExecutor);
15 | const lidoBalanceAsETH = useLidoBalance();
16 | const tokenBuyerBalanceAsETH = useTokenBuyerBalance();
17 |
18 | const zero = BigNumber.from(0);
19 | return ethBalance?.add(lidoBalanceAsETH ?? zero).add(tokenBuyerBalanceAsETH ?? zero) ?? zero;
20 | };
21 |
22 | /**
23 | * Computes treasury usd value of treasury assets (ETH + Lido) at current ETH-USD exchange rate
24 | *
25 | * @returns USD value of treasury assets (ETH + Lido) at current exchange rate
26 | */
27 | export const useTreasuryUSDValue = () => {
28 | const etherPrice = Number(useCoingeckoPrice('ethereum', 'usd'));
29 | const treasuryBalanceETH = Number(
30 | ethers.utils.formatEther(useTreasuryBalance()?.toString() || '0'),
31 | );
32 | return etherPrice * treasuryBalanceETH;
33 | };
34 |
--------------------------------------------------------------------------------
/src/hooks/useKeyPress.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 |
3 | /**
4 | * A function that listens for specific keyboard keys and returns a boolean if that key is pressed
5 | * @param targetKey keyboard key to listen to
6 | * @returns boolean value of specified key press state
7 | */
8 | export function useKeyPress(targetKey: KeyboardEvent['key']) {
9 | // State for keeping track of whether key is pressed
10 | const [keyPressed, setKeyPressed] = useState(false);
11 |
12 | // Add event listeners
13 | useEffect(() => {
14 | // If pressed key is our target key then set to true
15 | function downHandler({ key }: KeyboardEvent) {
16 | if (key === targetKey) {
17 | setKeyPressed(true);
18 | }
19 | }
20 | // If released key is our target key then set to false
21 | const upHandler = ({ key }: KeyboardEvent) => {
22 | if (key === targetKey) {
23 | setKeyPressed(false);
24 | }
25 | };
26 |
27 | window.addEventListener('keydown', downHandler);
28 | window.addEventListener('keyup', upHandler);
29 | // Remove event listeners on cleanup
30 | return () => {
31 | window.removeEventListener('keydown', downHandler);
32 | window.removeEventListener('keyup', upHandler);
33 | };
34 | }, [targetKey]); // rerun the effect if the targetKey changes
35 |
36 | return keyPressed;
37 | }
38 |
--------------------------------------------------------------------------------
/src/components/profileEvent/eventData/infoPills/DelegatePill/DelegatePill.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | display: flex;
3 | flex-flow: row nowrap;
4 | justify-content: flex-end;
5 | align-items: center;
6 | }
7 |
8 | .delegateHover {
9 | border-radius: 8px !important;
10 | background-color: var(--brand-gray-dark-text) !important;
11 | color: white;
12 | opacity: 0.75 !important;
13 | font-weight: 500;
14 | transition: ease-in-out 125ms;
15 | }
16 |
17 | .pill {
18 | background-color: var(--brand-gray-light-text-translucent);
19 | color: var(--brand-gray-light-text);
20 | height: 28px;
21 | border-radius: 8px;
22 | margin-top: 5px;
23 | margin-bottom: 5px;
24 | font-family: 'PT Root UI';
25 | font-weight: bold;
26 | vertical-align: middle;
27 | text-align: center;
28 | display: flex;
29 | flex-direction: row;
30 | justify-content: center;
31 | align-items: center;
32 | padding: 0px 8px;
33 | font-size: 14px;
34 | width: fit-content;
35 | margin-right: 0.5rem;
36 | transition: all 0.15s ease-in-out;
37 | }
38 |
39 | .pill:hover {
40 | transform: scale(0.95);
41 | }
42 |
43 | .icon {
44 | height: 16px;
45 | width: 16px;
46 | margin-right: 0.3rem;
47 | margin-left: 0.1rem;
48 | margin-top: 0.05rem;
49 | }
50 |
51 | @media (max-width: 992px) {
52 | .icon {
53 | margin-top: 0;
54 | margin-bottom: 0.12rem;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/components/ProposalContent/ProposalContent.module.css:
--------------------------------------------------------------------------------
1 | .section {
2 | word-wrap: break-word;
3 | padding-top: 2rem;
4 | margin-top: 2rem;
5 | }
6 |
7 | .section h5 {
8 | font-size: 1.7rem;
9 | margin-top: 1rem;
10 | font-family: 'Londrina Solid';
11 | }
12 |
13 | .markdown {
14 | font-family: 'PT Root UI';
15 | font-size: 1.1rem;
16 | }
17 |
18 | .markdown h1 {
19 | font-size: 1.7rem;
20 | margin-top: 1rem;
21 | font-weight: bold;
22 | }
23 |
24 | .markdown h2 {
25 | font-size: 1.5rem;
26 | margin-top: 1rem;
27 | font-weight: bold;
28 | }
29 |
30 | .markdown h3 {
31 | font-weight: bold;
32 | font-size: 1.3rem;
33 | }
34 |
35 | .markdown img {
36 | max-width: 100%;
37 | height: auto;
38 | }
39 |
40 | .txnInfoText {
41 | color: var(--brand-gray-light-text);
42 | margin-left: -0.1rem;
43 | margin-top: 0.25rem;
44 | margin-bottom: 0.25rem;
45 | font-weight: 500;
46 | font-size: 16px;
47 | display: flex;
48 | align-items: center;
49 | }
50 |
51 | @media (max-width: 992px) {
52 | .txnInfoText {
53 | align-items: flex-start;
54 | margin-top: 1rem;
55 | }
56 |
57 | .txnInfoIcon {
58 | margin-top: 0.25rem;
59 | margin-right: 0.5rem;
60 | }
61 | }
62 |
63 | .txnInfoIcon {
64 | height: 18px;
65 | width: 18px;
66 | opacity: 0.5;
67 | }
68 |
69 | .txnInfoIconWrapper {
70 | width: 25px;
71 | display: flex;
72 | align-items: center;
73 | }
74 |
--------------------------------------------------------------------------------
/functions/v0-proposal-votes/index.ts:
--------------------------------------------------------------------------------
1 | import { Handler } from '@netlify/functions';
2 | import { NormalizedNoun, NormalizedVote, nounsQuery } from '../theGraph';
3 | import { sharedResponseHeaders } from '../utils';
4 |
5 | interface ProposalVote {
6 | nounId: number;
7 | owner: string;
8 | delegatedTo: null | string;
9 | supportDetailed: number;
10 | }
11 |
12 | interface ProposalVotes {
13 | [key: number]: ProposalVote[];
14 | }
15 |
16 | const builtProposalVote = (noun: NormalizedNoun, vote: NormalizedVote): ProposalVote => ({
17 | nounId: noun.id,
18 | owner: noun.owner,
19 | delegatedTo: noun.delegatedTo,
20 | supportDetailed: vote.supportDetailed,
21 | });
22 |
23 | const reduceProposalVotes = (nouns: NormalizedNoun[]) =>
24 | nouns.reduce((acc: ProposalVotes, noun: NormalizedNoun) => {
25 | for (let i in noun.votes) {
26 | const vote = noun.votes[i];
27 | if (!acc[vote.proposalId]) acc[vote.proposalId] = [];
28 | acc[vote.proposalId].push(builtProposalVote(noun, vote));
29 | }
30 | return acc;
31 | }, {});
32 |
33 | const handler: Handler = async (event, context) => {
34 | const nouns = await nounsQuery();
35 | return {
36 | statusCode: 200,
37 | headers: {
38 | 'Content-Type': 'application/json',
39 | ...sharedResponseHeaders,
40 | },
41 | body: JSON.stringify(reduceProposalVotes(nouns)),
42 | };
43 | };
44 |
45 | export { handler };
46 |
--------------------------------------------------------------------------------
/src/components/DelegateHoverCard/DelegateHoverCard.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | display: flex;
3 | flex-direction: column;
4 | max-width: 11rem;
5 | }
6 |
7 | .stackedNounWrapper {
8 | display: flex;
9 | }
10 |
11 | .address {
12 | font-family: 'Londrina Solid';
13 | font-size: 24px;
14 | width: 100%;
15 | text-align: left;
16 | }
17 |
18 | .nounsRepresented {
19 | color: var(--brand-gray-dark-text);
20 | display: flex;
21 | margin-top: 0.25rem;
22 | font-weight: 500;
23 | font-size: 15px;
24 | }
25 |
26 | .bold {
27 | font-weight: bold;
28 | margin-right: 0.25rem;
29 | margin-left: 0.25rem;
30 | }
31 |
32 | .spinnerWrapper {
33 | color: var(--brand-gray-light-text);
34 | width: 100%;
35 | height: 185px;
36 | display: flex;
37 | flex-direction: column;
38 | justify-content: center;
39 | }
40 |
41 | .spinner {
42 | display: flex;
43 | width: 100%;
44 | justify-content: center;
45 | }
46 |
47 | .nounInfoWrapper {
48 | margin-top: 0.25rem;
49 | margin-bottom: 0.75rem;
50 | font-style: normal;
51 | font-weight: 500;
52 | font-size: 15px;
53 | line-height: 140%;
54 | /* identical to box height, or 21px */
55 |
56 | display: flex;
57 | align-items: center;
58 | font-feature-settings: 'tnum' on, 'lnum' on, 'ss06' on, 'ss01' on, 'liga' off;
59 |
60 | color: var(--brand-gray-dark-text);
61 | }
62 |
63 | .icon {
64 | margin-bottom: 5px;
65 | margin-right: 6px;
66 | }
67 |
--------------------------------------------------------------------------------
/src/utils/cssTransitionUtils.ts:
--------------------------------------------------------------------------------
1 | import { TransitionStyles } from '../components/NounsTransition';
2 |
3 | export const basicFadeInOut = {
4 | enteringStyle: { opacity: 1 },
5 | enteredStyle: { opacity: 1 },
6 | exitingStyle: { opacity: 0.5 },
7 | exitedStyle: { opacity: 0 },
8 | } as TransitionStyles;
9 |
10 | export const mobileModalSlideInFromBottm = {
11 | enteringStyle: {
12 | opacity: 1,
13 | transform: 'translateY(0rem) scale(1)',
14 | transition: 'opacity 100ms, transform 100ms',
15 | },
16 | enteredStyle: {
17 | opacity: 1,
18 | transform: 'translateY(0) scale(1)',
19 | transition: 'opacity 100ms, transform 100ms',
20 | },
21 | exitingStyle: {
22 | opacity: 0,
23 | transform: 'translateY(20rem) scale(0.9)',
24 | transition: 'opacity 100ms, transform 100ms',
25 | },
26 | exitedStyle: { opacity: 0 },
27 | };
28 |
29 | export const desktopModalSlideInFromTopAndGrow = {
30 | enteringStyle: {
31 | opacity: 1,
32 | transform: 'translateY(0rem) scale(1)',
33 | transition: 'opacity 100ms, transform 100ms',
34 | },
35 | enteredStyle: {
36 | opacity: 1,
37 | transform: 'translateY(0) scale(1)',
38 | transition: 'opacity 100ms, transform 100ms',
39 | },
40 | exitingStyle: {
41 | opacity: 0,
42 | transform: 'translateY(-1rem) scale(0)',
43 | transition: 'opacity 100ms, transform 100ms',
44 | },
45 | exitedStyle: { opacity: 0 },
46 | };
47 |
--------------------------------------------------------------------------------
/src/components/NounProfileVoteRow/NounProfileVoteRow.module.css:
--------------------------------------------------------------------------------
1 | .delegateHover {
2 | border-radius: 8px !important;
3 | background-color: var(--brand-gray-dark-text) !important;
4 | color: white;
5 | opacity: 0.75 !important;
6 | font-weight: 500;
7 | transition: ease-in-out 125ms;
8 | }
9 |
10 | .voteIcon {
11 | width: 38px;
12 | margin-right: 0px;
13 | margin-bottom: 4px;
14 | margin-left: 6px;
15 | }
16 |
17 | .voteInfoContainer {
18 | display: inline-block;
19 | margin-top: 4px;
20 | width: 100%;
21 | }
22 | @media (max-width: 992px) {
23 | .voteInfoContainer {
24 | overflow: hidden;
25 | white-space: nowrap;
26 | text-overflow: ellipsis;
27 | }
28 | }
29 |
30 | .voteProposalStatus {
31 | text-align: right;
32 | width: max-content;
33 | }
34 |
35 | .proposalTitle {
36 | text-decoration: none;
37 | color: black;
38 | font-family: 'PT Root UI';
39 | font-weight: bold;
40 | margin-left: 5px;
41 | }
42 |
43 | .voteInfoRow {
44 | cursor: pointer;
45 | transition: all 0.2s ease-in-out;
46 | }
47 |
48 | .voteInfoRow:hover {
49 | --bs-table-hover-bg: rgba(0, 0, 0, 0.03);
50 | }
51 |
52 | .voteStatusWrapper {
53 | display: flex;
54 | flex-flow: row nowrap;
55 | justify-content: flex-end;
56 | align-items: center;
57 | }
58 |
59 | .nullStateCopy:hover {
60 | background-color: rgba(0, 0, 0, 0) !important;
61 | }
62 |
63 | .voteInfoTableCell {
64 | max-width: 50vw;
65 | }
66 |
--------------------------------------------------------------------------------
/src/components/profileEvent/event/DesktopTransferEvent/DesktopTransferEvent.module.css:
--------------------------------------------------------------------------------
1 | .delegateHover {
2 | border-radius: 8px !important;
3 | background-color: var(--brand-gray-dark-text) !important;
4 | color: white;
5 | opacity: 0.75 !important;
6 | font-weight: 500;
7 | transition: ease-in-out 125ms;
8 | }
9 |
10 | .switchIconWrapper {
11 | display: flex;
12 | justify-content: center;
13 | align-items: center;
14 | background-color: var(--brand-gray-light-text-translucent);
15 | color: var(--brand-gray-light-text);
16 | border-radius: 100%;
17 | height: 38px;
18 | width: 38px;
19 | margin-left: 0.4rem;
20 | }
21 |
22 | .switchIcon {
23 | height: 22px;
24 | width: 22px;
25 | }
26 |
27 | .address {
28 | font-weight: bold;
29 | cursor: pointer;
30 | }
31 |
32 | .externalLinkIcon {
33 | height: 16px;
34 | width: 16px;
35 | margin-right: 0.3rem;
36 | }
37 |
38 | .transactionHashWrapper {
39 | background-color: var(--brand-gray-light-text-translucent);
40 | color: var(--brand-gray-light-text);
41 | height: 28px;
42 | border-radius: 8px;
43 | margin-top: 5px;
44 | margin-bottom: 5px;
45 | font-family: 'PT Root UI';
46 | font-weight: bold;
47 | vertical-align: middle;
48 | text-align: center;
49 | display: flex;
50 | flex-direction: row;
51 | justify-content: center;
52 | align-items: center;
53 | padding: 0px 8px;
54 | font-size: 14px;
55 | width: fit-content;
56 | }
57 |
--------------------------------------------------------------------------------
/src/components/ProposalEditor/ProposalEditor.module.css:
--------------------------------------------------------------------------------
1 | .proposalEditor {
2 | margin-top: 1rem;
3 | margin-bottom: 1rem;
4 | padding: 0.5rem 1rem 1rem 1rem;
5 | border: 1px solid #aaa !important;
6 | border-radius: 8px !important;
7 | outline: none !important;
8 | box-shadow: none !important;
9 | }
10 |
11 | .titleInput,
12 | .bodyInput {
13 | padding: 0;
14 | border: none;
15 | width: 100% !important;
16 | outline: none !important;
17 | box-shadow: none !important;
18 | }
19 |
20 | .titleInput {
21 | font-size: 1.25rem;
22 | }
23 |
24 | .propTitle {
25 | font-family: 'Londrina Solid';
26 | }
27 |
28 | .previewArea h3 {
29 | font-weight: 400;
30 | font-size: 14px;
31 | color: rgb(108, 117, 125);
32 | }
33 |
34 | .bodyInput {
35 | min-height: 340px !important;
36 | }
37 |
38 | .divider {
39 | width: 100%;
40 | margin: 0 0 0.5rem 0;
41 | }
42 |
43 | .previewArea {
44 | padding: 0.5rem 1rem 1rem 1rem;
45 | border: 1px solid #aaa !important;
46 | border-radius: 8px !important;
47 | outline: none !important;
48 | box-shadow: none !important;
49 | }
50 |
51 | .markdown h1 {
52 | font-size: 1.7rem;
53 | margin-top: 1rem;
54 | font-weight: bold;
55 | }
56 |
57 | .markdown h2 {
58 | font-size: 1.5rem;
59 | margin-top: 1rem;
60 | font-weight: bold;
61 | }
62 |
63 | .markdown h3 {
64 | font-size: 1.3rem;
65 | }
66 |
67 | .markdown img {
68 | max-width: 100%;
69 | height: auto;
70 | }
71 |
--------------------------------------------------------------------------------
/src/components/profileEvent/event/DesktopDelegationEvent/DesktopDelegationEvent.module.css:
--------------------------------------------------------------------------------
1 | .delegateHover {
2 | border-radius: 8px !important;
3 | background-color: var(--brand-gray-dark-text) !important;
4 | color: white;
5 | opacity: 0.75 !important;
6 | font-weight: 500;
7 | transition: ease-in-out 125ms;
8 | }
9 |
10 | .scaleIconWrapper {
11 | display: flex;
12 | justify-content: center;
13 | align-items: center;
14 | background-color: var(--brand-gray-light-text-translucent);
15 | color: var(--brand-gray-light-text);
16 | border-radius: 100%;
17 | height: 38px;
18 | width: 38px;
19 | margin-left: 0.4rem;
20 | }
21 |
22 | .scaleIcon {
23 | height: 22px;
24 | width: 22px;
25 | }
26 |
27 | .address {
28 | font-weight: bold;
29 | cursor: pointer;
30 | }
31 |
32 | .externalLinkIcon {
33 | height: 16px;
34 | width: 16px;
35 | margin-right: 0.3rem;
36 | }
37 |
38 | .transactionHashWrapper {
39 | background-color: var(--brand-gray-light-text-translucent);
40 | color: var(--brand-gray-light-text);
41 | height: 28px;
42 | border-radius: 8px;
43 | margin-top: 5px;
44 | margin-bottom: 5px;
45 | font-family: 'PT Root UI';
46 | font-weight: bold;
47 | vertical-align: middle;
48 | text-align: center;
49 | display: flex;
50 | flex-direction: row;
51 | justify-content: center;
52 | align-items: center;
53 | padding: 0px 8px;
54 | font-size: 14px;
55 | width: fit-content;
56 | }
57 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | body {
6 | margin: 0;
7 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
8 | 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 | }
12 |
13 | code {
14 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
15 | }
16 |
17 | .__react_component_tooltip.show {
18 | opacity: 1 !important;
19 | }
20 |
21 | /* Load custom fonts */
22 | @font-face {
23 | font-family: 'Londrina Solid';
24 | src: url(./fonts/Londrina_Solid/LondrinaSolid-Black.ttf);
25 | src: url(./fonts/Londrina_Solid/LondrinaSolid-Regular.ttf);
26 | }
27 | @font-face {
28 | font-family: 'PT Root UI';
29 | src: url(./fonts/PT_Root_UI/PT-Root-UI_Regular.woff2) format('woff2'),
30 | url(./fonts/PT_Root_UI/PT-Root-UI_Regular.woff) format('woff');
31 | }
32 | @font-face {
33 | font-family: 'PT Root UI';
34 | font-weight: 500;
35 | src: url(./fonts/PT_Root_UI/PT-Root-UI_Medium.woff2) format('woff2'),
36 | url(./fonts/PT_Root_UI/PT-Root-UI_Medium.woff) format('woff');
37 | }
38 |
39 | @font-face {
40 | font-family: 'PT Root UI';
41 | font-weight: bold;
42 | src: url(./fonts/PT_Root_UI/PT-Root-UI_Bold.woff2) format('woff2'),
43 | url(./fonts/PT_Root_UI/PT-Root-UI_Bold.woff) format('woff');
44 | }
45 |
--------------------------------------------------------------------------------
/src/components/ShortAddress/index.tsx:
--------------------------------------------------------------------------------
1 | import { useReverseENSLookUp } from '../../utils/ensLookup';
2 | import { resolveNounContractAddress } from '../../utils/resolveNounsContractAddress';
3 | import { useEthers } from '@usedapp/core';
4 | import classes from './ShortAddress.module.css';
5 | import { containsBlockedText } from '../../utils/moderation/containsBlockedText';
6 | import { useShortAddress } from '../../utils/addressAndENSDisplayUtils';
7 | import React from 'react';
8 | import Identicon from '../Identicon';
9 |
10 | const ShortAddress: React.FC<{ address: string; avatar?: boolean; size?: number }> = props => {
11 | const { address, avatar, size = 24 } = props;
12 | const { library: provider } = useEthers();
13 |
14 | const ens = useReverseENSLookUp(address) || resolveNounContractAddress(address);
15 | const ensMatchesBlocklistRegex = containsBlockedText(ens || '', 'en');
16 | const shortAddress = useShortAddress(address);
17 |
18 | if (avatar) {
19 | return (
20 |
21 | {avatar && (
22 |
23 |
24 |
25 | )}
26 |
{ens && !ensMatchesBlocklistRegex ? ens : shortAddress}
27 |
28 | );
29 | }
30 |
31 | return <>{ens && !ensMatchesBlocklistRegex ? ens : shortAddress}>;
32 | };
33 |
34 | export default ShortAddress;
35 |
--------------------------------------------------------------------------------
/src/components/Holder/Holder.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | margin-left: 0.5rem;
3 | margin-top: 2px;
4 | padding-left: 1.5rem;
5 | }
6 |
7 | .youCopy {
8 | margin-top: 0.25rem;
9 | }
10 |
11 | .holderCopy {
12 | min-width: 250px;
13 | }
14 |
15 | .section h4 {
16 | font-family: 'PT Root UI';
17 | font-size: 18px;
18 | line-height: 27px;
19 | }
20 |
21 | .section h2 {
22 | font-family: 'PT Root UI';
23 | font-weight: bold;
24 | font-size: 32px;
25 | }
26 |
27 | .leftCol {
28 | font-family: 'PT Root UI';
29 | }
30 |
31 | .leftCol h4 {
32 | font-weight: bold;
33 | }
34 |
35 | .link,
36 | .link:hover,
37 | .link:active {
38 | color: 'var(--brand-dark-white-text)';
39 | text-decoration: none;
40 | display: flex;
41 | cursor: pointer;
42 | }
43 |
44 | .holderContent {
45 | white-space: nowrap;
46 | }
47 |
48 | @media (max-width: 992px) {
49 | .section h4 {
50 | font-size: 18px;
51 | }
52 |
53 | .section h2 {
54 | font-size: 23px;
55 | }
56 |
57 | .section {
58 | justify-content: space-between;
59 | }
60 |
61 | .wrapper {
62 | margin-top: 0px;
63 | width: 100%;
64 | margin-left: 0;
65 | margin-right: 0;
66 | padding-left: 0;
67 | padding-right: 0;
68 | }
69 |
70 | .leftCol {
71 | padding-left: 0.5rem;
72 | }
73 |
74 | .holderContent {
75 | margin-right: 0.5rem;
76 | }
77 |
78 | .wrapper {
79 | width: 100%;
80 | margin-left: 0;
81 | margin-right: 0;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/components/profileEvent/event/DesktopNounWinEvent/DesktopNounWinEvent.module.css:
--------------------------------------------------------------------------------
1 | .delegateHover {
2 | border-radius: 8px !important;
3 | background-color: var(--brand-gray-dark-text) !important;
4 | color: white;
5 | opacity: 0.75 !important;
6 | font-weight: 500;
7 | transition: ease-in-out 125ms;
8 | }
9 |
10 | .switchIconWrapper {
11 | display: flex;
12 | justify-content: center;
13 | align-items: center;
14 | background-color: var(--brand-gray-light-text-translucent);
15 | color: var(--brand-gray-light-text);
16 | border-radius: 100%;
17 | height: 38px;
18 | width: 38px;
19 | margin-left: 0.4rem;
20 | }
21 |
22 | .switchIcon {
23 | height: 22px;
24 | width: 22px;
25 | }
26 |
27 | .address {
28 | font-weight: bold;
29 | cursor: pointer;
30 | }
31 |
32 | .externalLinkIcon {
33 | height: 16px;
34 | width: 16px;
35 | margin-right: 0.3rem;
36 | }
37 |
38 | .transactionHashWrapper {
39 | background-color: var(--brand-gray-light-text-translucent);
40 | color: var(--brand-gray-light-text);
41 | height: 28px;
42 | border-radius: 8px;
43 | margin-top: 5px;
44 | margin-bottom: 5px;
45 | font-family: 'PT Root UI';
46 | font-weight: bold;
47 | vertical-align: middle;
48 | text-align: center;
49 | display: flex;
50 | flex-direction: row;
51 | justify-content: center;
52 | align-items: center;
53 | padding: 0px 8px;
54 | font-size: 14px;
55 | width: fit-content;
56 | }
57 |
58 | .bold {
59 | font-weight: bold;
60 | }
61 |
--------------------------------------------------------------------------------
/src/components/Modal/index.tsx:
--------------------------------------------------------------------------------
1 | import classes from './Modal.module.css';
2 | import ReactDOM from 'react-dom';
3 | import xIcon from '../../assets/x-icon.png';
4 | import React from 'react';
5 |
6 | export const Backdrop: React.FC<{ onDismiss: () => void }> = props => {
7 | return ;
8 | };
9 |
10 | const ModalOverlay: React.FC<{
11 | title?: React.ReactNode;
12 | content?: React.ReactNode;
13 | onDismiss: () => void;
14 | }> = props => {
15 | const { title, content, onDismiss } = props;
16 | return (
17 |
18 |
21 |
{title}
22 |
{content}
23 |
24 | );
25 | };
26 |
27 | const Modal: React.FC<{
28 | title?: React.ReactNode;
29 | content?: React.ReactNode;
30 | onDismiss: () => void;
31 | }> = props => {
32 | const { title, content, onDismiss } = props;
33 | return (
34 | <>
35 | {ReactDOM.createPortal(
36 | ,
37 | document.getElementById('backdrop-root')!,
38 | )}
39 | {ReactDOM.createPortal(
40 | ,
41 | document.getElementById('overlay-root')!,
42 | )}
43 | >
44 | );
45 | };
46 |
47 | export default Modal;
48 |
--------------------------------------------------------------------------------
/src/components/NetworkAlert/index.tsx:
--------------------------------------------------------------------------------
1 | import { Modal } from 'react-bootstrap';
2 | import { CHAIN_ID } from '../../config';
3 |
4 | const networkName = () => {
5 | switch (Number(CHAIN_ID)) {
6 | case 1:
7 | return 'Ethereum Mainnet';
8 | case 4:
9 | return 'the Rinkeby network';
10 | default:
11 | return `Network ${CHAIN_ID}`;
12 | }
13 | };
14 |
15 | const metamaskNetworkName = () => {
16 | switch (Number(CHAIN_ID)) {
17 | case 1:
18 | return 'Ethereum Mainnet';
19 | case 4:
20 | return 'Rinkeby Test Network';
21 | default:
22 | return `Network ${CHAIN_ID}`;
23 | }
24 | };
25 |
26 | const NetworkAlert = () => {
27 | return (
28 | <>
29 |
30 |
31 | Wrong Network Detected
32 |
33 |
34 |
35 | Nouns DAO auctions require you to switch over {networkName()} to be able to participate.
36 |
37 |
38 | To get started, please switch your network by following the instructions below:
39 |
40 |
41 | - Open Metamask
42 | - Click the network select dropdown
43 | - Click on "{metamaskNetworkName()}"
44 |
45 |
46 |
47 | >
48 | );
49 | };
50 | export default NetworkAlert;
51 |
--------------------------------------------------------------------------------
/src/utils/constants.ts:
--------------------------------------------------------------------------------
1 | export const AVERAGE_BLOCK_TIME_IN_SECS = 12;
2 | export const STORAGE_KEY_INSCRIBER_ID = "INSCRIBER_ID"
3 | export const IS_DEVELOPMENT = true;
4 |
5 |
6 | export const MIN_FEE_RATE = 1;
7 | export const MAX_FEE_RATE = 1000;
8 |
9 | export const DEV_FILE_MAXSIZE = 20000; // 20KB - testnet
10 | export const PRO_FILE_MAXSIZE = 400000; // 400KB - mainnet
11 |
12 | export const FILE_MAXSIZE = DEV_FILE_MAXSIZE;
13 |
14 | export const UNISAT_NETWORK_NAME = IS_DEVELOPMENT ? "testnet" : "livenet"
15 |
16 | export const DEV_API_PATH = "http://localhost:3306/api";
17 | export const PROD_API_PATH = "https://ordinalart.backend.hariwhitedream.com/api";
18 |
19 | export const API_PATH = IS_DEVELOPMENT ? DEV_API_PATH : PROD_API_PATH;
20 |
21 | export const ADMIN_ADDRESS = IS_DEVELOPMENT ? "tb1q8zcn0ackfwq0jd7fjrxgc0k07x2sv3cf0lh4s6" : "";
22 |
23 | export const ALERT_EMPTY = "";
24 | export const ALERT_SUCCESS = "success";
25 | export const ALERT_WARN = "warning";
26 | export const ALERT_ERROR = "error";
27 |
28 | export const ALERT_REFETCH = 10000;
29 |
30 | export const ALERT_DELAY = 3000;
31 | export const ALERT_POSITION = "top-right";
32 |
33 | export const SUCCESS = "SUCCESS";
34 | export const FAIL = "FAIL";
35 |
36 | export const SERVICE_FEE = 40000;
37 | export const OUTPUT_UTXO = 10000;
38 |
39 | export const BECH32_EXAMPLE = "bc1pgrc6jtuaqajm347356xgsk7aeapnh6pnkac2mxa4dm3vq04ezc3qt6g8xs";
40 |
41 | ////// Signing Messages
42 | export const MESSAGE_LOGIN = "Sign in to the website!";
--------------------------------------------------------------------------------
/src/components/TightStackedCircleNoun/index.tsx:
--------------------------------------------------------------------------------
1 | import { useNounSeed } from '../../wrappers/nounToken';
2 | import { BigNumber } from 'ethers';
3 | import { getNoun } from '../StandaloneNoun';
4 | import { LoadingNoun } from '../Noun';
5 |
6 | interface TightStackedCircleNounProps {
7 | nounId: number;
8 | index: number;
9 | square: number;
10 | shift: number;
11 | }
12 |
13 | const TightStackedCircleNoun: React.FC = props => {
14 | const { nounId, index, square, shift } = props;
15 | const seed = useNounSeed(BigNumber.from(nounId));
16 |
17 | if (!seed) {
18 | return ;
19 | }
20 |
21 | const nounData = getNoun(BigNumber.from(nounId), seed);
22 | const image = nounData.image;
23 |
24 | return (
25 |
26 |
27 |
38 |
39 |
40 |
41 |
49 |
50 | );
51 | };
52 |
53 | export default TightStackedCircleNoun;
54 |
--------------------------------------------------------------------------------
/src/components/StandalonePart/index.tsx:
--------------------------------------------------------------------------------
1 | import { ImageData as imageData, getPartData } from '@nouns/assets';
2 | import { buildSVG } from '@nouns/sdk';
3 | import Image from 'react-bootstrap/Image';
4 | import classes from './StandalonePart.module.css';
5 | import cx from 'classnames';
6 |
7 | interface StandalonePartProps {
8 | partType: string;
9 | partIndex: number;
10 | }
11 |
12 | export const getBackground = (partIndex: number) => {
13 | const bgColor = imageData.bgcolors[partIndex];
14 | const svg = ``;
15 | const image = `data:image/svg+xml;base64,${btoa(svg)}`;
16 | return { image };
17 | };
18 |
19 | export const getPart = (partType: string, partIndex: number) => {
20 | const data = getPartData(partType, partIndex);
21 | const image = `data:image/svg+xml;base64,${btoa(buildSVG([{ data }], imageData.palette))}`;
22 |
23 | return { image };
24 | };
25 |
26 | export const StandalonePart: React.FC = (props: StandalonePartProps) => {
27 | let part;
28 |
29 | if (props.partType === 'backgrounds') {
30 | part = getBackground(props.partIndex);
31 | } else {
32 | part = getPart(props.partType, props.partIndex);
33 | }
34 |
35 | return (
36 | <>
37 |
38 | >
39 | );
40 | };
41 |
42 | export default StandalonePart;
43 |
--------------------------------------------------------------------------------
/src/state/slices/onDisplayAuction.ts:
--------------------------------------------------------------------------------
1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit';
2 |
3 | interface OnDisplayAuctionState {
4 | lastAuctionNounId: number | undefined;
5 | onDisplayAuctionNounId: number | undefined;
6 | }
7 |
8 | const initialState: OnDisplayAuctionState = {
9 | lastAuctionNounId: undefined,
10 | onDisplayAuctionNounId: undefined,
11 | };
12 |
13 | const onDisplayAuction = createSlice({
14 | name: 'onDisplayAuction',
15 | initialState: initialState,
16 | reducers: {
17 | setLastAuctionNounId: (state, action: PayloadAction) => {
18 | state.lastAuctionNounId = action.payload;
19 | },
20 | setOnDisplayAuctionNounId: (state, action: PayloadAction) => {
21 | state.onDisplayAuctionNounId = action.payload;
22 | },
23 | setPrevOnDisplayAuctionNounId: state => {
24 | if (!state.onDisplayAuctionNounId) return;
25 | if (state.onDisplayAuctionNounId === 0) return;
26 | state.onDisplayAuctionNounId = state.onDisplayAuctionNounId - 1;
27 | },
28 | setNextOnDisplayAuctionNounId: state => {
29 | if (state.onDisplayAuctionNounId === undefined) return;
30 | if (state.lastAuctionNounId === state.onDisplayAuctionNounId) return;
31 | state.onDisplayAuctionNounId = state.onDisplayAuctionNounId + 1;
32 | },
33 | },
34 | });
35 |
36 | export const {
37 | setLastAuctionNounId,
38 | setOnDisplayAuctionNounId,
39 | setPrevOnDisplayAuctionNounId,
40 | setNextOnDisplayAuctionNounId,
41 | } = onDisplayAuction.actions;
42 |
43 | export default onDisplayAuction.reducer;
44 |
--------------------------------------------------------------------------------
/src/components/CurrentDelegatePannel/CurrentDelegatePannel.module.css:
--------------------------------------------------------------------------------
1 | .header {
2 | padding: 1rem 0.75rem 0rem 0.75rem;
3 | }
4 |
5 | .title {
6 | font-family: 'Londrina Solid';
7 | display: flex;
8 | flex-direction: column;
9 | line-height: 42px;
10 | height: 2rem;
11 | font-size: 42px;
12 | color: var(--brand-cool-dark-text);
13 | }
14 |
15 | .copy {
16 | font-weight: 500;
17 | font-family: 'PT Root UI';
18 | color: var(--brand-cool-dark-text);
19 | }
20 |
21 | .copy .emph {
22 | font-weight: bold;
23 | }
24 |
25 | .contentWrapper {
26 | border-radius: 14px;
27 | background-color: white;
28 | padding: 1rem;
29 | display: flex;
30 | justify-content: space-between;
31 | }
32 |
33 | .current {
34 | color: var(--brand-cool-light-text);
35 | font-size: 18px;
36 | margin-top: 1rem;
37 | font-weight: 500;
38 | }
39 |
40 | .delegateInfoWrapper {
41 | display: flex;
42 | flex-direction: column;
43 | }
44 |
45 | .ens {
46 | display: flex;
47 | flex-direction: row;
48 | font-size: 26px;
49 | color: var(--brand-cool-dark-text);
50 | }
51 |
52 | .shortAddress {
53 | color: var(--brand-cool-light-text);
54 | font-weight: 500;
55 | font-size: 16px;
56 | text-align: right;
57 | }
58 |
59 | .buttonWrapper {
60 | display: flex;
61 | justify-content: space-between;
62 | margin-top: 0.5rem;
63 | }
64 |
65 | @media (max-width: 992px) {
66 | .wrapper {
67 | height: 100%;
68 | display: flex;
69 | flex-direction: column;
70 | justify-content: space-between;
71 | }
72 |
73 | .buttonWrapper {
74 | margin-bottom: 1rem;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/utils/nounderNoun.ts:
--------------------------------------------------------------------------------
1 | import { Auction } from '../wrappers/nounsAuction';
2 | import { AuctionState } from '../state/slices/auction';
3 | import { BigNumber } from '@ethersproject/bignumber';
4 |
5 | export const isNounderNoun = (nounId: BigNumber) => {
6 | return nounId.mod(10).eq(0) || nounId.eq(0);
7 | };
8 |
9 | const emptyNounderAuction = (onDisplayAuctionId: number): Auction => {
10 | return {
11 | amount: BigNumber.from(0).toJSON(),
12 | bidder: '',
13 | startTime: BigNumber.from(0).toJSON(),
14 | endTime: BigNumber.from(0).toJSON(),
15 | nounId: BigNumber.from(onDisplayAuctionId).toJSON(),
16 | settled: false,
17 | };
18 | };
19 |
20 | const findAuction = (id: BigNumber, auctions: AuctionState[]): Auction | undefined => {
21 | return auctions.find(auction => {
22 | return BigNumber.from(auction.activeAuction?.nounId).eq(id);
23 | })?.activeAuction;
24 | };
25 |
26 | /**
27 | *
28 | * @param nounId
29 | * @param pastAuctions
30 | * @returns empty `Auction` object with `startTime` set to auction after param `nounId`
31 | */
32 | export const generateEmptyNounderAuction = (
33 | nounId: BigNumber,
34 | pastAuctions: AuctionState[],
35 | ): Auction => {
36 | const nounderAuction = emptyNounderAuction(nounId.toNumber());
37 | // use nounderAuction.nounId + 1 to get mint time
38 | const auctionAbove = findAuction(nounId.add(1), pastAuctions);
39 | const auctionAboveStartTime = auctionAbove && BigNumber.from(auctionAbove.startTime);
40 | if (auctionAboveStartTime) nounderAuction.startTime = auctionAboveStartTime.toJSON();
41 |
42 | return nounderAuction;
43 | };
44 |
--------------------------------------------------------------------------------
/src/components/ExploreGrid/ExploreGridItem/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import Placeholder from 'react-bootstrap/Placeholder';
3 | import { BigNumber } from 'ethers';
4 | import { StandaloneNounImage } from '../../StandaloneNoun';
5 |
6 | interface ExploreGridItemProps {
7 | nounId: number | null;
8 | imgSrc: string | undefined;
9 | }
10 |
11 | const ExploreGridItem: React.FC = React.forwardRef(
12 | (props, ref: React.Ref) => {
13 | const [isImageLoaded, setIsImageLoaded] = useState();
14 | const [isImageError, setIsImageError] = useState();
15 |
16 | return (
17 | <>
18 |
setIsImageLoaded(true)}
22 | onError={() => setIsImageError(true)}
23 | alt={`Noun #${props.nounId}`}
24 | />
25 |
26 | {/* Show placeholder until image is loaded */}
27 |
36 |
37 | {/* If image can't be loaded, fetch Noun image internally */}
38 | {isImageError && props.nounId && (
39 |
40 | )}
41 | >
42 | );
43 | },
44 | );
45 |
46 | export default ExploreGridItem;
47 |
--------------------------------------------------------------------------------
/src/components/NavLocaleSwitcher/NavLocalSwitcher.module.css:
--------------------------------------------------------------------------------
1 | .desktopLanguageButton {
2 | justify-content: space-between;
3 | }
4 | .dropdownActive {
5 | background-color: white;
6 | color: black;
7 | }
8 |
9 | .dropDownTop {
10 | border-radius: 10px 10px 0px 0px;
11 | padding-top: 0.65rem;
12 | padding-bottom: 0.5rem;
13 | padding-left: 1rem;
14 | margin-left: 4px;
15 | border-bottom: 1px solid rgba(0, 0, 0, 0.06);
16 | transition: all 0.125s ease-in-out;
17 | }
18 |
19 | .dropDownTop:hover {
20 | color: black;
21 | }
22 |
23 | .dropDownBottom {
24 | border-radius: 0px 0px 10px 10px;
25 | padding-bottom: 0.65rem;
26 | padding-top: 0.5rem;
27 | padding-left: 1rem;
28 | margin-left: 4px;
29 | }
30 |
31 | .dropDownBottom:hover {
32 | color: black;
33 | }
34 |
35 | .dropDownInterior {
36 | border-bottom: 1px solid rgba(0, 0, 0, 0.06);
37 | padding-bottom: 0.65rem;
38 | padding-top: 0.5rem;
39 | padding-left: 1rem;
40 | margin-left: 4px;
41 | }
42 |
43 | .dropDownInterior:hover {
44 | color: black;
45 | }
46 |
47 | .languageButton {
48 | border: none;
49 | margin: 5px;
50 | padding: 5px 20px;
51 | border-radius: 5px;
52 | background-color: rgba(211, 211, 211, 0.664);
53 | color: var(--brand-black);
54 | border: none;
55 | }
56 | .languageButton img {
57 | margin-right: 10px;
58 | border-radius: 5px;
59 | }
60 | .languageButton:hover,
61 | .languageButton:active,
62 | .languageButton:focus,
63 | .languageButton:disabled {
64 | outline: none !important;
65 | box-shadow: none !important;
66 | background-color: lightgray !important;
67 | color: var(--brand-dark-green);
68 | color: white;
69 | }
70 |
--------------------------------------------------------------------------------
/src/pages/Playground/Playground.module.css:
--------------------------------------------------------------------------------
1 | .headerRow {
2 | margin: 2rem 0;
3 | }
4 | .headerRow span {
5 | color: #8c8d92;
6 | font-size: 24px;
7 | font-family: 'Londrina Solid';
8 | }
9 | .headerRow h1 {
10 | color: #14161b;
11 | font-size: 56px;
12 | font-family: 'Londrina Solid';
13 | }
14 | .headerRow p {
15 | color: #14161b;
16 | }
17 |
18 | .primaryBtn {
19 | width: 100%;
20 | height: 4rem;
21 | font-weight: bold;
22 | margin-bottom: 0.5rem;
23 | border-radius: 12px;
24 | background-color: var(--brand-dark-red);
25 | border: var(--brand-dark-red);
26 | }
27 | .primaryBtn:focus,
28 | .primaryBtn:hover {
29 | background-color: var(--brand-dark-red) !important;
30 | box-shadow: 0 0 0 0.2rem rgb(214, 60, 94, 0.75);
31 | }
32 | .primaryBtn:active {
33 | background-color: var(--brand-dark-red) !important;
34 | }
35 | .traitForm {
36 | height: 4rem;
37 | }
38 | .traitFormBtn {
39 | height: 100% !important;
40 | width: 100%;
41 | margin: 0.5rem 0;
42 | border-radius: 12px;
43 | background-color: white !important;
44 | border-color: #e2e3e8 !important;
45 | font-size: 18px;
46 | font-weight: bold;
47 | color: #14161b;
48 | }
49 | .traitFormBtn:hover,
50 | .traitFormBtn:focus {
51 | border-color: #e2e3e8 !important;
52 | background-color: #f4f4f8 !important;
53 | box-shadow: none !important;
54 | }
55 | .floatingLabel,
56 | .fileUpload {
57 | font-size: 15px;
58 | color: #8c8d92;
59 | }
60 | .nounYearsFooter {
61 | font-style: italic;
62 | }
63 | .nounImg {
64 | border-radius: 16px;
65 | }
66 | .nounImg:hover {
67 | cursor: pointer;
68 | transform: scale(1.01);
69 | }
70 | .nounWrapper {
71 | margin-bottom: 1rem;
72 | }
73 |
--------------------------------------------------------------------------------
/src/components/Identicon/index.tsx:
--------------------------------------------------------------------------------
1 | import Davatar, { Image } from '@davatar/react';
2 | import { Web3Provider } from '@ethersproject/providers';
3 | import { Component } from 'react';
4 |
5 | interface IdenticonInnerProps {
6 | address: string;
7 | provider: Web3Provider;
8 | size: number;
9 | }
10 |
11 | interface IdenticonOutterProps {
12 | address: string;
13 | provider?: Web3Provider;
14 | size?: number;
15 | }
16 |
17 | class IdenticonInner extends Component {
18 | state: { fallback: boolean } = { fallback: false };
19 |
20 | static getDerivedStateFromError() {
21 | // use Jazzicon if Davatar throws;
22 | return { fallback: true };
23 | }
24 |
25 | componentDidCatch(error: any, errorInfo: any) {
26 | console.log(error, errorInfo);
27 | }
28 |
29 | renderDavatar(address: string, provider: Web3Provider, size: number) {
30 | return ;
31 | }
32 |
33 | renderJazzicon(address: string, size: number) {
34 | return ;
35 | }
36 |
37 | render() {
38 | return (
39 | <>
40 | {this.state.fallback
41 | ? this.renderJazzicon(this.props.address, this.props.size)
42 | : this.renderDavatar(this.props.address, this.props.provider, this.props.size)}
43 | >
44 | );
45 | }
46 | }
47 |
48 | const Identicon: React.FC = props => {
49 | const { size, address, provider } = props;
50 |
51 | if (!provider) {
52 | return <>>;
53 | }
54 |
55 | return ;
56 | };
57 |
58 | export default Identicon;
59 |
--------------------------------------------------------------------------------
/src/components/CreateProposalButton/index.tsx:
--------------------------------------------------------------------------------
1 | import { Button, Spinner } from 'react-bootstrap';
2 | import { Trans } from '@lingui/macro';
3 | import { i18n } from '@lingui/core';
4 |
5 | const CreateProposalButton = ({
6 | className,
7 | isLoading,
8 | proposalThreshold,
9 | hasActiveOrPendingProposal,
10 | hasEnoughVote,
11 | isFormInvalid,
12 | handleCreateProposal,
13 | }: {
14 | className?: string;
15 | isLoading: boolean;
16 | proposalThreshold?: number;
17 | hasActiveOrPendingProposal: boolean;
18 | hasEnoughVote: boolean;
19 | isFormInvalid: boolean;
20 | handleCreateProposal: () => void;
21 | }) => {
22 | const buttonText = () => {
23 | if (hasActiveOrPendingProposal) {
24 | return You already have an active or pending proposal;
25 | }
26 | if (!hasEnoughVote) {
27 | if (proposalThreshold) {
28 | return (
29 |
30 | You must have {i18n.number((proposalThreshold || 0) + 1)} votes to submit a proposal
31 |
32 | );
33 | }
34 | return You don't have enough votes to submit a proposal;
35 | }
36 | return Create Proposal;
37 | };
38 |
39 | return (
40 |
41 |
49 |
50 | );
51 | };
52 | export default CreateProposalButton;
53 |
--------------------------------------------------------------------------------
/src/pages/CreateProposal/CreateProposal.module.css:
--------------------------------------------------------------------------------
1 | .createProposalPage {
2 | font-family: 'PT Root UI';
3 | }
4 |
5 | .createProposalPage a {
6 | color: var(--brand-dark-red);
7 | }
8 |
9 | .createProposalForm {
10 | border-radius: 5px;
11 | padding: 0rem 2.5rem;
12 | background-color: white;
13 | }
14 |
15 | .heading {
16 | margin: 1rem 0;
17 | font-family: 'Londrina Solid';
18 | font-size: 42px;
19 | }
20 |
21 | .section {
22 | border-top: 1px solid #e9ecef;
23 | word-wrap: break-word;
24 | padding-top: 2rem;
25 | margin-top: 2rem;
26 | }
27 |
28 | .addTransactionButton,
29 | .createProposalButton {
30 | margin-top: 1rem;
31 | }
32 |
33 | .backButton {
34 | -webkit-appearance: none;
35 | padding: 0;
36 | display: inline-block;
37 | width: 2rem;
38 | height: 2rem;
39 | border-radius: 50%;
40 | font-weight: bold;
41 | margin-right: 1rem;
42 | margin-top: 0.1rem;
43 | }
44 |
45 | .proposalActionButton {
46 | height: 50px;
47 | border-radius: 8px;
48 | font-family: 'PT Root UI';
49 | font-weight: bold;
50 | font-size: 24px;
51 | transition: all 0.125s ease-in-out;
52 | }
53 |
54 | .proposalActionButton:hover {
55 | opacity: 0.5;
56 | cursor: pointer;
57 | }
58 |
59 | .proposalActionButtonSection {
60 | border-top: 0px;
61 | }
62 |
63 | .voterIneligibleAlert {
64 | border-radius: 8px;
65 | }
66 |
67 | .tokenBuyerNotif {
68 | border-radius: 8px;
69 | margin-top: 1rem;
70 | }
71 |
72 | .createProposalButton {
73 | height: 50px;
74 | border-radius: 8px;
75 | font-family: 'PT Root UI';
76 | font-weight: bold;
77 | font-size: 18px;
78 | transition: all 0.125s ease-in-out;
79 | }
80 |
81 | .wrapper {
82 | display: flex;
83 | align-items: center;
84 | }
85 |
--------------------------------------------------------------------------------
/src/components/NavBarTreasury/index.tsx:
--------------------------------------------------------------------------------
1 | import classes from './NavBarTreasury.module.css';
2 | import { NavBarButtonStyle } from '../NavBarButton';
3 | import clsx from 'clsx';
4 | import { Trans } from '@lingui/macro';
5 | import { i18n } from '@lingui/core';
6 |
7 | interface NavBarTreasuryProps {
8 | treasuryBalance: string;
9 | treasuryStyle: NavBarButtonStyle;
10 | }
11 |
12 | const NavBarTreasury: React.FC = props => {
13 | const { treasuryBalance, treasuryStyle } = props;
14 |
15 | let treasuryStyleClass;
16 | switch (treasuryStyle) {
17 | case NavBarButtonStyle.WARM_INFO:
18 | treasuryStyleClass = classes.warmInfo;
19 | break;
20 | case NavBarButtonStyle.COOL_INFO:
21 | treasuryStyleClass = classes.coolInfo;
22 | break;
23 | case NavBarButtonStyle.WHITE_INFO:
24 | default:
25 | treasuryStyleClass = classes.whiteInfo;
26 | break;
27 | }
28 |
29 | return (
30 |
31 |
32 |
38 |
44 | Treasury
45 |
46 |
Ξ {i18n.number(Number(treasuryBalance))}
47 |
48 |
49 |
50 | );
51 | };
52 |
53 | export default NavBarTreasury;
54 |
--------------------------------------------------------------------------------
/src/components/NounHoverCard/NounHoverCard.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | width: max-content;
3 | height: fit-content;
4 | }
5 |
6 | .spinnerWrapper {
7 | color: var(--brand-gray-light-text);
8 | width: 100%;
9 | height: 125px;
10 | display: flex;
11 | flex-direction: column;
12 | justify-content: center;
13 | }
14 |
15 | .spinner {
16 | display: flex;
17 | width: 100%;
18 | justify-content: center;
19 | }
20 |
21 | .titleWrapper {
22 | display: flex;
23 | flex-direction: row;
24 | margin-bottom: 0.75rem;
25 | margin-top: 0.75rem;
26 | }
27 |
28 | .titleWrapper h1 {
29 | color: black;
30 | font-family: 'Londrina Solid';
31 | font-size: 24px;
32 | margin-top: 0.45rem;
33 | margin-left: 0.5rem;
34 | }
35 |
36 | .nounWrapper {
37 | height: 42px;
38 | width: 42px;
39 | }
40 |
41 | .nounInfoWrapper {
42 | margin-top: 0.75rem;
43 | font-style: normal;
44 | font-weight: 500;
45 | font-size: 15px;
46 | line-height: 140%;
47 | /* identical to box height, or 21px */
48 |
49 | display: flex;
50 | align-items: center;
51 | font-feature-settings: 'tnum' on, 'lnum' on, 'ss06' on, 'ss01' on, 'liga' off;
52 |
53 | color: var(--brand-gray-dark-text);
54 | }
55 |
56 | .bold {
57 | font-weight: bold;
58 | margin-left: 0.25rem;
59 | }
60 |
61 | .icon {
62 | margin-right: 7px;
63 | }
64 |
65 | .cta {
66 | color: var(--brand-gray-light-text);
67 | font-style: normal;
68 | font-weight: 500;
69 | font-size: 15px;
70 | line-height: 140%;
71 | /* identical to box height, or 21px */
72 |
73 | display: flex;
74 | align-items: center;
75 | font-feature-settings: 'tnum' on, 'lnum' on, 'ss06' on, 'ss01' on, 'liga' off;
76 | }
77 |
78 | .currentHolder {
79 | margin-bottom: 1rem;
80 | }
81 |
--------------------------------------------------------------------------------
/src/components/LanguageSelectionModal/index.tsx:
--------------------------------------------------------------------------------
1 | import Modal from '../Modal';
2 | import classes from './LanguageSelectionModal.module.css';
3 | import { setLocale } from '../../i18n/setLocale';
4 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
5 | import { faCheck } from '@fortawesome/free-solid-svg-icons';
6 | import { Trans } from '@lingui/macro';
7 | import { SUPPORTED_LOCALES, SupportedLocale, LOCALE_LABEL } from '../../i18n/locales';
8 | import { useActiveLocale } from '../../hooks/useActivateLocale';
9 |
10 | interface LanguageSelectionModalProps {
11 | onDismiss: () => void;
12 | }
13 |
14 | /**
15 | * Note: This is only used on mobile. On desktop, language is selected via a dropdown.
16 | */
17 | const LanguageSelectionModal: React.FC = props => {
18 | const { onDismiss } = props;
19 | const activeLocale = useActiveLocale();
20 |
21 | const modalContent = (
22 |
23 | {SUPPORTED_LOCALES.map((locale: SupportedLocale) => {
24 | return (
25 |
{
29 | setLocale(locale);
30 | onDismiss();
31 | }}
32 | >
33 | {LOCALE_LABEL[locale]}
34 | {locale === activeLocale && (
35 |
36 | )}
37 |
38 | );
39 | })}
40 |
41 | );
42 |
43 | return (
44 | Select Language} content={modalContent} onDismiss={onDismiss} />
45 | );
46 | };
47 | export default LanguageSelectionModal;
48 |
--------------------------------------------------------------------------------
/src/components/profileEvent/event/MobileDelegationEvent/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { buildEtherscanTxLink } from '../../../../utils/etherscan';
3 | import { DelegationEvent } from '../../../../wrappers/nounActivity';
4 | import classes from './MobileDelegationEvent.module.css';
5 | import MobileNounActivityRow from '../../activityRow/MobileNounActivityRow';
6 | import { ScaleIcon } from '@heroicons/react/solid';
7 | import ShortAddress from '../../../ShortAddress';
8 | import TransactionHashPill from '../../eventData/infoPills/TransactionHashPill';
9 | import { Trans } from '@lingui/macro';
10 |
11 | interface MobileDelegationEventProps {
12 | event: DelegationEvent;
13 | }
14 |
15 | const MobileDelegationEvent: React.FC = props => {
16 | const { event } = props;
17 |
18 | return (
19 | window.open(buildEtherscanTxLink(event.transactionHash), '_blank')}
21 | icon={
22 |
23 |
24 |
25 | }
26 | primaryContent={
27 |
28 | Delegate changed from
29 |
30 | {' '}
31 |
32 | {' '}
33 | to{' '}
34 |
35 |
36 |
37 |
38 | }
39 | secondaryContent={
40 | <>
41 |
42 | >
43 | }
44 | />
45 | );
46 | };
47 |
48 | export default MobileDelegationEvent;
49 |
--------------------------------------------------------------------------------