diff --git a/.github/workflows/test-list.yml b/.github/workflows/test-list.yml new file mode 100644 index 000000000..4918486f1 --- /dev/null +++ b/.github/workflows/test-list.yml @@ -0,0 +1,25 @@ +# This workflow adds a comment with tests list to the PRs with the release +# candidates (PRs that want to merge `release-*` branches to `main`). The test +# list is specified in the `./.github/workflows/test-list/release-test-list.md` +# file. The comment is added only once, right after the PR gets created. + +name: Add test list to release PRs + +on: + pull_request: + types: + - opened + branches: + - release + +jobs: + add-release-test-list: + runs-on: ubuntu-latest + if: github.head_ref == 'refs/heads/stage-live' + permissions: + pull-requests: write + steps: + - uses: actions/checkout@v3 + - uses: mshick/add-pr-comment@v2 + with: + message-path: ./.github/workflows/test-list/release-test-list.md \ No newline at end of file diff --git a/.github/workflows/test-list/release-test-list.md b/.github/workflows/test-list/release-test-list.md new file mode 100644 index 000000000..a9399bd5b --- /dev/null +++ b/.github/workflows/test-list/release-test-list.md @@ -0,0 +1,75 @@ +# Release checklist + +This release checklist should be performed before release is published. + +- if something is not working please create an issue and link it here. Checkbox + should be checked only if everything was fine +- in case of serious issues or doubts you should ask the team first + +**Approve the PR when the checklist is finished ✅** + +**Merge the PR when it is approved and we want to update the production +environment 🚀** + +## Manual tests + +1. Connect to wallet with $TAHO + - [ ] you see the Portal is Open message + - [ ] you can see the map after entering + - [ ] you see your address connected + +2. Connect to wallet that previously staked + - [ ] you see your address connected + - [ ] you see the name of your realm + - [ ] you can navigate to other realms but not stake in them + - [ ] you can stake more TAHO in that realm if you're not fully staked + - [ ] you can unstake + +3. Connect to wallet that hasn't staked but has $TAHO + - [ ] you see your address connected + - [ ] you don't see a realm name beside that + - [ ] you can explore realms and stake into one + +4. Connect to wallet that has NO $TAHO (& no $VETAHO) + - [ ] you see the portal is closed message and the wait list button + - [ ] wait list redirect works as expected + - [ ] you can't see the map + +5. Switch connected wallet in the extension + - [ ] dapp reloads and shows the portal screen + - [ ] reloads with Connect Wallet button if you go to a wallet that's not + connected + - [ ] reloads with that address connected if that's a connected website for + the address: if it's an eligible address, you see Access Granted & + Enter the portal + - [ ] reloads with that address connected if that's a connected website for + the address: if it's an inelligible address, you see the portal is + closed message and wait list button + +6. Helper Tool (note: this might be hard to test since with new releases, the + cache will always be fresh and these results may not be true) + - [ ] doesn't pop up automatically for users who have visited before and + closed it + - [ ] doesn't pop up automatically for staked users + - [ ] does come up with the correct message when you click it (standard + "hope you're enjoying...") + - [ ] does come up properly for a user connecting for the first time and + running through onboarding + +7. Population + - [ ] shows on each realm (how we test that it's accurate, not sure but it + should show) + - [ ] shows on the bottom bar + +8. Quests/Questline + - [ ] shows on each realm as expected + +9. Disconnect from dapp + - [ ] click disconnect on address drop down and it disconnects and shows the + portal screen + +10. Connect Wallet + - [ ] with Taho installed and default wallet + - [ ] with Taho installed and not default + - [ ] with Taho not installed and no other + - [ ] with Taho not installed and MM installed diff --git a/.github/workflows/update-environments.yml b/.github/workflows/update-environments.yml index 1ecda5919..6f2dffe17 100644 --- a/.github/workflows/update-environments.yml +++ b/.github/workflows/update-environments.yml @@ -23,7 +23,13 @@ jobs: FROM_BRANCH: "main" TO_BRANCH: "stage-live" PULL_REQUEST_TITLE: "🪄 [QA] Update stage environments" - PULL_REQUEST_BODY: "This is a pull request that upon merging will update stage environments with recent `main` changes." + PULL_REQUEST_BODY: | + This is a pull request that upon merging will update stage environments with recent `main` changes. + The environments that will be updated: + * Stage live: https://stage-live--taho-development.netlify.app/ + * Stage fork: https://stage-fork--taho-development.netlify.app/ + + Read more: [Deployment to Production Flow](https://github.com/tahowallet/dapp/blob/main/docs/testing-env.md) REVIEWERS: '["andreachapman"]' - uses: studroid/label-pr-or-issue-action@ff48a93f6e1a8d8a6befdae900f54da173b17215 # v1.0.1 with: @@ -58,7 +64,12 @@ jobs: FROM_BRANCH: "stage-live" TO_BRANCH: "release" PULL_REQUEST_TITLE: "🚀 [QA] Update release environment" - PULL_REQUEST_BODY: "This is a pull request that upon merging will update production environment with recent `stage-live` changes." + PULL_REQUEST_BODY: | + This is a pull request that upon merging will update production environment with recent `stage-live` changes. + The environment that will be updated: + * Production: https://taho-development.netlify.app/ (aka https://app.taho.xyz/) + + Read more: [Deployment to Production Flow](https://github.com/tahowallet/dapp/blob/main/docs/testing-env.md) REVIEWERS: '["andreachapman"]' - uses: studroid/label-pr-or-issue-action@ff48a93f6e1a8d8a6befdae900f54da173b17215 # v1.0.1 with: diff --git a/src/data/xp/4/leaderboard.json b/src/data/xp/4/leaderboard.json deleted file mode 100644 index 18eb73844..000000000 --- a/src/data/xp/4/leaderboard.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "totalAmount": "0xbe8c", - "merkleRoot": "0xac7e1eeac7c8c79f4227ba21175f53d1b315d4ec0d3f63747caa806f8d4d5240", - "claims": { - "0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc": { - "index": "0x0", - "amount": "0x5f46", - "proof": [ - "0x77d5949906c9d3f280b53e8f9ce4619cc3601e006a614e6e6b8efdb670851608" - ] - }, - "0x70997970c51812dc3a010c7d01b50e0d17dc79c8": { - "index": "0x1", - "amount": "0x3f84", - "proof": [ - "0x97d8034159ddd4aca63b7acfc7eee591e27cd83a6d690eac3ddb905ce99955b0", - "0xf955766c3e34d6d153fe72738c1b04f22d734532e735c48c7cbd61ea721b615a" - ] - }, - "0x6e80164ea60673d64d5d6228beb684a1274bb017": { - "index": "0x2", - "amount": "0x1fc2", - "proof": [ - "0x236261838f3f8aec57fe81caecc3216475c0a17841efddc10d879c609f7d430c", - "0xf955766c3e34d6d153fe72738c1b04f22d734532e735c48c7cbd61ea721b615a" - ] - } - } -} \ No newline at end of file diff --git a/src/data/xp/4/xp_4_1.json b/src/data/xp/4/xp_4_1.json deleted file mode 100644 index 18eb73844..000000000 --- a/src/data/xp/4/xp_4_1.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "totalAmount": "0xbe8c", - "merkleRoot": "0xac7e1eeac7c8c79f4227ba21175f53d1b315d4ec0d3f63747caa806f8d4d5240", - "claims": { - "0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc": { - "index": "0x0", - "amount": "0x5f46", - "proof": [ - "0x77d5949906c9d3f280b53e8f9ce4619cc3601e006a614e6e6b8efdb670851608" - ] - }, - "0x70997970c51812dc3a010c7d01b50e0d17dc79c8": { - "index": "0x1", - "amount": "0x3f84", - "proof": [ - "0x97d8034159ddd4aca63b7acfc7eee591e27cd83a6d690eac3ddb905ce99955b0", - "0xf955766c3e34d6d153fe72738c1b04f22d734532e735c48c7cbd61ea721b615a" - ] - }, - "0x6e80164ea60673d64d5d6228beb684a1274bb017": { - "index": "0x2", - "amount": "0x1fc2", - "proof": [ - "0x236261838f3f8aec57fe81caecc3216475c0a17841efddc10d879c609f7d430c", - "0xf955766c3e34d6d153fe72738c1b04f22d734532e735c48c7cbd61ea721b615a" - ] - } - } -} \ No newline at end of file diff --git a/src/redux-state/thunks/wallet.ts b/src/redux-state/thunks/wallet.ts index 3669a11ea..b82d7f3a8 100644 --- a/src/redux-state/thunks/wallet.ts +++ b/src/redux-state/thunks/wallet.ts @@ -1,4 +1,4 @@ -import { resolveAddressToName } from "shared/utils" +import { resolveAddressToWalletData } from "shared/utils" import { updateBalances, updateConnectedWallet, @@ -24,22 +24,23 @@ export const fetchWalletName = createDappAsyncThunk( claim: { useConnectedWallet }, } = getState() - const resolvedName = await resolveAddressToName(address) + const { name, avatar } = await resolveAddressToWalletData(address) - if (resolvedName) { + if (name) { dispatch( updateConnectedWallet({ address, - name: resolvedName, + name, + avatar, }) ) if (useConnectedWallet) { - dispatch(setClaimingUser({ name: resolvedName, address })) + dispatch(setClaimingUser({ name, address })) } } - return resolvedName + return name } ) diff --git a/src/shared/utils/ens.ts b/src/shared/utils/ens.ts index 691311d95..c26dc0704 100644 --- a/src/shared/utils/ens.ts +++ b/src/shared/utils/ens.ts @@ -10,9 +10,15 @@ export const resolveENS = (name: string) => { } export const resolveAddressToENS = async (address: string) => { - const name = await ethereumProvider.lookupAddress(address) - if (!name) { - throw Error("Invalid ENS domain name") + try { + const name = await ethereumProvider.lookupAddress(address) + const avatar = await ethereumProvider.getAvatar(address) + + if (!name) throw Error("Invalid ENS domain name") + if (!avatar) return { name } + + return { name, avatar } + } catch { + return null } - return name } diff --git a/src/shared/utils/names.ts b/src/shared/utils/names.ts index d19f686ef..ec21305d3 100644 --- a/src/shared/utils/names.ts +++ b/src/shared/utils/names.ts @@ -2,16 +2,21 @@ import { isProbablyEVMAddress, normalizeAddress } from "./address" import { isValidENSDomainName, resolveENS, resolveAddressToENS } from "./ens" import { isValidUNSDomainName, resolveAddressToUNS, resolveUNS } from "./uns" -type NameWithProvider = { - name: string +type WalletData = { + name?: string + avatar?: string +} + +type NameWithProvider = WalletData & { address: string type: "ens" | "uns" } + const NAMES_CACHE_STRORAGE_KEY = "taho.cachedNames" const MAX_CACHE_AGE = 1000 * 60 * 60 * 24 * 7 // 1 week const resolveAddressPromiseCache: { - [address: string]: Promise + [address: string]: Promise } = {} const getCachedNames = () => { @@ -21,25 +26,29 @@ const getCachedNames = () => { return JSON.parse(cachedNamesUnparsed) } -const addCachedName = ({ name, address, type }: NameWithProvider) => { +const addCachedName = ({ name, avatar, address, type }: NameWithProvider) => { const cachedNames = getCachedNames() const normalizedAddress = normalizeAddress(address) + const newData = name ? { [type]: { name, avatar } } : {} const newCache = JSON.stringify({ ...cachedNames, [normalizedAddress]: { ...(cachedNames[normalizedAddress] ?? {}), - [type]: name, + ...newData, lastUpdate: Date.now(), }, }) + localStorage.setItem(NAMES_CACHE_STRORAGE_KEY, newCache) } const resolveENSPromise = (address: string) => - resolveAddressToENS(address).then((name): string => { - addCachedName({ type: "ens", address, name }) - return name + resolveAddressToENS(address).then((data): WalletData | null => { + if (!data) return null + + addCachedName({ type: "ens", address, ...data }) + return data }) const resolveUNSPromise = (address: string) => @@ -53,25 +62,25 @@ const resolveUnknownNamePromise = () => setTimeout(() => resolve(null), 15000) }) -const resolveAddressToNameWithoutCache = async (address: string) => { +const resolveAddressToWalletDataWithoutCache = async (address: string) => { const normalizedAddress = normalizeAddress(address) if (resolveAddressPromiseCache[normalizedAddress] === undefined) { - resolveAddressPromiseCache[normalizedAddress] = Promise.any([ + resolveAddressPromiseCache[normalizedAddress] = Promise.any([ resolveENSPromise(normalizedAddress), resolveUNSPromise(normalizedAddress), resolveUnknownNamePromise(), - ]) + ]) as Promise } - const resolvedName = await resolveAddressPromiseCache[normalizedAddress] + const { name, avatar } = await resolveAddressPromiseCache[normalizedAddress] - return resolvedName + return { name, avatar } } -export const resolveAddressToName = async ( +export const resolveAddressToWalletData = async ( address: string -): Promise => { +): Promise => { const cachedNames = getCachedNames() const normalizedAddress = normalizeAddress(address) @@ -81,9 +90,11 @@ export const resolveAddressToName = async ( return cachedItem.ens ?? cachedItem.uns } - const name = await resolveAddressToNameWithoutCache(normalizedAddress) + const { name, avatar } = await resolveAddressToWalletDataWithoutCache( + normalizedAddress + ) - return name + return { name, avatar } } export const resolveNameToAddress = async (addressOrName: string) => { diff --git a/src/ui/Island/RealmDetails/LeaderboardList/LeaderboardItem.tsx b/src/ui/Island/RealmDetails/LeaderboardList/LeaderboardItem.tsx index 342f762a2..faf31f9bc 100644 --- a/src/ui/Island/RealmDetails/LeaderboardList/LeaderboardItem.tsx +++ b/src/ui/Island/RealmDetails/LeaderboardList/LeaderboardItem.tsx @@ -4,7 +4,7 @@ import crossIcon from "shared/assets/icons/plus.svg" import classNames from "classnames" import { isSameAddress, - resolveAddressToName, + resolveAddressToWalletData, separateThousandsByComma, truncateAddress, } from "shared/utils" @@ -23,14 +23,18 @@ export default function LeaderboardItem({ const { beneficiary: address, amount } = item const isCurrentUser = isSameAddress(address, currentUser) const avatar = useDappSelector(selectWalletAvatar) + const [username, setUsername] = useState("") + const [walletAvatar, setWalletAvatar] = useState(avatar) useEffect(() => { const getName = async () => { - const name = await resolveAddressToName(address) - if (name) { - setUsername(name) - } + const { name, avatar: userAvatar } = await resolveAddressToWalletData( + address + ) + + if (name) setUsername(name) + if (userAvatar) setWalletAvatar(userAvatar) } getName() }, [address]) @@ -52,7 +56,7 @@ export default function LeaderboardItem({ {isCurrentUser && ( diff --git a/src/ui/Nav/index.tsx b/src/ui/Nav/index.tsx index 092198f21..e976d57fa 100644 --- a/src/ui/Nav/index.tsx +++ b/src/ui/Nav/index.tsx @@ -55,6 +55,7 @@ export default function Nav(): JSX.Element { .rhs_container { margin-left: auto; align-items: center; + height: 40px; } .connect_wallet_btn { diff --git a/src/ui/Onboarding/JoinWaitlist.tsx b/src/ui/Onboarding/JoinWaitlist.tsx index 8e94a1930..f84b11815 100644 --- a/src/ui/Onboarding/JoinWaitlist.tsx +++ b/src/ui/Onboarding/JoinWaitlist.tsx @@ -4,7 +4,11 @@ import Icon from "shared/components/Icon" import newTab from "shared/assets/icons/m/new-tab.svg" import { LINKS } from "shared/constants" -export default function JoinWaitlist() { +export default function JoinWaitlist({ + children, +}: { + children: React.ReactNode +}) { return ( window.open(LINKS.WAITLIST)} > - The portal -
is closed at the
moment. + {children}
) } diff --git a/src/ui/Onboarding/Teaser.tsx b/src/ui/Onboarding/Teaser.tsx deleted file mode 100644 index 466bd737b..000000000 --- a/src/ui/Onboarding/Teaser.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from "react" -import OnboardingModal from "shared/components/Modals/OnboardingModal" - -export default function Teaser() { - return ( - - Portal opens -
- soon -
- ) -} diff --git a/src/ui/Onboarding/index.tsx b/src/ui/Onboarding/index.tsx index ef6758aa8..bbe882dcc 100644 --- a/src/ui/Onboarding/index.tsx +++ b/src/ui/Onboarding/index.tsx @@ -13,7 +13,6 @@ import ConnectWallet from "./ConnectWallet" import JoinWaitlist from "./JoinWaitlist" import EnterPortal from "./EnterPortal" import OnboardingModalLoader from "./Loader" -import Teaser from "./Teaser" function OnboardingModal() { const { isConnected } = useConnect() @@ -21,7 +20,13 @@ function OnboardingModal() { const hasRelevantTokens = useDappSelector(selectHasRelevantTokens) if (process.env.IS_COMING_SOON === "true") { - return + return ( + + Portal opens +
+ soon +
+ ) } if (!isConnected) { @@ -36,7 +41,12 @@ function OnboardingModal() { return } - return + return ( + + The portal +
is closed at the
moment. +
+ ) } export default function Onboarding() {