Skip to content

Commit

Permalink
current working implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
SouravInsights committed Dec 8, 2024
1 parent f67907b commit 7e7bc38
Show file tree
Hide file tree
Showing 12 changed files with 474 additions and 932 deletions.
209 changes: 36 additions & 173 deletions app/api/forms/[formId]/responses/[responseId]/claim/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,23 @@ import { db } from "@/db";
import { forms, responses } from "@/db/schema";
import { NextResponse } from "next/server";
import { eq } from "drizzle-orm";
import { createPublicClient, createWalletClient, http, parseEther } from "viem";
import { createWalletClient, http, keccak256, toBytes } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { baseSepolia } from "viem/chains";
import { FormRewardsABI } from "@/lib/contracts/abi";
import { FORM_REWARDS_ABI } from "@/lib/contracts/abi";

// Ensure private key exists
if (!process.env.PRIVATE_KEY) {
throw new Error("PRIVATE_KEY environment variable is not set");
}

const RPC_URL = process.env.BASE_SEPOLIA_URL;

// Initialize Viem clients
const publicClient = createPublicClient({
chain: baseSepolia,
transport: http(RPC_URL),
});

const account = privateKeyToAccount(
process.env.PRIVATE_KEY?.startsWith("0x")
? (process.env.PRIVATE_KEY as `0x${string}`)
: (`0x${process.env.PRIVATE_KEY}` as `0x${string}`)
);

