Skip to content
Open
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
35 changes: 35 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module.exports = {
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], // Example

plugins: ['@typescript-eslint', 'react', 'react-hooks', 'import'], // Example
parser: '@typescript-eslint/parser', // Specify ESLint parser
parserOptions: {
ecmaVersion: 2020, // Use the latest ECMAScript features
sourceType: 'module', // Allow the use of imports
ecmaFeatures: {
jsx: true, // Enable JSX
},
},
env: {
browser: true, // Enable browser global variables
es6: true, // Enable ES6 global variables
node: true, // Enable Node.js global variables
jest: true, // Enable Jest global variables
},

rules: {
// Your custom rules
'no-console': 'warn', // Warn on console.log statements
'no-unused-vars': 'warn', // Warn on unused variables
'no-undef': 'error', // Error on undefined variables
'no-extra-semi': 'error', // Error on unnecessary semicolons
'quotes': ['error', 'single'], // Enforce single quotes

},

settings: {
react: {
version: 'detect', // Automatically detect React version
},
},
};
4 changes: 3 additions & 1 deletion app/api/livepeer/livepeerActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import { Type, InputCreatorIdType } from "livepeer/models/components";
import { fullLivepeer } from "../../../lib/sdk/livepeer/fullClient";
import { WebhookContext } from "../livepeer/token-gate/route";
// import { WebhookContext } from "../livepeer/token-gate/route";
// TODO: Replace the following import path with the correct one where WebhookContext is exported
import type { WebhookContext } from "../livepeer/token-gate/lib/access-control-resolver";
Comment on lines +5 to +7
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Consider using a relative path to import WebhookContext to avoid potential issues with absolute paths in different environments. Also, it's good that you've added a TODO, but it would be even better to resolve it in this PR if possible.

Suggested change
// import { WebhookContext } from "../livepeer/token-gate/route";
// TODO: Replace the following import path with the correct one where WebhookContext is exported
import type { WebhookContext } from "../livepeer/token-gate/lib/access-control-resolver";
import type { WebhookContext } from "./token-gate/lib/access-control-resolver";


export const getLivepeerUploadUrl = async (
fileName: string,
Expand Down
149 changes: 149 additions & 0 deletions app/api/livepeer/token-gate/lib/access-control-resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// lib/access-control-resolver.ts
import { SmartAccountService } from "./smart-account-service";
import { createPublicClient, http, Address } from "viem";
import { base, baseSepolia, optimism, polygon } from "viem/chains";

// Define the ABI for the ERC1155 contract
// This is a simplified version, you may need to include more functions depending on your use case
// For example, you might want to include functions for minting, transferring, etc.
// You can also use a library like ethers.js to generate the ABI from the contract
// address and ABI
// For now, we will just include the balanceOf function
// This is a simplified version, you may need to include more functions depending on your use case

const erc1155ABI = [
{
inputs: [
{ name: "_owner", type: "address" },
{ name: "_id", type: "uint256" },
],
name: "balanceOf",
outputs: [{ name: "", type: "uint256" }],
stateMutability: "view",
type: "function",
},
];

const chainMapping: Record<number, any> = {
8453: base,
10: optimism,
137: polygon,
84532: baseSepolia
//filecoin: 3141
// flare: 3141
};

//
export interface WebhookPayload {
accessKey: string;
context: WebhookContext;
timestamp: number;
}

export interface WebhookContext {
creatorAddress: string;
tokenId: string;
contractAddress: string;
chain: number;
}

export class AccessControlResolver {
private readonly smartAccountService;

constructor() {
this.smartAccountService = new SmartAccountService();
}

async validateAccess(payload: WebhookPayload): Promise<boolean> {
const { accessKey, context } = payload;

// Validate the access key
const isValidKey = await this.validateAccessKey(accessKey, context.creatorAddress as Address, context);
if (!isValidKey) return false;

// Check if user has required token balance
const userAddress = this.smartAccountService.getAddress();
if (!userAddress) {
console.error("User address is undefined");
return false;
}
const hasTokens = await this.checkUserTokenBalances(
userAddress as Address,
context
);
if (!hasTokens) return false;

// Check if the asset is accessible
const isAccessible = await this.checkAssetAccessibility(context);
if (!isAccessible) return false;

return true;
}
// Module '"./lib/access-control-resolver"' has no exported member 'AccessKey'.Why is this?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This comment indicates a potential issue with the AccessKey export. It's important to ensure that all necessary members are correctly exported from the module. Please investigate why AccessKey is not being recognized as an exported member.


// This function is not implemented yet
// It should generate an access key for the user based on their address
// and the context provided
// You can use the smart account service to generate the access key
// and return it
// For now, we will just return a placeholder string
async AccessKey(address: Address | undefined, context: WebhookContext): Promise<string> {
if (address === undefined) {
address = this.smartAccountService.getAddress() as Address;
if (!address) {
console.error("User address is undefined");
throw new Error("User address is undefined");
}
}
throw new Error("AccessKey is not implemented");
}

async validateAccessKey(accessKey: string, address: Address, context: WebhookContext): Promise<boolean> {
// Implement access key validation logic
// Example:
// For now, return true as a placeholder
return true;

}

async checkUserTokenBalances(
address: Address,
context: WebhookContext
): Promise<boolean> {
try {
const chain = chainMapping[context.chain];

if (!chain) {
console.error("Chain not supported");
return false;
}

const publicClient = createPublicClient({
chain,
transport: http(),
});

const videoTokenBalance = (await publicClient.readContract({
address: context.contractAddress as Address,
abi: erc1155ABI,
functionName: "balanceOf",
args: [address, BigInt(context.tokenId)],
})) as bigint;

console.log({ videoTokenBalance });

return videoTokenBalance > BigInt(0);
} catch (error) {
console.error("Error checking token balances...", error);
return false;
}
}

async checkAssetAccessibility(
context: WebhookContext
): Promise<boolean> {
// Implement actual asset accessibility checking logic
// For example, check if asset is published or not restricted
return true;
}
}
15 changes: 15 additions & 0 deletions app/api/livepeer/token-gate/lib/env-validation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// lib/env-validation.ts

export function validateEnvVariables() {
const requiredVariables = [
"NEXT_PUBLIC_API_URL",
"NEXT_PUBLIC_ACCESS_KEY_SECRET",
// Add any other required environment variables here
];

for (const variable of requiredVariables) {
if (!process.env[variable]) {
throw new Error(`Missing environment variable: ${variable}`);
}
}
}
24 changes: 24 additions & 0 deletions app/api/livepeer/token-gate/lib/smart-account-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// lib/smart-account-service.ts

import { getSmartAccountClient } from "@account-kit/core";
import { config } from "@/config";

export class SmartAccountService {
private client;

constructor() {
this.client = getSmartAccountClient(
{
type: "ModularAccountV2",
accountParams: {
mode: "default",
},
},
config
);
}

getAddress(): string | undefined {
return this.client.address;
}
}
Loading