Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] start wearable claim #265

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/app/features/closet/Closet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { createParam } from 'solito';
import { H3, TextLink } from 'app/ui/typography';
import { Box } from 'app/ui/layout/Box';

export const { useParam } = createParam<{ username: string }>();

export function ClosetScreen() {
const [username] = useParam('username');

return (
<Box className="flex-1 items-center justify-center p-3">
<H3>{`Username: ${username}`}</H3>

<TextLink href="/">👈 Go Home</TextLink>
<TextLink href={`/u/${username}/posts`}>Go to Posts</TextLink>
</Box>
);
}
90 changes: 90 additions & 0 deletions packages/app/features/closet/useWearableClaim.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import _ from 'lodash';
import {
useContractReads,
usePrepareContractWrite,
useContractWrite,
} from 'wagmi';
import useWearableClaims from './useWearableClaims';
import nftGiveawayAbi from './utils/nftGiveaway.json';
import { NftClaim } from './utils/tempType';

const useWearableClaim = ({ id, address }: { id: string; address: string }) => {
const { data: wearableClaims } = useWearableClaims();
const rootHashes = _.map(
wearableClaims,
(nftClaim: NftClaim) => nftClaim.merkle_root_hash,
);
const { data: claimedStatuses } = useContractReads({
contracts: [
{
abi: nftGiveawayAbi,
address,
functionName: 'getClaimedStatus',
args: [address, rootHashes],
},
],
});

// ? is this the right way to get claim count?
// const claim = _.find(
// wearableClaims,
// (claim) => claim.merkle_root_hash === id,
// );
// const claimJson = _.get(claim, 'claim_json', {});
// const claimCount = _.size(_.get(claimJson, 'erc1155[0].ids'));

const unclaimedWearableClaims = _.reduce(
claimedStatuses as boolean[],
(sum: NftClaim[], currentValue: boolean, currentIndex: number) => {
if (currentValue === true) return sum;

const unclaimedRootHash = rootHashes[currentIndex];
const unclaimedNftClaim = _.find(
wearableClaims,
(claim: NftClaim) => claim.merkle_root_hash === unclaimedRootHash,
);

if (unclaimedNftClaim) sum.push(unclaimedNftClaim);

return sum;
},
[],
);

const claims = _.map(wearableClaims, (nftClaim: NftClaim) => ({
to: nftClaim.claim_json.to,
erc1155: nftClaim.claim_json.erc1155,
erc721: nftClaim.claim_json.erc721,
erc20: nftClaim.claim_json.erc20,
salt: nftClaim.claim_json.salt,
}));
const merkleProofs = _.map(wearableClaims, (nftClaim: NftClaim) =>
_.get(nftClaim, 'claim_json.proof'),
);

const { config } = usePrepareContractWrite({
address: '0x',
abi: nftGiveawayAbi,
functionName: 'claimMultipleTokensFromMultipleMerkleTree',
args: [rootHashes, claims, merkleProofs],
});

const { writeAsync } = useContractWrite({
...config,
onSuccess: () => {
console.log('success');
},
onError: (error) => {
console.log('error', error);
},
});

return {
writeAsync,
// claimCount,
unclaimedWearableClaims,
allIsClaimed: _.size(unclaimedWearableClaims),
};
};

export default useWearableClaim;
44 changes: 44 additions & 0 deletions packages/app/features/closet/useWearableClaims.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import _ from 'lodash';
import { useQuery } from '@tanstack/react-query';
import { hasuraClient } from 'services/graphql/client';
import { NftClaim } from './utils/tempType';

// ? handle auth on hasura client?
const useWearableClaims = () => {
const { data, isLoading, error } = useQuery(
['wearableClaims'],
async () => {
const data = await hasuraClient.query({
robot_merkle_claims: [
{ where: { merkle_root: { network: { _eq: 'mainnet' } } } },
{
claim_json: [{ path: 'erc1155' }, true], // ! guessed on this
merkle_root_hash: true,
},
],
});

const nftClaimArray: any[] = _.get(data, 'robot_merkle_claims', []);
const currentNftClaims = nftClaimArray.map((nftClaim: NftClaim) => {
const claim_count = _.size(
_.get(nftClaim, 'claim_json.erc1155[0].ids'),
);

return {
...nftClaim,
claim_json: {
...nftClaim.claim_json,
claim_count,
},
};
});

return currentNftClaims;
},
{ enabled: false },
);
return { data, isLoading, error };
};

export default useWearableClaims;
scottrepreneur marked this conversation as resolved.
Show resolved Hide resolved
85 changes: 85 additions & 0 deletions packages/app/features/closet/useWearables.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { useQuery } from '@tanstack/react-query';
import { utils, BigNumberish } from 'ethers';
import { useContractReads } from 'wagmi';
import _ from 'lodash';
import { NftItem } from './utils/tempType';
import nftWearablesAbi from './utils/nftWearables.json';
import { parseIds } from './utils/nft';

const useNfts = () => {
const fetchNfts = async () => {
return fetch('api/nfts')
.then((res) => res.json())
.then((data) => {
const initialObject: { [key: number]: NftItem } = {};
const nftListObject = _.reduce(
data,
(result: Record<string, NftItem>, nft: NftItem) =>
(result[nft.nft_token_id] = nft),
initialObject,
);

return { nfts: nftListObject, nftIds: parseIds(nftListObject) };
})
.catch((err) => {
console.log(err);
return { nfts: {}, nftIds: [] };
});
};

const { data, isLoading, error } = useQuery<
{ nfts: any; nftIds: any },
Error
>(['nfts'], fetchNfts);

const nfts = _.get(data, 'nfts', null);
const nftIds = _.get(data, 'nftIds', null);

return { nfts, nftIds, isLoading, error };
};

// useWearablesForAddress ?
/**
* Fetches the claimed wearables for a given address
* @param address address of the user
* @returns list of wearables `parsedBalances` or error
*/
const useWearables = ({ address }: { address: string }) => {
const { nfts, nftIds } = useNfts();
const { data: rawWearables, error } = useContractReads({
contracts: [
{
// TODO replace address
address: '0x8e9a29e7e8e3dcd7ea31b1792a8078b5723ed4d8',
scottrepreneur marked this conversation as resolved.
Show resolved Hide resolved
abi: nftWearablesAbi,
functionName: 'balanceOfBatch',
args: [Array(nftIds.length).fill(address), nftIds],
},
],
enabled: !!nfts && !!nftIds,
});

if (!nftIds || !rawWearables) return { wearables: [], parsedBalances: [] };

const parsedBalances = _.map(rawWearables, (balance: BigNumberish) =>
utils.formatUnits(balance, 0),
);

// reduce to nft items only with existing balance
const wearables = parsedBalances.reduce(
(sum: NftItem[], currentValue: boolean | string, currentIndex: number) => {
if (currentValue === '0') return sum;

const nftId = nftIds[currentIndex];
const currentItem = nfts[nftId];
sum.push(currentItem);

return sum;
},
[],
);

return { wearables, rawWearables, error };
};

export default useWearables;
5 changes: 5 additions & 0 deletions packages/app/features/closet/utils/nft.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const parseIds = (nfts: { [key: string]: any }) => {
if (!nfts) return [];

return Object.keys(nfts).map((key: string) => nfts[key].nft_token_id);
};
Loading