Skip to content
This repository was archived by the owner on Dec 26, 2023. It is now read-only.

Commit a26536d

Browse files
authored
Merge branch 'main' into feature/testnet-local-environment
2 parents 4f4f4d3 + 20e833f commit a26536d

File tree

14 files changed

+2044
-5
lines changed

14 files changed

+2044
-5
lines changed

frontend/.all-contributorsrc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,15 @@
290290
"code",
291291
"test"
292292
]
293+
},
294+
{
295+
"login": "theprojectcode",
296+
"name": "theprojectcode",
297+
"avatar_url": "https://avatars.githubusercontent.com/u/88997637?v=4",
298+
"profile": "https://github.com/theprojectcode",
299+
"contributions": [
300+
"code"
301+
]
293302
}
294303
],
295304
"contributorsPerLine": 7,

frontend/.github/ISSUE_TEMPLATE/missing-translations.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ body:
2222
id: 'missing-locales'
2323
attributes:
2424
label: 'List of missing locales'
25-
multiple: true
25+
multiple: false
2626
description: 'Check the locales that are missing translations'
2727
options:
2828
- 'de'

frontend/.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@ npm-debug.log*
2222
yarn-debug.log*
2323
yarn-error.log*
2424

25-
.next
25+
.next

frontend/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
3-
[![All Contributors](https://img.shields.io/badge/all_contributors-30-orange.svg?style=flat-square)](#contributors-)
3+
[![All Contributors](https://img.shields.io/badge/all_contributors-31-orange.svg?style=flat-square)](#contributors-)
44
<!-- ALL-CONTRIBUTORS-BADGE:END -->
55
<!-- LOGO -->
66

@@ -138,6 +138,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
138138
<tr>
139139
<td align="center"><a href="https://nagmakapoor.com"><img src="https://avatars.githubusercontent.com/u/16668970?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nagma</b></sub></a><br /><a href="https://github.com/Developer-DAO/developerdao.com/commits?author=nagmak" title="Code">💻</a></td>
140140
<td align="center"><a href="http://www.ibby.dev"><img src="https://avatars.githubusercontent.com/u/23090443?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Brian Eter</b></sub></a><br /><a href="https://github.com/Developer-DAO/developerdao.com/commits?author=Ibby-devv" title="Code">💻</a> <a href="https://github.com/Developer-DAO/developerdao.com/commits?author=Ibby-devv" title="Tests">⚠️</a></td>
141+
<td align="center"><a href="https://github.com/theprojectcode"><img src="https://avatars.githubusercontent.com/u/88997637?v=4?s=100" width="100px;" alt=""/><br /><sub><b>theprojectcode</b></sub></a><br /><a href="https://github.com/Developer-DAO/developerdao.com/commits?author=theprojectcode" title="Code">💻</a></td>
141142
</tr>
142143
</table>
143144

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { BigNumber } from '@ethersproject/bignumber';
2+
import { renderHook } from '@testing-library/react-hooks';
3+
import crypto from 'crypto';
4+
5+
import { useDevNFTSupply } from '../../src/hooks/useDevNFTSupply';
6+
import { Ddao__factory } from '../../types/ethers-contracts/factories/Ddao__factory';
7+
8+
jest.mock('../../types/ethers-contracts/factories/Ddao__factory');
9+
10+
const mockedDdaoFactory = Ddao__factory as jest.Mocked<typeof Ddao__factory>;
11+
12+
test('should return correct supply values & transition loading state', async () => {
13+
mockedDdaoFactory.connect.mockImplementation((address, provider) => {
14+
return {
15+
queryFilter: async () => [
16+
{
17+
args: {
18+
tokenId: BigNumber.from(1),
19+
to: '0x91d7a9e7c09477392290fe16c1b243e4a36d279a',
20+
},
21+
},
22+
{
23+
args: {
24+
tokenId: BigNumber.from(8000),
25+
to: '0x91d7a9e7c09477392290fe16c1b243e4a36d279a',
26+
},
27+
},
28+
],
29+
filters: { Transfer: () => {} },
30+
} as any;
31+
});
32+
33+
const { result, waitForValueToChange } = renderHook(() => useDevNFTSupply());
34+
35+
expect(result.current.isLoading).toBe(true);
36+
await waitForValueToChange(() => result.current.isLoading);
37+
expect(result.current.isLoading).toBe(false);
38+
39+
expect(result.current.maxSupply).toBe(8000);
40+
expect(result.current.totalSupply).toBe(2);
41+
expect(result.current.lockedSupply).toBe(1);
42+
expect(result.current.publicSupply).toBe(1);
43+
44+
expect(result.current.uniqueTokenHolders).toBe(1);
45+
46+
expect(result.current.remainingTotalSupply).toBe(8000 - 2);
47+
expect(result.current.remainingPublicSupply).toBe(7777 - 1);
48+
expect(result.current.remainingLockedSupply).toBe(8000 - 7777 - 1);
49+
});
50+
51+
test('should correctly handle "all minted" state', async () => {
52+
mockedDdaoFactory.connect.mockImplementation((address, provider) => {
53+
return {
54+
totalSupply: async () => ({ toNumber: () => 1 }),
55+
queryFilter: async () =>
56+
[...Array(8000)].map((_, idx) => ({
57+
args: {
58+
tokenId: BigNumber.from(idx + 1),
59+
to: `0x${crypto.randomBytes(32).toString('hex')}`,
60+
},
61+
})),
62+
filters: { Transfer: () => {} },
63+
} as any;
64+
});
65+
66+
const { result, waitForValueToChange } = renderHook(() => useDevNFTSupply());
67+
68+
expect(result.current.isLoading).toBe(true);
69+
await waitForValueToChange(() => result.current.isLoading);
70+
expect(result.current.isLoading).toBe(false);
71+
72+
expect(result.current.maxSupply).toBe(8000);
73+
expect(result.current.totalSupply).toBe(8000);
74+
expect(result.current.lockedSupply).toBe(8000 - 7777);
75+
expect(result.current.publicSupply).toBe(7777);
76+
77+
expect(result.current.uniqueTokenHolders).toBe(8000);
78+
79+
expect(result.current.remainingTotalSupply).toBe(0);
80+
expect(result.current.remainingPublicSupply).toBe(0);
81+
expect(result.current.remainingLockedSupply).toBe(0);
82+
});

frontend/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@
3939
"contributors:generate": "all-contributors generate",
4040
"type-check": "tsc --noEmit",
4141
"test": "jest",
42-
"test:watch": "jest --watch --detectOpenHandles"
42+
"test:watch": "jest --watch --detectOpenHandles",
43+
"generate-types": "typechain --target ethers-v5 src/artifacts/*.json",
44+
"postinstall": "yarn generate-types"
4345
},
4446
"eslintConfig": {
4547
"extends": [
@@ -63,6 +65,7 @@
6365
"devDependencies": {
6466
"@testing-library/cypress": "^8.0.1",
6567
"@testing-library/react-hooks": "^7.0.2",
68+
"@typechain/ethers-v5": "^8.0.1",
6669
"all-contributors-cli": "^6.20.0",
6770
"cypress": "^8.4.1",
6871
"eslint": "^7.32.0",
@@ -73,6 +76,7 @@
7376
"husky": "^7.0.2",
7477
"lint-staged": "^11.1.2",
7578
"prettier": "2.3.2",
79+
"typechain": "^6.0.2",
7680
"typescript": "^4.4.3"
7781
},
7882
"lint-staged": {

frontend/public/locales/en/common.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"ethereumDevNetworkPrompt": "Please Connect to the Ethereum Rinkeby Testnet",
3232
"ethereumNetworkPrompt": "Please Connect to the Ethereum Mainnet",
3333
"availableTokensText": "View the available Token IDs",
34+
"remainingTokensText": "{{remainingTokens}} tokens left / {{uniqueAddressCount}} devs joined",
3435
"mintPageHeader": "DEVS Token Minter",
3536
"projectsList": "A list of community projects created by the Developer DAO community.",
3637
"ddaoTokenSearch": "DDAO Token Search",

frontend/src/components/DirectMint/DirectMintBox.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,15 @@ import {
1616
DEVELOPER_DAO_CONTRACT_NETWORK,
1717
TOKEN_FINDER_URL,
1818
} from '../../utils/DeveloperDaoConstants';
19+
import { useDevNFTSupply } from '../../hooks/useDevNFTSupply';
1920

2021
// Layout for the Direct Mint Box
2122
// used on the minting page
2223
const DirectMintBox = () => {
2324
const { t } = useTranslation();
25+
const { isLoading, remainingPublicSupply, uniqueTokenHolders } =
26+
useDevNFTSupply();
27+
2428
return (
2529
<>
2630
<Container maxW="container.md" centerContent>
@@ -44,6 +48,16 @@ const DirectMintBox = () => {
4448
{t('here')}
4549
</Link>
4650
</Text>
51+
<Text fontSize={{ base: 'xs', sm: 'xl' }}>
52+
{t('remainingTokensText', {
53+
remainingTokens: isLoading
54+
? '...'
55+
: remainingPublicSupply.toLocaleString(),
56+
uniqueAddressCount: isLoading
57+
? '...'
58+
: uniqueTokenHolders?.toLocaleString(),
59+
})}
60+
</Text>
4761
<DirectMint />
4862
</Stack>
4963
</Box>

frontend/src/hooks/useDevNFTSupply.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { useEffect, useState } from 'react';
2+
import { ethers } from 'ethers';
3+
import {
4+
AlchemyProvider,
5+
FallbackProvider,
6+
InfuraProvider,
7+
} from '@ethersproject/providers';
8+
9+
import { DEVELOPER_DAO_CONTRACT } from '../utils/DeveloperDaoConstants';
10+
import { Ddao__factory } from '../../types/ethers-contracts/factories/Ddao__factory';
11+
12+
// TokenIDs start at 1. Tokens 1-7777 (supply of 7777) are open to mint by anyone. 7778-8000 are locked to mint by contract owner
13+
const MAX_SUPPLY = 8000;
14+
const PUBLIC_MAX_SUPPLY = 7777;
15+
16+
export function useDevNFTSupply() {
17+
const [totalSupply, setTotalSupply] = useState<number>(-1);
18+
const [lockedSupply, setLockedSupply] = useState<number>(-1);
19+
const [uniqueTokenHolderCount, setUniqueTokenHolderCount] =
20+
useState<number>(-1);
21+
22+
useEffect(() => {
23+
const fetch = async () => {
24+
const contract = Ddao__factory.connect(
25+
DEVELOPER_DAO_CONTRACT,
26+
new FallbackProvider([
27+
{ provider: new InfuraProvider() },
28+
{ provider: new AlchemyProvider() },
29+
]),
30+
);
31+
32+
// Get all TransferEvents
33+
const transferredTokens = await contract.queryFilter(
34+
contract.filters.Transfer(),
35+
0,
36+
'latest',
37+
);
38+
39+
// Track the latest owner of each token
40+
const claimedTokens: Record<string, string> = {};
41+
transferredTokens.forEach((tx) => {
42+
const tokenId = tx.args.tokenId.toNumber();
43+
claimedTokens[tokenId] = tx.args.to;
44+
});
45+
46+
// Amount of minted tokens
47+
const totalSupply = Object.keys(claimedTokens).length;
48+
49+
// Put the addresses in a set to get the amount of unique addresses
50+
const uniqueAddresses = new Set<string>(Object.values(claimedTokens));
51+
52+
// Count how many tokens of the locked supply have been minted
53+
let countLockedAndMinted = 0;
54+
for (
55+
let lockedTokenId = PUBLIC_MAX_SUPPLY + 1;
56+
lockedTokenId <= MAX_SUPPLY;
57+
lockedTokenId++
58+
) {
59+
if (
60+
claimedTokens[lockedTokenId] &&
61+
claimedTokens[lockedTokenId] !== ethers.constants.AddressZero
62+
)
63+
countLockedAndMinted++;
64+
}
65+
66+
setLockedSupply(countLockedAndMinted);
67+
setTotalSupply(totalSupply);
68+
setUniqueTokenHolderCount(uniqueAddresses.size);
69+
};
70+
71+
fetch();
72+
}, []);
73+
74+
return {
75+
isLoading:
76+
totalSupply === -1 ||
77+
lockedSupply === -1 ||
78+
uniqueTokenHolderCount === -1,
79+
80+
// This is the amount of total minted DEV NFTs, same as calling `totalSupply()` on the contract
81+
totalSupply,
82+
83+
// Count of unique token holder addresses
84+
uniqueTokenHolders: uniqueTokenHolderCount,
85+
86+
// This is the amount of minted DEV NFTs that only the contract owner can mint (TokenIDs 7778-8000, incl.)
87+
lockedSupply,
88+
89+
// This is the amount of publicly minted DEV NFTs, which is open to mint by anyone (TokenIDs 1-7777, incl.)
90+
publicSupply: totalSupply - lockedSupply,
91+
92+
// Total number of possible DEV NFT. This is a constant that cannot be changed
93+
maxSupply: MAX_SUPPLY,
94+
95+
// Some calculations to make these values easily accessible for display in UI
96+
remainingTotalSupply: MAX_SUPPLY - totalSupply,
97+
remainingPublicSupply: PUBLIC_MAX_SUPPLY - (totalSupply - lockedSupply),
98+
remainingLockedSupply: MAX_SUPPLY - PUBLIC_MAX_SUPPLY - lockedSupply,
99+
};
100+
}

0 commit comments

Comments
 (0)