const walletClient = createWalletClient({
account,
const client = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`),
chain: baseSepolia,
transport: http(RPC_URL),
transport: http(process.env.BASE_SEPOLIA_URL),
});

export async function POST(
req: Request,
{ params }: { params: { formId: string; responseId: string } }
) {
try {
// Get form and response
// 1. Get form and response
const [form] = await db
.select()
.from(forms)
Expand All @@ -48,170 +29,52 @@ export async function POST(
.from(responses)
.where(eq(responses.id, parseInt(params.responseId)));

if (!form || !response) {
return NextResponse.json(
{ error: "Form or response not found" },
{ status: 404 }
);
if (!form?.settings.web3?.rewards.enabled || !response?.walletAddress) {
return NextResponse.json({ error: "Invalid request" }, { status: 400 });
}

// Verify web3 settings
if (!form.settings.web3?.rewards.enabled) {
return NextResponse.json(
{ error: "Rewards not enabled for this form" },
{ status: 400 }
);
}

// Check if already claimed
if (response.rewardClaimed) {
return NextResponse.json(
{ error: "Reward already claimed" },
{ status: 400 }
);
}

if (!response.walletAddress) {
return NextResponse.json(
{ error: "No wallet address provided" },
{ status: 400 }
);
}

// Get contract details
const contractAddress = form.settings.web3.rewards.tokenAddress;
if (!contractAddress) {
return NextResponse.json(
{ error: "No reward token address configured" },
{ status: 400 }
);
}

const rewardAmount = form.settings.web3.rewards.rewardAmount;
if (!rewardAmount) {
return NextResponse.json(
{ error: "No reward amount configured" },
{ status: 400 }
);
return NextResponse.json({ error: "Already claimed" }, { status: 400 });
}

// Add server account as form operator if not already
try {
// Get the current nonce for the account
const nonce = await publicClient.getTransactionCount({
address: account.address,
});

// Prepare and sign the operator transaction
const { request } = await publicClient.simulateContract({
address: contractAddress as `0x${string}`,
abi: FormRewardsABI,
functionName: "addFormOperator",
args: [account.address],
account: account.address,
});

// Add nonce and gas parameters
const preparedRequest = {
...request,
nonce,
chainId: baseSepolia.id,
};

// Sign and send the transaction
const hash = await walletClient.writeContract(preparedRequest);
console.log("Operator transaction hash:", hash);

// Wait for confirmation
const receipt = await publicClient.waitForTransactionReceipt({ hash });
console.log("Operator transaction receipt:", receipt);

// Now set the reward limit
const limitNonce = nonce + 1;
const { request: limitRequest } = await publicClient.simulateContract({
address: contractAddress as `0x${string}`,
abi: FormRewardsABI,
functionName: "setFormRewardLimit",
args: [account.address, parseEther("1000")],
account: account.address,
});
// 2. Create a unique submission ID by hashing the concatenated values
console.log(
`formId: ${form.id}, responseId: ${response.id}, walletAddress: ${response.walletAddress}`
);

const preparedLimitRequest = {
...limitRequest,
nonce: limitNonce,
chainId: baseSepolia.id,
};
const submissionId = keccak256(
toBytes(`${form.id}-${response.id}-${response.walletAddress}`)
);

const limitHash = await walletClient.writeContract(preparedLimitRequest);
await publicClient.waitForTransactionReceipt({ hash: limitHash });
} catch (error) {
console.error("Error setting up operator:", error);
return NextResponse.json(
{
error: "Failed to set up reward operator",
details: error instanceof Error ? error.message : "Unknown error",
},
{ status: 500 }
);
}
console.log("Claiming reward with submission ID:", submissionId);

// Send reward using sendReward
try {
const { request } = await publicClient.simulateContract({
address: contractAddress as `0x${string}`,
abi: FormRewardsABI,
functionName: "sendReward",
args: [
response.walletAddress as `0x${string}`,
parseEther(rewardAmount),
],
account: account.address,
});
// 3. Send claim transaction
const hash = await client.writeContract({
address: form.settings.web3.rewards.tokenAddress as `0x${string}`,
abi: FORM_REWARDS_ABI,
functionName: "claimReward",
args: [submissionId],
});

const rewardNonce = await publicClient.getTransactionCount({
address: account.address,
});
console.log("Claim transaction hash:", hash);

const preparedRewardRequest = {
...request,
nonce: rewardNonce,
// 4. Update response
const [updatedResponse] = await db
.update(responses)
.set({
rewardClaimed: true,
transactionHash: hash,
chainId: baseSepolia.id,
};

const hash = await walletClient.writeContract(preparedRewardRequest);
console.log("Reward transaction hash:", hash);
})
.where(eq(responses.id, parseInt(params.responseId)))
.returning();

// Wait for transaction
const receipt = await publicClient.waitForTransactionReceipt({ hash });
console.log("Transaction receipt:", receipt);

// Update response with transaction details
const [updatedResponse] = await db
.update(responses)
.set({
rewardClaimed: true,
transactionHash: receipt.transactionHash,
chainId: baseSepolia.id,
})
.where(eq(responses.id, parseInt(params.responseId)))
.returning();

return NextResponse.json(updatedResponse);
} catch (error) {
console.error("Failed to send reward:", error);
return NextResponse.json(
{
error: "Failed to send reward",
details: error instanceof Error ? error.message : "Unknown error",
},
{ status: 500 }
);
}
return NextResponse.json(updatedResponse);
} catch (error) {
console.error("[CLAIM_REWARD]", error);
return NextResponse.json(
{
error: "Failed to process reward",
error: "Failed to claim reward",
details: error instanceof Error ? error.message : "Unknown error",
},
{ status: 500 }
Expand Down
29 changes: 10 additions & 19 deletions app/api/tokens/mint/route.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
import { FormRewardsABI } from "@/lib/contracts/abi";
import { FORM_REWARDS_ABI } from "@/lib/contracts/abi";
import { NextResponse } from "next/server";
import { createWalletClient, http, parseEther } from "viem";
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { baseSepolia } from "viem/chains";

const FORM_TOKEN_ADDRESS = "0x050918768C502d9D14D624FF543625df1696Df63";

if (!process.env.PRIVATE_KEY) {
throw new Error("PRIVATE_KEY environment variable is not set");
if (!process.env.PRIVATE_KEY || !process.env.FORM_REWARDS_ADDRESS) {
throw new Error("Missing environment variables");
}

const account = privateKeyToAccount(
process.env.PRIVATE_KEY.startsWith("0x")
? (process.env.PRIVATE_KEY as `0x${string}`)
: (`0x${process.env.PRIVATE_KEY}` as `0x${string}`)
);

const walletClient = createWalletClient({
account,
const client = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`),
chain: baseSepolia,
transport: http(),
});
Expand All @@ -33,12 +25,11 @@ export async function POST(req: Request) {
);
}

// Transfer tokens from owner to user
const hash = await walletClient.writeContract({
address: FORM_TOKEN_ADDRESS,
abi: FormRewardsABI,
const hash = await client.writeContract({
address: process.env.FORM_REWARDS_ADDRESS as `0x${string}`,
abi: FORM_REWARDS_ABI,
functionName: "transfer",
args: [address as `0x${string}`, parseEther(amount.toString())],
args: [address as `0x${string}`, BigInt(amount) * BigInt(1e18)],
});

return NextResponse.json({ hash });
Expand Down
Loading

0 comments on commit 7e7bc38

Please sign in to comment.