Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
8 changes: 4 additions & 4 deletions lib/price.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ export const mcv2BondAbi = [
type: "function",
name: "getRoyaltyInfo",
stateMutability: "view",
inputs: [{ name: "token", type: "address" }],
outputs: [
{ name: "royalty", type: "uint256" },
{ name: "royaltyBeneficiary", type: "address" },
inputs: [
{ name: "token", type: "address" },
{ name: "beneficiary", type: "address" },
],
outputs: [{ name: "unclaimed", type: "uint256" }],
},
{
type: "function",
Expand Down
21 changes: 11 additions & 10 deletions packages/cli/src/commands/claim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,20 @@ export function registerClaim(program: Command): void {
const tokenAddress = opts.address as Address;
const client = buildClient({ ipfs: false });

// Fetch bond data (creator = beneficiary, reserve token for display)
console.log("Checking royalties...");
const info = await client.getRoyaltyInfo(tokenAddress);
const bond = await client.publicClient.readContract({
address: MCV2_BOND_ADDRESS,
abi: mcv2BondAbi,
functionName: "tokenBond",
args: [tokenAddress],
});
const creator = (bond as readonly unknown[])[0] as Address;
const reserveToken = (bond as readonly unknown[])[4] as Address;

// Fetch reserve token address via tokenBond(), then read its decimals
let decimals = 18;
let symbol = "TOKEN";
try {
const bond = await client.publicClient.readContract({
address: MCV2_BOND_ADDRESS,
abi: mcv2BondAbi,
functionName: "tokenBond",
args: [tokenAddress],
});
const reserveToken = (bond as readonly unknown[])[4] as Address;
const [dec, sym] = await Promise.all([
client.publicClient.readContract({
address: reserveToken,
Expand All @@ -49,9 +49,10 @@ export function registerClaim(program: Command): void {
// Default to 18/TOKEN if calls fail
}

const info = await client.getRoyaltyInfo(tokenAddress, creator);
const formatted = formatUnits(info.unclaimed, decimals);
console.log(` Unclaimed: ${formatted} ${symbol}`);
console.log(` Beneficiary: ${info.beneficiary}`);
console.log(` Beneficiary: ${creator}`);

if (info.unclaimed === 0n) {
console.log("No royalties to claim.");
Expand Down
8 changes: 6 additions & 2 deletions packages/cli/src/commands/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,15 @@ export function registerStatus(program: Command): void {
// -----------------------------------------------------------------
let tokenSymbol = "TOKEN";
let tokenDecimals = 18;
let bondCreator: Address | null = null;
try {
const bond = await client.publicClient.readContract({
address: MCV2_BOND_ADDRESS,
abi: mcv2BondAbi,
functionName: "tokenBond",
args: [info.tokenAddress],
});
bondCreator = (bond as readonly unknown[])[0] as Address;
const reserveToken = (bond as readonly unknown[])[4] as Address;
const [sym, dec] = await Promise.all([
client.publicClient.readContract({
Expand Down Expand Up @@ -90,8 +92,10 @@ export function registerStatus(program: Command): void {
// -----------------------------------------------------------------
let unclaimedRoyalty: bigint | null = null;
try {
const royalty = await client.getRoyaltyInfo(info.tokenAddress);
unclaimedRoyalty = royalty.unclaimed;
if (bondCreator) {
const royalty = await client.getRoyaltyInfo(info.tokenAddress, bondCreator);
unclaimedRoyalty = royalty.unclaimed;
}
} catch {
// Token may not have a bond yet
}
Expand Down
8 changes: 4 additions & 4 deletions packages/sdk/src/abi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,11 @@ export const mcv2BondAbi = [
type: "function",
name: "getRoyaltyInfo",
stateMutability: "view",
inputs: [{ name: "token", type: "address" }],
outputs: [
{ name: "royalty", type: "uint256" },
{ name: "royaltyBeneficiary", type: "address" },
inputs: [
{ name: "token", type: "address" },
{ name: "beneficiary", type: "address" },
],
outputs: [{ name: "unclaimed", type: "uint256" }],
},
{
type: "function",
Expand Down
15 changes: 6 additions & 9 deletions packages/sdk/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ export interface SetAgentWalletResult {

export interface RoyaltyInfo {
unclaimed: bigint;
beneficiary: Address;
}

export interface TokenPriceInfo {
Expand Down Expand Up @@ -495,20 +494,18 @@ export class PlotLink {
* Get unclaimed royalty info for a storyline token.
*
* @param tokenAddress - The storyline's ERC-20 token address
* @returns Unclaimed royalty amount and beneficiary address
* @param beneficiary - The royalty beneficiary (usually the bond creator)
* @returns Unclaimed royalty amount
*/
async getRoyaltyInfo(tokenAddress: Address): Promise<RoyaltyInfo> {
const result = await this.publicClient.readContract({
async getRoyaltyInfo(tokenAddress: Address, beneficiary: Address): Promise<RoyaltyInfo> {
const unclaimed = await this.publicClient.readContract({
address: this.mcv2Bond,
abi: mcv2BondAbi,
functionName: "getRoyaltyInfo",
args: [tokenAddress],
args: [tokenAddress, beneficiary],
});

return {
unclaimed: (result as [bigint, Address])[0],
beneficiary: (result as [bigint, Address])[1],
};
return { unclaimed: unclaimed as bigint };
}

/**
Expand Down
5 changes: 3 additions & 2 deletions src/app/dashboard/writer/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export default function WriterDashboard() {

<div className="mt-8 space-y-4">
{storylines.map((s) => (
<StorylineDetail key={s.id} storyline={s} />
<StorylineDetail key={s.id} storyline={s} writerAddress={address!} />
))}
{!isLoading && !error && storylines.length === 0 && (
<p className="text-muted py-8 text-center text-sm">
Expand All @@ -80,7 +80,7 @@ export default function WriterDashboard() {
);
}

function StorylineDetail({ storyline }: { storyline: Storyline }) {
function StorylineDetail({ storyline, writerAddress }: { storyline: Storyline; writerAddress: Address }) {
return (
<div className="border-border rounded border px-4 py-4">
<div className="flex items-start justify-between gap-3">
Expand Down Expand Up @@ -139,6 +139,7 @@ function StorylineDetail({ storyline }: { storyline: Storyline }) {
<ClaimRoyalties
tokenAddress={storyline.token_address as Address}
plotCount={storyline.plot_count}
beneficiary={writerAddress}
/>
</>
)}
Expand Down
14 changes: 6 additions & 8 deletions src/components/ClaimRoyalties.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ type TxState = "idle" | "confirming" | "pending" | "done" | "error";
interface ClaimRoyaltiesProps {
tokenAddress: Address;
plotCount: number;
beneficiary: Address;
}

export function ClaimRoyalties({ tokenAddress, plotCount }: ClaimRoyaltiesProps) {
export function ClaimRoyalties({ tokenAddress, plotCount, beneficiary }: ClaimRoyaltiesProps) {
const [txState, setTxState] = useState<TxState>("idle");
const [error, setError] = useState<string | null>(null);
const [claimedAmount, setClaimedAmount] = useState<bigint>(BigInt(0));
Expand All @@ -26,18 +27,15 @@ export function ClaimRoyalties({ tokenAddress, plotCount }: ClaimRoyaltiesProps)

// Fetch unclaimed royalty balance
const { data: royaltyInfo, refetch } = useQuery({
queryKey: ["royalty-info", tokenAddress],
queryKey: ["royalty-info", tokenAddress, beneficiary],
queryFn: async () => {
const result = await publicClient.readContract({
const unclaimed = await publicClient.readContract({
address: MCV2_BOND,
abi: mcv2BondAbi,
functionName: "getRoyaltyInfo",
args: [tokenAddress],
args: [tokenAddress, beneficiary],
});
return {
unclaimed: result[0],
beneficiary: result[1] as Address,
};
return { unclaimed };
},
refetchInterval: 30000,
});
Expand Down
25 changes: 20 additions & 5 deletions src/components/WriterTradingStats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,36 @@ export function WriterTradingStats({ storyline }: WriterTradingStatsProps) {
enabled: !!tokenAddress,
});

// Fetch unclaimed royalties
const { data: royaltyData } = useQuery({
queryKey: ["writer-royalty", tokenAddress],
// Fetch bond creator (royalty beneficiary)
const { data: bondData } = useQuery({
queryKey: ["writer-bond", tokenAddress],
queryFn: async () => {
const result = await publicClient.readContract({
address: MCV2_BOND,
abi: mcv2BondAbi,
functionName: "getRoyaltyInfo",
functionName: "tokenBond",
args: [tokenAddress],
});
return { unclaimed: result[0] };
return { creator: result[0] as `0x${string}` };
},
enabled: !!tokenAddress,
});

// Fetch unclaimed royalties (requires creator address as beneficiary)
const { data: royaltyData } = useQuery({
queryKey: ["writer-royalty", tokenAddress, bondData?.creator],
queryFn: async () => {
const unclaimed = await publicClient.readContract({
address: MCV2_BOND,
abi: mcv2BondAbi,
functionName: "getRoyaltyInfo",
args: [tokenAddress, bondData!.creator],
});
return { unclaimed };
},
enabled: !!tokenAddress && !!bondData?.creator,
});

// Fetch total donations for this storyline
const { data: donationsTotal } = useQuery({
queryKey: ["writer-donations", storyline.storyline_id],
Expand Down
Loading