Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
8702a49
[TOOL-4689] Dashboard: Integrate ERC20Asset contract in token creatio…
MananTank Jun 16, 2025
991051d
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Jul 1, 2025
2702e39
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Jul 1, 2025
4f457e9
add POST v1/tokens after coin creation
MananTank Jul 3, 2025
b6563b8
Fix createTokenOnUniversalBridge
MananTank Jul 3, 2025
5277e18
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Jul 7, 2025
7f7d97a
Fix lint
MananTank Jul 7, 2025
1aebfc8
set initialTick
MananTank Jul 9, 2025
1e8ca5a
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Jul 10, 2025
1d31fd0
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Jul 10, 2025
8a2e39c
update
MananTank Jul 10, 2025
f29a2c2
update tick calc
MananTank Jul 10, 2025
f61d0e0
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Jul 10, 2025
3e45e9d
remove unused stuff
MananTank Jul 10, 2025
ee8e2be
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Jul 15, 2025
3c745cc
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Jul 15, 2025
df8e5a4
add claim rewards page
MananTank Jul 15, 2025
6bfce15
load amounts
MananTank Jul 16, 2025
db396a7
update
MananTank Jul 16, 2025
e8e69ae
update UI
MananTank Jul 16, 2025
e946458
copy changes
MananTank Jul 16, 2025
e222e3d
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Jul 16, 2025
aabe00e
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Jul 17, 2025
2951738
update recent transfers table
MananTank Jul 17, 2025
1366058
update creation UI
MananTank Jul 17, 2025
9170c8f
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
jakeloo Jul 18, 2025
3117a3b
updates
MananTank Jul 18, 2025
23a9a62
add pricing strategy selector
MananTank Jul 18, 2025
f07f9b4
distribute page ui updates
MananTank Jul 18, 2025
bc6691c
ui tweaks
MananTank Jul 18, 2025
6f59b86
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Jul 22, 2025
9842f23
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Jul 23, 2025
67bc000
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Jul 23, 2025
db4a397
update imports
MananTank Jul 23, 2025
500a68c
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Jul 23, 2025
893e05d
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Jul 23, 2025
7a1767e
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Jul 23, 2025
0d7728e
show 2 steps for airdrop
MananTank Jul 23, 2025
07a879d
update chain selectors
MananTank Jul 24, 2025
3245a3e
padding fix
MananTank Jul 24, 2025
54d4b45
UI tweaks
MananTank Jul 24, 2025
178f72d
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
jakeloo Jul 26, 2025
0ebf6cb
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
jakeloo Jul 26, 2025
b99542b
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
jakeloo Jul 26, 2025
e09257e
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
jakeloo Jul 26, 2025
5b7a730
Update tokens SDK
jakeloo Jul 26, 2025
5f8c503
Update referrer -> developer
jakeloo Aug 12, 2025
3ab336a
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Aug 12, 2025
a37c296
fix import
MananTank Aug 12, 2025
bf70bb7
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Aug 13, 2025
3048bd4
fix lint warning
MananTank Aug 13, 2025
0d961d9
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Aug 13, 2025
cad1074
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Aug 13, 2025
e93b4e9
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Aug 13, 2025
87a89ec
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Aug 14, 2025
a07fada
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Aug 14, 2025
484113b
update
MananTank Aug 14, 2025
49103bf
debug crash
MananTank Aug 14, 2025
f6ab2fd
test
MananTank Aug 14, 2025
61edd0e
fix
MananTank Aug 14, 2025
ee0dee1
Merge branch 'yash/ocr-contracts-integration' into 06-16-_tool-4689_d…
MananTank Aug 15, 2025
63940eb
Add tokendrop as fallback
MananTank Aug 15, 2025
f5c875a
reset token on chain change
MananTank Aug 15, 2025
2247a73
changes
MananTank Aug 15, 2025
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
Prev Previous commit
Next Next commit
add claim rewards page
  • Loading branch information
MananTank committed Jul 15, 2025
commit df8e5a40924e32f7cc5d1552b56ccc7e82d64ca3
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export type ContractPageMetadata = {
isAccount: boolean;
isAccountPermissionsSupported: boolean;
functionSelectors: string[];
showClaimRewards: boolean;
};

