Skip to content

Commit

Permalink
fix: prevent double minting
Browse files Browse the repository at this point in the history
  • Loading branch information
wa0x6e committed Sep 30, 2023
1 parent 86549e6 commit 2c04330
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 3 deletions.
9 changes: 7 additions & 2 deletions src/lib/nftClaimer/mint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
numberizeProposalId,
validateMintInput,
mintingAllowed,
hasVoted
hasVoted,
hasMinted
} from './utils';
import abi from './spaceCollectionImplementationAbi.json';
import { FormatTypes, Interface } from '@ethersproject/abi';
Expand Down Expand Up @@ -40,10 +41,14 @@ export default async function payload(input: {
throw new Error('Space has closed minting');
}

if (!hasVoted(params.recipient, proposal as Proposal)) {
if (!(await hasVoted(params.recipient, proposal as Proposal))) {
throw new Error('Minting is open only for voters');
}

if (await hasMinted(params.recipient, proposal as Proposal)) {
throw new Error('You can only mint once per vote');
}

const message = {
proposer: params.proposalAuthor,
recipient: params.recipient,
Expand Down
31 changes: 31 additions & 0 deletions src/lib/nftClaimer/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ export async function hasVoted(address: string, proposal: Proposal) {
return vote !== undefined;
}

export async function hasMinted(address: string, proposal: Proposal) {
const mint = await getMint(address, proposal.id);
return mint !== undefined;
}

export async function validateSpace(address: string, space: Space | null) {
if (!space) {
throw new Error('RECORD_NOT_FOUND');
Expand Down Expand Up @@ -133,6 +138,32 @@ export async function getSpaceCollection(spaceId: string) {
return spaceCollections[0];
}

const MINT_COLLECTION_QUERY = gql`
query Mints($voter: String, $proposalId: String) {
mints(where: { minterAddress: $voter, proposal: $proposalId }, first: 1) {
id
}
}
`;

type Mint = {
id: string;
};

export async function getMint(voter: string, proposalId: string) {
const {
data: { mints }
}: { data: { mints: Mint[] } } = await client.query({
query: MINT_COLLECTION_QUERY,
variables: {
voter,
proposalId
}
});

return mints[0];
}

export function numberizeProposalId(id: string) {
return BigNumber.from(id.startsWith('0x') ? id : CID.parse(id).bytes).toString();
}
Expand Down
14 changes: 13 additions & 1 deletion test/unit/lib/nftClaimer/mint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ const mockMintingAllowed = jest.fn((space: any): boolean => {
const mockHasVoted = jest.fn((address: string, proposal: string): boolean => {
return true;
});
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const mockHasMinted = jest.fn((address: string, proposal: string): boolean => {
return false;
});
jest.mock('../../../../src/lib/nftClaimer/utils', () => {
// Require the original module to not be mocked...
const originalModule = jest.requireActual('../../../../src/lib/nftClaimer/utils');
Expand All @@ -46,7 +50,8 @@ jest.mock('../../../../src/lib/nftClaimer/utils', () => {
getProposalContract: (id: string) => mockGetProposalContract(id),
validateProposal: (id: any) => mockValidateProposal(id),
mintingAllowed: (space: any) => mockMintingAllowed(space),
hasVoted: (address: string, proposal: string) => mockHasVoted(address, proposal)
hasVoted: (address: string, proposal: string) => mockHasVoted(address, proposal),
hasMinted: (address: string, proposal: string) => mockHasMinted(address, proposal)
};
});

Expand Down Expand Up @@ -122,6 +127,13 @@ describe('nftClaimer', () => {
});
});

describe('when address has already minted', () => {
it('throws an error', () => {
mockHasMinted.mockReturnValueOnce(true);
return expect(async () => await payload(input)).rejects.toThrow();
});
});

describe('when maxSupply has been reached', () => {
it.todo('throws an error');
});
Expand Down

0 comments on commit 2c04330

Please sign in to comment.