export async function getContractPageMetadata(contract: ThirdwebContract) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { ThirdwebContract } from "thirdweb";
import { getDeployedEntrypointERC20 } from "thirdweb/assets";
import { contractType as getContractType } from "thirdweb/extensions/thirdweb";
import { resolveFunctionSelectors } from "@/lib/selectors";
import { getValidReward } from "../../../../../team/[team_slug]/[project_slug]/contract/[chainIdOrSlug]/[contractAddress]/claim-rewards/utils/rewards";
import {
isERC20ClaimConditionsSupported,
isERC721ClaimConditionsSupported,
Expand Down Expand Up @@ -43,6 +45,7 @@ type ContractPageMetadata = {
isAccount: boolean;
isAccountPermissionsSupported: boolean;
functionSelectors: string[];
showClaimRewards: boolean;
};

export async function getContractPageMetadataSetup(
Expand All @@ -53,10 +56,14 @@ export async function getContractPageMetadataSetup(
functionSelectorsResult,
isInsightSupportedResult,
contractTypeResult,
claimRewardResult,
] = await Promise.allSettled([
resolveFunctionSelectors(contract),
isAnalyticsSupportedFn(contract.chain.id),
getContractType({ contract }),
isClaimRewardsSupported({
assetContract: contract,
}),
]);

const functionSelectors =
Expand All @@ -72,6 +79,11 @@ export async function getContractPageMetadataSetup(
const contractType =
contractTypeResult.status === "fulfilled" ? contractTypeResult.value : null;

const showClaimRewards =
claimRewardResult.status === "fulfilled"
? !!claimRewardResult.value
: false;

return {
embedType: getEmbedTypeToShow(functionSelectors),
functionSelectors,
Expand All @@ -93,5 +105,30 @@ export async function getContractPageMetadataSetup(
isSplitSupported: contractType === "Split",
isVoteContract: contractType === "VoteERC20",
supportedERCs: supportedERCs(functionSelectors),
showClaimRewards,
};
}

async function isClaimRewardsSupported(params: {
assetContract: ThirdwebContract;
}): Promise<boolean> {
try {
const entrypointContract = await getDeployedEntrypointERC20({
chain: params.assetContract.chain,
client: params.assetContract.client,
});

if (!entrypointContract) {
return false;
}

const reward = await getValidReward({
assetContract: params.assetContract,
entrypointContract,
});

return !!reward;
} catch {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,12 @@ export function getContractPageSidebarLinks(data: {
href: `${layoutPrefix}/permissions`,
label: "Permissions",
},
{
exactMatch: true,
hide: !data.metadata.showClaimRewards,
href: `${layoutPrefix}/claim-rewards`,
label: "Claim Rewards",
},
];

const extensionsToShow = extensionsLinks.filter((l) => !l.hide);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
"use client";

import { useQuery } from "@tanstack/react-query";
import { ArrowRightIcon } from "lucide-react";
import { readContract, type ThirdwebContract } from "thirdweb";
import { claimReward } from "thirdweb/assets";
import { useSendAndConfirmTransaction } from "thirdweb/react";
import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { parseError } from "@/utils/errorParser";
import type { getValidReward } from "../utils/rewards";

const maxInt128 = 2n ** (128n - 1n) - 1n;

export function ClaimRewardsPage(props: {
assetContractClient: ThirdwebContract;
entrypointContractClient: ThirdwebContract;
reward: NonNullable<Awaited<ReturnType<typeof getValidReward>>>;
rewardLockerContractClient: ThirdwebContract;
v3PositionManagerContractClient: ThirdwebContract;
}) {
const sendAndConfirmTransaction = useSendAndConfirmTransaction();

async function handleClaim() {
const tx = claimReward({
asset: props.assetContractClient.address,
contract: props.rewardLockerContractClient,
});

await sendAndConfirmTransaction.mutateAsync(tx);
}

const collectionQuery = useQuery({
queryKey: [
"unclaimed-fees",
{
...props,
reward: {
...props.reward,
tokenId: props.reward?.tokenId.toString(),
},
},
],
queryFn: async () => {
const result = await readContract({
contract: props.v3PositionManagerContractClient,
method:
"function collect((uint256 tokenId,address recipient,uint128 amount0Max,uint128 amount1Max)) returns (uint256,uint256)",
params: [
{
tokenId: props.reward.tokenId,
recipient: props.reward.recipient,
amount0Max: maxInt128,
amount1Max: maxInt128,
},
],
});

return result;
},
});

return (
<div>
<h2 className="font-semibold text-2xl tracking-tight mb-3">
Claim Rewards
</h2>

<div className="mb-4">
{collectionQuery.isPending && (
<div className="flex items-center gap-2">
<Spinner className="size-4" />
Loading unclaimed fees
</div>
)}
{collectionQuery.error && (
<div className="text-red-500">
Failed to load unclaimed fees {parseError(collectionQuery.error)}
</div>
)}

{collectionQuery.data && (
<div className="flex items-center gap-2">
<p> Amount0: {collectionQuery.data[0]} </p>
<p> Amount1: {collectionQuery.data[1]} </p>
</div>
)}
</div>

<Button onClick={handleClaim} className="gap-2">
Claim Rewards
{sendAndConfirmTransaction.isPending ? (
<Spinner className="size-4" />
) : (
<ArrowRightIcon className="size-4" />
)}
</Button>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { notFound, redirect } from "next/navigation";
import { getContract } from "thirdweb";
import {
getDeployedEntrypointERC20,
getRewardLocker,
v3PositionManager as getV3PositionManager,
} from "thirdweb/assets";
import { getProject } from "@/api/projects";
import { getContractPageParamsInfo } from "../../../../../../../(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/getContractFromParams";
import type { ProjectContractPageParams } from "../types";
import { ClaimRewardsPage } from "./components/claim-rewards-page";
import { getValidReward } from "./utils/rewards";

export default async function Page(props: {
params: Promise<ProjectContractPageParams>;
}) {
const params = await props.params;
const project = await getProject(params.team_slug, params.project_slug);

if (!project) {
notFound();
}

const info = await getContractPageParamsInfo({
chainIdOrSlug: params.chainIdOrSlug,
contractAddress: params.contractAddress,
teamId: project.teamId,
});

if (!info) {
notFound();
}

const assetContractClient = info.clientContract;

const entrypointContractClient = await getDeployedEntrypointERC20({
chain: assetContractClient.chain,
client: assetContractClient.client,
});

const reward = await getValidReward({
assetContract: assetContractClient,
entrypointContract: entrypointContractClient,
});

const rewardLocker = await getRewardLocker({
contract: entrypointContractClient,
}).catch(() => null);

if (!reward || !rewardLocker) {
redirect(
`/team/${params.team_slug}/${params.project_slug}/contract/${params.chainIdOrSlug}/${params.contractAddress}`,
);
}

const rewardLockerContractClient = getContract({
address: rewardLocker,
chain: assetContractClient.chain,
client: assetContractClient.client,
});

const v3PositionManager = await getV3PositionManager({
contract: rewardLockerContractClient,
}).catch(() => null);

// const v4PositionManager = await getV4PositionManager({
// contract: rewardLockerContractClient,
// }).catch(() => null);

if (!v3PositionManager || v3PositionManager !== reward.positionManager) {
redirect(
`/team/${params.team_slug}/${params.project_slug}/contract/${params.chainIdOrSlug}/${params.contractAddress}`,
);
}

const v3PositionManagerContract = getContract({
address: reward.positionManager,
chain: assetContractClient.chain,
client: assetContractClient.client,
});

return (
<ClaimRewardsPage
assetContractClient={assetContractClient}
entrypointContractClient={entrypointContractClient}
rewardLockerContractClient={rewardLockerContractClient}
reward={reward}
v3PositionManagerContractClient={v3PositionManagerContract}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { type ThirdwebContract, ZERO_ADDRESS } from "thirdweb";
import { getReward } from "thirdweb/assets";

export async function getValidReward(params: {
assetContract: ThirdwebContract;
entrypointContract: ThirdwebContract;
}) {
try {
const reward = await getReward({
contract: params.entrypointContract,
asset: params.assetContract.address,
});

if (
reward.positionManager === ZERO_ADDRESS ||
reward.recipient === ZERO_ADDRESS ||
reward.referrer === ZERO_ADDRESS ||
reward.referrerBps === 0 ||
reward.tokenId === BigInt(0)
) {
return null;
}

return reward;
} catch {
return null;
}
}
7 changes: 6 additions & 1 deletion packages/thirdweb/src/exports/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,10 @@ export type {
PoolConfig,
TokenParams,
} from "../assets/types.js";
export { getInitBytecodeWithSalt } from "../utils/any-evm/get-init-bytecode-with-salt.js";
export { getReward } from "../extensions/assets/__generated__/ERC20AssetEntrypoint/read/getReward.js";
export { getRewardLocker } from "../extensions/assets/__generated__/ERC20AssetEntrypoint/read/getRewardLocker.js";
export { claimReward } from "../extensions/assets/__generated__/ERC20AssetEntrypoint/write/claimReward.js";
export { positions } from "../extensions/assets/__generated__/RewardLocker/read/positions.js";
export { v3PositionManager } from "../extensions/assets/__generated__/RewardLocker/read/v3PositionManager.js";
export { v4PositionManager } from "../extensions/assets/__generated__/RewardLocker/read/v4PositionManager.js";
export { getInitBytecodeWithSalt } from "../utils/any-evm/get-init-bytecode-with-salt.js";
Loading